Payment Components

Every payment in MoneyHash moves through three layers. A single field - payment_status - always tells you where the money is, regardless of which layer you're inspecting.

Layer 1 - IntentLayer 2 - TransactionLayer 3 - Operation
The payment session. Holds context, orchestrates retries, maps to your order.A single attempt inside the intent. Each retry or provider switch is a new transaction.A financial action inside a transaction - authorize, capture, void, purchase, refund.

How they relate

One intent can have many transactions (retries, fallbacks). One transaction can have many operations (authorize → capture → refund). An intent stays UNPROCESSED until a transaction succeeds or it is closed/expired - only then does it reach a terminal state and lock.

Key rule

As long as an intent is UNPROCESSED, new transactions can be created. Once it reaches a terminal status (Processed, Closed, Expired), no further transactions are allowed.

Layer 1
Intent — the payment session
Maps to your order. Holds context. Orchestrates all attempts.
UNPROCESSED — while trying PROCESSED — on success CLOSED / EXPIRED — terminal
Layer 2 · Trial 1
Transaction
Failed
Purchase · failed
Layer 2 · Trial 2
Transaction
Successful
Authorize · successful
Capture · successful
Refund · successful
Transaction — Trial ...
More trials possible while intent is UNPROCESSED
Authorize — reserve funds
Capture — collect funds
Purchase — authorize + capture in one
Refund — return funds
Failed attempt

Intent lifecycle statuses

StatusWhenFurther transactions?
UNPROCESSEDInitial. No successful transaction yet.Yes
PROCESSEDA transaction succeeded.No - locked
CLOSEDManually terminated. Any pending attempt moves to failed.No - locked
EXPIREDTime limit exceeded. Any pending attempt moves to failed.No - locked

Transaction lifecycle statuses

A transaction represents one attempt. Its status is available in two places, with different formats:

  • active_transaction.status on a GET Intent - uppercased, e.g. SUCCESSFUL, FAILED, PENDING_AUTHENTICATION
  • data.status on a GET Transaction - prefixed with operation type, e.g. purchase.successful, purchase.failed, purchase.pending_authentication

The webhook event follows the same pattern: transaction.{operation}.{status} - for example transaction.purchase.pending_authentication or transaction.purchase.successful.

StatusDescription
PENDING_AUTHENTICATIONAwaiting 3DS authentication with the cardholder's bank. Not a failure - do not act on this.
SUCCESSFULAttempt completed successfully.
FAILEDAttempt failed. Intent remains UNPROCESSED if not yet terminal - a retry may follow.
REFUNDEDCaptured funds have been returned.
VOIDEDAuthorization was cancelled before capture.
Important

active_transaction.status can show PENDING_AUTHENTICATION even after a payment resolves successfully (e.g. after 3DS completes). Never use active_transaction.status alone as your source of truth. Always rely on payment_status.status and operations[].latest_status.value for the definitive outcome.


Operation lifecycle statuses

Each operation progresses through the following statuses:

StatusDescription
pendingOperation initiated, awaiting provider response.
pending_authenticationAwaiting 3DS authentication. Applies to purchase and authorize operations.
successfulOperation completed successfully.
failedOperation failed.

A webhook fires on every status change, following the pattern transaction.{operation}.{status} - for example transaction.purchase.pending_authentication, transaction.purchase.successful, or transaction.refund.failed.

The full status progression for a 3DS purchase is: pendingpending_authenticationsuccessful (or failed). The statuses[] array on each operation contains the complete timeline.


Payment status reference

payment_status is present on every webhook and GET response. It is the single monetary indicator that cuts across all layers - always telling you where the money is.

Customer eventpayment_statusIntent status
Payment created, no attemptsno_authorize_attemptsUNPROCESSED
Authorization in progress (incl. 3DS)authorize_attempt_pendingUNPROCESSED
Attempt failedauthorize_attempt_failedUNPROCESSED
Authorized, not yet capturedauthorizedPROCESSED
Money capturedcapturedPROCESSED
Authorization voidedvoidedPROCESSED
Payment refundedrefundedPROCESSED
Expired or manually closedabortedEXPIRED / CLOSED

Balances field

The balances object inside payment_status tracks monetary amounts across operations. Use it to detect partial captures, partial refunds, and available amounts without manual reconciliation.

ScenarioKey balances
Full capturetotal_authorized: 100 , total_captured: 100 , available_to_capture: 0
Partial capturetotal_authorized: 100 , total_captured: 60 , available_to_capture: 40
Full refundtotal_captured: 100 , total_refunded: 100 , available_to_refund: 0
Partial refundtotal_captured: 100 , total_refunded: 30 , available_to_refund: 70
Full voidtotal_authorized: 100 , total_voided: 100 , available_to_void: 0

When is a payment done?

A payment has reached its terminal (eventual) state when:

  • Intent status is PROCESSED, CLOSED, or EXPIRED
  • payment_status is captured, voided, refunded, or aborted

Once terminal, no new transactions or operations can be created on the intent.

Safest signal

Always use payment_status.status combined with operations[].latest_status.value as your source of truth — not active_transaction.status, which can be misleading after 3DS flows. Both fields are present on every webhook and API response at every layer.


Step-by-step flow

1
Intent created
Maps to your order, stores customer context.
Status UNPROCESSED
payment_status NO_AUTHORIZE_ATTEMPTS
2
Transaction created
A Transaction is created. MoneyHash routes to a provider.
payment_status AUTHORIZE_ATTEMPT_PENDING
3
Operation fires
purchase or authorize initiated.
Webhook transaction.purchase.pending
payment_status AUTHORIZE_ATTEMPT_PENDING
4
3DS triggered 3DS only
Operation moves to pending_authentication — wait, do not treat this as a failure.
Webhook transaction.purchase.pending_authentication
payment_status AUTHORIZE_ATTEMPT_PENDING
5
Attempt fails if failed
Intent stays UNPROCESSED. MoneyHash may automatically retry on another provider — check intent status before marking an order as failed.
Webhook transaction.purchase.failed
payment_status AUTHORIZE_ATTEMPT_FAILED
6
Attempt succeeds if successful
Intent moves to PROCESSED and locks — no further transactions allowed.
Webhook transaction.purchase.successful
payment_status CAPTURED
7
Post-capture operations
Issue a capture, void, or refund as needed. Each fires its own webhook.
Webhook transaction.{operation}.{status}
payment_status CAPTURED VOIDED REFUNDED
8
Terminal state reached
payment_status reflects the final monetary outcome. No further actions possible.
Webhook intent.processed
payment_status CAPTURED VOIDED REFUNDED ABORTED

Did this page help you?