Authorization flows

A card payment is really two events: the issuer promising the money (authorization) and you taking it (capture). Collapse them for e-commerce; split them for hotels, rentals, and delayed shipping. This page covers the whole pre-auth toolkit — incrementing, capturing in parts, and undoing at each stage.

Purchase vs. pre-authorization

The capture_method you pass at create time picks the model:

capture_method: "automatic" (purchase)capture_method: "manual" (pre-auth)
Auth + captureOne step — approval captures.Two steps — authorize now, capture when ready.
Status after approvalsucceededrequires_capture
Customer seesA chargeA hold, until you capture or release it
Time limitCapture within 7 days, or the hold releases and the payment moves to canceled.
MethodsAllCards only — bank redirects, wallets, and real-time rails are single-message and capture on approval.

A pre-auth's life, drawn out — a €49.00 hold that grows to €69.00 and is captured in two parts:

requires_capture auth 4900 · captured 0 requires_capture auth 6900 · captured 0 requires_capture auth 6900 · captured 3000 succeeded captured 6900 increment capture 3000 capture 3900 canceled hold released void (cancel) 7-day expiry

Void and expiry apply from any requires_capture state — drawn once each for legibility. On networks that support multiple capture the payment stays capturable between partial captures; elsewhere the first capture closes the authorization and releases the remainder.

Incremental authorization

When the amount grows before capture — a hotel folio, a bar tab, an upsell — raise the hold instead of asking the customer to pay again. amount is the new total in minor units, not the delta; the issuer approves the difference:

POST /v1/payments/{id}/increment_authorization
curl https://api.finscale.dev/v1/payments/pay_2Xw9cJ6pF3Md/increment_authorization \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Idempotency-Key: idem_ord_9f21_0716_incr1" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 6900 }'

# 4900 was authorized; 6900 is the NEW TOTAL — the issuer approves the 2000 difference
  • Pre-authorizations only: the payment must be in requires_capture.
  • The new total must exceed the current amount — you can increment multiple times before capturing.
  • Not every card network supports it. Where it isn't supported — or the payment isn't a pre-auth — you get 400 invalid_request_error with code payment_not_incrementable, and the original authorization is untouched.

Partial and multiple capture

POST /v1/payments/{id}/capture takes the money. Omit amount to capture the remaining authorization in full; pass less for a partial capture:

POST /v1/payments/{id}/capture — first shipment
curl https://api.finscale.dev/v1/payments/pay_2Xw9cJ6pF3Md/capture \
  -H "Authorization: Bearer sk_test_51FinscaleDemo…" \
  -H "Idempotency-Key: idem_ord_9f21_0716_cap1" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 3000 }'

# where the network supports multiple capture: status stays requires_capture,
# amount_captured: 3000 — capture again later for up to the remaining 3900
  • amount must be ≤ the remaining authorized amount (amount − amount_captured).
  • Multiple capture — on supporting networks, a partial capture leaves the payment in requires_capture; amount_captured accumulates across calls until the authorization is drawn down. Ideal for split shipments.
  • Single capture — on networks without multiple-capture support, the first capture closes the authorization; the un-captured remainder is released to the customer.
  • The payment reaches succeeded when the authorization closes with funds captured. Refund flags then compare against amount_captured, not amount.
  • Capturing a payment that isn't requires_capture returns 400 invalid_request_error, code payment_not_capturable.

Void vs. reversal vs. refund

Three ways money "goes back", with very different mechanics. Pick by where the payment is in its lifecycle:

Cancel (before auth)Void / authorization reversalRefund
CallPOST /v1/payments/{id}/cancelPOST /v1/payments/{id}/cancelPOST /v1/refunds
Payment statusrequires_confirmation / requires_actionrequires_capturesucceeded
What happensThe payment stops before any money is held.Finscale sends an authorization reversal to the provider — the hold releases immediately instead of expiring days later.Captured funds move back over the payment rail; a rf_ object tracks them.
Customer seesNothingThe hold disappears from their available balance.Money re-arrives — timing varies by method.
Resultstatus: "canceled"status: "canceled", provider: nullStatus stays succeeded; amount_refunded / refunded track state.
Partial possibleNo — a void is all-or-nothing.Yes, by minor-unit amount.
On settlement reportsNever appearsNever appearsA negative line in the report's refunds total.
Prefer void over expiry, and void over refund A void costs nothing and clears the customer's hold instantly. Letting a hold expire ties up their money for days; capturing then refunding moves real funds twice and shows up in your settlement reports. If you know you won't capture — void.

Payments that are processing or terminal can't be canceled — refund a succeeded payment instead. See Refunds for splitting and settlement behavior.

Webhook events

  • payment.processing — the authorization is being routed.
  • payment.succeeded — fires when the authorization closes with funds captured (after the final capture), not at authorization time.
  • payment.failed — the authorization itself was declined.

There is no dedicated "authorized" event: the synchronous response (or a GET) showing requires_capture is your authorization signal. Voids and expiries surface as the payment reading canceled.

Error scenarios

ScenarioResponse
Capture on a payment not in requires_capture400 invalid_request_error, code payment_not_capturable
Capture more than the remaining authorization400 invalid_request_error — increment first, then capture
Increment on a non-pre-auth, or an unsupported network400 invalid_request_error, code payment_not_incrementable; the original hold is unaffected
Increment to an amount ≤ the current total400 invalid_request_erroramount is the new total, and it must grow
Capture after the 7-day windowThe payment is already canceled — the capture returns payment_not_capturable. Re-charge the customer with a fresh payment.
Cancel a processing or terminal payment400 invalid_request_error — refund instead if it succeeded
  • Payment lifecycle — where requires_capture sits in the full state machine.
  • Refunds — after capture, this is the only way back.
  • Cards & 3DS — the only method family with a two-step auth model.
  • Idempotency — key every increment and capture; retries must be safe.