Dynamic QR

A fresh QR code per transaction, with the amount embedded. Best for POS terminals, kiosks, and any display that can show a unique QR per sale.

A dynamic QR is a fresh code generated for each transaction, with the amount already embedded. The customer scans it, confirms the amount they're shown, and pays. No typing, no manual amount entry.

This is the right pattern for any setup where you can render a different QR per sale: POS terminals, supermarket checkouts, kiosks, vending machines, digital signage, parking pay-on-foot, restaurant table billing.


When to use a dynamic QR

✅ Good fit❌ Wrong fit
POS terminal with a screenStatic signage that can't change
Kiosk or vending machineCheap setups with no display (use Static QR)
Restaurant bill on a tabletE-commerce web checkouts (use Bluebox hosted checkout)
Customer-facing display showing each saleHigh-frequency, low-value scenarios where bulk static is faster

The flow

For every transaction:

  1. Create a fresh, use-once code for the exact amount via POST /code/create.
  2. Display the returned code as a QR on your terminal screen.
  3. Customer scans and authorises in the Scan to Pay app.
  4. Scan to Pay sends a webhook to your notification URL.
  5. Your handler returns HTTP 200 within 45 seconds.
  6. Display the outcome to the customer at the till.

Steps 1, 5, and 6 are your responsibility; the platform handles the rest.


Create the code

curl -X POST "https://qa.scantopay.io/pluto/code/create" \
  -u "$USERNAME:$PASSWORD" \
  -H 'Content-Type: application/json' \
  -d '{
    "merchantReference": "POS-T07-2025-05-14-00037",
    "amount": 149.50,
    "useOnce": true,
    "shortDescription": "Acme Coffee — 1x cappuccino, 1x muffin"
  }'

Response:

{
  "code": "0123456789",
  "expiryDate": 1747242000000,
  "codeUrl": null
}

Key fields for dynamic QR:

FieldValueWhy
amountThe exact amount in your merchant's currency (typically ZAR)The customer can't change this — they confirm and pay
useOncetrue (the default)Code invalidates after one successful payment, preventing double-charges
merchantReferenceUnique per transactionYour idempotency key — see Idempotency
shortDescriptionOptional, 5–45 charsDisplayed on the customer's phone — useful for receipts and audit
expiryDateOptional — defaults to 30 minutesUse a shorter expiry for high-traffic terminals to keep the code list clean

Display the QR

Two options for rendering:

Option A — use the Scan to Pay-rendered PNG. Embed an <img> tag pointing at the QR endpoint and the platform returns a styled QR with optional colour theming:

<img src="https://qa.scantopay.io/pluto/public/stpqr/0123456789" alt="Scan to Pay" />

Option B — render the QR yourself. Encode the 10-digit code as a QR using any QR library on your terminal. The payload is just the digits.

See Rendering the QR for the full endpoint reference, colour options, and unstyled variants.


Set the right expiry

The default expiry is 30 minutes from creation. For dynamic QR at a POS, that's longer than you need — most customers complete within 60 seconds, and stale codes lying around make reconciliation harder.

Set expiryDate to a short window (epoch milliseconds):

const expiry = Date.now() + 2 * 60 * 1000; // 2 minutes
{
  "merchantReference": "POS-T07-...",
  "amount": 149.50,
  "useOnce": true,
  "expiryDate": 1747235520000
}

When the code expires unused, the platform automatically transitions it to a terminal state. See QR code lifecycle.


Handle the response at the till

Once the customer authorises, Scan to Pay sends a webhook to your notification URL. Your terminal should:

  1. Poll your backend for the latest state of merchantReference every 1–2 seconds while the customer is at the till — or push from your backend via WebSocket.
  2. Display a clear outcome on screen as soon as the state is SUCCESS or a terminal failure.
  3. Don't release goods until you see SUCCESS. A code that's been scanned but not yet authorised is not a confirmed payment.

The notification flow is documented on Webhooks.


What's next