Payment lifecycle
Two questions matter, and they have different answers.
Has the customer paid? That decides whether you fulfil the order. Read
outcome.
Has the money reached my wallet? That is your payout, and it happens after
fulfilment. Read settlement.
Fulfil on outcome
Section titled “Fulfil on outcome”curl https://hub.stablezact.com/api/v1/payments/PAYMENT_UUID/status \ -H "X-Public-Key: pk_your_public_key"{ "success": true, "payment": { "paymentId": "e6c1…", "outcome": "paid", "isFinal": true, "amount": "50.00", "amountReceived": "50.00", "currency": "USDC", "network": "Base", "transactionHash": "0x…" }}outcome has three values.
outcome |
Meaning | Fulfil |
|---|---|---|
pending |
not paid yet, and not finished | no |
paid |
the customer’s money is in | yes |
failed |
expired, cancelled, or failed | no |
That is the whole rule. Fulfil on paid, and nothing earlier.
outcome is the same regardless of how the customer paid. A payment made
straight from a wallet and one made to a generated deposit address both reach
paid, so your code needs one branch, not two.
isFinal tells you whether the answer can still change. outcome: pending
with isFinal: false means keep waiting.
Then watch settlement for your payout
Section titled “Then watch settlement for your payout”A payment reads paid the moment the customer’s money is in. Getting it to
your wallet is a separate step, and on rare occasions it is slower than the
payment itself. settlement is where that shows up:
curl https://hub.stablezact.com/api/v1/payments/PAYMENT_UUID \ -H "X-Public-Key: pk_your_public_key"{ "success": true, "payment": { "settlement": { "state": "settled", "message": "Paid out to your wallet." } }}state |
Meaning |
|---|---|
pending |
waiting for the customer |
settling |
payment received, payout on its way |
delayed |
payout still in progress, retrying on its own |
settled |
in your wallet |
failed |
payout could not be completed, we contact you |
delayed is not a problem you have to act on. It retries automatically and the
money is accounted for. failed is the only state that needs anyone, and we
come to you rather than waiting for you to notice.
Every state carries a message written for a person, so you can surface it in
your own dashboard without writing the copy yourself.
How much arrived: paymentState
Section titled “How much arrived: paymentState”paymentState answers a third question: what the money did, as distinct from
whether the payment succeeded.
paymentState |
Meaning |
|---|---|
awaiting |
nothing has arrived yet |
underpaid |
some arrived, less than invoiced |
completed |
the full amount arrived |
overpaid_refunding |
more arrived than invoiced, excess being handled |
expired_refunding |
expired underpaid, what arrived is being returned |
underpaid is not a failure. The payment stays open and completes if the
customer sends the difference, and you receive a payment.underpaid webhook
when it happens, which is the moment to prompt them.
Confirming
Section titled “Confirming”Webhooks, if you can receive them. You are told, rather than asking. See Webhooks.
Polling, otherwise, against the status endpoint above.
Poll from your server, not the browser. A status read in the customer’s browser can be faked by the customer.
The SDK’s onSuccess callback fires when the customer’s wallet accepts the
transaction. At that moment the transaction has been broadcast, not confirmed.
It can still fail, be replaced by a higher-fee transaction, or be dropped.
Treat onSuccess as “the customer has submitted” and confirm separately.
Deposit-address payments
Section titled “Deposit-address payments”Some payments are made to an address generated for that one payment, rather than sent straight to your wallet. The money is then forwarded on to you.
This changes nothing about how you integrate. outcome still goes to paid,
and settlement still tells you when the payout lands. The extra step is only
visible in the deposit webhook events, listed in
Webhooks.
The amount that arrives is not always the amount asked for. See Overpayments and refunds.
Raw status values
Section titled “Raw status values”status is a diagnostic field describing our internal progress through a
payment. You do not need it to integrate: outcome and settlement carry
everything above. It is listed here so that a value seen in a raw API response,
a dashboard export or a support conversation can be looked up.
status |
Reported as |
|---|---|
pending |
outcome: pending |
completed |
outcome: paid |
sweeping |
outcome: paid, settlement: settling |
swept |
outcome: paid, settlement: settled |
sweep_failed |
outcome: paid, settlement: failed |
expired |
outcome: failed |
cancelled |
outcome: failed |
failed |
outcome: failed |
Do not branch on these. They describe our settlement mechanics, they are
subject to change, and anything you need from them is already in outcome and
settlement.
