PPayNow Docs
Menu — Browser Handoff

Integration Guide

Browser Handoff

Query params the gateway expects, how the return URL is validated, and what happens on cancel.

After the merchant server returns { redirectUrl, paymentId }, the browser navigates to the gateway. The redirect URL carries everything the gateway needs to bootstrap the checkout session.

Query parameters

Param Required Example Notes
merchant_id yes ivv_demo_001 Display + audit.
reference yes inv_demo_001 Your order ID.
amount_minor yes 25900 Smallest currency unit.
currency yes LYD ISO-4217.
payment_id yes wpr_42 Returned by /web-payment/initiate.
token yes eyJhbGciOi… Bearer JWT for downstream endpoints.
locale no en / ne Defaults to en.
return_url no https://shop.example/cart Where to send the customer on cancel / completion.
selected_method no qr / account Pre-select a tab.
selected_qr_provider no OnePay / PayNow Pre-select a QR rail.

Currency minor units

The amount is always in the smallest unit. PayNow's primary market currencies use 3 fraction digits, not the more common 2:

  • LYD, BHD, KWD, OMR, TND → 3 digits (25900 = 25.900 LYD)
  • Most others (USD, EUR, …) → 2 digits (25900 = 259.00 USD)
  • JPY, KRW → 0 digits

The helper currencyFractionDigits(currency) and formatCurrencyAmount(amountMinor, currency) in paynow_core handle this for you.

Return URL safety

The gateway runs validateReturnUrl(url) from paynow_core. It rejects:

  • relative paths (/cart)
  • non-HTTP(S) schemes (ftp://…, javascript:…)
  • bare hostnames (shop — must contain a . or be a loopback host)

When validation fails the gateway falls back to https://www.fawri.ly/. This protects against open-redirect bugs where a malicious return URL could phish your customer.

Locale

Two locales ship today:

  • en — English (default)
  • ne — Nepali

The gateway picks up ?locale= and applies the matching translation set from paynow_core/checkout_localization.dart. Adding a locale is a single map entry plus translated strings — see the source.

What if the customer hits "Back"?

The gateway exposes a "Back to store" link (visible on the receipt and on bootstrap-error screens) pointing at the validated return_url. There's no separate "cancel" callback — your server should treat any paymentId that never reaches a terminal state (within your business timeout) as abandoned.