Order Module API & Integration Guide
Order API contracts for mobile/customer and admin surfaces with synchronous payment initiation.
Audience: Mobile/web frontend developers Scope: Checkout, payment, order endpoints
Order Module - API & Integration Guide
1. How to Read / Quick Metadata
- Module:
Order - Auth models:
- Customer/mobile routes:
JwtAuthGuard - Admin routes:
JwtAuthGuard + RoleGuard + @Permissions(...)
- Customer/mobile routes:
- Primary base URLs:
- Customer:
/api/orders - Mobile-composed customer:
/api/mobile/orders - Admin:
/api/admin/orders - Payment redirect callbacks:
/api/payments/redirect/:paymentId/success|failure
- Customer:
- Response envelope: successful endpoints return
ResponseDto<T> - Swagger tags used by controllers:
Orders (Mobile)Orders (Admin)
2. High-Level Overview
Order API is split into:
- synchronous payment-initiation endpoints (
checkout,buy-now) returning gateway payload immediately - synchronous read endpoints (list/detail/request-status)
- async command endpoints for cancellation and admin refund processing
Payment verification is completed through redirect callbacks, then finalized by queue workers.
Buy-now product behavior:
productKinddeterminesorderType:physical→physical,digital→digital,care_package→care_package.- All three product kinds are supported for buy-now.
Checkout order type:
- Order type resolves from the cart's items. Single-kind carts inherit that type; mixed-kind carts produce
orderType: "mixed". - Digital-only and care-package-only checkouts skip shipping (no
shippingDistrictrequired,shippingFee = 0).
3. Core Concepts and Terminology
- order: primary transaction entity (
ordertable) - order item: line-level monetary snapshot (
order_itemtable) - status history: append-only transition log with actor provenance (
order_status_history) - async request: command-tracking record for async mutations (
async_request) - outbox event: durable dispatch record for background jobs (
outbox_event) - payment-init response: payload containing
initiationType,redirectUrl, andgatewayPayload - final payable (physical):
totalSp - couponDiscount + shippingFee
Current async order jobs:
order.payment_successorder.payment_failedorder.payment_retryorder.cancelorder.process_refundorder.auto_cancel
Current queue payload contracts:
| Job | Payload shape |
|---|---|
order.payment_success | "" orderId, paymentReference, transactionId? "" |
order.payment_failed | "" orderId, reason? "" |
order.payment_retry | "" orderId, paymentId, attempt, maxAttempts "" |
order.cancel | "" orderId, reason?, cancelledBy, cancelledByActorType "" |
order.process_refund | "" orderId, adminId, action, amount?, remarks "" |
order.auto_cancel | "" orderId, reason "" |
Transactional notification routing events used by order flow:
ORDER_PLACED_CHECKOUT,ORDER_PLACED_BUYNOWORDER_PAYMENT_RECEIVED_CHECKOUT,ORDER_PAYMENT_RECEIVED_BUYNOWORDER_PAYMENT_FAILED,ORDER_CANCELLEDORDER_REFUND_REQUESTED,ORDER_REFUND_APPROVED,ORDER_REFUND_REJECTED
Order-to-inventory outbox events emitted by lifecycle stage:
stock.reserveafter checkout/buy-now order creationstock.finalizeafter verified online payment success, or after COD reservation succeeds and the order enterspaymentStatus = "cod_pending"stock.releaseafter payment failure, cancellation, or expiry
Outbox/queue lifecycle for inventory coupling:
- Order service writes
outbox_eventswithtargetQueuefor inventory processing. - Outbox dispatcher claims pending inventory-target rows only from its owned queue set.
- Dispatcher enqueues reserve/finalize/release inventory jobs.
- Inventory processors mutate stock state and acknowledge job completion.
Delivery channels for transactional events:
- push (
mobile_push, in-app targets) - email (template-based renderer output)
4. Route Summary
4.1 Customer / Mobile
| Method | Path |
|---|---|
POST | /api/orders/checkout |
POST | /api/orders/buy-now |
GET | /api/orders |
GET | /api/orders/:id |
GET | /api/orders/requests/:requestId |
POST | /api/orders/:id/cancel |
POST | /api/orders/:id/request-refund |
Mobile-composed equivalents are available under /api/mobile/orders/....
GET /api/orders/requests/:requestId is for async operations (for example cancel/refund processing), not for checkout/buy-now initiation.
4.2 Admin
| Method | Path | Permission |
|---|---|---|
GET | /api/admin/orders | Orders_READ |
GET | /api/admin/orders/:id | Orders_READ |
GET | /api/admin/orders/analytics | Orders_READ |
GET | /api/admin/orders/analytics/districts | Orders_READ |
GET | /api/admin/orders/:id/invoice-download | Orders_READ |
GET | /api/admin/orders/requests/:requestId | Orders_READ |
POST | /api/admin/orders/:id/cancel | Orders_UPDATE |
POST | /api/admin/orders/:id/process-refund | Orders_UPDATE |
POST | /api/admin/orders/:id/notes | Orders_UPDATE |
POST | /api/admin/orders/:id/activate-care-package | CarePackageSubscriptions_UPDATE |
PATCH | /api/admin/orders/:id/items/:orderItemId/serial | Orders_UPDATE |
4.3 Payment Redirect and Result
| Method | Path | Owner |
|---|---|---|
GET | /api/payments/redirect/:paymentId/success | PaymentRedirectController |
POST | /api/payments/redirect/:paymentId/success | PaymentRedirectController |
GET | /api/payments/redirect/:paymentId/failure | PaymentRedirectController |
POST | /api/payments/redirect/:paymentId/failure | PaymentRedirectController |
GET | /api/payments/result | PaymentResultController |
Route Details
Checkout
| Aspect | Details |
|---|---|
| Endpoint | POST /api/orders/checkout or /api/mobile/orders/checkout |
| Auth | JwtAuthGuard |
| Request | gateway: `"stripe" |
| Response | ResponseDto<PaymentInitResponseDto> |
| Errors | 400 invalid cart, 400 payment initiation/validation failure |
Validation rule:
- Checkout and buy-now require shipping district to be selected on the active cart (
ORDER_SHIPPING_DISTRICT_REQUIRED). - Order creation sets
orderStatus=payment_pendingand does not clear cart immediately. - Cart clear is executed only after verified payment success (
order.payment_success-> cart clear worker). - COD is the exception to online payment finalization: after reservation succeeds, checkout sets
paymentStatus=cod_pending, keepsorderStatus=payment_pending, clears the cart through the confirmed-order cart clear job, and does not emitorder.payment_success.
Get Orders
| Aspect | Details |
|---|---|
| Endpoint | GET /api/orders or /api/mobile/orders |
| Auth | JwtAuthGuard |
| Request | pagination, status filter |
| Response | ResponseDto<OrderDto[]> |
| Errors | - |
5. Query Parameters
5.1 Customer list (GET /api/orders)
| Param | Type | Default |
|---|---|---|
page | number | 1 |
size | number | 20 |
pagination | boolean | true |
status | enum(order_status) | optional |
sort | createdAt | updatedAt | createdAt |
order | asc | desc | desc |
5.2 Admin list (GET /api/admin/orders)
| Param | Type | Default |
|---|---|---|
page | number | 1 |
size | number | 20 |
pagination | boolean | true |
status | enum(order_status) | optional |
paymentMethod | enum(order_payment_method) | optional |
paymentStatus | pending | completed | failed | cod_pending | cod_collected | optional |
orderType | enum(order_type) | optional |
search | string | optional |
sort | createdAt | updatedAt | createdAt |
order | asc | desc | desc |
6. Response Shape Examples
6.1 Checkout request
"
"gateway": "stripe",
"returnUrl": "https://example.com/payment/return"
"6.2 Buy-now request
"
"productId": 101,
"gateway": "stripe",
"returnUrl": "https://example.com/payment/return",
"promoCode": "SUMMER2026"
"6.3 Payment-init response (200 OK)
"
"orderId": 101,
"status": "payment_pending",
"paymentId": 9001,
"gatewayTransactionId": "cs_test_a1b2c3d4e5",
"initiationType": "redirect",
"redirectUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5",
"gatewayPayload": "
"sessionId": "cs_test_a1b2c3d4e5"
""
"6.4 Async request status response
"
"requestId": "cancel_9f67c1bc67694347b6509e1ee1277f49",
"status": "completed",
"result": "
"orderId": 101,
"orderStatus": "cancelled",
"paymentStatus": "failed",
"correlationId": "order_cancel_user_abc_79f9c2fa11a7"
"",
"createdAt": "2026-03-10T10:00:00.000Z",
"completedAt": "2026-03-10T10:00:05.000Z"
"6.5 Admin status history item (actor provenance)
"
"status": "cancelled",
"notes": "Cancelled by support",
"actorType": "admin",
"createdByCustomerId": null,
"createdByAdminId": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"createdBy": "019d2c3e-3ca0-76ae-83ba-53e81e74eed1",
"createdAt": "2026-03-30T12:00:00.000Z"
"6.6 Order response (with shipping)
"
"id": 101,
"orderNumber": "ORD-20260417-001",
"orderType": "physical",
"orderStatus": "payment_pending",
"paymentStatus": "pending",
"totalSp": 12000,
"couponDiscount": 1000,
"shippingDistrict": "Kathmandu",
"shippingMobileNum": "+977-9801234567",
"shippingFee": 500,
"shippingAddressLine1": "Boudha Stupa Road",
"shippingAddressLine2": "House 12, Floor 2",
"shippingCity": "Kathmandu",
"shippingState": "Bagmati",
"shippingPostalCode": "44600",
"shippingLandmark": "Near main gate",
"shippingLatitude": 27.7172,
"shippingLongitude": 85.324,
"receiver": "Kunga Lama",
"contactNumber": "+61 7 4720 9931",
"fulfillmentStatus": "in_transit",
"fulfillmentTimeline": [
{ "status": "received", "label": "Received", "completed": true, "current": false },
{ "status": "ready", "label": "Ready", "completed": true, "current": false },
{ "status": "picked_up", "label": "Picked Up", "completed": true, "current": false },
{ "status": "in_transit", "label": "In Transit", "completed": true, "current": true },
{ "status": "delivered", "label": "Delivered", "completed": false, "current": false }
],
"fulfillmentTimeline (care_package order)": [
{ "status": "received", "label": "Received", "completed": true, "current": false },
{ "status": "activated", "label": "Activated", "completed": false, "current": false }
],
"fulfillmentTimeline (digital order)": [
{ "status": "received", "label": "Received", "completed": true, "current": false },
{ "status": "key_sent", "label": "Key Sent", "completed": false, "current": false }
],
"orderThumbnails": [
"https://cdn.example.com/products/101-thumb.jpg",
"https://cdn.example.com/products/102-thumb.jpg"
],
"items": [
{
"id": 1,
"productId": 101,
"quantity": 1,
"unitMrp": 12000,
"unitSp": 11500,
"discount": 500,
"finalValue": 11500,
"thumbnailUrl": "https://cdn.example.com/products/101-thumb.jpg",
"productTitle": "Green Tara Thangka",
"productSlug": "green-tara-thangka",
"serialNumber": "SN-2024-ABC123",
"subscriptionInfo": null
}
],
"finalPayable": 11500
"For physical orders, finalPayable includes shipping fee.
7. Enums
7.1 order_type
digitalphysicalcare_packagemixed
7.2 order_payment_method
onlinecodpos
7.3 order_status
createdpayment_pendingpaidpayment_failedcancelledrefund_requestedrefund_pendingrefund_approvedrefund_rejected
Current manual refund flow does not transition through refund_pending; approvals/rejections are applied directly by admin action.
7.4 order_status_actor_type
customeradminsystem
7.5 order_source
checkoutbuy_nowtraining_enrolladmin_manual
7.6 refund_status
requestedpendingapprovedrejected
Current manual refund flow uses requested -> approved|rejected; pending remains a legacy enum value.
7.7 order_fulfillment_status
received— default initial stateready— physical/mixed onlypicked_up— physical/mixed onlyin_transit— physical/mixed onlydelivered— physical/mixed terminalkey_sent— digital terminalactivated— care_package terminal
8. Integration Diagram
9. Caching
Cache keyspaces:
order:customer:list:order:detail:order:admin:list:order:admin:detail:order:(detail-specific state key)
TTL envs:
ORDER_HISTORY_CACHE_TTL_SECONDS(default120)ORDER_ADMIN_CACHE_TTL_SECONDS(default60)
Invalidation triggers:
- customer/admin mutation services invalidate related prefixes after accepted commands
10. Endpoint Reference + Payload Cheatsheet
10.1 Payload Cheatsheet Table (Every Endpoint)
| Method | Path | Auth / Permission | Request DTO / Params | Success DTO | Notes |
|---|---|---|---|---|---|
| POST | /api/orders/checkout | User JWT | CheckoutDto "" gateway: "stripe"|"esewa"|"fonepay"|"connect_ips"|"cod", returnUrl: string "" | PaymentInitResponseDto | Creates order from active cart, initiates payment; idempotency via header |
| GET | /api/orders | User JWT | QueryOrdersDto "" page?, size?, pagination?, status?, fulfillmentStatus?, sort?, order? "" | OrderListItemResponseDto[] paginated | Returns current user's order history; filters by order status and fulfillment status; includes orderThumbnails (max 2) |
| GET | /api/orders/:id | User JWT | path: id (int) | OrderDetailResponseDto | Fetches single order detail; validates ownership |
| GET | /api/orders/requests/:requestId | User JWT | path: requestId (string) | AsyncRequestResponseDto | Polls async operation status (cancel/refund processing) |
| POST | /api/orders/:id/cancel | User JWT | path: id (int), body: CancelOrderDto "" reason?: string "" | AsyncRequestResponseDto | Async cancellation; returns requestId for polling |
| POST | /api/orders/:id/request-refund | User JWT | path: id (int), body: RequestRefundDto "" reason: string, remarks?: string "" | OrderResponseDto | Synchronous refund request; checks order eligibility |
| GET | /api/admin/orders/analytics | Admin + Orders_READ | QueryOrderAnalyticsAdminDto "" range?, bucket?, fromDate?, toDate?, district? "" | OrderAdminAnalyticsDto | Order analytics summary with optional district filter |
| GET | /api/admin/orders/analytics/districts | Admin + Orders_READ | QueryOrderAnalyticsAdminDto "" range?, bucket?, fromDate?, toDate?, district? "" | OrderDistrictAggregationDto[] | Full district aggregation for map/heatmap; optional district filter |
| GET | /api/admin/orders | Admin + Orders_READ | QueryOrdersAdminDto "" page?, size?, pagination?, status?, paymentMethod?, paymentStatus?, orderType?, search?, sort?, order? "" | OrderAdminListItemDto[] paginated | Admin order list with filters; search by order number/customer |
| GET | /api/admin/orders/:id | Admin + Orders_READ | path: id (int) | OrderAdminDetailDto | Admin order detail with status history and items |
| GET | /api/admin/orders/:id/invoice-download | Admin + Orders_READ | path: id (int) | application/pdf | Admin downloads fresh invoice PDF for any order |
| GET | /api/admin/orders/requests/:requestId | Admin + Orders_READ | path: requestId (string) | AsyncRequestResponseDto | Admin polls async operation status |
| POST | /api/admin/orders/:id/cancel | Admin + Orders_UPDATE | path: id (int), body: CancelOrderAdminDto "" reason?: string "" | AsyncRequestResponseDto | Admin-triggered order cancellation |
| POST | /api/admin/orders/:id/process-refund | Admin + Orders_UPDATE | path: id (int), body: ProcessRefundDto "" action: "approve"|"reject", remarks: string "" | AsyncRequestResponseDto | Admin approves/rejects refund request |
| POST | /api/admin/orders/:id/notes | Admin + Orders_UPDATE | path: id (int), body: AddNoteDto "" note: string "" | OrderAdminNoteResponseDto | Adds internal admin note to order |
| POST | /api/admin/orders/:id/send-digital-delivery | Admin + DigitalKeys_SEND_DELIVERY | path: id (int) | SendDigitalDeliveryResponseDto | Assigns digital keys and emails delivery to customer |
| POST | /api/admin/orders/:id/activate-care-package | Admin + CarePackageSubscriptions_UPDATE | path: id (int) | ActivateCarePackageResponseDto | Manually activates pending care package subscriptions on the order |
| PATCH | /api/admin/orders/:id/items/:orderItemId/serial | Admin + Orders_UPDATE | path: orderId (int), orderItemId (int); body: AssignSerialNumberDto { serialNumber: string } | AssignSerialNumberResponseDto | Assigns a serial number to a physical order item |
| PUT | /api/admin/orders/:id/cod-collect | Admin + Orders_UPDATE | path: id (int); body: CollectCodDto { remarks: string } | CollectCodResponseDto | Records physical COD cash collection. Idempotent: returns existing data if already collected. |
| GET | /api/admin/orders/:id/digital-keys | Admin + Orders_READ | path: id (int) | OrderDigitalKeysResponseDto | Returns digital keys assigned to order items |
| GET | /api/payments/redirect/:paymentId/success | Public | path: paymentId (int), query: gateway response params | HTTP 302 redirect | Processes payment success callback; queues verification job |
| POST | /api/payments/redirect/:paymentId/success | Public | path: paymentId (int), body: gateway response params | HTTP 302 redirect | Handles form-post payment success callback |
| GET | /api/payments/redirect/:paymentId/failure | Public | path: paymentId (int), query: gateway response params | HTTP 302 redirect | Processes payment failure callback |
| POST | /api/payments/redirect/:paymentId/failure | Public | path: paymentId (int), body: gateway response params | HTTP 302 redirect | Handles form-post payment failure callback |
| GET | /api/payments/result | Public | query: payment_status, payment_id, reference_type, reference_id, next | HTML page | Renders payment result page with redirect to frontend |
10.1.1 Customer List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/orders | (no params) | Default: page=1, size=20, sort=createdAt, order=desc |
| GET | /api/orders | ?page=2&size=10 | Custom page and size |
| GET | /api/orders | ?status=paid | Filter by order status |
| GET | /api/orders | ?fulfillmentStatus=delivered | Filter by fulfillment status |
| GET | /api/orders | ?status=paid&fulfillmentStatus=in_transit | Filter by order + fulfillment status |
| GET | /api/orders | ?status=payment_pending&sort=updatedAt&order=asc | Filter + sort combo |
| GET | /api/orders | ?pagination=false | Returns all records (no pagination) |
10.1.2 Admin List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/admin/orders | (no params) | Default: page=1, size=20, sort=createdAt, order=desc |
| GET | /api/admin/orders | ?page=3&size=50 | Custom pagination |
| GET | /api/admin/orders | ?status=paid&paymentStatus=completed | Filter by order + payment status |
| GET | /api/admin/orders | ?paymentMethod=cod&paymentStatus=cod_pending | Filter uncollected COD orders |
| GET | /api/admin/orders | ?paymentMethod=online&orderType=physical | Filter by payment method + order type |
| GET | /api/admin/orders | ?search=ORD123 | Search by order number, customer name/email/phone |
| GET | /api/admin/orders | ?status=cancelled&sort=updatedAt&order=desc | Filter + custom sort |
| GET | /api/admin/orders | ?paymentStatus=failed | Filter by failed payment |
| GET | /api/admin/orders | ?refundStatus=requested | Filter by refund status (admin view) |
10.1.3 Checkout/Buy-now Request Variations
| Method | Path | Request Body | Notes |
|---|---|---|---|
| POST | /api/orders/checkout | "" "gateway": "stripe", "returnUrl": "https://example.com/return" "" | Full cart checkout (preferred) |
| POST | /api/orders/checkout | "" "gateway": "esewa", "returnUrl": "https://example.com/return" "" | Legacy cart checkout |
| POST | /api/orders/checkout | "" "gateway": "cod", "returnUrl": "https://example.com/return" "" | COD checkout for physical or mixed carts; returns status=payment_pending |
| POST | /api/orders/buy-now | "" "productId": 101, "gateway": "stripe", "returnUrl": "https://example.com/return" "" | Single product buy-now (preferred) |
| POST | /api/orders/buy-now | "" "productId": 101, "gateway": "fonepay", "returnUrl": "https://example.com/return", "promoCode": "SUMMER2026" "" | Legacy gateway with promo code |
10.1.4 Payment Init Response Variations
| Field | Value | Notes |
|---|---|---|
| initiationType | form_post | Form submission required |
| initiationType | redirect | URL redirect with query params |
| initiationType | cod | No gateway redirect; frontend navigates to returned redirectUrl |
| gateway | stripe | Preferred gateway |
| gateway | esewa / fonepay / connect_ips | Legacy gateways still supported |
| gateway | cod | Cash on delivery; payment row remains pending until admin collection |
| paymentId | 9001 | Internal payment reference |
| gatewayTransactionId | cs_test_a1b2c3d4e5 | Gateway-specific transaction ID |
| orderStatus | payment_pending | Initial order status after creation |
| paymentStatus | pending | Payment awaiting completion |
| paymentStatus | cod_pending | COD order confirmed for fulfillment, cash not collected |
| paymentStatus | cod_collected | Admin recorded COD cash and completed the payment row |
10.1.5 Async Request Status Response Variations
| Field | Value | Notes |
|---|---|---|
| status | pending | Operation in progress |
| status | completed | Operation finished successfully |
| status | failed | Operation failed |
| result.orderStatus | cancelled | Order cancelled |
| result.paymentStatus | failed | Payment marked failed |
10.1.6 Order Status Flow States
| Status | Payment Status | Next Possible States | Notes |
|---|---|---|---|
created | pending | payment_pending | Initial state |
payment_pending | pending | paid, payment_failed, cancelled | Awaiting online payment |
payment_pending | cod_pending | cancelled; payment status later changes to cod_collected | COD confirmed for fulfillment, cash uncollected; warranty remains blocked |
payment_pending | cod_collected | cancelled | COD cash collected; order status remains unchanged by collection; warranty becomes eligible |
paid | completed | refund_requested, cancelled | Payment successful |
payment_failed | failed | cancelled | Payment not received; warranty blocked |
cancelled | failed | - (terminal) | Order cancelled; warranty blocked |
refund_requested | completed | refund_approved, refund_rejected | Customer requested refund; warranty still allowed until approval |
refund_approved | completed | - (terminal) | Refund approved; warranty blocked |
refund_rejected | completed | - (terminal) | Refund rejected; warranty remains allowed |
10.1.7 Admin Action Variations
| Method | Path | Body | Notes |
|---|---|---|---|
| POST | /api/admin/orders/101/cancel | "" "reason": "Customer request" "" | Cancel with reason |
| POST | /api/admin/orders/101/cancel | "" | Cancel without reason |
| POST | /api/admin/orders/101/process-refund | "" "action": "approve", "remarks": "Verified with payment team" "" | Approve with remarks |
| POST | /api/admin/orders/101/process-refund | "" "action": "reject", "remarks": "Duplicate charge not confirmed" "" | Reject refund |
| POST | /api/admin/orders/101/notes | "" "note": "Customer called, order will ship tomorrow" "" | Add note |
| POST | /api/admin/orders/101/activate-care-package | (no body) | Activate all pending subscriptions on care package order |
| PATCH | /api/admin/orders/101/items/5/serial | { "serialNumber": "SN-2024-ABC123" } | Assign serial number to item |
| PUT | /api/admin/orders/101/cod-collect | { "remarks": "Collected at door by rider #12, cash NPR 3500" } | Record COD cash collection |
10.1.8 Error Response Variations
| HTTP | errorCode | Message | Scenario |
|---|---|---|---|
| 400 | ORDER_CART_EMPTY | Cart has no items. | Checkout with empty cart |
| 400 | ORDER_PRODUCT_NOT_AVAILABLE | Product is not available for purchase. | Buy-now unavailable product |
| 400 | ORDER_SHIPPING_DISTRICT_REQUIRED | Shipping district is required before checkout or buy-now. | Checkout/buy-now without selected shipping district |
| 400 | ORDER_COD_PHYSICAL_ONLY | COD checkout attempted on digital-only or care-package-only cart/product | COD checkout on non-physical cart |
| 400 | ORDER_NOT_COD_METHOD | Admin COD cash collection attempted on a non-COD order | Admin collect on non-COD order |
| 400 | ORDER_PAYMENT_METHOD_NOT_SUPPORTED | Only online payment is supported. | Invalid payment method |
| 400 | ORDER_CANCEL_NOT_ALLOWED | Order cannot be cancelled in current status. | Cancel non-cancellable order |
| 400 | ORDER_DIGITAL_NOT_REFUNDABLE | Digital product orders are non-refundable. | Request refund on digital-only order |
| 400 | ORDER_NOT_PENDING_REFUND | Order is not pending refund processing. | Process refund on non-pending order |
| 404 | ORDER_NOT_FOUND | Order not found. | Invalid order ID |
| 404 | ORDER_ACTIVE_CART_NOT_FOUND | Active cart not found. | Checkout without cart |
| 404 | ORDER_ASYNC_REQUEST_NOT_FOUND | Async request not found. | Invalid request ID |
| 409 | ORDER_IDEMPOTENCY_MISMATCH | Request with this idempotency key is already in progress. | Duplicate request |
| 409 | ORDER_PAYMENT_FAILED | Previous request with this idempotency key has failed. | Retry failed checkout |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. | Rate limit hit |
| 503 | ORDER_ASYNC_REQUEST_CREATION_FAILED | Failed to create async order request. | Queue failure |
| 400 | ORDER_FULFILLMENT_NOT_PHYSICAL_TYPE | Order type does not support fulfillment advance. | Advance physical fulfillment on digital order |
| 400 | ORDER_ACTIVATE_CARE_PACKAGE_NOT_APPLICABLE | Order is not a care package type. | Activate care package on physical order |
| 400 | ORDER_NOT_PAID | Order must be in paid status. | Serial assignment on unpaid order |
| 409 | ORDER_SERIAL_ALREADY_ASSIGNED | Serial number already assigned to this item. | Duplicate serial assign |
| 409 | ORDER_SERIAL_DUPLICATE | Serial number already in use. | Same serial on different item |
| 422 | ORDER_BUYNOW_CARE_PACKAGE_NOT_ALLOWED | Care package products do not support buy-now. | Buy-now with care_package productKind |
| 422 | ORDER_SERIAL_ITEM_NOT_PHYSICAL | Serial number can only be assigned to physical items. | Serial on digital item |
| 422 | ORDER_SERIAL_AFTER_DELIVERED | Cannot assign serial after order is delivered. | Serial on delivered order |
10.1.9 Cache Key Patterns
| Cache Key | Pattern | TTL (seconds) | Invalidation |
|---|---|---|---|
| Customer order list | order:customer:list:pagination:"page"-size:"size"-sort:"sort"-order:"order"-status:"status"-fulfillmentStatus:"fulfillmentStatus" | 120 | Order mutation (cancel, refund) |
| Customer order detail | order:detail:"orderId" | 120 | Order status change |
| Admin order list | order:admin:list:pagination:"page"-size:"size"-sort:"sort"-order:"order"-filters... | 60 | Admin order mutations |
| Admin order detail | order:admin:detail:"orderId" | 60 | Admin status changes |
| Admin analytics | order:admin:analytics:admin:"adminId"-range:"range"-bucket:"bucket"-from:"from"-to:"to"-district:"district" | 60 (env ORDER_ADMIN_CACHE_TTL_SECONDS) | Order mutation |
| Admin district aggregation | order:admin:analytics:districts:admin:"adminId"-range:"range"-from:"from"-to:"to"-district:"district" | 60 (env ORDER_ADMIN_CACHE_TTL_SECONDS) | Order mutation |
10.1.10 Rate Limit Patterns
| Route Family | Key Pattern | Limit |
|---|---|---|
| Customer checkout | rl:orders:customer:action:checkout-key:"userId" | 10 req/min |
| Customer buy-now | rl:orders:customer:action:buynow-key:"userId" | 10 req/min |
| Customer list | rl:orders:customer:action:list-key:"userId" | 60 req/min |
| Customer cancel | rl:orders:customer:action:cancel-key:"userId" | 5 req/min |
| Admin orders | rl:orders:admin:action:list-key:"adminId" | 60 req/min |
| Admin mutations | rl:orders:admin:action:mutate-key:"adminId" | 20 req/min |
10.1.11 Digital Delivery Endpoints
POST /api/admin/orders/:id/send-digital-delivery
| Property | Value |
|---|---|
| Auth | JWT + DigitalKeys_SEND_DELIVERY permission |
| Path param | id — order ID (integer) |
Success response (HTTP 200):
{
"orderId": 101,
"deliveredItems": [
{
"orderItemId": 1,
"productTitle": "Microsoft Office 365 Home — Annual",
"keyValue": "N7TV2-2WY7D-MXV8T-P3D9R-6K4FB",
"licenseType": "annual",
"duration": 12,
"durationUnit": "months"
}
],
"emailSentTo": "customer@example.com",
"fulfillmentStatus": "key_sent"
}| Error | HTTP | Condition |
|---|---|---|
ORDER_NOT_PAID | 400 | Order is not in paid status |
ORDER_NOT_DIGITAL | 400 | Order has no digital items |
ORDER_DIGITAL_ALREADY_DELIVERED | 409 | Digital keys already sent for this order |
DIGITAL_KEY_NOT_FOUND | 422 | Insufficient available keys in pool for one or more products |
GET /api/admin/orders/:id/digital-keys
| Property | Value |
|---|---|
| Auth | JWT + Orders_READ permission |
| Path param | id — order ID (integer) |
Success response (HTTP 200):
{
"orderId": 101,
"items": [
{
"orderItemId": 1,
"productId": 42,
"productTitle": "Microsoft Office 365 Home — Annual",
"keyValue": "N7TV2-2WY7D-MXV8T-P3D9R-6K4FB",
"status": "assigned",
"assignedAt": "2026-06-11T12:00:00.000Z"
}
]
}10.1.12 Job/Queue Payloads
| Job Name | Payload | Trigger |
|---|---|---|
order.payment_success | "" orderId: number, paymentReference: string, transactionId?: string "" | Payment gateway success callback |
order.payment_failed | "" orderId: number, reason?: string "" | Payment gateway failure callback |
order.payment_retry | "" orderId: number, paymentId: number, attempt: number, maxAttempts: number "" | Retry logic for online payments; skips COD cod_pending orders |
order.cancel | "" orderId: number, reason?: string, cancelledBy: string, cancelledByActorType: "customer"|"admin" "" | Cancel endpoint |
order.process_refund | "" orderId: number, adminId: string, action: "approve"|"reject", amount?: number, remarks: string "" | Admin refund processing |
order.auto_cancel | "" orderId: number, reason: string "" | Scheduled auto-cancel for unpaid online orders; skips COD |
10.1.12 Transactional Notification Events
| Event | Payload Highlights | Channels |
|---|---|---|
ORDER_PLACED_CHECKOUT | orderId, orderNumber, total, items | Push, Email |
ORDER_PLACED_BUYNOW | orderId, orderNumber, product, price | Push, Email |
ORDER_PAYMENT_RECEIVED_CHECKOUT | orderId, orderNumber, paymentReference | Push, Email |
ORDER_PAYMENT_RECEIVED_BUYNOW | orderId, orderNumber, paymentReference | Push, Email |
ORDER_PAYMENT_FAILED | orderId, orderNumber, reason | Push, Email |
ORDER_CANCELLED | orderId, orderNumber, cancelledBy, reason | Push, Email |
ORDER_REFUND_REQUESTED | orderId, orderNumber, reason, amount | Push, Email (admin) |
ORDER_REFUND_APPROVED | orderId, orderNumber, amount | Push, Email |
ORDER_REFUND_REJECTED | orderId, orderNumber, reason | Push, Email |
10.1.13 Inventory Outbox Events (Post-Payment Lifecycle)
| Event | Payload | Conditions |
|---|---|---|
stock.reserve | orderId, items[] | Order created from checkout/buy-now |
stock.finalize | orderId, items[] | Online payment verified as completed, or COD reservation confirmed with paymentStatus=cod_pending |
stock.release | orderId, items[] | Payment failed, timed out, or cancelled before finalize |
stock.adjust | orderId, items[] | Cancellation after finalized stock deduction (restock compensation) |
10.1.14 DTO Field Reference
CheckoutDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| gateway | enum | Yes | "stripe"|"esewa"|"fonepay"|"connect_ips"|"cod" | Payment gateway selection |
| returnUrl | string | Yes | Valid URL | Frontend return destination |
BuyNowCheckoutDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| productId | number | Yes | Positive integer | Product to purchase |
| gateway | enum | Yes | "stripe"|"esewa"|"fonepay"|"connect_ips"|"cod" | Payment gateway |
| returnUrl | string | Yes | Valid URL | Frontend return destination |
| promoCode | string | No | 3-50 chars | Optional promo code |
QueryOrderAnalyticsAdminDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
range | enum | No | "daily" | "weekly" | "monthly" | "yearly" | "custom" | Preset range. Use fromDate/toDate for custom range. Default: "monthly" |
bucket | enum | No | "day" | "week" | "month" | Trend bucket granularity for chart data |
fromDate | string | No | ISO date (YYYY-MM-DD) | Start date. Required when range=custom |
toDate | string | No | ISO date (YYYY-MM-DD) | End date. Required when range=custom |
district | string | No | Non-empty string | Filter analytics by shipping district. Omitting returns all districts |
CancelOrderDto / CancelOrderAdminDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| reason | string | No | Max 500 chars | Cancellation reason |
RequestRefundDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| reason | string | Yes | 1-500 chars | Refund reason |
| remarks | string | No | Max 1000 chars | Additional details |
ProcessRefundDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| action | enum | Yes | "approve"|"reject" | Refund decision |
| remarks | string | Yes | 1-500 chars | Admin remarks |
AddNoteDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| note | string | Yes | 1-1000 chars | Admin note content |
OrderItemResponseDto (customer)
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
serialNumber | string | null | No | — | Assigned serial number (physical items only) |
subscriptionInfo | object | null | No | — | Care package subscription info (care_package items only); shape: { status, startDate, expiryDate, durationMonths, carePackageName } |
OrderAdminItemResponseDto (admin)
| Field | Type | Notes |
|---|---|---|
id | number | Order item ID |
productId | number | null | Product ID |
quantity | number | — |
unitMrp | number | — |
unitSp | number | — |
discount | number | — |
finalValue | number | — |
productKind | string | null | physical, digital, care_package |
serialNumber | string | null | Admin-assigned serial (physical only) |
subscriptionDetail | object | null | { status, startDate, expiryDate, durationMonths, carePackageName } — care_package items only |
digitalKeyValue | string | null | Assigned key value — admin view only, NOT exposed to customer |
AssignSerialNumberDto
| Field | Type | Required | Validation |
|---|---|---|---|
serialNumber | string | Yes | Non-empty, max 100 chars |
OrderAdminDetailDto (admin detail) — updated fields
fulfillmentStatus: string— current fulfillment statuseligibleFulfillmentActions: string[]— available actions based onorderType+fulfillmentStatusitems: OrderAdminItemResponseDto[]— replaces genericOrderItemResponseDto[]
11. Testing Scenarios
11.1 Customer Checkout Flow
Test Case: Successful checkout via stripe
- User has items in active cart
- POST
/api/orders/checkoutwith"" "gateway": "stripe", "returnUrl": "https://app.com/payment/return" "" - Expect
200withPaymentInitResponseDtocontainingredirectUrlandgatewayPayload - Redirect user to Stripe checkout URL
- Verify user redirected back to
/api/payments/redirect/:paymentId/success - Verify order status becomes
paidafter queue processing
11.2 Customer Buy-now Flow
Test Case: Buy-now with promo code
- User selects single product
- POST
/api/orders/buy-nowwith"" "productId": 101, "gateway": "stripe", "returnUrl": "https://app.com/payment/return", "promoCode": "SUMMER2026" "" - Expect
200with payment initiation payload - Complete payment via Stripe Checkout
- Verify promo discount applied to finalPayable
11.3 Customer Order List with Filters
Test Case: Filter orders by status
- Authenticated user with multiple orders
- GET
/api/orders?status=paid&page=1&size=10 - Expect
200with paginated list of paid orders only - Verify pagination metadata accurate
11.4 Customer Cancel Order
Test Case: Cancel order in valid status
- User has order with status
payment_pending - POST
/api/orders/101/cancelwith"" "reason": "Changed my mind" "" - Expect
202withAsyncRequestResponseDtocontainingrequestId - Poll GET
/api/orders/requests/:requestIduntilstatus: "completed" - Verify order status changed to
cancelled
11.5 Customer Request Refund
Test Case: Request refund on paid order
- User has order with status
paid - POST
/api/orders/101/request-refundwith"" "reason": "Product not as described", "remarks": "Please check with seller" "" - Expect
200with order detail showingrefundStatus: "requested" - Verify refund reason stored in order
11.6 Admin Process Refund
Test Case: Admin approves refund request
- Admin retrieves order with
refundStatus: "requested" - POST
/api/admin/orders/101/process-refundwith"" "action": "approve", "remarks": "Approved after verification" "" - Expect
202with async request response - Poll until completion
- Verify order
refundStatusbecomesapproved
11.7 Admin Order Search
Test Case: Search orders by order number
- Admin wants to find specific order
- GET
/api/admin/orders?search=ORD12345678 - Expect
200with matching orders - Verify search matches order number, customer name, email, or phone
11.8 Payment Failure Handling
Test Case: Payment gateway returns failure
- User initiates checkout
- User cancels/aborts at gateway
- Gateway redirects to
/api/payments/redirect/:paymentId/failure - Backend processes failure callback
- Order status changes to
payment_failedorcancelled - User sees payment failure page with retry option
12. Integration Flow Recipes
12.1 Mobile App: Complete Purchase Flow
- Initialize cart: Ensure user has items in cart via Cart module
- Initiate checkout:
- POST
/api/mobile/orders/checkoutwith"" "gateway": "stripe", "returnUrl": "https://yourapp.com/payment-result" "" - Capture returned
paymentId,redirectUrl,gatewayPayload
- POST
- Redirect to gateway:
- If
initiationType === "form_post", auto-submit form withgatewayPayloadtoredirectUrl - If
initiationType === "redirect", navigate browser toredirectUrlwith query params
- If
- Handle callback:
- Gateway redirects to
/api/payments/redirect/:paymentId/success(or failure) - Backend processes and redirects to payment result page with
nextparam
- Gateway redirects to
- Poll for status:
- After redirect, call GET
/api/orders/:orderIdto verify status - Or GET
/api/orders/requests/:requestIdif used async flow
- After redirect, call GET
- Show result:
- If
orderStatus === "paid", show success, unlock access - If
orderStatus === "payment_failed", show failure, offer retry
- If
12.2 Admin: Handle Customer Refund Request
- Receive refund request: Customer submits via app
- Review order:
- GET
/api/admin/orders/:orderIdto view full order details - Check payment reference, transaction history
- Verify product not already consumed (for digital: check access grants)
- GET
- Make decision:
- Approve: POST
/api/admin/orders/:orderId/process-refundwith"" "action": "approve", "remarks": "Refund approved" "" - Reject: POST
/api/admin/orders/:orderId/process-refundwith"" "action": "reject", "remarks": "Reason for rejection" ""
- Approve: POST
- Track async operation:
- Poll GET
/api/admin/orders/requests/:requestIduntil completed - Verify final order state reflects decision
- Poll GET
- Add admin note:
- POST
/api/admin/orders/:orderId/noteswith note for audit trail
- POST
12.3 Webhook: Payment Gateway Callback Processing
- Receive callback:
- Payment gateway POSTs to
/api/payments/redirect/:paymentId/success(or/failure) - Request contains gateway-specific fields (transaction_uuid, total_amount, etc.)
- Payment gateway POSTs to
- Validate payload:
- Backend verifies signature if provided
- Matches paymentId with internal records
- Queue processing job:
order.payment_successororder.payment_failedjob added to queue
- Async processing:
- Worker updates order status
- Worker creates/entitles product access (on success)
- Worker emits transactional notifications
- Redirect to frontend:
- Backend redirects to
/api/payments/resultwith status params - Frontend shows result page and redirects to app deep link
- Backend redirects to
13. Error Handling
All order API errors return the standard error envelope:
"
"statusCode": 404,
"errorCode": "ORDER_NOT_FOUND",
"message": "Order not found.",
"timestamp": "2026-03-20T10:00:00.000Z",
"path": "/api/orders/123"
"Representative error map:
| HTTP | errorCode | Message |
|---|---|---|
| 400 | ORDER_CART_EMPTY | Cart has no items. |
| 400 | ORDER_PRODUCT_NOT_AVAILABLE | Product is not available for purchase. |
| 400 | ORDER_PAYMENT_METHOD_NOT_SUPPORTED | Only online payment is supported. |
| 400 | ORDER_CANCEL_NOT_ALLOWED | Order cannot be cancelled in current status. |
| 400 | ORDER_DIGITAL_NOT_REFUNDABLE | Digital product orders are non-refundable. |
| 400 | ORDER_NOT_PENDING_REFUND | Order is not pending refund processing. |
| 400 | ORDER_IDEMPOTENCY_KEY_TOO_LONG | Idempotency key must be 200 characters or less. |
| 404 | ORDER_ACTIVE_CART_NOT_FOUND | Active cart not found. |
| 404 | ORDER_ASYNC_REQUEST_NOT_FOUND | Async request not found. |
| 404 | ORDER_NOT_FOUND | Order not found. |
| 409 | ORDER_IDEMPOTENCY_MISMATCH | Request with this idempotency key is already in progress. |
| 409 | ORDER_PAYMENT_FAILED | Previous request with this idempotency key has failed. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. |
| 503 | ORDER_ASYNC_REQUEST_CREATION_FAILED | Failed to create async order request. |
| 503 | ERR_CONFIG_INVALID | Configuration validation failed. |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Order - Feature List Section 6 (State Models) for order lifecycle diagram.
- Backend Reference: See Order - Backend Documentation Section 9 for system architecture diagram.