Payment lifecycle

A Payment is the one object you create, track, and reconcile — whatever the method your customer picks, and whichever provider ends up processing it. This page covers the object, the API state machine, and the full acquiring chain underneath it — from created to reconciled.

The Payment object

Create a payment with POST /v1/payments, retrieve it with GET /v1/payments/{id}. The same shape comes back in webhooks, list responses, and settlement lines. Here is the recurring example order — ord_9f21_0716, €49.00 — after it succeeded:

GET /v1/payments/pay_8Q2mX4nT1cVb
{
  "id": "pay_8Q2mX4nT1cVb",
  "object": "payment",
  "amount": 4900,
  "amount_captured": 4900,
  "currency": "EUR",
  "status": "succeeded",
  "payment_method": "ideal",
  "reference": "ord_9f21_0716",
  "customer": "cus_4T8nQb2Wp1Zr",
  "provider": "prov_eu_acq_01",
  "provider_attempts": [
    { "provider": "prov_eu_acq_01", "outcome": "approved", "latency_ms": 412 }
  ],
  "risk": null,
  "capture_method": "automatic",
  "amount_refunded": 0,
  "refunded": false,
  "partially_refunded": false,
  "next_action": null,
  "created_at": "2026-07-16T09:24:31Z",
  "livemode": false
}
FieldTypeNotes
idstringUnique id, prefix pay_. Use it for retrieval, refunds, capture, cancel.
amountintegerMinor units. 4900 = €49.00. See Amounts & minor units.
amount_capturedintegerMinor units captured so far. Equals amount after automatic capture; can trail it during partial capture.
currencystringISO 4217 uppercase, e.g. EUR.
statusstringOne of the lifecycle statuses below.
payment_methodstringMethod code — card, ideal, pix, … See Payment methods.
referencestringYour order id. Echoed on every webhook and settlement line.
customerstringThe cus_ id of the attached customer, or null. The create request accepts an inline { id | email, name } object; responses always return the id string.
providerstringOpaque id of the provider that processed (or is processing) the payment.
provider_attemptsarrayEach routing attempt: provider, outcome, latency_ms. See Routing.
riskobjectRisk result for card-family payments: score 0–99, decision (approved / review / blocked), AVS/CVV checks. null for methods that aren't risk-scored, like this iDEAL payment. See Risk engine.
capture_methodstringautomatic (default) or manual. See Capture.
amount_refundedintegerMinor units refunded so far, across all refunds.
refundedbooleantrue once amount_refunded == amount.
partially_refundedbooleantrue while 0 < amount_refunded < amount.
next_actionobjectWhat the customer must do next — currently always a redirect — or null.
created_atstringISO 8601, UTC.
livemodebooleanfalse for payments created with sk_test_ keys.

Lifecycle & statuses

Every payment moves through one state machine, regardless of method or provider. Forward-only: a payment never returns to an earlier status.

create requires_confirmation requires_action processing succeeded confirm: false confirm: true (default) canceled requires_capture failed cancel — from requires_confirmation, _action, _capture final decline capture_method=manual POST /v1/payments/{id}/capture void terminal — funds captured terminal — failed terminal — canceled

Refunds never change status. After success, refund state lives in the flags amount_refunded, partially_refunded and refunded — see Refund state.

StatusMeaningTerminal
requires_confirmationCreated with confirm: false, awaiting an explicit POST /v1/payments/{id}/confirm. Only seen in two-step integrations.No
requires_actionThe customer must act: bank redirect, 3-D Secure challenge, QR scan, in-app approval. Follow next_action.No
processingFinscale is routing the transaction across your provider set. Usually sub-second for cards; minutes-to-days for direct debit.No
requires_captureAuthorized — funds held at the issuer, waiting for your capture. Only with capture_method: "manual".No
succeededFunds captured. The payment will appear on a settlement report.Yes
failedNo provider approved the payment. Inspect provider_attempts for the per-attempt outcomes.Yes
canceledYou (or expiry) canceled it before completion. Any authorization hold is released.Yes

Payments skip states they don't need. The canonical request below confirms at create time (the default confirm: true), so the payment is born at requires_action — no requires_confirmation stop:

POST /v1/payments
curl https://api.finscale.dev/v1/payments \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Idempotency-Key: idem_ord_9f21_0716" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "EUR",
    "payment_method": "ideal",
    "reference": "ord_9f21_0716",
    "customer": { "email": "anna@example.com" },
    "return_url": "https://shop.example.com/checkout/return"
  }'
Don't poll for the outcome Redirect the customer to next_action.url, then rely on the payment.succeeded / payment.failed webhooks. The return URL tells you the customer came back — the webhook tells you what happened.

The full acquiring chain

The status enum is the API's view. Underneath it runs the acquiring chain every card payment travels — nine stages from your create call to money reconciled in your bank. The first five live on the Payment; the last four live on settlement objects:

