Request Lifecycle
Overview
This document traces a POST /v2/payments request end-to-end — from the API gateway through the service layers, to the payment vendor, and finally to the webhook dispatch. Understanding this flow is essential before implementing or modifying any payment logic.
End-to-End Sequence — Create Payment
Key Checkpoints
| Step | What Happens | Failure Behavior |
|---|---|---|
| 1 — Gateway auth | OAuth2 token validated, scope checked (merchant, user, merchant-pci) | 401 Unauthorized or 403 Forbidden |
| 2 — Request validation | JSON schema, field constraints, allocation sum check | 400 Bad Request with field-level errors |
| 3 — Idempotency check | merchantTransactionId + merchantId uniqueness | Returns existing payment if ≤ 5 attempts; 422 if exceeded |
| 4 — Insert transaction | Transaction record persisted with status=INITIATED before 202 is returned | Database error → 500 |
| 5 — Customer check (async) | Customer lookup/creation runs post-processing (after 202 returned). Failures transition transaction to FAILED and publish events | Visible via GET /v2/payments polling — never surfaces as synchronous HTTP error |
| 6 — Vendor processing | Stripe PaymentIntent created per allocation | Allocation marked FAILED; triggers rollback for split-tender |
| 7 — State persistence | Terminal state written atomically | Database transaction ensures consistency |
| 8 — Webhook dispatch | Async delivery with at-least-once guarantee | Retries with exponential backoff; does not block response |
Synchronous vs Asynchronous Boundaries
Important
POST /v2/payments always returns 202 Accepted with status INITIATED. Customer check runs post-processing — any customer-service failure (e.g. invalid metadata key, unknown customer) is surfaced asynchronously via GET /v2/payments polling or the PAYMENT_UPDATED webhook, never as a synchronous HTTP error.
See Customer Creation During Payment and Error Strategy — Async Error Surfacing for full details.
GET Payment Lifecycle
Cancel / Capture Flow Summary
| Operation | Method | Precondition | Async? |
|---|---|---|---|
| Cancel | PATCH /v2/payments/{paymentId}/cancel | Payment in AUTHORIZED, ACCEPTED, or PENDING state | Yes — returns 202 |
| Capture | PATCH /v2/payments/{paymentId}/capture | Payment in AUTHORIZED state | Yes — returns 202 |