Bluebox hosted checkout

Redirect customers to a Scan to Pay-hosted checkout page. Fastest way to accept e-commerce payments — most of the UI is on us.

Bluebox is the Scan to Pay-hosted checkout page. When a customer picks Scan to Pay on your cart, you redirect them to our page, we render the QR and handle the customer-facing flow, and we send them back to you when it's done. You receive a callback with the result.

This is the right integration for almost everyone selling online. The flow is well-tested across every major SA bank and acquirer. You're live in a day or two; you don't have to maintain the checkout UI as schemes evolve.


The flow

   Customer cart                  Your backend            Scan to Pay (Bluebox)
   ─────────────                  ────────────            ─────────────────────
        │                              │                          │
        │ "Pay with Scan to Pay"       │                          │
        ├─────────────────────────────►│                          │
        │                              │   POST /bluebox/.../createTx                 │
        │                              ├─────────────────────────►│
        │                              │                          │
        │                              │◄────────── redirectUrl ──┤
        │                              │                          │
        │◄────────── redirect ─────────┤                          │
        │                                                         │
        │  ←──────────── customer lands on Bluebox page ──────────┤
        │             (scans QR, authorises in bank/wallet app)   │
        │                                                         │
        │                              │   CB2: encrypted webhook │
        │                              │◄─────────────────────────┤
        │                              │                          │
        │◄─── CB1: browser redirect ───┼──────────────────────────┤
        │     to your callbackUrl                                 │
        │       (?transactionId, status)                          │
        │                              │   ack 200                │
        │                              ├─────────────────────────►│
        │                                                         │
        │  ← your success/failure page rendered to customer       │

Two callbacks come back, in either order:

  • CB1 — an unencrypted browser redirect. Scan to Pay sends the customer back to your callbackUrl with transactionId and status query parameters. This is what the customer sees.
  • CB2 — an encrypted server-to-server webhook to your merchant notification URL (configured in the Portal). This carries the full transaction details. See Webhooks.

Your handler needs to deal with both, in any order.


Step 1 — Create the Bluebox transaction

curl -X POST "https://qa.scantopay.io/pluto/bluebox/secure/createTx" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "callbackUrl": "https://yourstore.example/checkout/scantopay/return",
    "amount": 149.50,
    "reference": "order-2025-05-14-00081",
    "description": "1x cappuccino, 1x muffin",
    "requestShipping": false
  }'

Response:

{
  "ref": "56c5181a-4485-4805-9925-ac6e22e9d4f5",
  "redirectUrl": "https://qa.scantopay.io/bluebox/public/redirect/91f8f61d58fa8dd3",
  "status": "SUCCESS"
}

Key fields:

FieldRequiredDescription
callbackUrlThe URL on your site where Bluebox will send the customer after they finish (or cancel). Up to 400 characters.
amountThe amount in your merchant's currency.
referenceYour unique reference for this order. Up to 45 characters. Must be unique per merchant — see Idempotency.
descriptionoptionalShort product description, shown to the customer at checkout.
requestShippingoptionalIf true, Bluebox asks the customer for a shipping address. Returns it on the webhook.
terminalIdoptionalOverride for super merchants.
subMerchantNameoptionalSub-merchant for super merchants. Shows on the consumer's bank statement.
cartItemsoptionalArray of basket items. Used for airtime limit checking.

If status is FAILED, no redirectUrl is returned — don't redirect; surface the error to the customer.


Step 2 — Redirect the customer

Send the customer to the redirectUrl returned in step 1. This is a 302 redirect:

response.redirect(302, redirectUrl);

The customer lands on the Bluebox page. Scan to Pay handles the QR display, the customer instructions, the bank/wallet app handoff, and the success/failure UI on our side.


Step 3 — Handle CB1 (browser redirect)

Once the customer is done — successfully or otherwise — Bluebox redirects the browser back to your callbackUrl with two query parameters:

https://yourstore.example/checkout/scantopay/return?transactionId=81234&status=SUCCESS
ParameterValues
transactionIdScan to Pay's internal transaction ID. Use this to look up details.
statusSUCCESS or one of the failure values.

Your CB1 handler should:

  1. Extract transactionId and status from the query string.
  2. If status is not SUCCESS, show the customer a "transaction was cancelled / failed" page.
  3. If status is SUCCESS, check whether CB2 (the encrypted webhook) has already arrived. If it has, render the success page with full transaction details. If not, wait 2–3 seconds and try again.
⚠️

Don't release goods on CB1 alone. CB1 is a browser-controlled redirect — an attacker could fake it. Wait for CB2's encrypted webhook before treating the order as paid.


Step 4 — Handle CB2 (encrypted webhook)

This is the standard Scan to Pay webhook, delivered to the notification URL configured for your merchant in the Portal. The payload is encrypted, structured as TransactionNotificationV3, and includes the bank response, card info, and customer details. See Webhooks for the full payload and decryption pattern.

Your CB2 handler should:

  1. Decrypt the payload — see Signing and verifying webhooks.
  2. Match the reference (your order ref) and transactionId to the in-flight order.
  3. If CB1 has already arrived for this transactionId, push the result data through to your CB1 server so it can render the success page.
  4. Release the goods (or mark the order paid).
  5. Return HTTP 200 within 45 seconds. Late or non-200 acks cause the transaction to reverse — see the reversal safety net.
📘

CB1 and CB2 can arrive in either order. Build your handlers to be order-independent. The reliable pattern is: persist whatever CB2 arrives with first, and have CB1 poll your own backend for it.


Without a webhook URL configured

If you can't expose a public HTTPS endpoint for webhooks (some integrators with strict network policies can't), the integration still works — just differently:

  1. CB1 still fires as normal.
  2. Your CB1 handler polls POST /pluto/code/queryRef with code and merchantReference until the status is terminal.
  3. Refer to Querying transactions.

This is the polling fallback. Webhooks are still strongly recommended for production.


Reversing a Bluebox transaction

Use the standard reversal API:

curl -X DELETE "https://qa.scantopay.io/pluto/purchase/{transactionId}" \
  -u "$USERNAME:$PASSWORD"

See Refunds and reversals for the full flow.


What's next