Miracle Docs

Payment Lifecycle

Every payment follows a lifecycle from creation to a final status. Understanding this lifecycle is essential for building a reliable integration — your backend logic, order fulfillment, and customer communication all depend on reacting to the right status at the right time.


Payment statuses

When you create a payment, it starts in processing and moves through statuses until it reaches a terminal state. You receive a webhook at each transition.

StatusDescriptionTerminalWebhook
processingPayment sent to provider for processing.Nopayment.processing
requires_actionCustomer action needed (3D Secure redirect, bank authentication, QR scan).Nopayment.requires_action
requires_captureAuthorized successfully, waiting for you to capture manually.Nopayment.requires_capture
capturedCapture completed (manual capture flow only). Funds have been charged.Yes (except refund transitions to refunded or partially_refunded)payment.captured
succeededPayment completed successfully (auto-capture flow). Funds have been charged.Yes (except refund transitions to refunded or partially_refunded)payment.succeeded
failedPayment declined by provider or errored during processing.Yespayment.failed
canceledPayment voided before capture or canceled by the merchant/system. Authorization expiry leads to cancellation.Yespayment.canceled
partially_refundedPart of the captured amount has been refunded to the customer.Yes (except transition to refunded when remaining amount is refunded)payment.partially_refunded
refundedFull amount has been refunded to the customer.Yespayment.refunded

Terminal vs non-terminal: Once a payment reaches a terminal status, it will not change — except that succeeded and captured payments can later become refunded or partially_refunded through a refund operation.


Payment flow

Create Payment
    |
    v
processing ---------------------> succeeded  (auto-capture)
    |                                 |
    |---> requires_action             |---> partially_refunded ---> refunded
    |         |                       |---> refunded
    |         |---> processing -----> succeeded
    |         |                  |--> requires_capture ---> captured
    |         |                  |--> failed
    |         |---> failed
    |         |---> canceled
    |
    |---> requires_capture ---------> captured
    |         |                          |
    |         |---> canceled             |---> partially_refunded ---> refunded
    |                                    |---> refunded
    |
    |---> failed
    |---> canceled

Auto-capture vs manual capture

By default, payments use auto-capture: authorization and capture happen in a single step. The payment transitions directly from processing to succeeded.

If you need to authorize first and capture later (e.g., charge only when you ship the order), set captureMethod to "manual" when creating the payment:

{
  "amount": { "currency": "USD", "valueMinor": 5000 },
  "merchantReference": "order-1234",
  "captureMethod": "manual",
  "paymentMethod": { ... }
}

With manual capture, the flow changes:

  1. Payment reaches requires_capture — funds are held on the customer's card.
  2. You call POST /v1/payments/{id}/capture to charge the held amount.
  3. Payment moves to captured.

If you decide not to charge, call POST /v1/payments/{id}/cancel to release the hold. The payment moves to canceled.

Authorizations expire (typically 7 days for cards, varies by provider). If you do not capture or cancel before expiry, the hold is released automatically and the payment is canceled.

See Capture for the full auth-and-capture guide.


Async nature of payments

Payment processing is inherently asynchronous. When you create a payment, the API response returns the initial status (usually processing), but the final result arrives later via webhook.

Always use webhooks as the source of truth for payment status. Do not rely solely on the synchronous API response or customer redirects to determine whether a payment succeeded.

A typical flow:

  1. You call POST /v1/payments — response returns status: "processing".
  2. The provider processes the payment (may take milliseconds or minutes).
  3. Miracle sends a payment.succeeded or payment.failed webhook to your endpoint.
  4. Your webhook handler fulfills the order or notifies the customer.

You can also poll GET /v1/payments/{id} at any time, but webhooks are the recommended approach.


Amount format

All monetary amounts use the MoneyAmount format with integer minor units in the valueMinor field. No decimals, no floating point.

CurrencyExponentExamplevalueMinor
USD, EUR2$50.005000
JPY050005000
BHD35.000 BHD5000
{
  "amount": {
    "currency": "USD",
    "valueMinor": 5000
  }
}

Key concepts

Dive deeper into specific payment topics:

  • HPP Integration — redirect-based checkout using the Hosted Payment Page
  • Capture — authorization and capture (two-step) flow
  • Refunds — returning funds to the customer
  • Cancel / Void — canceling a payment before capture
  • 3D Secure — handling customer authentication challenges
  • Payment Methods — supported methods and discovery API
  • Idempotency — safe retries with idempotency keys

On this page