Accept payment with hosted invoice

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 and line items. Commerce generates invoice URLs automatically—one for web payment, one for PDF download. Store the order ID for status tracking.

Create order

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

Response

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

Step 2: Direct customer to payment

Choose how to get the customer to the payment page:

Extract invoice.format.web.url from the response and return it to your client. The client redirects immediately—customer lands on the payment page while they're still engaged. This works for in-app checkouts where the customer initiated the purchase.

// Server: Return URL to client
return {
  invoiceURL: 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 YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"order_id": "ord_XyZ9kL2mQrTfVqYz1wNb"}'

Customization

Set due date

{
  "due_date": "2025-05-15T23:59:59Z"
}

Shows prominently on invoice. Order becomes "overdue" after this date but payment still works.

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 Invoice 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?