Payment Lifecycle
State diagram
┌──────────────────────────┐ │ requires_payment_method │◄── (created) └─────────────┬────────────┘ │ payment method attached ▼ ┌──────────────────────────┐ │ requires_confirmation │ └─────────────┬────────────┘ │ confirm called ┌────────────┴─────────────────────┐ │ │ │ scheduled_payment_date │ no schedule / date = today │ is in the future │ ▼ ▼ ┌──────────────┐ ┌──────────────────┴──────────────┐ │ ready │ │ │ │ (scheduled) │ ▼ ▼ └──────┬───────┘ ┌────────────────┐ ┌────────────────────────┐ │ │ processing │ │ requires_action │ process_at │ │ (PSP pending) │ │ (3DS challenge) │ reached │ └───────┬────────┘ └────────────┬───────────┘ │ │ │ ▼ ┌──────┴──────┐ ┌──────┴──────┐ (joins processing ▼ ▼ ▼ ▼ flow above) ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │succeeded │ │ failed │ │succeeded │ │ failed │ └─────┬────┘ └──────────┘ └─────┬────┘ └──────────┘ │ full refund │ full refund ▼ ▼ ┌──────────┐ ┌──────────┐ │ refunded │ │ refunded │ └──────────┘ └──────────┘
Any non-terminal state → canceled (terminal)State descriptions
requires_payment_method
The initial state for all payment intents created via the API. No payment method has been attached. The intent stays here until a payment_method is provided at confirm time.
requires_confirmation
A payment method has been attached but confirm has not been called. In most flows this state is transient — you attach the payment method and call confirm in the same step.
ready
The payment intent has been confirmed with a future scheduled_payment_date. No money moves yet. The intent is queued — on the scheduled date, EnqueueScheduledPaymentIntentsJob picks it up and submits it for processing.
A ready intent can still be canceled before its scheduled date.
See Scheduled Payments for details on creating scheduled intents.
requires_action
The payment requires an additional customer action, typically a 3D Secure authentication challenge. The widget handles this automatically. The intent transitions to processing after the action completes, or failed if the customer abandons the challenge.
processing
The payment has been submitted to the PSP and is awaiting a final result. This state can last from milliseconds to several business days depending on the payment method.
succeeded
The payment completed successfully. Terminal state — the customer has been charged. Partial refunds keep the intent in succeeded; only a full refund transitions to refunded.
refunded
All captured funds have been refunded. The intent transitions here automatically when the total refunded amount equals the original charge. Terminal state.
failed
The payment was declined or failed for another reason. Check the failure code for details. Terminal state — create a new payment intent to retry.
canceled
The payment intent was explicitly canceled. Terminal state.
What to build on
- Use webhooks, not polling. The synchronous confirm response may show
processingorready— do not assume success until you receivepayment_intent.succeeded. succeededis authoritative. Build your fulfillment logic on this webhook event.failedis permanent. Create a new payment intent to retry a failed payment.readyis not charged yet. Areadyresponse means the payment is scheduled, not collected. Monitorpayment_intent.succeededto confirm funds were captured.
Refund states
After a payment intent reaches succeeded, refunds are tracked as separate objects. A partial refund keeps the payment intent in succeeded. A full refund (total refunded equals original charge) transitions the payment intent to refunded. Use the refund object’s status (succeeded, pending, failed) and the payment intent’s status to track state.