Orders

Orders represent complete customer transactions—from cart contents and pricing through payment and fulfillment. Each order captures what was purchased, who bought it, how much they paid, and whether payment succeeded. Orders serve as the authoritative record connecting line items, customers, payments, and invoices into a single coherent transaction.


Why orders matter

Orders solve the coordination problem between shopping carts, payments, and fulfillment. Without orders, you're manually linking payment records to product lists, reconstructing customer intent from fragments, and building custom tracking for every transaction.

Immutable transaction records

Once an order is finalized (sealed), its line items and total become immutable. The customer sees exactly what they agreed to pay, accounting systems have reliable totals for reconciliation, and dispute resolution has definitive evidence of what was purchased. This immutability enables trustworthy audit trails and eliminates pricing ambiguity.

Payment coordination

Orders orchestrate payment execution from initiation through completion. When you create an order, Commerce automatically creates the associated payment, validates the total matches line items, and manages the state transitions as payment progresses. Your code watches order status rather than coordinating payment state manually.

Clear fulfillment workflow

Orders separate payment from fulfillment through distinct states. When payment succeeds, check the payment's paid status to trigger fulfillment. When fulfillment completes, mark the order as completed. This separation lets you track both financial and operational milestones accurately.

Built-in invoicing

Every finalized order automatically generates an invoice with customer details, line item breakdowns, and totals. The invoice serves as both a receipt and—when needed—a hosted payment page where customers select payment methods and complete checkout in a Commerce-managed interface.


Order lifecycle

Orders use a state-machine architecture that enforces clear transitions between stages. This design prevents invalid operations (like shipping before payment), ensures auditability (every transition is logged), and makes integration predictable—your application responds to well-defined state changes rather than coordinating complex payment flows manually. Orders progress from preparing (editing line items) to requires_payment (finalized and ready for payment) to paid (funds captured) to completed (fulfilled).

Understanding how orders transition between states—and how order status differs from payment status—is essential for building reliable checkout and fulfillment flows. See the Order Lifecycles guide for detailed state transitions, payment next actions at each stage, and the relationship between order status and payment status.


Creating and finalizing orders

Orders begin in the preparing state where line items can be modified, then transition to requires_payment when finalized. Understanding the finalization options helps you build the right checkout flow.

Basic order creation

Create an order with the Create an Order endpoint:

curl https://api.zebo.dev/orders/new \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "number": "ORDER-001",
  "customer_data": {
    "name": "Kwame Mensah",
    "phone_number": "+233241234567",
    "email_address": "[email protected]"
  },
  "line_items": [
    {
      "type": "product",
      "product": {
        "name": "Premium Widget",
        "reference": "WIDGET-001",
        "price": {
          "currency": "ghs",
          "value": 10000
        },
        "quantity": 2,
        "type": "physical",
        "category": "electronics"
      }
    }
  ]
}'

This creates an order in preparing state. Line items can still be modified with Update an order while payment has not started.

Finalization options

You control when an order finalizes through three parameters:

finalize: true – Explicitly finalize the order during creation. The current order state is sealed, the invoice is generated, but payment isn't executed. Use this when you want the order finalized but need to initiate payment later.

execute_payment: true – Finalize the order AND initiate payment execution. This is automatic execution mode—one API call creates, finalizes, and starts payment. Use this for standard checkout flows where the customer expects immediate payment processing.

Important: execute_payment: true requires a payment method. You must include either payment_method_id (for saved methods) or payment_method_data (for new methods) in the same request. Without a payment method, there's nothing to execute.

Neither flag – Order stays in preparing state. Update it later with Update an order, finalize it with Finalize an order, or pay it directly with Pay for an order.

The key distinction: finalize seals the current order state and generates the invoice. execute_payment does both of those AND starts payment execution. Since execution requires a payment method, you must provide one when using execute_payment: true.

Finalize and pay later

Create an order without finalization, then finalize and pay in separate steps:

# Step 1

curl https://api.zebo.dev/orders/new \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "number": "ORDER-002",
  "customer_data": {
    "email_address": "[email protected]",
    "name": "Kwame Mensah",
    "phone_number": "+233241234567"
  },
  "line_items": [
    {
      "type": "product",
      "product": {
        "name": "Premium Widget",
        "price": {
          "currency": "ghs",
          "value": 10000
        },
        "quantity": 1,
        "type": "physical"
      }
    }
  ]
}'

