PPayNow Docs
Menu — QR Flow

Integration Guide

QR Flow

How the gateway requests a QR, what the customer sees, and how settlement is detected.

The QR flow lets the customer pay from their own banking app without typing anything into the gateway. PayNow renders a QR; the customer scans, confirms, and the gateway's status poller flips to the receipt.

Sequence

Browser              Merchant SSR        Gateway SSR       OnePay
               │                       │                  │                 │
               │ POST /api/initiate-payment              │                 │
               │──────────────────────▶│                 │                 │
               │                       │ POST /web-payment/initiate        │
               │                       │────────────────────────────────▶ │
               │                       │ ◀─── { paymentId, token } ───────│
               │ ◀── { redirectUrl } ─│                  │                 │
               │                                                            │
               │ navigate(redirectUrl)                                     │
               │─────────────────────────────────────▶│                   │
               │                                       │ POST /merchant-qr │
               │                                       │─────────────────▶│
               │                                       │ ◀── { qrCode } ─ │
               │ ◀── HTML with QR + status poller ───│                   │
               │                                                            │
               │ (customer scans + confirms in their banking app)          │
               │                                                            │
               │ POST /check-status (every 3s, then 10s)                  │
               │─────────────────────────────────────▶│                   │
               │                                       │ POST /check-status │
               │                                       │─────────────────▶│
               │                                       │ ◀── { paymentStatus: SUCCESS, ... } │
               │ ◀── transition to receipt ──────────│                   │
            

What the gateway calls

Step Endpoint Method Purpose
1 /web-payment/initiate POST (Merchant server) Create the payment session. Returns paymentId + bearer JWT.
2 /web-payment/{paynow,one-pay}/merchant-qr POST (Gateway) Generate the QR payload bound to the session.
3 /web-payment/check-status POST (Gateway, polling) Read settlement status.

State transitions

The session moves through pending → qrGenerated → waitingPayment → success (terminal). Failed scans go to failed; idle sessions go to expired. The PaymentStateMachine enforces these transitions; arbitrary jumps throw PaymentStateTransitionException.

There are two non-obvious "fast-path" transitions the state machine accepts:

  • qrGenerated → success — backend settles before the gateway ever sees waitingPayment (e.g. the webhook fires fast).
  • qrGenerated → otpRequired — customer abandons QR, switches to the account tab.

Polling cadence

The status poller activates as soon as the page reaches qrGenerated and runs against /web-payment/check-status with this schedule:

  • 3 seconds for the first 30 seconds.
  • 10 seconds after that.
  • Hard cap of 5 minutes.

It self-stops on success / failed / expired. See Status Polling for the request body shape and how to override the schedule.

What the customer sees

  1. The amount, merchant name, and a single "Scan to pay" panel with the QR.
  2. A subtle "Awaiting confirmation" message under the code.
  3. Receipt screen with transaction ID and timestamp once /check-status reports SUCCESS.

The "Refresh status" button stays available throughout — it triggers a one-shot /check-status call, identical to what the poller does on its own. Most users never need it.