Skip to content

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.

Terminal window
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.

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:

Terminal window
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.

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.

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.

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.

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.