Testland
Browse all skills & agents

payment-flow-states-reference

Pure-reference catalog of payment lifecycle state machines across Stripe, Adyen, PayPal, and Braintree. Covers the canonical states (created / requires_action / processing / succeeded / requires_capture / canceled / failed), authorisation vs capture (separate vs combined), the asynchronous webhook states, refund / dispute / chargeback transitions, and the per-platform terminology mapping (Stripe PaymentIntent.status vs Adyen Authorisation/Capture vs PayPal Order). Use when designing tests for payment flows or auditing state-handling code. Composes 3ds-test-flow-reference.

payment-flow-states-reference

Overview

Every payment platform exposes a state machine - the PaymentIntent in Stripe, the Authorisation in Adyen, the Order in PayPal, the Transaction in Braintree. Each has different terminology for what is fundamentally the same lifecycle.

Per stripe.com/docs/payments/payment-intents: "The PaymentIntent encapsulates the lifecycle of a customer payment."

When to use

  • Designing a payment-flow test suite.
  • Auditing state-handling code for a payment integration.
  • Mapping equivalent states across multiple providers.
  • Investigating "stuck payment" reports.

The canonical states

Most payment systems share the same conceptual states:

StateStripeAdyenPayPalBraintree
Createdrequires_payment_methodReceivedCREATEDcreated
Awaiting action (3DS, etc.)requires_actionRedirectShopperPAYER_ACTION_REQUIREDn/a (handled inline)
ProcessingprocessingPendingPENDINGsubmitted_for_settlement
Authorized (not captured)requires_captureAuthorisedAPPROVED (no immediate capture)authorized
Captured / succeededsucceeded[Capture] SettledCOMPLETEDsettled
Failedfailed (charge)RefusedDECLINEDgateway_rejected / failed
CancelledcanceledCancelledVOIDEDvoided
Refundedsucceeded + refund object[Refund] SettledREFUNDEDrefunded
Disputed / chargebackdisputed (in dispute object)[Chargeback]disputedisputed

Authorisation vs capture

Two-step:

  1. Authorize - bank reserves funds; merchant doesn't get them yet.
  2. Capture - funds transferred to merchant.

Default in most systems is auto-capture (auth + capture in one call). Separate auth-then-capture is used for:

  • Hold-then-charge flows (rental cars, hotels).
  • Inventory-confirm-before-charge.
  • Manual fraud review.

Per stripe.com/docs/payments/capture: PaymentIntent with capture_method=manual requires explicit capture call.

Webhook event sequence

The webhook events emitted per state transition. Stripe example:

1. customer.created
2. payment_intent.created
3. payment_intent.requires_action   (if 3DS)
4. payment_intent.processing
5. payment_intent.succeeded
   AND
   charge.succeeded

The asynchronous nature means tests must wait for webhook arrival, not rely on synchronous API return.

Refund states

captured payment

   refund created (status: pending)

   refund succeeded (or failed)

Per stripe.com/docs/refunds: refunds are async; the API call returns immediately with pending, then a webhook delivers the final state minutes to hours later.

Dispute / chargeback states

The most-complex part of the state machine. Per Visa's chargeback reason codes (cite by stable ID: Visa Chargeback Reason Codes), the customer's bank initiates the chargeback; the merchant has a fixed window to respond.

StateMeaning
InquiryBank requests info; not yet a chargeback
Pre-arbitrationInitial dispute filed
WonMerchant evidence accepted
LostMerchant evidence rejected; funds returned to customer
Pre-arbitration acceptedMerchant accepts the loss

Per stripe.com/docs/disputes: disputes resolve over weeks; test scenarios use Stripe's test- mode dispute API to trigger transitions synchronously.

Idempotency

Most payment APIs accept an Idempotency-Key header (Stripe, Adyen) or equivalent. The pattern: retry with the same key produces the same response.

Per stripe.com/docs/api/idempotent_requests: "Stripe supports idempotency for safely retrying requests without accidentally performing the same operation twice."

Tests should verify the merchant code uses idempotency keys for every mutating call.

State-handling test surface

SurfaceTest
Created → succeeded (happy path)Standard test-card; assert each state observed
Requires-action (3DS)Per 3ds-test-flow-reference
Failed (insufficient funds)Use insufficient-funds test card; assert state
Cancelled before captureManual-capture + cancel; assert state
Webhook idempotencyReplay webhook twice; assert idempotent handling
Refund fullCapture + full refund; assert state sequence
Refund partialCapture + partial refund; assert state
Dispute wonTrigger dispute; respond; assert won
Dispute lostTrigger dispute; don't respond; assert lost

Anti-patterns

Anti-patternWhy it failsFix
Treating the API return as the final stateAsync; succeeded comes laterWait for webhook
No idempotency keyNetwork retries duplicate-charge customersAlways set idempotency
Hardcoded sleep waiting for webhooksFlakyPoll webhook endpoint or queue with timeout
Skipping the requires-action flow3DS regulations require it for most EU cardsAlways test 3DS path
Stale state stored locallyLocal DB diverges from platformWebhook-driven update
Trust the request-body statusWebhooks can be replayed by attackersVerify signature + idempotency
One test for all platformsState terminology differsPer-platform test suite
Refund tests in sync flowRefunds are asyncWebhook-based

Limitations

  • Platforms evolve. Stripe added the setup_intent for saved payment methods; PayPal's Orders API is newer than the legacy Payments API.
  • Regulatory states change. EU PSD2 introduced strong customer authentication; states evolved to support it.
  • Refund + dispute timelines. Real-world chargebacks take weeks; test environments shortcut this.

References