Commit Graph

4 Commits

Author SHA1 Message Date
soroush.asadi bb0be19dac 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
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>
2026-06-02 16:44:32 +03:30
soroush.asadi 75d5bbc84a fix(i18n): localize API error messages by code (no more raw English)
Error toasts surfaced the raw English backend message. Added an errors namespace (fa/ar/en) keyed by error code + a useApiError() resolver that maps ApiClientError.code to the localized message (fallback to a localized generic). Wired into menu, tables, demo banner, and subscription checkout; hardened getErrorMessage so it never returns the raw backend message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:04:48 +03:30
soroush.asadi 57c83185da fix: zarinpal silent failure + show payment error in checkout
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m15s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 43s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 4m45s
Previously the subscribe mutation had no onError handler, so any
payment initiation failure (wrong merchant ID, ZarinPal API error,
disabled payment method) would silently re-enable the button with
no user feedback. Now errors are shown below the Pay button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 22:40:04 +03:30
soroush.asadi 09c55669ca Add proforma invoice step to subscription checkout
Insert a factor/invoice page between plan selection and payment showing
billing-period choice, line items, and totals before redirecting to the
gateway, moving payment-method selection to where the charge happens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 00:29:17 +03:30