Are there statuses that are not in the published list?
Two, and both are worth knowing about because they explain confusing observations rather than being things you act on.
The second one is the answer to a question merchants ask often, so it is worth
stating plainly:
AUTHORIZATION_REQUIRED covers both “the payment method is
waiting for the user to authorize” and “nothing has happened on this order
yet”. You cannot tell those apart from the status alone.
My order is stuck in AUTHORIZATION_REQUIRED. Did the user close the WebView?
Maybe — but the status does not tell you that, and it is not the only
explanation. AUTHORIZATION_REQUIRED means no result has come back, which is
consistent with all of:
- The user never opened the payment page at all.
- The user opened it and abandoned it — closed the WebView, killed the app, walked away.
- The user is still on it, mid-authentication.
- The payment attempt has not yet been made.
Which statuses are final, and can I store one as my outcome?
Two statuses are final:PAY_SUCCESS and ORDER_CLOSE. Everything else
is transitional, and an order in a transitional status can still move.
PAY_SUCCESS and ORDER_CLOSE do not flip into each other. Once an order is
closed, a later success report from the payment method does not reopen it and
does not rewrite it to PAY_SUCCESS; once it succeeded, it stays succeeded.
So yes — you can persist a final status as your business outcome.
The one thing that is not safe to persist is the assumption that a final status
means nothing further can happen to the money. A successful order can still be
refunded or disputed afterwards, and neither of those changes orderStatus — see
the refund question below.
What can I do to an order in each status?
Read the two edges carefully, because they are where integrations get it wrong:
- Refund is only possible from
PAY_SUCCESS. There is no “refund a pending order” — if the money has not landed, the action you want is cancel. - Cancel is the way to release an authorization you are not going to capture, and it is materially better than authorizing, capturing and then refunding.
I called cancel and it was refused. What do the errors mean?
How to act on each:
A0013is not an error to retry. The user paid. If you do not want to fulfil, the action is a refund, not a cancel.A0015is a fact about the payment method, not about your request. Some methods have no cancel operation at all; for those, the way an unpaid order ends is expiry. Design for “cancel may be unavailable” and let the expiry do the work.A0016came from the payment method’s side. A retry can be worth one attempt, but if it repeats, stop and let the order expire.- Cancelling an already-closed order succeeds and does nothing. That makes cancel safe to call from a cleanup job without checking the status first.
paymentRequestId or acquiringOrderId to identify the
order, plus your merchantId; sending neither id is a validation failure.
After I cancel, can the user still pay on the payment method’s page?
Cancel puts the order intoORDER_CLOSE — a final status — and asks the payment
method to cancel its side too. From that moment the order cannot complete: a
success report arriving afterwards is not applied, so the order will not silently
turn into a paid order behind your back.
What cancel cannot guarantee is that a third-party payment page already open in
front of the user stops accepting input. Where the payment method does not
support cancellation at all (A0015), nothing was cancelled on their side by
definition. So the honest answer to “can the user still pay?” is: they may be
able to hand over money, and it will not become a completed order. Money that
moves in that window is a recovery case, not a fulfilment case — the handling is
described in
Timeouts & When a Result Is Final.
Practical rule: cancel when you have decided not to sell, and do not treat cancel
as a way to close a checkout the user is still looking at.
Is “set a short expiry, then cancel when it passes” a good design?
It is a reasonable design, with one correction and one caveat.- The correction: you do not need the cancel call. An order that reaches its expiry without being paid is closed for you, and you are notified. Adding your own cancel afterwards is harmless — it is a no-op on a closed order — but it is not what makes the order end.
- The caveat: a short expiry does not shorten the payment method’s own window, and it does not stop a late payment from being taken on their side. So a three-minute expiry buys you a three-minute checkout, not a three-minute guarantee that no money will move afterwards.
Will an order close exactly at the expiry I set?
Close to it, but not to the second. Closing is performed by a periodic sweep, so an order can remain technically open for a short interval after its nominal expiry, and under high volume a batch of due orders can spill to the next pass. Before closing, the payment method is checked, and an order with a payment already in flight is left alone rather than being closed underneath the payment. What this means for you:- Do not use “the order is closed” as a clock. If your fulfilment logic needs a deadline, use your own timer against the expiry you sent; use the order status to learn the outcome, not the time.
- Do not treat a slightly-late close as an incident. It is the design.
- An expiry in the past is not rejected — it is discarded. If
orderExpiredAtis already behind the clock when the order is created, the value is dropped and the order gets the payment method’s default expiry instead. So a clock bug on your side does not fail loudly; it produces an order that stays open longer than you intended. Check the timestamp before you send it.
orderExpiredAt at all, the order still gets closed eventually,
but the time is not a value you set or should depend on. Send an explicit expiry
whenever the window matters to your business.
Do I get a notification when an order is closed on timeout?
Yes. A closed order produces the ordinary payment notification —PAYMENT_NOTIFICATION, carrying the same order structure as Order Inquiry, with
orderStatus = ORDER_CLOSE and the reason in orderFailedReason. There is no
separate “order expired” event type to subscribe to, and there is nothing extra
to enable.
So an integration that handles PAYMENT_NOTIFICATION and branches on
orderStatus already handles timeout closes. If you are not receiving them,
that is a webhook delivery question — see
Portal webhook log and
Webhook retry.
Can I look up orderFailedReason.orderFailedCode in the error code table?
No — it is a different set of codes. The Axxxx codes are what an API call
returns to you synchronously when your request is wrong or refused. The code
inside orderFailedReason describes why an order ended the way it did, and it
is a separate, reason-oriented namespace. Looking one up in the other’s table is
why merchants conclude the code “does not exist”.
Can you automate on it? Partly, and carefully:
- Do branch on the broad distinction you can rely on — a timeout close is a different business event from a payment refusal, and both differ from a cancellation.
- Do always store
orderFailedDescriptionnext to the code, because it is the part that stays readable when a code you have never seen arrives. - Do not build an exhaustive switch that treats an unrecognised reason as a failure of your own code. The set is larger than the handful you will see in testing, and new reasons appear as payment methods change. Default to “closed, not paid, reason recorded” and alert a human rather than guessing.
Can I capture less than I authorized?
Yes. A capture amount may be anything greater than zero and up to the authorized amount; the remainder of the authorization is released rather than held. Capturing more than was authorized is refused.
That last row is the one that surprises people. Capture only exists for orders
created in manual-capture mode — you opt in at order creation with
paymentInfo.captureMode, and the only value accepted there is manualCapture.
An ordinary order is captured automatically and there is nothing for you to call.
The published product behaviour matches the code on the point that matters most:
capture is effectively single — once you capture, even partially, the rest of
the authorization is released, so you cannot draw down an authorization in
instalments. For usage-based billing, the pattern is a stored token and periodic
charges, not repeated captures. See
Auth-Capture and
Tokenization.
How long do I have to capture?
The window is counted in whole days from when the order was created, not from when you noticed the authorization. It is a single platform-wide setting, applied to every merchant alike — it is not negotiated per account, and the wording of the refusal message is not a reliable guide to the current figure. Confirm the window with support before you design a fulfilment process around it. After the window lapses two things happen, and you should build for both:- Your capture call starts failing. It is refused as unsupported, not queued.
- The authorization is released for you. Expired authorizations are cancelled proactively, so the customer’s funds do not stay held because you forgot.
Why is refundExpiryAt empty on some of my orders?
refundExpiryAt is the point after which the order can no longer be refunded. It
is derived from the refund window in your agreement for that payment method,
counted from the order’s creation date — not from the moment the payment
succeeded — and it lands at the end of that day.
Two consequences:
- A window expressed in days is a calendar window. An order created late in the day gets a slightly shorter usable window than one created early, because the boundary is the end of a day either way.
- The clock does not restart when the payment succeeds. An order paid a long time after it was created has already spent part of its refund window.
null — in every case where the question does not apply:
So do not read a missing
refundExpiryAt as “refundable forever”. Read it as “no
deadline is being published for this order”, check
Refund Limits by Payment Method
for what applies, and use remainingRefundAmount on the refund APIs to decide
what is still refundable.
Does a refund change the original order’s status?
No — and this is the single most common false alarm on this endpoint. A refund never rewritesorderStatus; a fully refunded order still reports
PAY_SUCCESS. Refund progress lives on the refund record and on the order’s
separate refund-status field, not on the payment status.
If your bookkeeping keys refund state off orderStatus, it will look permanently
stuck. Key it off the refund record instead —
Refund Limits by Payment Method
sets out where to look for each question.
Stuck on a status that will not move? Contact support with the
acquiringOrderId and the status you are seeing.