Payment Flow - mukular/food-delivery-feastfast--architecture GitHub Wiki
Payment Flow & Transactions
This document explains how payments are handled across Cash on Delivery (COD), Wallet, and Online Payments (Razorpay), including order creation, verification, webhooks, and failure handling.
1. Supported Payment Methods
| Method | Description |
|---|---|
| COD | Pay on delivery |
| Wallet | Internal wallet balance |
| Online | Razorpay (UPI / Card / Netbanking) |
Each method follows a different trust and confirmation model.
2. Core Principles
- Order is always created before payment
- Database is the source of truth
- Webhooks finalize online payments
- Coupons are consumed only once
- Refunds are explicit & controlled
3. High-Level Payment Flow
Customer
↓
Create Order API
↓
Order Created (pending / paid)
↓
Payment Handling
↓
Order Confirmed(after seller acceptance)
4. Order Creation (Common for All)
Endpoint
POST /order/create
What Happens
- Validate cart items
- Validate shop availability
- Apply coupon (if any)
- Calculate:
- Subtotal
- Delivery fee
- Tax
- Total amount
- Create Order document
- Create
shopOrders[]inside order
At this stage:
- No online payment is trusted
- Order exists even if payment is pending
5. Wallet Payment Flow
Flow
User → Create Order
→ Wallet Balance Check
→ Deduct Wallet Amount
→ Order = Paid
Key Rules
- Wallet balance is deducted inside DB transaction
- If insufficient balance → order creation fails
- Payment is instant & final
Result
paymentMethod: "wallet"
paymentStatus: "paid"
6. Cash on Delivery (COD) Flow
Flow
User → Create Order
→ No payment collected
→ Order confirmed
Key Rules
- No payment verification required
- Higher cancellation risk
- Used for trusted users only (configurable)
Result
paymentMethod: "cod"
paymentStatus: "pending"
7. Online Payment (Razorpay) Flow
7.1 Order Creation (Backend)
Create Order → Razorpay Order Generated
Backend Responsibilities
- Create Razorpay order
- Store
razorpayOrderIdin DB - Return payment details to frontend
7.2 Frontend Payment
Frontend:
- Opens Razorpay checkout modal
- User completes payment
- No success is trusted on frontend
7.3 Webhook Verification (Critical)
All online payments are finalized only via webhook.
8. Razorpay Webhook Handling
Event Used
payment.captured
Verification Steps
- Verify webhook signature
- Parse payload
- Find order by
razorpayOrderId - Ensure:
- Amount matches
- Order not already paid
- Mark order as paid
Idempotency Protection
Webhook is safe to retry:
if (order.paymentStatus === "paid") return;
9. Coupon Handling & Edge Cases
Design Decision
Coupons are consumed at order creation, not webhook.
Why?
- Prevents coupon overuse
- Avoids race conditions
- Keeps system deterministic
Tradeoff
- If payment fails → coupon still consumed
This is an acceptable real-world compromise
10. Refund Handling
When Refund Happens
- Order cancelled
- Payment captured later
Flow
Webhook → detect cancelled order
→ Razorpay refund API
→ Update paymentStatus = refunded
Refund amount = full amount paid by customer.
Note: COD orders do not require refunds as no payment is collected upfront.
11. Failure Scenarios & Handling
| Scenario | Handling |
|---|---|
| User closes Razorpay modal | Order remains pending |
| Payment succeeds but webhook delayed | Order updates later |
| Webhook received twice | Idempotent safe |
| Amount mismatch | Reject webhook |
| Cancelled order paid | Refund immediately |
12. Security Measures
- Signature verification mandatory
- No trust in frontend callbacks
- Amount verification enforced
- Order state checks before updates
13. Data Model Fields (Relevant)
Order
- paymentMethod
- paymentStatus
- totalAmount
- razorpayOrderId
- razorpayPaymentId
Coupon
- usedCount
- userUsed[]
- usageLimitTotal
- usageLimitPerUser
14. Why This Payment System Is Robust
- No frontend trust
- Webhook-driven confirmation
- Idempotent updates
- Race-condition safe
- Clear refund strategy
- Production-aligned