Payment State Management
Provider A says: SUCCEEDED Provider B says: CAPTURED Provider C says: PAID Are those the same payment state? Sometimes. Not always. The words themselves are not enough. One provider may use succeeded to mean that a payment has been fully captured. Another may use paid for a metho
Payment State Management: Designing a Reliable Payment State Machine
Provider A says:
SUCCEEDED
Provider B says:
CAPTURED
Provider C says:
PAID
Are those the same payment state?
Sometimes.
Not always.
The words themselves are not enough.
One provider may use succeeded to mean that a payment has been fully captured. Another may use paid for a method whose funds are confirmed but not yet represented the same way in settlement. Another may expose separate authorization and capture states. A bank-based payment may remain pending long after the customer has left the checkout.
This is why a merchant cannot safely define its payment architecture as:
merchant_payment.status = provider.status
A payment provider knows the state of its own transaction object.
The merchant needs something broader:
A durable model of what the payment currently means to the merchant, which financial operations have occurred, what remains possible, and how that conclusion was reached.
That is payment state management.
The goal is not to invent one universal status vocabulary.
It is to establish a merchant-owned state model that:
Gives business logic stable meanings
Preserves provider-native detail
Handles synchronous and asynchronous updates
Distinguishes uncertainty from failure
Rejects invalid state transitions
Keeps a history of what happened
Can be reconciled against provider and financial records
A good payment state machine normalizes what the merchant needs to know.
It does not pretend different payment providers or payment methods are identical.
Start with the merchant payment, not the PSP transaction
Before defining payment states, define what object is changing state.
A useful merchant model separates at least:
Order
The commercial obligation.
Example:
Order ORD-1842
Amount due: EUR 100
Payment intent
The merchant’s desired financial outcome.
Payment Intent PI-8921
Collect EUR 100 for ORD-1842
Payment attempt
One attempt to achieve that intent.
PI-8921
|
+-- PA-1 -> Provider A
Provider transaction
The provider-native object representing that attempt or one of its financial operations.
PA-1
provider = PSP_A
provider_transaction_id = TX-66382
These distinctions matter because one payment intent can involve more than one attempt.
For example:
Payment Intent PI-8921
|
+-- PA-1 -> Provider A -> known decline
|
+-- PA-2 -> Provider B -> authorized
The merchant-level answer to:
Has this order been paid?
cannot be derived safely from whichever provider transaction happened to be read last.
Payment state belongs to the merchant payment model.
Payment intent state and payment-attempt state are related but different
Suppose an order needs one successful EUR 100 payment.
Attempt 1 fails.
Attempt 2 succeeds.
At the attempt level:
PA-1 = FAILED
PA-2 = COMPLETED
At the payment-intent level:
PI-8921 = COMPLETED
If the merchant only stores one payment_status field, these realities become difficult to represent.
That creates problems for:
Retry analysis
Multi-PSP routing
Duplicate detection
Customer support
Provider comparison
Reconciliation
A merchant should therefore decide explicitly:
Which state belongs to the desired payment outcome, and which state belongs to each attempt to achieve it?
The distinction becomes increasingly important as the business adds:
Multiple providers
Retries
Failover
Asynchronous methods
Customer authentication
Canonical state expresses merchant meaning
A canonical payment state is a merchant-defined state whose meaning remains stable even when provider vocabularies differ.
A high-level model might include:
CREATED
REQUIRES_ACTION
SUBMITTED
PENDING
AUTHORIZED
COMPLETED
FAILED
CANCELED
EXPIRED
UNKNOWN
This is an example, not a universal industry state machine.
Different businesses and methods may require:
Fewer states
Additional states
Separate authorization/capture models
Separate refund/reversal models
The design principle is more important than the labels:
Every canonical state should answer a merchant-operational question.
For example:
CREATED
Merchant has created the payment intent or attempt, but it has not yet reached the relevant execution stage.
REQUIRES_ACTION
Further customer action is required.
SUBMITTED
Merchant has submitted the attempt to the relevant payment path but does not yet have the next meaningful result.
PENDING
The payment is progressing through a known non-terminal lifecycle.
AUTHORIZED
The payment has reached an authorization state where that concept applies.
COMPLETED
The merchant-defined successful financial state has been reached.
FAILED
Merchant has reliable evidence that the attempt cannot complete successfully in its current lifecycle.
CANCELED
The attempt was intentionally stopped before normal completion.
EXPIRED
The relevant payment instruction or attempt expired.
UNKNOWN
Merchant cannot currently establish the financial outcome.
The definition behind each state matters more than the word.
Provider-native state should not disappear
Suppose Provider A sends:
status = "CAPTURED"
reason_code = "00"
and the merchant maps that to:
canonical_state = COMPLETED
Store both.
A useful record might look like:
payment_attempt_id: PA-1
canonical_state: COMPLETED
provider: PSP_A
provider_state: "CAPTURED"
provider_reason_code: "00"
provider_transaction_id: "TX-66382"
Why preserve the native state?
Because the canonical state answers:
What does this mean to our systems?
The provider state answers:
What did the financial counterparty actually report?
That detail can matter for:
Provider support
Incident investigation
Dispute research
Reconciliation
API migrations
Provider-specific operations
Canonicalization should reduce provider coupling.
It should not erase evidence.
Normalize semantics, not labels
A dangerous mapping table looks like this:
Provider status
Merchant state
succeeded
COMPLETED
paid
COMPLETED
captured
COMPLETED
processed
COMPLETED
That mapping assumes the words mean the same thing.
They may not.
Before mapping any provider state, establish:
Which financial operation occurred?
Is the state terminal?
Can it transition again?
Is customer action still required?
Has authorization occurred?
Has capture occurred?
Is the result merely processing?
Is settlement implied or separate?
Can the payment later be reversed?
Provider-specific semantics must come from current provider documentation.
Visa Acceptance documentation, for example, exposes explicit AUTHORIZED states and treats capture as a follow-on transaction in applicable card flows, illustrating why authorization and capture should not be collapsed merely because both represent successful progress.
The mapping question should therefore be:
What merchant meaning corresponds to this provider-defined state?
not:
Which canonical word looks most similar?
Authorized is not captured
This is one of the most important distinctions in card payment state.
An authorization generally indicates that the issuer has approved the relevant authorization request and, in applicable flows, funds or credit have been reserved according to the card-payment process.
Capture is a later operation that moves the transaction into the subsequent clearing/settlement lifecycle.
Visa Acceptance’s current documentation explicitly describes capture as a follow-on transaction to authorization in applicable payment flows.
Conceptually:
CREATED
|
v
SUBMITTED
|
v
AUTHORIZED
|
v
CAPTURE OPERATION
|
v
COMPLETED
Do not make:
AUTHORIZED = COMPLETED
unless the merchant’s business definition and provider/payment-method contract genuinely support that interpretation.
For a sale-style transaction where authorization and capture occur together, the state path can legitimately differ.
That is why the state machine should model payment semantics, not force every method through identical stages.
Failed and canceled are different
Both can mean that the attempt did not produce the desired successful payment.
But they describe different histories.
Failed
The attempt encountered an outcome that prevents successful completion in that lifecycle.
Potential examples:
Issuer decline
Payment-method rejection
Provider validation outcome that terminates the attempt
Canceled
The merchant, customer, provider, or applicable system intentionally terminated the attempt before completion.
Possible examples:
Merchant voids an uncaptured transaction
Customer cancels an applicable flow
Merchant explicitly abandons a pending attempt
Why preserve the distinction?
Because operationally they can imply different:
Customer messaging
Retry eligibility
Provider actions
Reporting
Analysis
A cancellation is not necessarily a payment failure.
A failure is not necessarily a cancellation.
Pending and unknown are fundamentally different
This distinction deserves first-class representation.
Pending
The merchant has evidence that the payment is in a known, non-terminal processing state.
Example:
Provider reports:
status = PENDING
The merchant understands that:
A payment object exists
The provider is still processing it
A later result is expected
Unknown
The merchant cannot currently establish whether the financial operation succeeded.
Example:
Merchant sends authorization.
Provider receives request.
Merchant connection closes.
No reliable response is received.
Provider status endpoint is temporarily unavailable.
The merchant does not know whether authorization occurred.
That is:
UNKNOWN
not:
FAILED
The distinction matters because the next safe action differs.
Pending
Usually:
Wait for or retrieve the expected later state.
Unknown
Usually:
Establish financial truth before creating another financial outcome.
A timeout therefore does not automatically create FAILED.
HTTP semantics do not make non-idempotent financial operations safe to repeat simply because a client failed to obtain the response; RFC 9110 distinguishes idempotent methods precisely because repeated requests can otherwise have additional effects.
The detailed recovery decision belongs in Handling Payment API Timeouts.
State management owns the fact that uncertainty must be representable.
Unknown should be measurable, not hidden
Teams often dislike UNKNOWN because it complicates application code.
Removing the state does not remove the uncertainty.
It usually converts uncertainty into an incorrect answer.
For example:
timeout -> FAILED
looks simpler.
But if the authorization actually succeeded, the merchant now has:
merchant state: failed
provider state: authorized
That discrepancy will eventually surface through:
Webhook
Customer complaint
Reconciliation
Refund request
Finance exception
A better architecture admits:
We do not yet know.
Then measures how often that happens and how long those cases remain unresolved.
Event is not state
A payment event represents something that happened or was reported.
A payment state represents the merchant’s current interpretation of the payment.
These are related but not equivalent.
Suppose the merchant receives:
event 1:
payment.pending
event 2:
payment.authorized
event 3:
payment.pending # delayed older event
The third event exists.
That does not mean current state should become PENDING.
This is the difference between:
Event history
What messages or facts have been observed.
Current state
The merchant’s current canonical interpretation after applying transition rules.
The system should preserve both.
Current state is a projection of history
A useful conceptual model is:
EVENT / OBSERVATION HISTORY
|
v
STATE-TRANSITION RULES
|
v
CURRENT CANONICAL STATE
Possible observations include:
Synchronous provider response
Webhook
Status retrieval
Merchant action
Reconciliation result
The current state is therefore not necessarily “the latest webhook value.”
It is the result of applying known facts to the merchant’s state model.
This has an important debugging benefit.
If support asks:
Why is this payment marked completed?
the system should be able to answer something like:
10:00 Attempt created
10:00 Submitted to PSP A
10:00 Provider returned PENDING
10:02 Webhook reported AUTHORIZED
10:05 Capture succeeded
10:05 Merchant state -> COMPLETED
That is much stronger than:
status = completed
with no history explaining why.
Define allowed transitions explicitly
A state machine should define which transitions are legal.
A simplified model might allow:
CREATED
|
+--> REQUIRES_ACTION
|
+--> SUBMITTED
then:
REQUIRES_ACTION
|
+--> SUBMITTED
+--> CANCELED
+--> EXPIRED
and:
SUBMITTED
|
+--> PENDING
+--> AUTHORIZED
+--> COMPLETED
+--> FAILED
+--> UNKNOWN
then, depending on the payment method:
PENDING
|
+--> AUTHORIZED
+--> COMPLETED
+--> FAILED
+--> EXPIRED
+--> UNKNOWN
and for two-step card payments:
AUTHORIZED
|
+--> COMPLETED
+--> CANCELED / REVERSED
This is illustrative.
Do not copy it blindly into production.
The correct state graph depends on:
Payment method
Provider
Merchant fulfillment rules
Authorization/capture model
Asynchronous behavior
Reject invalid backward transitions
Suppose current state is:
COMPLETED
Then an old webhook arrives saying:
PENDING
If the system simply overwrites state:
COMPLETED -> PENDING
the merchant can suddenly believe a paid order is unresolved.
A state transition layer should instead ask:
Is this transition legal given what we already know?
Conceptually:
if transition_allowed(current_state, candidate_state):
apply()
else:
preserve event
flag if necessary
The event should not necessarily be discarded.
It can remain in history for audit and diagnostics.
But not every observed event deserves to become current state.
Terminal state needs a precise definition
A terminal state means:
No further normal state transition is expected for this state machine.
But payment systems have multiple lifecycles.
A card payment might reach:
COMPLETED
and later be:
Refunded
Partially refunded
Disputed
Reversed in an exceptional lifecycle
That does not necessarily mean COMPLETED was never terminal for the payment-attempt execution state.
It may mean refunds and disputes belong to subsequent financial lifecycle objects.
This is why one giant status field often becomes confusing.
Refunds should not necessarily rewrite the original payment state
Suppose:
Payment = COMPLETED
Then the merchant refunds 30% of the amount.
What should payment.status become?
Possible bad answer:
PARTIALLY_REFUNDED
if that single field now destroys the information that the original payment itself completed successfully.
A clearer model can preserve:
Payment execution state
COMPLETED
Refund state
Refund REF-1 = COMPLETED
Financial summary
captured_amount: EUR 100
refunded_amount: EUR 30
net_amount: EUR 70
Likewise, a full refund can be represented without pretending the original capture never happened.
The right model depends on merchant needs, but the general principle is powerful:
Subsequent financial operations should not erase the historical truth of earlier operations.
Reversal is not necessarily refund
These terms are often used interchangeably in product code.
They should not be.
A reversal can unwind or release an authorization depending on the payment flow.
A refund generally returns value after the relevant payment has completed/captured.
Provider and network terminology varies, so confirm exact behavior.
At the merchant-state level, preserve the underlying operation:
authorization
capture
reversal
refund
rather than flattening everything into:
payment.status = reversed
unless that field has a carefully defined purpose.
One payment can have multiple state machines
A sophisticated merchant does not necessarily need one enormous state enum.
It can be cleaner to model separate but related lifecycles.
For example:
Payment intent state
OPEN
COMPLETED
CANCELED
Attempt state
CREATED
REQUIRES_ACTION
PENDING
AUTHORIZED
COMPLETED
FAILED
UNKNOWN
Refund state
CREATED
SUBMITTED
PENDING
COMPLETED
FAILED
UNKNOWN
Dispute state
Maintained separately where relevant.
This keeps each state machine tied to a specific object.
Trying to express everything in:
payment_status
often produces ambiguous states such as:
AUTHORIZED_PARTIALLY_REFUNDED_DISPUTED
which is a sign that several financial dimensions have been collapsed together.
Customer action is not financial success
A customer can:
Complete 3DS
Return from a wallet
Return from a bank redirect
Scan a QR code
without the merchant necessarily having the final payment result.
Therefore:
customer_returned = true
should not automatically mean:
payment = COMPLETED
For asynchronous payment methods, later confirmation may arrive through:
Provider webhook
Status retrieval
Bank/payment-method confirmation
The dedicated Asynchronous Payment Methods article should own the full lifecycle.
State management owns the principle that customer interaction state and financial state are not interchangeable.
Webhook delivery is not payment state
A webhook is an input.
For example:
provider webhook:
"payment.completed"
The merchant still needs to:
Authenticate/validate the event according to provider requirements.
Associate it with the correct payment/attempt.
Determine whether it is new or duplicate.
Interpret the provider-native status.
Validate the state transition.
Persist the resulting state.
Trigger permitted downstream business action.
Current payment-provider documentation demonstrates why webhook handling must remain provider-specific at the transport layer; providers can define distinct signing, retry, schema, and event semantics. The merchant state machine sits downstream of those mechanics rather than being replaced by them.
The dedicated Webhooks for Payment Processing article should own delivery architecture.
Duplicate events should not create duplicate state effects
Suppose the provider sends the same completed-payment event twice.
The merchant may legitimately receive:
event EV-1
event EV-1
The payment should not be:
COMPLETED
COMPLETED
in a way that triggers fulfillment twice.
Store enough identity to make event processing safe.
Conceptually:
if event already processed:
acknowledge
no repeated business side effect
The state itself can already be COMPLETED.
The second delivery changes nothing.
That is a message-processing concern feeding the state machine, not a new payment state.
Preserve provider event time and merchant processing time separately
Distributed systems can deliver information late.
Useful timestamps may include:
Provider event creation time
Merchant event receipt time
Merchant event processing time
State-effective time
Last provider-status verification time
These tell different stories.
Suppose:
provider event occurred: 10:02
merchant received event: 10:07
merchant processed event: 10:08
That five-minute delay can matter during:
Incident analysis
Fulfillment
SLA measurement
Reconciliation
Do not overwrite all of them with one generic:
updated_at
if the distinction is operationally meaningful.
The merchant should own canonical state—but not invent financial truth
This is the most important source-of-truth nuance.
A merchant can define:
Its canonical state names
Transition rules
Business consequences
Historical event model
But it cannot make a payment successful merely by setting:
status = COMPLETED
Provider/acquirer/payment-method information still supplies evidence about what happened financially.
A useful architecture is:
Merchant payment ledger/system = merchant system of record for canonical state, continuously informed and reconciled against authoritative external financial evidence.
That is different from saying:
The merchant database is always right.
It is only reliable if the merchant reconciles it.
Source of truth is really a hierarchy of evidence
Different facts can have different authoritative sources.
For example:
Merchant intent
Merchant is authoritative.
We intended to collect EUR 100.
Provider transaction existence
Provider can be authoritative.
Transaction TX-66382 exists.
Merchant canonical interpretation
Merchant owns the mapping.
TX-66382 means PA-1 is currently AUTHORIZED.
Settlement
Relevant acquiring/provider/financial records become critical evidence.
This captured transaction appears in settlement batch X.
That produces a more accurate model than trying to name one universal “source of truth.”
Reconciliation is part of state management
State management is not complete when the last webhook arrives.
A merchant can believe:
COMPLETED
while the provider later reports something inconsistent in:
Transaction retrieval
Settlement
Refund
Reversal records
A reconciliation process should therefore test whether merchant state agrees with relevant financial records.
Conceptually:
MERCHANT STATE
|
+------ compare ------ PROVIDER STATE
|
+------ compare ------ FINANCIAL / SETTLEMENT RECORD
|
v
CONSISTENT
or
RECONCILIATION EXCEPTION
The finance mechanics belong in broader reconciliation content.
But the state architecture must preserve enough identifiers to make the comparison possible.
Reconciliation should create evidence, not silently overwrite history
Suppose the merchant has:
PA-1 = UNKNOWN
and later reconciliation proves:
Provider = AUTHORIZED
The system can transition:
UNKNOWN -> AUTHORIZED
but it should ideally preserve why.
For example:
state_before: UNKNOWN
state_after: AUTHORIZED
source: reconciliation_status_query
observed_at: ...
provider_state: AUTHORIZED
Do not silently rewrite the old record as though the merchant had always known the payment was authorized.
The uncertainty itself can be operationally important.
Model state provenance
For each state change, useful fields can include:
payment_attempt_id
previous_state
new_state
source_type
source_id
provider
provider_state
provider_transaction_id
provider_event_time
merchant_received_time
merchant_processed_time
reason_code
transition_version
Possible source_type values might include:
api_response
webhook
status_query
reconciliation
merchant_action
system_action
This creates an audit trail capable of answering:
What evidence caused this state change?
Version the transition logic
Payment systems change.
A state mapping that was correct under:
provider API version X
may need modification under:
provider API version Y
Likewise, the merchant may refine its canonical state model over time.
If state behavior materially changes, store or log enough information to determine:
Provider/API version
Mapping version
Merchant transition-rule version
That makes production investigations much easier.
Do not assume a current state mapping will remain semantically stable forever.
New provider statuses must not crash the payment system
Providers can expand enumerations.
A merchant that writes:
switch provider_status:
case "AUTHORIZED":
case "DECLINED":
case "PENDING":
default:
crash()
has created fragility.
A safer philosophy is:
known provider state
-> mapped canonical meaning
unknown new provider state
-> preserve raw value
-> avoid unsafe financial assumption
-> alert / investigate
Do not map an unknown value automatically to FAILED.
Unknown provider vocabulary should not produce invented financial truth.
State mapping should be provider and payment-method aware
The same provider may expose different lifecycles for:
Cards
Wallets
Bank transfers
Other local methods
Therefore:
provider_status -> canonical_state
may be insufficient.
A more accurate mapping key can require:
provider
payment_method
operation
provider_status
and sometimes additional context.
That prevents a card-specific interpretation from being incorrectly applied to an asynchronous method.
A practical Canonical State Mapping Template
For each provider/method, maintain a reviewed mapping.
Provider state
Operation/method
Canonical state
Terminal?
Customer action?
Financial meaning
Allowed next states
AUTHORIZED
Card auth
AUTHORIZED
No
No
Authorization approved
COMPLETED, CANCELED/REVERSED
PENDING
Provider-defined
PENDING
No
Depends
Provider still processing
Provider-specific
DECLINED
Card auth
FAILED
Usually attempt-terminal
Possibly new attempt later
Authorization declined
—
Unknown new enum
Any
UNKNOWN/REVIEW
No assumption
Unknown
Not yet mapped safely
Investigate
Rows are illustrative only.
Each provider mapping must be verified against current documentation.
State machine guards should include business invariants
Transition rules can validate more than status order.
Examples:
Amount invariant
A completion event should correspond to the amount/currency expected for that financial operation.
Provider identity
An event must correspond to the provider/merchant account that owns the attempt.
Operation lineage
A capture must refer to an applicable authorization.
Visa Acceptance documentation, for example, describes capture as a follow-on transaction using the authorization transaction identifier in applicable flows.
Refund limit
Cumulative successful refunds should not exceed the refundable amount according to the merchant/provider lifecycle unless the specific payment system allows another behavior.
These checks help prevent correct-looking states from being attached to the wrong financial object.
Concurrency can corrupt otherwise correct state logic
Payment state can be updated simultaneously by:
API-response handler
Webhook worker
Status-reconciliation job
Customer request
Operations tool
Example:
Thread A reads: PENDING
Thread B reads: PENDING
Thread A writes: AUTHORIZED
Thread B writes: FAILED
Which wins?
Without concurrency control, the answer may simply be:
whichever database write happened last.
That is not a financial state model.
Possible implementation techniques include:
Optimistic versioning
Compare-and-set
Row locking
Serialized state transitions
Event-stream ordering within a payment aggregate
The right mechanism depends on architecture.
The requirement is that state transitions are atomic enough at the merchant boundary to preserve invariants.
Maintain a current-state view and an immutable history where practical
The merchant usually needs fast access to:
current_state = AUTHORIZED
But investigations need history.
A useful pattern is:
Current payment record
Optimized for operational reads.
State-transition history/event log
Records how the current state was reached.
Conceptually:
Current:
PA-1 = AUTHORIZED
History:
10:00 CREATED
10:00 SUBMITTED
10:01 PENDING
10:03 AUTHORIZED
The history does not need to imply full event-sourcing architecture.
The simpler principle is:
Do not throw away the sequence of financially meaningful state changes.
Separate state from side effects
A state transition can trigger business actions such as:
Fulfillment
Customer notification
Entitlement
Invoice generation
But make those relationships explicit.
For example:
payment state -> COMPLETED
|
v
publish merchant event
|
v
fulfillment service acts idempotently
rather than tightly coupling:
process PSP webhook
-> ship order directly
Why?
Because the same payment state might later be established through:
Synchronous response
Webhook
Status query
Reconciliation
The business action should depend on the merchant state transition, not one transport mechanism.
Define fulfillment state separately from payment state
A paid order can still be:
NOT_FULFILLED
A refunded payment can correspond to an already delivered order.
Therefore do not overload payment state with:
Shipping state
Entitlement state
Order fulfillment state
A useful conceptual relationship is:
ORDER STATE
PAYMENT STATE
FULFILLMENT STATE
REFUND STATE
related, but distinct.
This separation becomes essential when partial capture, partial fulfillment, partial refunds, or asynchronous methods enter the system.
State should survive provider migration
Suppose the merchant migrates from PSP A to PSP B.
Old payments remain:
PA-OLD
provider = PSP_A
canonical_state = COMPLETED
New payments become:
PA-NEW
provider = PSP_B
canonical_state = COMPLETED
Customer support can reason about both through one merchant vocabulary while still retaining the provider-specific transaction history needed for:
Refunds
Disputes
Reconciliation
That is one of the major architectural benefits of merchant-owned state.
The merchant does not need to rewrite historical business meaning because the provider changed.
Do not normalize away provider-specific financial capabilities
Canonical state does not imply provider equivalence.
Provider A may support:
Partial capture
Provider B may not.
Provider C may represent:
Delayed settlement confirmation
differently.
Preserve those capabilities separately from state.
For example:
canonical_state = AUTHORIZED
capabilities:
partial_capture = true
The state tells the merchant where the payment is.
Capability tells the merchant which operation can happen next.
Those are different concerns.
The Merchant Payment State Machine
A useful high-level state model is:
┌─────────────┐
│ CREATED │
└──────┬──────┘
|
┌─────────┴─────────┐
| |
v v
REQUIRES_ACTION SUBMITTED
| |
| |
└─────────┬─────────┘
|
┌────────────────┼────────────────┐
| | |
v v v
PENDING AUTHORIZED UNKNOWN
| | |
| | |
v v |
COMPLETED COMPLETED |
| |
| resolve via
| status/event/reconcile
| |
| ┌────────────────┼──────────────┐
| v v v
| PENDING AUTHORIZED FAILED
|
+--------------------------------------------
Other terminal/attempt-ending paths:
FAILED
CANCELED
EXPIRED
This is not a universal payment state machine.
Its purpose is to expose the important merchant distinctions:
Customer action
Known processing
Authorization
Completion
Known failure
Intentional cancellation
Expiration
Uncertainty
Refunds and reversals should generally be modeled as subsequent financial lifecycle operations rather than pretending they erase what happened earlier.
Add an explicit state-transition record
A practical state-transition record might contain:
transition_id
payment_intent_id
payment_attempt_id
from_state
to_state
transition_source
source_reference
provider
provider_transaction_id
provider_native_state
financial_operation
event_created_at
received_at
processed_at
mapping_version
reason
This allows:
Audit
Incident review
Provider debugging
Reconciliation
State-model migrations
without requiring engineers to reverse-engineer state from logs months later.
Measure state quality
Payment state is infrastructure.
It needs operational metrics.
Unknown-state backlog
Count of attempts still in UNKNOWN beyond the merchant-defined investigation interval.
Useful because uncertainty that never resolves is an operational debt queue.
Unknown-state rate
initiated attemptsattempts entering unresolved unknown state×100
Define the interval and population precisely.
Invalid transition count
Number of attempted transitions rejected by state rules.
Break down by:
Provider
Method
Event type
Integration version
A rise can reveal:
Out-of-order events
Changed provider behavior
Incorrect mappings
Software defects
State conflict rate
payments evaluatedpayments where material data sources disagree on state×100
For example:
merchant = FAILED
provider = AUTHORIZED
Define what counts as a material conflict.
Reconciliation exception rate
payment records reconciledpayment records requiring manual state investigation×100
This connects engineering quality to Finance workload.
Manual payment investigation rate
eligible payment casespayment cases requiring human state investigation×100
Useful for measuring whether state architecture actually scales operationally.
Alerts should focus on contradictions
A useful state system does not merely alert on:
payment failed
Failures can be normal.
More important signals can include:
UNKNOWN aging beyond threshold
Invalid transition spike
Merchant/provider state disagreement
Completed payment lacking expected financial reference
Refund state inconsistent with provider
Sudden appearance of unmapped provider statuses
Webhook/state-processing backlog
Those are signs the merchant may no longer understand its own payment truth.
Testing should attack the state machine, not just the happy path
A useful test suite includes:
Valid progression
CREATED -> SUBMITTED -> AUTHORIZED -> COMPLETED
Customer-action progression
CREATED -> REQUIRES_ACTION -> SUBMITTED -> AUTHORIZED
Pending progression
SUBMITTED -> PENDING -> COMPLETED
Known failure
SUBMITTED -> FAILED
Uncertain outcome
SUBMITTED -> UNKNOWN -> AUTHORIZED
Duplicate event
Same event processed twice, state changes once.
Out-of-order event
AUTHORIZED processed before older PENDING; current state does not regress.
Concurrent updates
API response and webhook attempt state change simultaneously.
Unknown provider enum
Raw status preserved; no unsafe automatic financial conclusion.
The Payment Sandbox Best Practices article should own the larger testing methodology.
Here, the requirement is simply:
Test state invariants, not only provider endpoint responses.
The simplest useful state model should win
Not every merchant needs twenty-five canonical states.
Too few states create ambiguity.
Too many can create operational complexity no system actually uses.
A good design criterion is:
Does this distinction change merchant behavior?
For example, preserving:
PENDING vs UNKNOWN
is valuable because recovery behavior differs.
Preserving:
AUTHORIZED vs COMPLETED
is valuable when capture matters.
Creating four merchant states corresponding to four PSP-internal processing labels may add no useful business distinction.
Canonicalization should reduce noise.
Not recreate provider complexity under new names.
A payment state machine is not an analytics taxonomy
Operations and analytics may want additional dimensions such as:
Decline category
Authentication result
Provider route
Issuer
Fraud result
Settlement state
Do not force all of them into one payment-state enum.
A state machine should primarily answer:
Where is this payment in its merchant-relevant lifecycle?
Analytics can attach richer attributes.
This separation keeps state transitions understandable.
Payment state should be inspectable by humans
A support or payment-operations tool should ideally show more than:
Status: FAILED
Useful context can include:
Merchant payment: PI-8921
Attempt: PA-2
Canonical state: FAILED
Provider: PSP B
Provider state: DECLINED
Provider code: ...
Provider transaction: ...
Previous state: SUBMITTED
Transition source: synchronous API response
Timestamp: ...
For an unknown payment:
Canonical state: UNKNOWN
Reason: request timed out after submission
Last provider lookup: unavailable
Next reconciliation action: scheduled status query
This reduces dependence on engineers querying several systems to reconstruct every payment.
Document the semantics, not just the enum
A state definition should specify:
Meaning
What is known?
Financial implication
What operation has occurred?
Terminal?
Can normal execution continue?
Allowed next states
What transitions are legal?
Merchant actions
Can the order be fulfilled?
Recovery
What should happen if state remains here?
Provider mapping
Which native provider states can map here?
That produces documentation such as:
Canonical state
Meaning
Terminal?
Fulfillment?
Recovery
CREATED
Merchant object exists; no relevant execution yet
No
No
Continue
REQUIRES_ACTION
Customer step required
No
No
Resume customer flow
PENDING
Known nonterminal provider/method state
No
Usually no unless policy says otherwise
Await event/status
AUTHORIZED
Authorization succeeded where applicable
No or business-dependent
Merchant-policy dependent
Capture/void
COMPLETED
Merchant-defined successful completion
Often execution-terminal
Usually eligible
Reconcile
FAILED
Known unsuccessful attempt
Attempt-terminal
No
New attempt if appropriate
UNKNOWN
Outcome cannot currently be established
No
No
Resolve before unsafe retry
Illustrative only; each merchant must define its own semantics.
Until those details are verified, the appropriate positioning is architectural:
A payment provider can expose transaction states and events, but merchants operating across methods, markets, or providers benefit from maintaining a stable internal interpretation of payment state while retaining the provider-native identifiers and status detail required for support and financial reconciliation.
Universal canonical states
Guaranteed event ordering
Exactly-once callbacks
Automatic reconciliation
Cross-provider state normalization
without direct evidence.
Final recommendation
Do not ask:
What status does the PSP return?
Ask:
What does the merchant know about this payment now, what financial operation has occurred, which evidence supports that conclusion, and what transitions remain valid?
Then design state around those answers.
Keep:
Payment intent
separate from:
Payment attempt.
Keep:
Canonical state
alongside:
Provider-native state.
Keep:
Authorized
separate from:
Completed/captured.
Keep:
Failed
separate from:
Canceled.
Keep:
Pending
separate from:
Unknown.
Keep:
Current state
separate from:
State history.
And treat:
API responses
Webhooks
Status queries
Reconciliation
as sources of evidence that update merchant state, not as independent versions of payment truth competing with one another.
The provider should remain authoritative about the transaction behavior it actually executed.
The merchant should remain authoritative about what those facts mean within its own payment system.
And reconciliation should be the process that proves the two have not drifted apart.
That is the real purpose of payment state management:
to maintain a durable, explainable, financially correct representation of the payment even when providers use different vocabularies, events arrive asynchronously, systems disagree temporarily, and the payment continues changing long after the original API request has ended.
This article provides general information and does not constitute legal, regulatory, security, financial, network-rule, or compliance advice. Payment states, event behavior, authorization/capture semantics, refund mechanics, and status APIs vary by provider, payment method, network, jurisdiction, and product. Merchants should verify current behavior with the relevant payment providers and qualified payments, engineering, security, and compliance specialists.
🙂 🙂 🙂 🙂 🙂 🙂 🙂
The brief makes this article the cluster’s merchant-payment-truth article. Its job is not to teach webhook delivery or timeout recovery in depth; it should define the durable state model those mechanisms update. The strongest architecture is merchant-owned canonical state plus preserved provider-native detail, with reconciliation as the mechanism that keeps the two aligned over time.
Brief interpretation
Merchant engineering problem: Multiple providers expose incompatible payment statuses, so business logic becomes tied to PSP-specific words such as succeeded, captured, paid, processing, or pending.
Unique evidence anchor: The provider knows the status of its own transaction. The merchant still needs a durable model describing what that transaction means to the merchant.
Editorial position: Canonical state should normalize business meaning without erasing financially useful provider information.
Common assumption challenged: “We can just store the PSP status.”
Primary system boundary: Provider-native transaction state ↔ merchant-owned payment state.
Primary technical distinction: Current canonical state versus the event/history that produced it.
Merchant decision: Which payment facts should become stable merchant semantics, and which provider-native details should remain attached for reconciliation, support, and financial operations?
Selected format: State-machine design.
Structural rationale: Start with the semantic mismatch between providers, establish merchant intent and attempts, define canonical states and legal transitions, then show how API responses, webhooks, polling, and reconciliation update—not replace—the merchant state model.
ARTICLE
SEO, EDITORIAL & PRODUCTION ADVICE
Payment State Management: How to Design a Payment State Machine
H1
Payment State Management: Designing a Reliable Payment State Machine
Meta title
Payment State Management | Payment State Machine Guide
Meta description
Learn how to design merchant-owned payment states across PSPs, including intents, attempts, pending vs unknown, authorization vs capture, canonical state, event history, and reconciliation.
URL slug
/payment-state-management/
payment state management
Secondary keywords
Prioritize naturally:
payment state machine
payment status lifecycle
payment processing states
payment intent state
payment transaction status
payment status architecture
payment lifecycle states
payment status management
payment state model
canonical payment state
Related entities
Payment intent
Payment attempt
PSP
Payment gateway
Authorization
Capture
Refund
Reversal
Webhook
Event
Payment status
Reconciliation
Idempotency
Asynchronous payment
Article excerpt
Payment providers expose their own transaction statuses, but merchants still need a durable payment-state model that describes what each payment means to the business. A reliable payment state machine separates payment intent from attempts, canonical state from provider-native state, pending from unknown, and current state from the history and evidence used to derive it.
Featured-snippet opportunity
Use high on the page:
What is payment state management?
Payment state management is the merchant-side process of maintaining a durable representation of a payment’s lifecycle using explicit states and transition rules. It translates provider-native statuses into stable merchant meaning while preserving provider detail, asynchronous event history, and reconciliation evidence.
Original practical asset
Merchant Payment State Machine
Recommended primary graphic:
CREATED
|
┌─────────────┴─────────────┐
| |
v v
REQUIRES_ACTION SUBMITTED
| |
└─────────────┬─────────────┘
|
┌─────────────────┼──────────────────┐
| | |
v v v
PENDING AUTHORIZED UNKNOWN
| | |
┌─────┴─────┐ | ┌───────┼───────┐
| | | | | |
v v v v v v
COMPLETED FAILED COMPLETED PENDING AUTHORIZED FAILED
Other attempt-ending states:
CANCELED | EXPIRED
Subsequent financial lifecycle:
COMPLETED
|
+--> REFUND OBJECT(S)
|
+--> REVERSAL / DISPUTE LIFECYCLE AS APPLICABLE
Visual caption
Merchant state should normalize business meaning while preserving the provider-native transaction evidence that produced each transition.
Alt text
Merchant payment state machine showing created, requires action, submitted, pending, authorized, unknown, completed, failed, canceled and expired states, with unknown payments resolved through later evidence and refunds tracked as subsequent financial operations.
Canonical State Mapping Template
A second, highly useful article asset:
Field
Purpose
Provider
Which PSP/payment system
Payment method
Card, wallet, bank method, etc.
Operation
Create, authorize, capture, refund
Provider-native state
Exact current documented status
Canonical merchant state
Merchant business interpretation
Financial meaning
What has actually occurred
Terminal?
Whether normal attempt execution ends
Customer action?
Whether customer must do anything
Allowed next states
Valid merchant transitions
Fulfillment implication
Whether merchant may fulfill
Reconciliation evidence
IDs/data required later
Mapping version
Which rule version produced interpretation
This is the operational artifact I would recommend engineering maintain alongside the code.
Internal-link recommendations
Payment API Integration Guide
Use near the payment-object definitions.
Suggested anchor:
how payment intents and attempts fit into a production payment API integration
Boundary: that article owns the overall API architecture.
Webhooks for Payment Processing
Use from event-vs-state discussion.
Suggested anchor:
how to receive and process payment webhooks safely
Boundary: Webhooks owns delivery, validation, retries, duplicate receipt, and processing pipelines.
Asynchronous Payment Methods
Use from pending/customer-action sections.
Suggested anchor:
designing asynchronous payment lifecycles
Boundary: that article owns redirects, delayed confirmation, expiration, and customer return behavior.
Handling Payment API Timeouts
Recommended additional contextual link from UNKNOWN.
Suggested anchor:
how to resolve an unknown payment after an API timeout
Payment Idempotency Explained
Optional link where state uncertainty intersects retry safety.
Suggested anchor:
using idempotency for repeated financial operations
Authoritative source recommendations
This article should rely lightly on provider-specific documentation because the state model itself is a merchant architecture recommendation.
Visa Acceptance — authorization/capture semantics
Current Visa Acceptance documentation is useful as primary evidence that authorization and capture are separate operations in applicable card flows. Capture documentation describes capture as a follow-on transaction tied to the authorization transaction.
This supports the article’s distinction:
authorized ≠ necessarily completed.
Do not generalize exact Visa states to other PSPs or payment methods.
IETF RFC 9110
RFC 9110 is useful where payment-state uncertainty intersects transport failure and retries. HTTP defines idempotent methods based on the intended effect of repeated requests; it does not imply that arbitrary repeated financial POST operations are harmless after an uncertain response.
Keep the deep idempotency discussion in the dedicated article.
PCI SSC
PCI SSC is relevant only if the final implementation discussion expands into storage/logging of payment credentials. PCI DSS applies to environments that store, process, transmit, or can impact the security of payment account data.
State-management records should generally use identifiers and non-sensitive operational metadata rather than sensitive account credentials.
Proposed author and reviewers
Proposed author: Senior payment-platform engineer or payment architect.
Relevant experience should include:
Payment state machines
Multi-PSP integrations
Asynchronous event processing
Financial reconciliation
Primary technical reviewer: Senior backend/payment engineer.
Review:
Intent/attempt distinction
Canonical states
Transition invariants
Concurrency
State history
Provider mapping
Payment Operations reviewer: Validate:
Investigation workflows
Provider-native information
State visibility
Manual exception handling
Finance/reconciliation reviewer: Validate:
Authorization/capture/refund lineage
Settlement references
Reconciliation-conflict treatment
First-hand contribution opportunity
A particularly strong contribution would be an approved state-reconstruction case.
For example:
Merchant state before incident
→ provider event sequence
→ incorrect transition
→ financial discrepancy
→ revised state model/guard
→ measured reduction in manual exceptions
Potential evidence could include:
Unknown-state backlog
Invalid-transition counts
Reconciliation exceptions
Manual investigation volume
Do not invent the incident or metrics.
A strong expert prompt is:
Which provider status or event sequence most clearly demonstrated that storing the PSP’s status directly was not a sufficient merchant payment model?
Technical fact-check items
For each provider/payment method represented in a mapping table, verify:
Exact current provider state names
Meaning of each state
Terminal/non-terminal behavior
Whether customer action remains
Whether authorization occurred
Whether capture occurred
Whether later reversal is possible
Whether status can change after apparent completion
Whether provider exposes separate payment/authorization/capture objects
Whether status retrieval exists
Do not map by vocabulary alone.
Payment-state assumptions requiring verification
Before publication of any specific transition such as:
AUTHORIZED -> COMPLETED
verify:
Provider
API/product
Payment method
Merchant transaction type
Authorization/capture model
Current documentation
Likewise verify whether:
PENDING can become FAILED
PENDING can expire
COMPLETED is terminal
CANCELED can later receive a late success
UNKNOWN can be queried
Refund status is synchronous or asynchronous
Webhook/state fact-check items
For each provider verify:
Event type
Event ID
Native state in payload
Duplicate-delivery behavior
Ordering guarantees, if any
Event-created timestamp
Retry behavior
Replay support
Signature/authentication
Whether event means state changed or merely that a provider process occurred
Never assume event arrival order equals financial state order.
Concurrency fact-check items
Review implementation behavior for simultaneous:
API callback + webhook
Duplicate webhooks
Two status jobs
Customer retry + provider event
Concurrent capture/refund operations
Verify the system has:
Version/locking control
Transition validation
Idempotent side-effect handling
Transaction-history preservation
Reconciliation requirements
The state model should retain enough identifiers to compare:
ORDER
↓
PAYMENT INTENT
↓
ATTEMPT
↓
PROVIDER TRANSACTION
↓
AUTHORIZATION / CAPTURE
↓
REFUND / REVERSAL
↓
SETTLEMENT
Reconciliation should be able to detect at least:
Merchant says completed; provider does not
Provider says completed; merchant does not
Merchant says failed; provider shows authorized/captured
Refund state disagreement
Missing provider transaction reference
Unexpected amount/currency mismatch
The detailed financial matching methodology belongs to the reconciliation cluster.
State-observability schema
Useful non-sensitive fields can include:
payment_intent_id
payment_attempt_id
order_id
canonical_state
previous_canonical_state
provider
provider_transaction_id
provider_native_state
provider_reason_code
operation
transition_source
source_reference
event_created_at
received_at
processed_at
mapping_version
integration_version
last_provider_status_check
reconciliation_status
Do not include raw card credentials or secrets.
Metric definitions
Unknown-state backlog
Count of payment attempts remaining in merchant-defined UNKNOWN state beyond the investigation interval.
Always define that interval.
Invalid-transition rate
state-transition attemptsrejected invalid state transitions×100
Useful by provider/method/integration version.
State-conflict rate
payments evaluated for conflictpayments with material merchant/provider state disagreement×100
Define material disagreement explicitly.
Reconciliation-exception rate
payment records reconciledpayment records requiring manual state investigation×100
Manual-payment-investigation rate
eligible paymentspayments requiring human state investigation×100
These metrics make “good state management” operationally measurable rather than architectural rhetoric.
API/version fact-check items
State mapping is vulnerable to API change.
For every provider maintain:
API version
Event/webhook schema version
Integration version
Known status enumeration
Last status-definition review
Mapping-rule version
Deprecated state values
Newly introduced state values
A provider adding an enum value can be operationally breaking even if the provider regards the change as backward-compatible.
Unknown status values should fail safely into investigation rather than fabricated state.
Sandbox/test requirements
The sandbox/test plan should attempt, where supported, to exercise:
Successful state progression
Known failure
Customer action
Pending state
Timeout/unknown
Duplicate event
Out-of-order event
Repeated status response
Invalid transition
Authorization/capture
Refund state
Concurrency
Do not claim the sandbox can reproduce every production state transition unless the provider documents that capability.
Migration implications
Merchant-owned canonical state can reduce provider migration coupling, but verify:
Legacy provider transactions remain retrievable
Refunds against legacy payments remain possible
Dispute references remain available
Provider-native history is retained
Old state mappings remain interpretable
New provider statuses can map into the merchant model without rewriting historical states
Do not rewrite historical provider-native values to make them resemble the new PSP.
Security fact-check items
Payment state storage should be reviewed to ensure it does not inadvertently contain:
PAN
CVV
Secret keys
Access tokens
Sensitive authentication data
PCI DSS applies to environments that store, process, transmit, or can affect the security of payment account data.
A state machine does not need sensitive credentials merely to describe financial state.
Objects and identifiers
Authorization/capture IDs where applicable
Refund IDs
Status model
State definitions
Terminal states
Pending states
Failure states
Cancellation/expiration states
Method differences
Card lifecycle
Wallet lifecycle
Bank/local-payment lifecycle
Async method states
Events
Callback/webhook mechanism
Event identifiers
Delivery behavior
Authentication/signature
Retry
Ordering
State retrieval
Status/query APIs
Queryable identifiers
Timing/availability
Financial lifecycle
Authorization
Capture
Void/reversal
Refund
Partial refund
Reconciliation
Reporting identifiers
Settlement linkage
Payment/refund references
Until verified, avoid claims such as:
Amber-flag claims
Require current evidence before saying:
succeeded universally means captured.
paid universally means settled.
processing and pending are interchangeable.
failed means no financial action occurred.
canceled means a payment can never arrive later.
A timeout means UNKNOWN for every provider circumstance.
Every payment method uses authorization.
Every authorization must be captured.
Every webhook indicates a state transition.
Webhooks arrive in order.
Latest event always represents current state.
Provider status is authoritative for merchant fulfillment.
Merchant database alone is authoritative financial truth.
Refunds should overwrite payment state.
One canonical state machine works for every payment method.
Cluster sameness audit
Article: Payment State Management
Opening device: Semantic comparison of provider statuses—succeeded, captured, paid—rather than another API-failure scenario.
Merchant engineering problem: Provider-native statuses leak into merchant business logic and create incompatible interpretations.
Integration lifecycle stage: Runtime financial-state management.
Primary system boundary: Provider-native transaction state ↔ merchant canonical state.
Primary technical distinction: Current canonical state vs event/history.
Secondary distinctions: intent vs attempt; authorization vs capture; failed vs canceled; pending vs unknown; event vs state.
Primary format: State-machine design.
Unique evidence anchor: Provider understands its transaction; merchant still needs durable merchant meaning.
Editorial position: Normalize business semantics while retaining provider-native evidence.
Original framework: Merchant Payment State Machine.
Secondary asset: Canonical State Mapping Template.
Main technical risk: Incorrect transitions create false financial truth even when every upstream event was technically valid.
Primary metrics: Unknown-state backlog, invalid transitions, state conflicts, reconciliation exceptions, manual investigations.
Merchant decision: Which meanings belong in stable merchant state, and which provider details should remain contextual evidence?
Conclusion: Merchant state is a durable, reconciled interpretation of financial evidence—not a copy of the last PSP status observed.
Primary internal links: Payment API Integration Guide → Webhooks for Payment Processing → Asynchronous Payment Methods → Handling Payment API Timeouts.
Boundary: Does not own webhook-delivery implementation, timeout recovery procedure, idempotency-key design, or the full asynchronous-payment lifecycle.
That gives the article a distinct role in the cluster: the API article explains how payments are executed; the webhook article explains how events arrive; the timeout article explains uncertainty; Payment State Management explains how all of those inputs become one durable and auditable merchant interpretation of the payment.
Need help narrowing down payment options?
Answer a few questions about your business and compare the capabilities that matter to you.
Explore matching options ↗