vermilion
guides

Payments

A payment moves through one normalized status machine regardless of PSP: created → processing → requires_action? → authorized → captured, ending in voided, failed, partially_refunded or refunded. Amounts are always integers in the currency's minor unit.

Payment methods

typepayloadroutes to
sim_cardnone — outcome scripted by amountsimulator (sandbox test PSP)
cardstripe.token and/or adyen.encrypted* fields from the PSP's client-side componentsany PSP whose section is present
tokentoken: "tok_…"PSPs holding a reference for the token

For card payments you can include multiple provider sections — that's what lets a single request survive fallback across PSPs.

Manual capture

# authorize now, capture when you ship POST /v1/payments { …, "capture_method": "manual" } → status: authorized POST /v1/payments/pay_…/capture { "amount": 900 } → status: captured (partial allowed) POST /v1/payments/pay_…/void → status: voided

Refunds

# omit amount to refund the remaining balance POST /v1/payments/pay_…/refunds { "amount": 1500, "reason": "requested" }

Multiple partial refunds are supported; the platform enforces the refundable balance and rejects over-refunds with 400 invalid_request before the PSP is ever called. Payment status moves to partially_refunded and finally refunded.

Searching

GET /v1/payments?status=failed&limit=50&created_gte=2026-07-01 GET /v1/payments?starting_after=pay_… # cursor pagination

3DS / SCA

When a challenge is required the payment returns status: requires_action with next_action.url when the PSP provides a redirect. Completion arrives via the PSP's webhook and is reflected in the payment status — subscribe to payment.captured / payment.failed rather than polling.