Workflowscare-package
Care Package Purchase Flow
Step-by-step flow from care package selection to active subscription.
Care Package Purchase Flow
1. Prerequisites
- Customer has a physical product selected or in their cart (
productKind = physical). - Care package is
isActive = true,isDraft = false. - Pricing tier is
isActive = true. - Eligibility rule on the care package permits the product kind.
2. Step-by-Step Flow
- Eligibility check —
GET /api/mobile/care-packages/available?productId=...— returns active packages the product qualifies for, with pricing tiers. - Guest quote — guest users may send
items[].carePackagetoPOST /api/mobile/cart/guest/quotefor read-only totals before login; the backend enforces product kind, package state, eligibility rules, and tier state. - Login sync — after login,
POST /api/mobile/cart/syncpersists the same optionalitems[].carePackageselection while replacing the authenticated cart and re-validating eligibility. - Direct authenticated add — authenticated users with an existing cart item may call
POST /api/mobile/cart/items/:cartItemId/care-packagewith{ carePackageId, pricingTierId }. Backend validates eligibility and writescartCarePackageItemsrow with selected IDs andunitPrice. - Cart response —
GET /api/mobile/cartincludescarePackageItemsarray alongsideitems; totals include care package unit prices. - Checkout —
POST /api/mobile/orders/checkout.OrderService.createFromCart():- For each
cartCarePackageItem: createsorder_itemwithitemType = "care_package", createscare_package_subscriptionsrow withstatus = "pending_activation",carePackageSnapshot,pricingSnapshot,durationMonths,purchasedAt = now. - Order
orderTypeincludescare_packagein type resolution. - Care package items contribute to
finalPayable(no shipping fee for care packages).
- For each
- Payment — customer completes payment via gateway.
- Payment success —
order.payment_successBullMQ job runs.OrderProcessorcallscarePackageQueue.add(CarePackageJob.ACTIVATE_SUBSCRIPTION, { subscriptionId, orderId })for each subscription. - Activation —
CarePackageProcessor.processActivation()setsstatus = "active",startDate,expiryDate, schedules reminders. - Subscription live — customer can view at
GET /api/mobile/me/care-packages/:id.
3. Order Type Resolution with Care Packages
- Cart contains only
care_packageitems →orderType = "care_package", no shipping required. - Cart contains
physical+care_packageitems →orderType = "mixed", shipping required for physical lines. - Care package
unitPriceis included infinalPayable; shipping fee applies only to physical items.
4. Subscription Creation Data
On checkout, for each cartCarePackageItem:
care_package_subscriptions row:
customerId = order.userId
orderId = order.id
orderItemId = newly created order_item.id
carePackageId = cartCarePackageItem.carePackageId
pricingTierId = cartCarePackageItem.pricingTierId
carePackageSnapshot = cartCarePackageItem.carePackageSnapshot (immutable)
pricingSnapshot = cartCarePackageItem.pricingSnapshot (immutable)
durationMonths = pricingSnapshot.durationMonths
status = "pending_activation"
purchasedAt = now()startDate and expiryDate are null until activation.
5. Failure Modes
| Failure | Behavior |
|---|---|
| Payment fails before confirmation | Subscription remains pending_activation; admin can manually activate or cancel |
| Activation job fails (invalid snapshot) | Processor logs warning, returns without status change; job not retried (Warn-and-return pattern prevents queue poisoning) |
| Duplicate activate job | Guard: status !== "pending_activation" exits early — safe replay |
| Order cancelled after payment | care_package.cancel_subscription job dispatched — sets status to cancelled |