# Step 2

curl https://api.zebo.dev/orders/finalize \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "order_id": "or_xyz123"
}'

# Step 3

curl https://api.zebo.dev/orders/pay \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "order_id": "or_xyz123",
  "payment_method_data": {
    "type": "mobile_money",
    "mobile_money": {
      "account_number": "0544998605",
      "network": "mtn"
    }
  }
}'

Note: Calling the Pay for an Order endpoint automatically finalizes the order if it isn't already sealed. The explicit Finalize an Order endpoint step is optional—useful when you want to lock line items before payment execution.


Payment execution and confirmation

Once an order is finalized, its associated payment progresses through execution, authorization, and settlement. Understanding payment states and next actions is critical for building checkout flows that handle confirmation tokens and provider authorization.

Payment execution

When you create an order with execute_payment: true or call the Pay for an Order endpoint, Commerce executes the payment. Payment execution requires a payment method—either pass payment_method_id (saved method) or payment_method_data (new method details).

With payment method provided – Commerce initiates payment immediately. For mobile money, this may trigger SMS confirmation (if configured). For cards, this may redirect to 3D Secure. Check the payment's next_action to determine what happens next.

Without payment method – If you don't provide a payment method when creating the order, don't use execute_payment: true. Instead, finalize the order to get a hosted checkout page where the customer selects and enters payment details. The checkout URL is automatically available in the invoice.format.web.url field of the finalized order.

Payment next actions

After executing payment, check the order's payment.next_action field to determine what your application must do:

confirm_payment – Customer must confirm transaction intent. Commerce sent a confirmation token (typically via SMS) to the customer's phone. Your application must collect this token from the customer and submit it via the Confirm a Payment endpoint. This verification step prevents unauthorized charges—the customer proves they initiated the transaction by retrieving the token from their SMS.

authorize_payment – Customer must authorize payment with their provider. For mobile money, this means entering their PIN in the provider's USSD prompt. For cards, this means completing 3D Secure authentication. Your role is passive—display a waiting state and poll the Lookup an Order endpoint to detect when authorization completes.

redirect – Payment requires browser redirect. The next_action.redirect object contains the URL. Redirect the customer to that URL (typically a bank authentication page). After completing authentication, the provider redirects back to your configured redirect_url.

No next action – Payment is processing or complete. Poll the Lookup an Order endpoint to track progress. When payment status becomes paid, funds are captured and you can fulfill the order.

Confirmation flow for mobile money

Mobile money payments can require explicit customer confirmation:

  1. Create order with execute_payment: true and mobile money payment method
  2. Inspect the returned confirmation request for its delivery channel, token size, and expiry
  3. Payment's next_action becomes confirm_payment
  4. Your UI prompts customer to check SMS and enter the token
  5. Submit the token with order_id, payment_id, and confirmation_id via Confirm a payment
  6. Inspect the returned order for any additional action
  7. Poll Lookup an order until payment becomes paid or the order reaches a terminal state

If the API returns confirmation_max_reached or confirmation_expired, request a new confirmation via Request confirmation.

Polling for completion

Payment execution is asynchronous. Never assume instant completion:

Payment execution is asynchronous. After Pay for an order, inspect the returned order and poll Lookup an order until its payment is paid or the order reaches a terminal state. If order.payment.next_action.type is confirm_payment, submit order.id, order.payment.id, order.payment.next_action.confirm_payment.request.id, and the customer's token using Confirm a payment. That reference includes language-native HTTPS examples for all eight supported languages.

Poll every 3-5 seconds initially. Implement exponential backoff if payment extends beyond 60 seconds, but continue polling for at least 5 minutes—mobile money authorization can occasionally take 2-3 minutes during provider issues.


Line items and totals

Orders are containers for line items—products, fees, and shipping charges. Understanding line item types and total calculation is essential for building accurate checkout flows.

Line item types

product – Goods or services the customer purchases. Products have name, price, quantity, reference ID, category, and custom metadata. Each product contributes price × quantity to the total.

fee – Fixed charges like processing fees or service charges. Fees have a flat amount (not multiplied by quantity) and a descriptive label.

