Shop It Docs
Developer ResourcesPayment

Cash on Delivery (COD)

COD is a pending-collection gateway — no redirect, no webhook, and no paid/completed status until admin records cash.

Cash on Delivery (COD)

Audience: Backend developers, admin operators Scope: COD checkout flow, pending collection status, admin cash collection endpoint


Overview

PropertyValue
Gateway namecod
Initiation typecod
Redirect requiredNo
Webhook requiredNo
Payment finalizationAdmin cash collection completes the existing pending payment row
Checkout order statuspayment_pending
Checkout payment statuscod_pending
Physical collectionAdmin endpoint: PUT /api/admin/orders/:id/cod-collect

COD is available for physical orders only. Digital and care-package orders cannot use COD.

Warranty note: COD checkout does not activate warranty eligibility. Warranty remains blocked while orders.paymentStatus = "cod_pending" and becomes eligible only after admin collection sets paymentStatus = "cod_collected".


Payment Flow


Checkout Request

{
  "gateway": "cod",
  "returnUrl": "https://example.com/orders/101/confirmation"
}

Checkout Response (200)

{
  "orderId": 101,
  "status": "payment_pending",
  "paymentId": 9001,
  "gatewayTransactionId": "cod_9001",
  "initiationType": "cod",
  "redirectUrl": "https://example.com/orders/101/confirmation",
  "gatewayPayload": {}
}

Note: redirectUrl echoes the returnUrl sent in the request. Frontend navigates there directly after receiving the cod response — no gateway redirect occurs.

The created order is confirmed for fulfillment after reservation succeeds, but it is not paid yet:

FieldCheckout valueCash collected value
orders.orderStatuspayment_pendingunchanged by collection
orders.paymentStatuscod_pendingcod_collected
payment.statuspendingcompleted
orders.paymentReferencecod_<paymentId>unchanged

COD checkout does not emit payment.completed, does not enqueue order.payment_success, and does not create a paid status-history entry. It records COD pending collection history using payment_pending.

During that pending-collection window, warranty claim creation and warranty-status access treat the order as unpaid.


COD Restrictions

Cart / Product TypeCOD Allowed?Error Code
Physical only✅ Yes
Mixed (physical + digital)✅ Yes
Digital only❌ NoORDER_COD_PHYSICAL_ONLY
Care package only❌ NoORDER_COD_PHYSICAL_ONLY
Buy-now digital product❌ NoORDER_COD_PHYSICAL_ONLY

Admin Cash Collection

After the order is delivered, an admin records physical cash collection:

PUT /api/admin/orders/:id/cod-collect
Authorization: Bearer <admin-token>

Request body:

{
  "remarks": "Collected by rider #12 at 10:30 AM — cash NPR 3500"
}

Response (200):

{
  "message": "COD cash collected successfully",
  "data": {
    "orderId": 101,
    "codCashCollectedAt": "2026-06-22T04:45:00.000Z",
    "paymentStatus": "cod_collected"
  }
}

Idempotency: If cash is already recorded, the endpoint returns the existing codCashCollectedAt without error.

On first collection, the backend finds the existing pending COD payment row for referenceType = "order", referenceId = orderId, gateway = "cod", and status = "pending". It marks that payment row completed, stores or reuses gatewayTransactionId = "cod_<paymentId>", then updates the order to paymentStatus = "cod_collected".

That cod_collected transition is also the point where warranty becomes eligible, assuming the order is delivered and not later cancelled or refund-approved.

Required permission: Orders_UPDATE

Remarks rules: Non-blank string, max 500 characters. Whitespace-only is rejected (400).


Schema Columns Added (SP1)

ColumnTypeDescription
orders.payment_methodorder_payment_method enum"online" or "cod"
orders.cod_cash_collected_attimestampWhen admin recorded physical cash collection
orders.cod_collection_remarksvarchar(500)Free-text remarks from admin at collection

Error Codes

CodeHTTPTrigger
ORDER_COD_PHYSICAL_ONLY400COD used with digital-only or care-package-only cart/product
ORDER_NOT_COD_METHOD400Admin cash collection on a non-COD order
ORDER_PAYMENT_FAILED400Admin collection cannot find the pending COD payment row

Implementation Files

  • apps/api/src/modules/payment/gateways/cod.gateway.ts
  • apps/api/src/modules/order/customer/order-customer.service.ts (COD derivation, early return)
  • apps/api/src/modules/order/order.service.ts (COD restriction in createFromCart / createBuyNow)
  • apps/api/src/modules/order/admin/order-admin.service.ts (collectCodPayment)
  • apps/api/src/modules/order/admin/order-admin.controller.ts (PUT /:id/cod-collect)
  • packages/db/src/schema/payment.ts (PAYMENT_GATEWAYS enum)
  • packages/db/src/schema/order/orders.ts (cod columns)
  • packages/db/src/migrations/0007_cod_payment.sql