Accept payment with Zebo Checkout

Create an order and redirect your customer to the invoice URL. Commerce handles the payment flow, OTP verification, and receipt generation.


Step 1: Create an order

Call /orders/new with customer details, line items, and finalize: true. Finalization seals the order and generates its hosted invoice URLs. Store the order ID for status tracking.

Create order

POST
/orders/new
curl https://api.zebo.dev/orders/new \
  -H "Authorization: Bearer $COMMERCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "request_meta": {
    "idempotency_key": "order_inv_001"
  },
  "customer_data": {
    "name": "Gloria Kesewaa",
    "email_address": "[email protected]",
    "phone_number": "+233544998605"
  },
  "finalize": true,
  "line_items": [
    {
      "type": "product",
      "product": {
        "type": "physical",
        "name": "Utility Sneakers",
        "quantity": 1,
        "price": {
          "currency": "ghs",
          "value": 20000
        }
      }
    }
  ]
}'

Response

{
  "order": {
    "id": "or_XyZ9kL2mQrTfVqYz1wNb",
    "number": "INV-2025-001",
    "status": "requires_payment",
    "invoice": {
      "format": {
        "web": {
          "url": "https://pages.zebo.dev/invoices/or_XyZ9kL2mQrTfVqYz1wNb"
        },
        "pdf": {
          "url": "https://pages.zebo.dev/invoices/or_XyZ9kL2mQrTfVqYz1wNb/pdf"
        }
      }
    }
  }
}

Step 2: Direct customer to payment

Choose how to get the customer to the payment page:

Extract response.order.invoice.format.web.url from the response envelope and return it to your client. Check that order.invoice is present before redirecting; it is omitted when the order is still a draft.

// Server: Return URL to client
return {
  invoiceURL: response.order.invoice.format.web.url,
}
// Client: Redirect to invoice
const response = await fetch('/api/create-order', { ... })
const { invoiceURL } = await response.json()
window.location.href = invoiceURL

Step 3: Track payment status

Use /orders/lookup to check payment status. Poll periodically or check when the customer returns to your app. When status becomes "paid", funds are in your Commerce balance.

Check status

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

Customization

Configure redirect and cancel URLs

{
  "checkout_settings": {
    "redirect_url": "https://yoursite.com/order/complete",
    "cancel_url": "https://yoursite.com/order/cancelled"
  }
}

Strongly recommended for a delightful customer experience. When payment completes, customers land on your redirect_url where you can show order confirmation and next steps. When customers abandon checkout without paying, they land on your cancel_url where you can show incomplete order details or offer alternative payment methods.

Set payment due time

{
  "payment_due_after": "2026-09-15T23:59:59Z"
}

Use an RFC 3339 timestamp. The value is returned as payment_due_at on the order.

Add line item details

{
  "product": {
    "name": "Utility Sneakers",
    "about": "Size: 42 | Color: Black"
  }
}

Appears below product name on invoice.

Update branding

Change logo, business name, and colors in the Commerce dashboard under Settings → Branding. Applies to all new invoices instantly.

See Checkout customization for more options.


Common patterns

Subscriptions: Create new order each cycle, send new invoice URL.

Deposits: Create separate orders for deposit and balance. Link via reference field.

Multi-currency: Create one order per currency.

See Common patterns for implementation details.


Next steps

Was this page helpful?