shipping – Delivery charges. Functions like a fee but semantically distinct. Orders support at most one shipping line item.

tax and discount – Reserved for future use. To apply taxes or discounts today, use fee line items with appropriate amounts.

Total calculation

When an order is finalized, Commerce computes the total from line items:

  1. Sum all product amounts: Σ(product.price × product.quantity)
  2. Add shipping fee: + shipping.fee
  3. Add all fees: + Σ(fee.amount)

The resulting computed_total becomes immutable after finalization. This total determines what the payment charges.

Currency consistency

All line items must use the same currency. Commerce validates this during order creation. If you add a product in GHS and a fee in USD, order creation fails. Multi-currency transactions require separate orders.


Invoices and hosted checkout

Every finalized order automatically generates an invoice. Invoices serve dual purposes: receipts showing what was purchased, and—when needed—hosted payment pages where customers complete checkout.

Automatic invoice generation

Finalization triggers invoice generation. The invoice captures:

  • Order number and timestamps
  • Your business details (name, legal entity type)
  • Customer details (name, contact info)
  • Line item breakdown with quantities and prices
  • Total amount with currency
  • Payment status

Invoices update automatically as order status changes. Call the Lookup an Order endpoint to force regeneration if needed.

Hosted checkout pages for payment

When you create an order without specifying a payment method, Commerce automatically generates a hosted checkout page when the order is finalized. Customers visit this page to select a payment method, enter details, and complete payment—all in the Commerce interface.

The checkout URL is available in the finalized order response:

curl https://api.zebo.dev/orders/new \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "finalize": true,
  "customer_data": {
    "name": "Jane Doe"
  },
  "line_items": [
    {
      "type": "product",
      "product": {
        "name": "Premium Widget",
        "price": {
          "currency": "ghs",
          "value": 10000
        },
        "quantity": 1,
        "type": "physical"
      }
    }
  ]
}'

After payment completes, Commerce redirects the customer back to your configured redirect_url with the order ID as a query parameter. Check the order status to confirm payment succeeded.

Use hosted checkout when you want Commerce to handle payment UI and PCI compliance. Perfect for simple integrations or mobile-first experiences.


Customer and payment method handling

Orders require a customer (who's buying) and may require a payment method (how they'll pay). You can create these inline during order creation or reference existing records.

Providing customer information

New customer – Pass customer_data with name, phone, and/or email. Commerce creates the customer record and links it to the order. Use this for guest checkout or first-time buyers.

Existing customer – Pass customer_id to reference a previously created customer. Commerce validates the customer exists and belongs to your application.

Providing payment methods

New payment method – Pass payment_method_data with method type and details (like mobile money number and network). Commerce creates the payment method, saves it to the customer, and attaches it to the order.

Existing payment method – Pass payment_method_id to reference a saved payment method. Commerce validates it belongs to the specified customer.

No payment method – Omit both parameters. Commerce generates a Zebo Checkout page where the customer selects and enters their payment method. Control which methods appear with payment_method_types.

Payment method types

Use payment_method_types to restrict allowed payment methods:

{
  "payment_method_types": ["mobile_money", "card"],
  ...
}

If you provide a specific method via payment_method_id or payment_method_data, Commerce uses that method regardless of payment_method_types.


API operations

The Orders API provides endpoints for creating, finalizing, paying, and managing orders:

Create an Order – Create a new order with line items and customer info. Optionally finalize and execute payment in one call.

Lookup an Order – Retrieve an order by ID. Returns current status, line items, payment state, and next actions.

Finalize an Order – Finalize an existing order. Makes line items immutable and generates invoice without executing payment.

Pay for an Order – Execute payment for an order. Automatically finalizes if not already sealed. Initiates payment with attached method or generates Zebo Checkout.

Confirm a Payment – Submit customer confirmation token. Required when payment's next_action is confirm_payment.

Request Confirmation – Manually request a new confirmation token. Use when customer didn't receive initial SMS or exhausted retry attempts.

Complete an Order – Mark an order as fulfilled and complete. Call this after the customer receives their items or you deliver the service. Payment must be successful before completion. For offline payments (cash, bank transfer), optionally pass paid_out_of_band: true to mark the payment as paid in the same request.

Page Through Orders – List orders with pagination. Filter by status, customer, date range, or other criteria.

Was this page helpful?