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 stageOrder statusPayment statusPayment next actionWhat's happening
Createpreparingrequires_actionexecuteOrder created with line items, customer data, and payment method. Payment exists but hasn't been executed yet.
Finalizepreparingrequires_actionexecuteOrder has been finalized with complete details including shipping, discounts, or additional fees. Ready for payment execution.
Payrequires_paymentrequires_actionconfirm_paymentPayment execution started. OTP sent to customer's phone. Waiting for customer to provide the 6-digit code.
OTP confirmedrequires_paymentrequires_actionauthorize_paymentOTP verified. USSD prompt sent to customer's phone. Waiting for customer to approve payment with their mobile money provider.
Payment completepaidpaidnullCustomer approved payment. Provider confirmed charge. Order is now paid and ready for fulfillment.
CompletecompletedpaidnullGoods 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/finalize to add shipping, apply discounts, or adjust amounts
  • Execute the payment immediately with /orders/pay
  • Execute the payment during creation by setting execute_payment: true in /orders/new
  • Cancel the order with /orders/cancel if 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 shipping parameter
  • 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_at to 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/lookup periodically to detect when authorization completes
  • Check payment.next_action.authorize.expires_at to show how long the customer has
  • Redirect to checkout_settings.cancel_url if 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_url if 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/complete after 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 paid to completed)
  • Display a "completed" status in your admin dashboard
  • Nothing else—completed is 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/pay to 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-payment with 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 to next_action.redirect.url.

  • No action required (next_action is null) - 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.


Was this page helpful?