Ecommerce payments
Ecommerce is the only channel that uses Razorpay. COD and Razorpay are toggled in admin ecommerce settings (payment_cod_enabled / payment_razorpay_enabled on EcommerceSettings (backend-main/ecommerce/models.py)). Keys live in backend env only — see docs/RAZORPAY_SETUP.md.
Implementation: ecommerce/views.py (backend-main/ecommerce/views.py) (EcommerceCheckoutView, RazorpayPaymentVerifyView, RazorpayWebhookView), ecommerce/checkout_orders.py (backend-main/ecommerce/checkout_orders.py), ecommerce/payments.py (backend-main/ecommerce/payments.py).
Method selection
POST /api/ecommerce/checkout/ (EcommerceCheckoutSerializer):
paymentMethod | Gate | Immediate result |
|---|---|---|
cod (default) | payment_cod_enabled | Stock decremented → POSOrder created |
razorpay | payment_razorpay_enabled + keys configured | PendingRazorpayCheckout + Razorpay order; no POSOrder yet |
Admin cannot enable Razorpay in manage settings if RAZORPAY_KEY_ID / RAZORPAY_KEY_SECRET are missing (ecommerce/serializers.py (backend-main/ecommerce/serializers.py)).
COD path
flowchart LR
Checkout[POST /checkout] --> Prep[Validate items / prices]
Prep --> Stock[allocate_and_decrement_stock]
Stock --> Order["create_cod_order → POSOrder"]
create_cod_order (backend-main/ecommerce/checkout_orders.py) sets:
order_source=ecommercepayment_method=codpayment_status=not_requiredpayment_done_later=True
Processing is allowed without online verification (PAYMENT_STATUSES_ALLOW_PROCESSING includes not_required — see Order lifecycle).
Razorpay path: pending → verify / webhook
flowchart TD
Checkout[POST /checkout razorpay] --> Start[start_razorpay_checkout]
Start --> Pend["PendingRazorpayCheckout status=open<br/>snapshot + stock_items JSON"]
Start --> Rzp[create_razorpay_order]
Rzp --> Client[Return key_id / order_id / amount to storefront]
Client --> Pay[Customer pays in Razorpay Checkout]
Pay --> Verify[POST …/razorpay/verify/]
Pay --> Hook[POST …/razorpay/webhook/]
Verify --> Complete[complete_razorpay_payment]
Hook --> Complete
Complete --> Fulfilled["POSOrder payment_status=verified<br/>pending → fulfilled"]
Start (start_razorpay_checkout)
- Build cart snapshot (
build_checkout_snapshot) including line items andstock_items. - Create
PendingRazorpayCheckout(STATUS_OPEN). - Create Razorpay order (
payment_capture: 1) with receiptpb{pending.pk}and notes (pending_id, email). - Store
razorpay_order_idon the pending row. - Return
checkout_payload(key_id,order_id, amount in paise, prefill). Response also echoespayment_status: pending— this is intent metadata, not aPOSOrderyet.
Stock is not reserved at start. See Stock rules and Known limitations.
Client verify (RazorpayPaymentVerifyView)
Body: razorpay_order_id, razorpay_payment_id, razorpay_signature.
verify_checkout_signature— HMAC oforder_id|payment_idwithRAZORPAY_KEY_SECRET(backend-main/ecommerce/payments.py).complete_razorpay_payment(…).- Returns serialized
POSOrder, or404if nothing matched.
Purpose: let the success page continue immediately. Setup doc: webhook remains the source of truth for capture.
Webhook (RazorpayWebhookView)
- Verify
X-Razorpay-Signatureover raw body withRAZORPAY_WEBHOOK_SECRET. - Handle only
payment.capturedandorder.paid(parse_webhook_payment). - Call the same
complete_razorpay_payment. - Other events →
{ "status": "ignored" }.
Fulfillment (complete_razorpay_payment / _create_order_from_pending)
Inside transaction.atomic() + select_for_update on the pending row:
- If pending already has
pos_order→ idempotent return (optionally attachrazorpay_payment_id). - Else decrement snapshot
stock_items(_decrement_snapshot_stock), thencreate_ecommerce_pos_orderwithpayment_method=razorpay,payment_status=verified, Razorpay ids set,payment_verified_atstamped. - Mark pending
STATUS_FULFILLED.
If stock decrement fails for some lines, the verified order is still created and a system comment lists missing stock — documented under Known limitations.
Legacy fallback: if no pending row exists, mark_razorpay_order_paid flips an existing Razorpay POSOrder to verified.
Env and ops
| Variable | Role |
|---|---|
RAZORPAY_KEY_ID | Public key returned to checkout UI |
RAZORPAY_KEY_SECRET | Order create + checkout signature verify |
RAZORPAY_WEBHOOK_SECRET | Webhook HMAC |
Production webhook URL and admin enable steps: docs/RAZORPAY_SETUP.md.
Related
- Payments — POS & links — UPI screenshot (no Razorpay)
- Stock rules
- Ecommerce API — route table
- User: Ecommerce checkout — COD / Razorpay