Order lifecycle
Every order moves through a predictable sequence of statuses as it progresses from creation to payment completion. Understanding these transitions helps you build accurate UI states and debug payment flows with confidence.
Status transitions
Orders and their associated payments transition through distinct statuses at each stage of the lifecycle. The table below shows exactly what status each object holds at every phase. When you create an order, a payment object is created immediately with requires_action status—the payment is ready to be executed. If you set execute_payment: true during creation, it automatically progresses through Create and Pay in a single API call:
| Order stage | Order status | Payment status | Payment next action | What's happening |
|---|---|---|---|---|
| Create | preparing | requires_action | execute | Order created with line items, customer data, and payment method. Payment exists but hasn't been executed yet. |
| Finalize | preparing | requires_action | execute | Order has been finalized with complete details including shipping, discounts, or additional fees. Ready for payment execution. |
| Pay | requires_payment | requires_action | confirm_payment | Payment execution started. OTP sent to customer's phone. Waiting for customer to provide the 6-digit code. |
| OTP confirmed | requires_payment | requires_action | authorize_payment | OTP verified. USSD prompt sent to customer's phone. Waiting for customer to approve payment with their mobile money provider. |
| Payment complete | paid | paid | null | Customer approved payment. Provider confirmed charge. Order is now paid and ready for fulfillment. |
| Complete | completed | paid | null | Goods or services have been successfully delivered to the customer. This is the final terminal state. |
Stage details
Create
When you call /orders/new, the order enters preparing status and a payment object is created immediately with requires_action status. The next_action indicates that the payment is ready to be executed but hasn't started yet. The order remains in preparing until you explicitly execute the payment with /orders/pay, or you create it with execute_payment: true in the initial request (which advances it immediately to payment execution).
What you can do:
- Finalize the order with
/orders/finalizeto add shipping, apply discounts, or adjust amounts - Execute the payment immediately with
/orders/pay - Execute the payment during creation by setting
execute_payment: truein/orders/new - Cancel the order with
/orders/cancelif needed - Look up the order to inspect its current state
Finalize
The finalize stage is optional but essential when you need to add details after initial order creation. When you call /orders/finalize, you can add shipping information, apply discount codes, or include additional fees that weren't known during creation. The order remains in preparing status with the payment still at requires_action—nothing about payment execution changes, but the order now has complete details.
Why finalize separately? Some orders can't be created with all details upfront. You might need to calculate shipping costs based on the customer's address, validate a discount code against your system, or add handling fees for special items. Finalizing lets you build the order incrementally without triggering payment too early.
What you can do:
- Add or update shipping information with
shippingparameter - Apply discount codes or promotional adjustments
- Add handling fees or other post-calculation charges
- Execute the payment after finalization with
/orders/pay - Skip finalization entirely if the order was created complete
Pay (execute payment)
When you call /orders/pay (or create an order with execute_payment: true), Commerce begins payment execution and sends a 6-digit OTP to the customer's phone. The order transitions from preparing to requires_payment status because the payment cycle has started. The payment remains in requires_action status but its next_action now indicates that it's waiting for OTP confirmation from the customer.
Why this status? The order stays requires_payment throughout the entire payment cycle—from OTP confirmation through provider authorization—until payment fully completes. This makes it easy to identify orders that need attention: if status is requires_payment, check payment.next_action to see what step you're on.
What you can do:
- Display an OTP input field to the customer
- Submit the OTP with
/orders/confirm-payment - Check
payment.next_action.confirm_payment.expires_atto show a countdown timer - Handle OTP expiration by retrying with
/orders/pay
OTP confirmed
After you submit the OTP via /orders/confirm-payment, Commerce verifies it matches what was sent to the customer. The order remains in requires_payment status (payment cycle is still active), but the payment's next_action now indicates that it's waiting for provider authorization. This means Commerce is waiting for the customer to approve the charge with their mobile money provider via USSD prompt.
The customer receives a USSD dialog on their phone asking them to enter their PIN and approve the payment. This authorization happens entirely between the customer and their provider—no additional API calls are needed from your end.
Why this status? The payment is still requires_action because the customer must take action (approve the USSD prompt). The order remains requires_payment because payment hasn't completed yet. The next_action progression from OTP confirmation to provider authorization signals that you've moved to the next step.
What you can do:
- Display a message like "Please approve this payment on your phone"
- Poll
/orders/lookupperiodically to detect when authorization completes - Check
payment.next_action.authorize.expires_atto show how long the customer has - Redirect to
checkout_settings.cancel_urlif the customer abandons the flow
Payment complete
When the customer approves the payment on their phone, their mobile money provider confirms the charge and notifies Commerce. Both order.status and payment.status transition to paid, and payment.next_action is cleared since no further action is required.
The payment is now settled and funds will be included in your next payout, subject to the 7-day aging window for dispute protection. At this point, the order is ready for fulfillment—you should ship physical goods, grant access to digital products, or deliver services to the customer.
What you can do:
- Redirect the customer to
checkout_settings.redirect_urlif provided - Display a success message or order confirmation page
- Fulfill the order (ship products, grant access to digital goods, etc.)
- Send a receipt or confirmation email
- Trigger any post-payment internal processes
- Mark the order as complete with
/orders/completeafter successful delivery
Complete
After you've successfully delivered goods or services to the customer, call /orders/complete to mark the order as completed. This is the final terminal state in the order lifecycle—a completed order indicates that both payment and fulfillment are done. The payment remains at paid status, and no further actions are possible on the order.
Why mark orders complete? Tracking completion gives you accurate order fulfillment metrics, helps identify orders that are paid but not yet delivered, and provides a clear audit trail for customer service. An order at paid status means "payment successful, ready for fulfillment," while completed means "fully delivered to customer."
What you can do:
- Archive or store the order for historical records
- Generate analytics on fulfillment times (time from
paidtocompleted) - Display a "completed" status in your admin dashboard
- Nothing else—
completedis irreversible and terminal
Understanding next_action
The payment.next_action object tells you what needs to happen next to move the payment forward. When present, it always includes a type field that indicates which action is required, plus additional context like expiration times and confirmation details.
Key patterns:
-
Payment ready to execute (
type: "execute") - Payment is ready but hasn't been executed yet. Call/orders/payto begin payment execution and send the OTP. -
Waiting for OTP confirmation (
type: "confirm_payment") - Customer must provide the OTP sent to their phone. Call/orders/confirm-paymentwith the 6-digit code they received. -
Waiting for provider authorization (
type: "authorize_payment") - Customer must approve the payment via USSD on their phone. No API call needed—just wait for provider confirmation or poll/orders/lookup. -
Waiting for redirect (
type: "redirect") - Customer must complete payment on an external page (used for some payment methods). Redirect them tonext_action.redirect.url. -
No action required (
next_actionisnull) - Payment has reached a terminal state (paid,failed,canceled). No further action is needed.
The next_action object is automatically cleared when payment reaches a terminal state (paid, failed, canceled, expired).
Failed and canceled states
Not all orders complete successfully. Here's what happens when things go wrong:
Payment fails - If the customer's payment method is declined, has insufficient funds, or times out, the payment transitions to failed status. The order remains in requires_payment, and you can retry the payment with /orders/pay using a different payment method.
Order canceled - If you explicitly cancel the order with /orders/cancel, it transitions to canceled status. Any in-progress payment is also canceled. This is irreversible—you cannot restart payment on a canceled order.
Order expires - Orders expire automatically after 7 days if payment isn't completed. The order transitions to expired status, and you cannot retry payment. Create a new order instead.
Related resources
- Orders API - Complete order endpoint documentation
- Accept a payment - Step-by-step payment guide with code examples
- Product: Orders - High-level overview of the Orders API