Standard QR Flow - Web Button
An embeddable, self-contained JavaScript payment button that lets any merchant website accept Fex wallet payments with a few lines of code.How It Works
- Merchant calls
FexPay.pay()with amount, currency, and optional description. - The widget creates a payment intent on the Fex backend and renders a modal with the QR code.
- The customer opens the Fex mobile app, scans the QR, and approves the payment.
- The widget polls the backend every 5 seconds (up to 5 minutes) and automatically detects when the payment completes.
- On completion, the
onSuccesscallback fires and the success screen is shown.
FexPay.confirm() to resolve the payment immediately without waiting for the next poll cycle.
Installation
window.FexPay.
Quick Start
API Reference
FexPay.init(config)
Must be called once before any pay() call. Typically called on page load.
| Parameter | Type | Required | Description |
|---|---|---|---|
config.merchantId | string | Yes | Your Fex merchant UUID |
config.baseUrl | string | No | Override the API base URL (default: production endpoint) |
Error if merchantId is missing.
FexPay.pay(params)
Opens the payment modal, creates a payment intent, and starts polling for completion. Returns a Promise that resolves with the created PaymentIntent object once the modal appears (not when payment completes — use onSuccess for that).
| Parameter | Type | Required | Description |
|---|---|---|---|
params.amount | number | Yes | Amount to charge. Must be a positive number |
params.currency | string | Yes | ISO 4217 currency code, e.g. "USD", "EUR", "GBP" |
params.description | string | No | Payment description shown to the customer in the modal |
params.callbackUrl | string | No | Webhook URL the backend will POST to when payment status changes |
params.onSuccess | function | No | Called when payment completes successfully |
params.onError | function | No | Called when payment fails or times out |
params.onCancel | function | No | Called when the customer closes the modal |
onSuccess(intent)
Called with the completed PaymentIntent object:
onError(error)
Called with an error object:
| Error code | Cause |
|---|---|
VALIDATION | Invalid params passed to pay() (e.g. missing amount) |
API_ERROR | Network or backend error when creating/fetching the payment intent |
TIMEOUT | Customer did not complete payment within 5 minutes |
PAYMENT_FAILED | Intent status became expired, cancelled, or failed |
onCancel()
Called when the customer clicks the × button or the backdrop. No arguments.
Example with all params
FexPay.confirm(paymentIntentId?)
Tells the widget that a payment has been confirmed — typically called from your own webhook handler after the Fex backend notifies your server. Stops polling immediately, fetches the latest intent data, and shows the success screen.
| Parameter | Type | Required | Description |
|---|---|---|---|
paymentIntentId | string | No | The payment intent UUID to confirm. If omitted, confirms the current active session |
Webhook Integration
UsingcallbackUrl + FexPay.confirm() is the most reliable way to detect payment completion — it avoids waiting for the next 5-second poll cycle.
Recommended flow
Server-side webhook handler (Node.js example)
Client-side with WebSocket
Client-side with Server-Sent Events
Polling Behaviour
When no webhook is used, the widget polls automatically:| Setting | Value |
|---|---|
| First poll delay | 1 second after QR is shown |
| Poll interval | 5 seconds |
| Maximum polls | 120 (≈ 5 minutes total) |
| Timeout behaviour | Shows error, fires onError({ code: 'TIMEOUT' }) |
| Transient errors | Logged to console, polling continues |
Modal States
| State | Trigger |
|---|---|
| Loading | While POST /payment-intents is in progress |
| QR code | Payment intent created successfully |
| Success | Payment completed (onSuccess also fires) |
| Error | API failure or payment expired/cancelled (onError also fires) |
Security Considerations
- The widget sends your Merchant ID as an
X-Merchant-IDheader on every API request. This ID is visible in client-side code — it is a public identifier, not a secret. - Never embed API keys or secrets in the widget configuration. Authentication of payment outcomes should be verified server-side using the webhook.
- The modal is rendered in a Shadow DOM (
mode: closed) so widget styles are fully isolated from your page and vice versa. - All dynamic text inserted into the modal is HTML-escaped to prevent XSS.
Browser Support
| Browser | Support |
|---|---|
| Chrome / Edge 80+ | Full |
| Firefox 75+ | Full |
| Safari 14+ | Full |
| iOS Safari 14+ | Full |
| Android Chrome 80+ | Full |
fetch, Promise, Shadow DOM, Intl.NumberFormat — all standard in modern browsers. No polyfills needed.
Demo
Openwidget/demo.html directly in a browser (or serve it locally) to test the widget interactively:
File Reference
| File | Description |
|---|---|
fex-pay.js | The widget — copy this to your project or CDN |
demo.html | Interactive demo page |
