feat(billing): queue subscriptions bought while active + cancel queued
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m1s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 49s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m3s
CI/CD / CI · Admin Web (tsc) (push) Successful in 34s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 3m9s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m1s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 49s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m3s
CI/CD / CI · Admin Web (tsc) (push) Successful in 34s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 3m9s
Before, buying a plan immediately switched the tier and stacked the duration.
Now a purchase made while the café still has paid coverage is QUEUED to start
when the current coverage ends, and the owner can cancel a queued one.
Model:
- SubscriptionPayment gains EffectiveFrom/EffectiveTo; status gains Scheduled
(paid, queued) and Cancelled. EF migration AddSubscriptionScheduling (nullable).
BillingService:
- On payment completion, compute coverage end (latest of active expiry + furthest
queued period). If it is in the future → Scheduled (queued, café tier/expiry
untouched); else activate immediately as before. Periods chain correctly.
- GetStatusAsync lazily promotes any due queued period to active, and returns the
queue (QueuedPlans).
- CancelQueuedAsync cancels a Scheduled period (owner-only) and re-packs the queue
so later periods slide earlier. Active prepaid plan is never cut short; no
automatic refund (manual, per product decision).
- Confirmation SMS distinguishes "activated until X" vs "queued, starts X".
API: BillingStatusDto.QueuedPlans + DELETE /api/billing/queued/{paymentId}.
Dashboard:
- Subscription screen shows a "Queued subscriptions" card (tier, window, cancel
with confirm).
- Checkout shows "you already have an active subscription — this will start on
{date}" when the café is still covered.
- i18n fa/en/ar.
81 API tests pass; dashboard typechecks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,5 +13,13 @@ public class SubscriptionPayment : TenantEntity
|
||||
public string? RefId { get; set; }
|
||||
public SubscriptionPaymentStatus Status { get; set; } = SubscriptionPaymentStatus.Pending;
|
||||
|
||||
/// <summary>When this paid period starts. For an immediately-activated purchase this is
|
||||
/// (around) the payment time; for a queued (Scheduled) purchase it is the end of the
|
||||
/// current coverage. Null until the payment completes.</summary>
|
||||
public DateTime? EffectiveFrom { get; set; }
|
||||
|
||||
/// <summary>When this paid period ends (EffectiveFrom + Months). Null until completed.</summary>
|
||||
public DateTime? EffectiveTo { get; set; }
|
||||
|
||||
public Cafe Cafe { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,9 @@ public enum SubscriptionPaymentStatus
|
||||
{
|
||||
Pending = 0,
|
||||
Completed = 1,
|
||||
Failed = 2
|
||||
Failed = 2,
|
||||
/// <summary>Paid, but queued to start after the current coverage ends.</summary>
|
||||
Scheduled = 3,
|
||||
/// <summary>A queued (Scheduled) subscription the owner cancelled before it started.</summary>
|
||||
Cancelled = 4
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user