Shop It Docs
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

  1. Eligibility checkGET /api/mobile/care-packages/available?productId=... — returns active packages the product qualifies for, with pricing tiers.
  2. Guest quote — guest users may send items[].carePackage to POST /api/mobile/cart/guest/quote for read-only totals before login; the backend enforces product kind, package state, eligibility rules, and tier state.
  3. Login sync — after login, POST /api/mobile/cart/sync persists the same optional items[].carePackage selection while replacing the authenticated cart and re-validating eligibility.
  4. Direct authenticated add — authenticated users with an existing cart item may call POST /api/mobile/cart/items/:cartItemId/care-package with { carePackageId, pricingTierId }. Backend validates eligibility and writes cartCarePackageItems row with selected IDs and unitPrice.
  5. Cart responseGET /api/mobile/cart includes carePackageItems array alongside items; totals include care package unit prices.
  6. CheckoutPOST /api/mobile/orders/checkout. OrderService.createFromCart():
    • For each cartCarePackageItem: creates order_item with itemType = "care_package", creates care_package_subscriptions row with status = "pending_activation", carePackageSnapshot, pricingSnapshot, durationMonths, purchasedAt = now.
    • Order orderType includes care_package in type resolution.
    • Care package items contribute to finalPayable (no shipping fee for care packages).
  7. Payment — customer completes payment via gateway.
  8. Payment successorder.payment_success BullMQ job runs. OrderProcessor calls carePackageQueue.add(CarePackageJob.ACTIVATE_SUBSCRIPTION, { subscriptionId, orderId }) for each subscription.
  9. ActivationCarePackageProcessor.processActivation() sets status = "active", startDate, expiryDate, schedules reminders.
  10. Subscription live — customer can view at GET /api/mobile/me/care-packages/:id.

3. Order Type Resolution with Care Packages

  • Cart contains only care_package items → orderType = "care_package", no shipping required.
  • Cart contains physical + care_package items → orderType = "mixed", shipping required for physical lines.
  • Care package unitPrice is included in finalPayable; 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

FailureBehavior
Payment fails before confirmationSubscription 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 jobGuard: status !== "pending_activation" exits early — safe replay
Order cancelled after paymentcare_package.cancel_subscription job dispatched — sets status to cancelled