THE PAYMENT — STATUS ON pay_ created 3-D Secure authenticated authorized captured POST /v1/payments requires_action processing requires_capture succeeded provider batches captured transactions THE MONEY — SETTLEMENT OBJECTS cleared settled funded reconciled provider clearing — no API state stl_ report ready settlement.report.ready po_ payout paid → your bank report rows ↔ payments ↔ payout

Stage by stage, mapped to what you can observe in the API:

StageWhat happensWhere you see it
createdYou call POST /v1/payments; Finscale validates, screens risk, and assigns pay_….The 201 response. With confirm: false, status requires_confirmation.
3-D SecureThe issuer challenges the customer to authenticate. Skipped when no challenge is required.status: "requires_action" + next_action.url; event payment.requires_action. See Cards & 3DS.
authenticatedThe challenge passed; the transaction proceeds to authorization via routing.status: "processing"; event payment.processing.
authorizedThe issuer approves and holds the funds on the customer's account.status: "requires_capture" with capture_method: "manual". With automatic, the payment passes through without stopping. See Authorization flows.
capturedYou (or automatic capture) draw down the authorization — the money is now owed to you.status: "succeeded", amount_captured set; event payment.succeeded.
clearedThe provider submits its capture batch to the card networks; funds move between banks.No API state — this happens provider-side between capture and settlement.
settledFinscale normalizes every provider's settlement files into one report per provider, day, and currency.A stl_… settlement report flips to readytotals obey gross + refunds + fees = net; event settlement.report.ready.
fundedThe report's net is paid out to your bank account.A po_… payout referencing the report, with an arrival_date.
reconciledYou match report rows to your orders, and the payout to the bank credit.Your reference on every CSV row; the id chain pay_ ↔ stl_ ↔ po_. Workflow in Reconciliation.

Non-card rails compress the chain: bank redirects and real-time payments are single-message (authorization and capture are one step, and the bank's own login replaces 3-D Secure), but every method still ends in the same clear → settle → fund → reconcile tail.

Amounts & minor units

amount is always an integer in the currency's minor unit, per ISO 4217. No floats, ever — 49.00 is rejected with an invalid_request_error.

You want to chargeamountcurrencyExponent
€49.004900EUR2
¥4,9004900JPY0 — zero-decimal
KD 4.9004900KWD3 — three-decimal
R$ 265.0026500BRL2

Rule of thumb: amount = display value × 10^exponent. All derived fields — amount_refunded, refund amount, settlement gross/fee/net — use the same unit, so arithmetic across objects is always integer-safe.

Capture now vs. manual capture

By default (capture_method: "automatic") authorization and capture are one step: processing resolves straight to succeeded. For card payments you can split them — authorize now, capture when you ship:

POST /v1/payments — manual capture
curl https://api.finscale.dev/v1/payments \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Idempotency-Key: idem_ord_9f21_0716" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "EUR",
    "payment_method": "card",
    "capture_method": "manual",
    "reference": "ord_9f21_0716"
  }'

# → status: "requires_capture" once the authorization is approved

Capture the full amount, or less (for example after a partial shipment). Whatever you don't capture is released back to the customer:

POST /v1/payments/{id}/capture
curl https://api.finscale.dev/v1/payments/pay_8Q2mX4nT1cVb/capture \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 4900 }'

# → status: "succeeded". Omit "amount" to capture in full.
  • Authorizations expire — capture within 7 days, or the hold is released and the payment moves to canceled.
  • Void an authorization you no longer need with POST /v1/payments/{id}/cancel. The hold is released immediately, no fee.
  • Manual capture is a card feature. Bank redirects, RTP and wallets are single-message rails — they capture on approval.
  • The full pre-auth toolkit — raising a hold with increment_authorization, partial and multiple capture, and void vs. refund semantics — is in Authorization flows.

Multi-currency

Charge in the customer's currency by setting currency — the same integration handles EUR, PLN, BRL, SEK and every other currency your account is enabled for. Three things follow from the currency:

  • Method availability. Methods are currency-bound: PIX is BRL-only, Blik is PLN-only, Swish is SEK-only. The method catalog lists each method's currencies.
  • Routing. Currency is a hard filter in the routing decision — only providers that process the currency are candidates.
  • Settlement. You receive one payout per currency. Optional conversion to a single payout currency is applied at settlement, and every conversion is itemized on the report.

Presentment vs. settlement currency — and exactly when FX applies — is covered in Multicurrency.

Refund state

Refunds are their own objects (rf_…) and never rewind the payment's status. A fully refunded payment still reads status: "succeeded" — with refunded: true. Track refund state through the three flags:

Stateamount_refundedpartially_refundedrefunded
No refunds0falsefalse
Partially refunded1500truefalse
Fully refunded4900falsetrue

How refunds are created, split, and settled — including the local-method caveats — is covered in Refunds.