Payment Idempotency
Payment idempotency ensures that multiple submissions of the same payment request only take effect once, avoiding double charge. Core mechanisms: idempotency key, state machine, reconciliation with payment provider. This article explains implementation points with a reference table.
Overview
- Idempotency key: Merchant order id or client-provided idempotency_key; before payment, check if already processed; if yes, return previous result.
- State machine: Pending → InProgress → Paid/Closed/Failed. Only certain states allow charge; duplicate requests return current state.
- Provider idempotency: WeChat, Alipay, etc. support same out_trade_no for multiple queries or submissions; only process once. Follow provider docs for correct params.
- Reconciliation: Periodically reconcile with provider; handle anomalies (over-charge, under-charge).
Example
Example 1: Flow
Plain text1. Request carries order_id or idempotency_key 2. Check payment record: exists and final state → return original result 3. Not exists or non-final → call payment provider 4. Provider success → update to paid, persist 5. Provider in-progress → poll or async callback 6. Duplicate requests intercepted at step 2
Example 2: Main points
| Point | Description |
|---|---|
| Idempotency key | Order id or idempotency_key; unique index |
| State machine | Final states (paid/closed) immutable |
| Provider | Pass out_trade_no etc.; use provider idempotency |
| Reconciliation | Daily reconciliation; fix inconsistencies manually or automatically |
Example 3: Concurrency
- Same order, concurrent requests: unique index + insert to "grab" slot, or SELECT FOR UPDATE; only one request proceeds to payment; others return "in progress" or original result.
Core Mechanism / Behavior
- Idempotency check: Before calling provider, check DB for existing record with same key; return if final state.
- State transitions: Only valid transitions allowed; final states block further changes.
- Provider: Use provider's idempotency (e.g. out_trade_no) to avoid double charge on their side.
Key Rules
- Idempotency key required; reject if missing, or generate server-side key and return to client.
- State machine must be strict; final states irreversible; in-progress, closed, etc. must be well-defined.
- Reconciliation is last line of defense; have a process when provider and local records differ.
What's Next
See Idempotency Design, Distributed Transactions. See Database Transactions for atomic state updates.