Cart Checkout Payment Flow
Checkout initiation for all payment gateways, including COD pending collection flow.
Cart Checkout Payment Flow
Overview
Cart checkout is the primary payment path for active cart orders.
- Endpoint:
POST /api/orders/checkout - Mobile-composed endpoint:
POST /api/mobile/orders/checkout - Supported gateways:
stripe,esewa,fonepay,connect_ips,cod - Preferred/default gateway:
stripe
Checkout initiation is synchronous. Payment finalization is asynchronous through payment events and order workers.
Checkout Request (Stripe Default)
{
"gateway": "stripe",
"returnUrl": "https://example.com/payment/return"
}Payment-Init Response (200)
{
"orderId": 101,
"status": "payment_pending",
"paymentId": 9001,
"gatewayTransactionId": "cs_test_a1b2c3d4",
"initiationType": "redirect",
"redirectUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4",
"gatewayPayload": {
"sessionId": "cs_test_a1b2c3d4"
}
}Sequence
Queue Contract Stability
No queue name or job contract changes were introduced:
payment.completedstill drivesorder.payment_successpayment.failedstill drivesorder.payment_failed
Edge Cases
| Edge Case | Behavior |
|---|---|
| Empty cart | Rejected before payment initiation |
| Duplicate idempotency key | Existing initiation result replayed |
| Stripe webhook replay | Idempotent; terminal payments are not double-processed |
| Gateway failure/expiry | Payment marked failed; order failure flow runs |
COD Checkout (Cash on Delivery)
COD uses initiationType: "cod" — no redirect and no webhook. The order remains payment_pending, while paymentStatus becomes cod_pending after stock reservation succeeds.
This is enough for fulfillment to proceed, but not enough for warranty eligibility.
COD Request
{
"gateway": "cod",
"returnUrl": "https://example.com/orders/101/confirmation"
}COD Response (200)
{
"orderId": 101,
"status": "payment_pending",
"paymentId": 9001,
"gatewayTransactionId": "cod_9001",
"initiationType": "cod",
"redirectUrl": "https://example.com/orders/101/confirmation",
"gatewayPayload": {}
}
redirectUrlechoes the originalreturnUrl. No gateway redirect is performed. Frontend navigates toreturnUrldirectly.
The COD payment row remains pending at checkout. Admin cash collection later completes that same payment row and changes the order payment status to cod_collected. COD checkout does not emit payment.completed and does not use order.payment_success.
Warranty-related behavior follows the same state boundary: cod_pending is treated as unpaid for warranty, and cod_collected is the first COD state that allows warranty access.
COD restriction: Only available for physical orders. Digital-only or care-package-only carts return 400 ORDER_COD_PHYSICAL_ONLY.
COD-Specific Edge Cases
| Edge Case | Behavior |
|---|---|
| Digital-only cart + COD | 400 ORDER_COD_PHYSICAL_ONLY |
| Care-package-only cart + COD | 400 ORDER_COD_PHYSICAL_ONLY |
| Mixed cart (physical + digital) + COD | Allowed |
| Admin cash collection already recorded | Idempotent 200 — returns existing codCashCollectedAt |
Related Docs
apps/fumadocs/content/docs/developer/payment/browser-return-flow.mdxapps/fumadocs/content/docs/developer/payment/stripe.mdxapps/fumadocs/content/docs/developer/order/api.mdx