✨ Subscription required level, optimized cancellation logic
This commit is contained in:
parent
fb885e138d
commit
6449926334
@ -92,7 +92,7 @@ public abstract class SubscriptionPaymentMethod
|
|||||||
public enum SubscriptionStatus
|
public enum SubscriptionStatus
|
||||||
{
|
{
|
||||||
Unpaid,
|
Unpaid,
|
||||||
Paid,
|
Active,
|
||||||
Expired,
|
Expired,
|
||||||
Cancelled
|
Cancelled
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ public class Subscription : ModelBase
|
|||||||
if (BegunAt > now) return false;
|
if (BegunAt > now) return false;
|
||||||
if (EndedAt.HasValue && now > EndedAt.Value) return false;
|
if (EndedAt.HasValue && now > EndedAt.Value) return false;
|
||||||
if (RenewalAt.HasValue && now > RenewalAt.Value) return false;
|
if (RenewalAt.HasValue && now > RenewalAt.Value) return false;
|
||||||
if (Status != SubscriptionStatus.Paid) return false;
|
if (Status != SubscriptionStatus.Active) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class SubscriptionRenewalJob(
|
|||||||
// Find subscriptions that need renewal (due for renewal and are still active)
|
// Find subscriptions that need renewal (due for renewal and are still active)
|
||||||
var subscriptionsToRenew = await db.WalletSubscriptions
|
var subscriptionsToRenew = await db.WalletSubscriptions
|
||||||
.Where(s => s.RenewalAt.HasValue && s.RenewalAt.Value <= now) // Due for renewal
|
.Where(s => s.RenewalAt.HasValue && s.RenewalAt.Value <= now) // Due for renewal
|
||||||
.Where(s => s.Status == SubscriptionStatus.Paid) // Only paid subscriptions
|
.Where(s => s.Status == SubscriptionStatus.Active) // Only paid subscriptions
|
||||||
.Where(s => s.IsActive) // Only active subscriptions
|
.Where(s => s.IsActive) // Only active subscriptions
|
||||||
.Where(s => !s.IsFreeTrial) // Exclude free trials
|
.Where(s => !s.IsFreeTrial) // Exclude free trials
|
||||||
.OrderBy(s => s.RenewalAt) // Process oldest first
|
.OrderBy(s => s.RenewalAt) // Process oldest first
|
||||||
@ -49,6 +49,17 @@ public class SubscriptionRenewalJob(
|
|||||||
"Processing renewal for subscription {SubscriptionId} (Identifier: {Identifier}) for account {AccountId}",
|
"Processing renewal for subscription {SubscriptionId} (Identifier: {Identifier}) for account {AccountId}",
|
||||||
subscription.Id, subscription.Identifier, subscription.AccountId);
|
subscription.Id, subscription.Identifier, subscription.AccountId);
|
||||||
|
|
||||||
|
if (subscription.RenewalAt is null)
|
||||||
|
{
|
||||||
|
logger.LogWarning(
|
||||||
|
"Subscription {SubscriptionId} (Identifier: {Identifier}) has no renewal date or has been cancelled.",
|
||||||
|
subscription.Id, subscription.Identifier);
|
||||||
|
subscription.Status = SubscriptionStatus.Cancelled;
|
||||||
|
db.WalletSubscriptions.Update(subscription);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate next cycle duration based on current cycle
|
// Calculate next cycle duration based on current cycle
|
||||||
var currentCycle = subscription.EndedAt!.Value - subscription.BegunAt;
|
var currentCycle = subscription.EndedAt!.Value - subscription.BegunAt;
|
||||||
|
|
||||||
|
@ -53,6 +53,18 @@ public class SubscriptionService(
|
|||||||
if (existingSubscription is not null)
|
if (existingSubscription is not null)
|
||||||
return existingSubscription;
|
return existingSubscription;
|
||||||
|
|
||||||
|
if (subscriptionInfo.RequiredLevel > 0)
|
||||||
|
{
|
||||||
|
var profile = await db.AccountProfiles
|
||||||
|
.Where(p => p.AccountId == account.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (profile is null) throw new InvalidOperationException("Account profile was not found.");
|
||||||
|
if (profile.Level < subscriptionInfo.RequiredLevel)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Account level must be at least {subscriptionInfo.RequiredLevel} to subscribe to {identifier}."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isFreeTrial)
|
if (isFreeTrial)
|
||||||
{
|
{
|
||||||
var prevFreeTrial = await db.WalletSubscriptions
|
var prevFreeTrial = await db.WalletSubscriptions
|
||||||
@ -146,7 +158,7 @@ public class SubscriptionService(
|
|||||||
existingSubscription.PaymentDetails.OrderId = order.Id;
|
existingSubscription.PaymentDetails.OrderId = order.Id;
|
||||||
existingSubscription.EndedAt = order.BegunAt.Plus(cycleDuration);
|
existingSubscription.EndedAt = order.BegunAt.Plus(cycleDuration);
|
||||||
existingSubscription.RenewalAt = order.BegunAt.Plus(cycleDuration);
|
existingSubscription.RenewalAt = order.BegunAt.Plus(cycleDuration);
|
||||||
existingSubscription.Status = SubscriptionStatus.Paid;
|
existingSubscription.Status = SubscriptionStatus.Active;
|
||||||
|
|
||||||
db.Update(existingSubscription);
|
db.Update(existingSubscription);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
@ -159,7 +171,7 @@ public class SubscriptionService(
|
|||||||
BegunAt = order.BegunAt,
|
BegunAt = order.BegunAt,
|
||||||
EndedAt = order.BegunAt.Plus(cycleDuration),
|
EndedAt = order.BegunAt.Plus(cycleDuration),
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
Status = SubscriptionStatus.Paid,
|
Status = SubscriptionStatus.Active,
|
||||||
Identifier = subscriptionIdentifier,
|
Identifier = subscriptionIdentifier,
|
||||||
PaymentMethod = provider,
|
PaymentMethod = provider,
|
||||||
PaymentDetails = new PaymentDetails
|
PaymentDetails = new PaymentDetails
|
||||||
@ -192,10 +204,15 @@ public class SubscriptionService(
|
|||||||
var subscription = await GetSubscriptionAsync(accountId, identifier);
|
var subscription = await GetSubscriptionAsync(accountId, identifier);
|
||||||
if (subscription is null)
|
if (subscription is null)
|
||||||
throw new InvalidOperationException($"Subscription with identifier {identifier} was not found.");
|
throw new InvalidOperationException($"Subscription with identifier {identifier} was not found.");
|
||||||
if (subscription.Status == SubscriptionStatus.Cancelled)
|
if (subscription.Status != SubscriptionStatus.Active)
|
||||||
throw new InvalidOperationException("Subscription is already cancelled.");
|
throw new InvalidOperationException("Subscription is already cancelled.");
|
||||||
|
if (subscription.RenewalAt is null)
|
||||||
|
throw new InvalidOperationException("Subscription is no need to be cancelled.");
|
||||||
|
if (subscription.PaymentMethod != SubscriptionPaymentMethod.InAppWallet)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Only in-app wallet subscription can be cancelled. For other payment methods, please head to the payment provider."
|
||||||
|
);
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Cancelled;
|
|
||||||
subscription.RenewalAt = null;
|
subscription.RenewalAt = null;
|
||||||
|
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
@ -226,7 +243,7 @@ public class SubscriptionService(
|
|||||||
.OrderByDescending(s => s.BegunAt)
|
.OrderByDescending(s => s.BegunAt)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (subscription is null) throw new InvalidOperationException("No matching subscription found.");
|
if (subscription is null) throw new InvalidOperationException("No matching subscription found.");
|
||||||
|
|
||||||
var subscriptionInfo = SubscriptionTypeData.SubscriptionDict
|
var subscriptionInfo = SubscriptionTypeData.SubscriptionDict
|
||||||
.TryGetValue(subscription.Identifier, out var template)
|
.TryGetValue(subscription.Identifier, out var template)
|
||||||
? template
|
? template
|
||||||
@ -276,7 +293,7 @@ public class SubscriptionService(
|
|||||||
subscription.EndedAt = nextEndedAt;
|
subscription.EndedAt = nextEndedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Paid;
|
subscription.Status = SubscriptionStatus.Active;
|
||||||
|
|
||||||
db.Update(subscription);
|
db.Update(subscription);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
@ -306,7 +323,7 @@ public class SubscriptionService(
|
|||||||
// Find active subscriptions that have passed their end date
|
// Find active subscriptions that have passed their end date
|
||||||
var expiredSubscriptions = await db.WalletSubscriptions
|
var expiredSubscriptions = await db.WalletSubscriptions
|
||||||
.Where(s => s.IsActive)
|
.Where(s => s.IsActive)
|
||||||
.Where(s => s.Status == SubscriptionStatus.Paid)
|
.Where(s => s.Status == SubscriptionStatus.Active)
|
||||||
.Where(s => s.EndedAt.HasValue && s.EndedAt.Value < now)
|
.Where(s => s.EndedAt.HasValue && s.EndedAt.Value < now)
|
||||||
.Take(batchSize)
|
.Take(batchSize)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user