Utopia PaymentsDocs

Payment lifecycle

Understand checkout, payment and subscription states and decide when to fulfil an order.

Checkout sessions

StatusMeaning
openThe customer can still open the hosted checkout and pay.
completeA payment for the session was confirmed.
expiredThe 24-hour session expired or a related renewal link was closed.

A checkout status is not a substitute for the payment record. Use the payment_id returned on a completed session to retrieve the payment.

Payments

A payment record exists from the moment the customer submits the hosted checkout's details form, before any card details are entered. It starts as processing, so GET /payments?checkout_session_id=… finds an attempt that is still under way, and an abandoned checkout leaves a processing or failed payment behind.

StatusMeaningTerminal?Fulfil?
processingThe final gateway result is not known yet.NoNo
succeededThe payment was authenticated and recorded.No: a refund still changes itYes
failedThe attempt failed or was cancelled.Almost always; see the recovery window belowNo
partially_refundedSome of the successful payment was returned.No: a further refund makes it refundedAlready fulfilled; apply your refund policy
refundedThe full payment amount was returned.YesAlready fulfilled; apply your refund policy

The browser's return URL carries payment_id, status and session_id as convenient hints. Do not fulfil from those query parameters. Fulfil when you receive a verified payment.succeeded event, or after GET /payments/:id returns succeeded.

Deliveries can be delayed or duplicated. Make fulfilment idempotent using the event id or payment id. If a payment stays processing, keep checking rather than creating another charge automatically.

The recovery window

An attempt whose result never reached Utopia — the customer closed the tab, a callback was lost — is looked up again at the card processor for 7 days after the payment was created. That covers a payment still processing and one closed as failed before the processor gave its verdict, which is why failed is not quite final: a late capture found this way settles as a normal succeeded payment and sends payment.succeeded then. After 7 days an unresolved payment is left as it stands.

What happens after checkout

  1. Your server creates a checkout session and sends the customer to its checkout_url.
  2. The customer fills in their details and submits the form. The payment record is created as processing.
  3. They enter their card on the processor's secure page. Card details never reach Utopia or your server.
  4. They come back to Utopia's result page, which confirms the outcome with the processor. On success the session becomes complete, the payment succeeded, and payment.succeeded is queued for your endpoints. A decline leaves the session open and sends payment.failed.
  5. The customer is shown the outcome, and where they go next depends on return_url.

A declined card shows a failure page telling the customer the payment was not successful and to retry or use another payment method. What return_url changes:

  • With a return_url, a successful payment is shown briefly and then the browser goes to your URL with payment_id, status and session_id appended. After a decline the page offers a link back to your URL instead — it does not redirect on its own, and it does not offer a second attempt, so handle the retry on your own page.
  • Without a return_url, the result page is the end of the journey. After a decline it offers Try payment again, which reopens the same checkout session so the customer can try another card. The session stays usable until it is paid or its 24 hours run out.

If the processor's answer has not arrived by the time the customer returns, the page checks ten times over about 45 seconds and then offers a Check payment status button. The payment stays processing and the recovery window above resolves it.

Subscriptions

Subscriptions use active, on_hold and cancelled. A successful renewal creates a normal payment whose billing_reason is subscription_cycle. See Subscriptions for the two renewal engines and cancellation behaviour.

A refund, made from the dashboard or with POST /v1/payments/{payment_id}/refunds (see Refunds), updates the payment and emits refund.succeeded.

On this page