Payment lifecycle
Every payment has a status. It changes as the payment moves from creation to a final outcome.
Your code should act on status, not on HTTP status codes or on where the customer’s browser
ends up. This page lists every status, shows the usual paths between them, and maps them to
webhook events.
Payment statuses
| Status | Meaning | Final? | What you do |
|---|---|---|---|
requires_payment_method | The payment exists but has no payment method yet, or the last attempt failed and needs a new method. | No | Attach a method and confirm the payment. |
requires_confirmation | A payment method is attached, but the payment has not been confirmed. | No | Confirm the payment. |
requires_customer_action | The customer must do something: complete 3-D Secure, approve in a bank or wallet app, or scan a QR code. | No | Read next_action. See
Redirects and customer actions. |
requires_merchant_action | The payment is waiting for a decision from you, for example after a manual fraud review. | No | Review the payment in the dashboard. |
processing | The payment provider is still working on it. | No | Wait for a webhook, or
retrieve the payment with force_sync=true. |
requires_capture | The full amount is authorised and held. Only happens with capture_method: manual. | No | Capture or cancel it. |
partially_authorized_and_requires_capture | Only part of the amount was authorised, and it is waiting for capture. | No | Capture what was authorised, or cancel. |
partially_captured_and_capturable | Part of the amount was captured and more can still be captured. | No | Capture the rest, or leave it. |
partially_captured_and_processing | Part of the amount was captured and the rest is still being processed. | No | Wait for a webhook. |
succeeded | The money is collected. You can refund it. | Yes | Fulfil the order. |
partially_captured | Part of the authorised amount was captured; the rest can no longer be captured. | Yes | Fulfil for the captured amount (amount_received). |
failed | The payment did not go through. No money was collected. | Yes | Show the customer an error. If manual_retry_allowed is true, you can confirm the same payment again. |
cancelled | You cancelled the payment before money was collected. | Yes | Nothing. |
cancelled_post_capture | The payment was cancelled after capture. | Yes | Nothing. |
expired | The payment was not completed or captured in time. | Yes | Create a new payment if the customer still wants to pay. |
conflicted | The amount or currency the provider processed differs from what you sent. | No | Contact before fulfilling. |
review | The payment is held for manual review after an unexpected answer from the provider, for example a capture that failed after it first succeeded. | No | Do not fulfil yet. Wait for a webhook. |
How statuses connect
create (confirm: false)
|
v
requires_payment_method
| attach payment method
v
requires_confirmation
| confirm
v
+-----------------+-------------------+
| | |
v v v
requires_ processing ------> failed
customer_action |
| customer |
| completes |
v v
+-----> (authorised) -------+
| |
capture_method: capture_method:
automatic manual
| |
v v
succeeded requires_capture
| capture
v
succeeded
Before capture, cancel -> cancelled
Not completed in time -> expired
(authorised) is a step, not a status value.Automatic capture flow
This is the default (capture_method: "automatic"). Authorisation and capture happen together.
- Create the payment:
requires_payment_method. - Confirm it with a payment method.
- The payment becomes
succeeded, orrequires_customer_actionfirst if the customer has to authenticate, orprocessingif the provider answers later. - You receive the
payment_succeededwebhook. Fulfil the order.
Manual capture flow
Use capture_method: "manual" when you want to hold the money first and collect it later, for
example when you ship goods or confirm a booking.
- Create the payment with
capture_method: "manual"and confirm it. - Once authorised, the status is
requires_captureand you receivepayment_authorized.amount_capturableshows how much you can collect. - When you are ready,
capture the payment. A full capture gives
succeeded; a partial capture givespartially_captured. - If you do not want the money,
cancel the payment to release the hold. The
status becomes
cancelled.
Authorisations do not last forever. The payment provider sets the limit,
often a few days for cards. Capture before capture_before when it is present.
Otherwise the hold is released and the payment can end as expired.
Customer action flow
Many payment methods need the customer to do something outside your page: 3-D Secure for cards, approval in a banking or wallet app, or scanning a QR code.
- You confirm the payment.
- The status is
requires_customer_actionand the response has anext_action. You receive theaction_requiredwebhook. - You send the customer to the redirect URL or show the QR code. See Redirects and customer actions.
- The customer finishes. The status moves to
succeeded,requires_capture,processingorfailed. - You confirm the result on your server, as described in Confirm the outcome on your server.
Failed payments and retries
A failed payment collected no money. The response tells you why:
error_codeanderror_message: the payment provider’s own code and text.unified_codeandunified_message: the same failure in a form that does not depend on the provider. Use these for your logic and customer messages.
If manual_retry_allowed is true, you can call
Confirm a payment again on
the same payment, with the same or a different payment method. Otherwise create a new payment.
See
Errors for API errors (HTTP 4xx and 5xx), which are different from a failed payment.
Refund statuses
A refund is a separate object with its own status. You can refund a payment that is succeeded
or partially_captured.
| Refund status | Meaning | Final? |
|---|---|---|
pending | The refund was sent and is waiting for the provider. | No |
succeeded | The money was returned to the customer. | Yes |
failed | The refund did not go through. | Yes |
review | The refund needs manual review. | No |
See Refunds for the refund operations.
Payment webhook events
VNSwitch sends a webhook when a payment or refund reaches one of these statuses. The
event_type field in the webhook body tells you which. See
Webhooks for how to
receive and verify them.
event_type | Sent when the payment status becomes |
|---|---|
payment_succeeded | succeeded |
payment_failed | failed |
payment_processing | processing or partially_captured_and_processing |
payment_authorized | requires_capture |
payment_partially_authorized | partially_authorized_and_requires_capture |
payment_captured | partially_captured or partially_captured_and_capturable |
payment_cancelled | cancelled |
payment_cancelled_post_capture | cancelled_post_capture |
payment_expired | expired |
action_required | requires_customer_action, requires_merchant_action, conflicted or review |
refund_succeeded | Refund status succeeded |
refund_failed | Refund status failed |
refund_review | Refund status review |
No event is sent for requires_payment_method or requires_confirmation, because those only
change when you call the API yourself. A fully captured payment is reported as
payment_succeeded.
Confirm the outcome on your server
Never fulfil an order just because the customer’s browser reached your
return_url, or because your front end says the payment worked. Anyone can open that
URL or change its query parameters.
Before you ship goods or credit an account, confirm the status from VNSwitch on your server in one of two ways:
- Webhook: wait for
payment_succeeded(orpayment_authorizedfor manual capture), after verifying the webhook signature. See Webhooks. - Retrieve: call
Retrieve a payment with your secret key. Add
force_sync=trueif the status is not final yet.
Check that status, amount and currency match your order. Webhooks can arrive more than once
and in any order, so make your handler idempotent: record the payment_id and the status you
acted on, and skip events you have already processed.