Skip to main content

Authorization Flow

This guide explains how SaaS platforms use Wonder BizConnect's QR code authorization flow to connect merchant accounts. The flow is secure, fast, and requires no credential sharing.

Before You Start​

Before integrating, make sure you have received the following from the Wonder team:

CredentialDescription
client_idPublic client ID used for Basic Authentication
client_secretClient secret — store only on your server, never in frontend code
app_idYour SaaS App ID
ScopesThe fixed permission set for your App, configured by the Wonder team
App typeAlways PUBLIC for SaaS platforms (any merchant can authorize)
Keep client_secret safe

The client_secret must never be exposed to browsers, mobile apps, or client-side code. All BizConnect API calls from your platform must go through your backend.

Flow Overview​

SaaS Backend           BizConnect API               Merchant (Wonder App)
| | |
1 |-- client_id/secret -->| Get app token |
|<-- app token ---------| |
| | |
2 |-- Create QR code ---->| Generate QR (PENDING) |
|<-- content + | |
| server_code | |
| | |
3 | Show QR code to | |
| merchant in your | |
| admin dashboard | |
| | |
4 | |<---- Scan QR code ---- |
| | |
5 | |< Approve authorization |
| | Write grant + issue |
| | user_grant token |
| | QR -> SUCCESS |
| | |
6 |-- Poll QR status -----| |
|<-- SUCCESS + token ---| |
| | |
7 | Save token & | |
| refresh_token | |
| for the merchant | |

Step 1: Get an App Token​

Your backend exchanges client_id and client_secret for a short-lived app token. This token is only used to create and poll QR codes — it cannot call business APIs.

curl -X POST 'https://api-stg.wonder.app/api/v1/biz/token' \
-u '{{CLIENT_ID}}:{{CLIENT_SECRET}}'

Response:

{
"access_token": "<biz_app JWT>",
"token_type": "Bearer",
"expires_in": 1800,
"app_id": "{{APP_ID}}",
"env": "UAT"
}
Cache the app token

Cache the app token on your server and reuse it until it expires. Avoid requesting a new token for every QR code creation.

Step 2: Create a QR Code​

Call the QR code creation endpoint with the app token. Each merchant authorization attempt gets its own QR code.

curl -X POST 'https://api-stg.wonder.app/api/v1/biz/login/qrcodes' \
-H 'Authorization: Bearer {{APP_TOKEN}}' \
-H 'Content-Type: application/json' \
-d '{"device_id": "{{MERCHANT_ID_IN_YOUR_SYSTEM}}"}'

Request body:

FieldRequiredDescription
device_idNoA unique identifier for this merchant session in your system (max 64 chars). Recommended: use your internal merchant/tenant ID. Enables multi-merchant isolation.

Response (201 Created):

{
"content": "https://wonder.app/biz-qr?user_code=<24-char-code>",
"server_code": "<64-char-handle>",
"status": "PENDING",
"device_id": "merchant_12345",
"expires_at": "2026-11-09T03:20:00Z",
"expires_in": 120,
"app": {
"id": "{{APP_ID}}",
"name": "Your SaaS Platform",
"developer": "Your Company",
"scopes": ["<scope_1>", "<scope_2>"]
}
}

Key fields:

FieldDescription
contentRender this as the QR code shown to the merchant. Do not modify or concatenate it.
server_codeUse this to poll for the authorization result. Keep it server-side — do not show it to users.
expires_inQR code validity in seconds (default 120s / 2 minutes).

Step 3: Display the QR Code to the Merchant​

Render the content string as a QR code in your platform's admin dashboard or settings page. Suggested UI:

  • Headline: "Connect your Wonder account"
  • Subtitle: "Open the Wonder App and scan this QR code to authorize"
  • App info: Show your app name and the scopes you're requesting (transparency builds trust)
  • Expiry countdown: Show a timer based on expires_in, with a "Refresh QR code" button

Step 4: Merchant Scans & Approves​

The merchant opens their Wonder App, scans the QR code, and sees an authorization confirmation page showing:

  • Your SaaS App name and developer
  • The list of permissions (scopes) being requested
  • QR code expiry time

The merchant taps Approve to grant access, or Reject to decline.

Fixed permissions

For SaaS platform apps, the scopes shown on the confirmation page are fixed per App. All merchants see the same permission list. This is by design — merchants know exactly what they're authorizing.

Step 5: Poll for Authorization Result​

Your backend polls the QR code status endpoint using server_code. We recommend polling every 2 seconds.

curl 'https://api-stg.wonder.app/api/v1/biz/login/qrcodes/{{SERVER_CODE}}' \
-H 'Authorization: Bearer {{APP_TOKEN}}'

QR Code States​

StatusMeaningAction on your side
PENDINGWaiting for scan or approvalKeep polling
SUCCESSMerchant approvedStop polling, save tokens
REJECTEDMerchant declinedStop polling, show message
EXPIREDQR timed outStop polling, offer refresh

SUCCESS Response​

{
"server_code": "<handle>",
"status": "SUCCESS",
"expires_at": "2026-11-09T03:20:00Z",
"user_id": "<wonder-user-id>",
"business_id": "<business-id>",
"user": {
"id": "<wonder-user-id>",
"name": "Store Manager",
"email": "manager@store.com"
},
"business": {
"id": "<business-id>",
"business_name": "Cafe Wonder",
"business_legal_name": "Cafe Wonder Ltd."
},
"jti": "<session-jti>",
"device_id": "merchant_12345",
"token": "<user_grant JWT>",
"refresh_token": "<refresh-token>",
"token_expired_at": "2026-11-09T03:50:00Z",
"refresh_token_expired_at": null
}

Key fields for SaaS platforms:

FieldDescription
business.idThe merchant's Wonder business ID. Store this mapped to your internal merchant ID.
business.business_nameDisplay name of the merchant business.
tokenThe user_grant token used to call business APIs.
refresh_tokenUsed to get a new user_grant token when it expires. null means never expires.
token_expired_atWhen the access token expires.
Store tokens per merchant

Save token, refresh_token, business_id, and token_expired_at in your database, linked to the merchant's account in your system. Each merchant has their own token set.

Step 6: Refreshing Tokens​

When the user_grant token is about to expire, use the refresh_token to get a new one — no re-scanning required.

curl -X POST 'https://api-stg.wonder.app/api/v1/biz/token/refresh' \
-u '{{CLIENT_ID}}:{{CLIENT_SECRET}}' \
-H 'Content-Type: application/json' \
-d '{"refresh_token": "{{REFRESH_TOKEN}}"}'

Response:

{
"access_token": "<new user_grant JWT>",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "<same refresh_token>",
"token_expired_at": "2026-11-09T04:20:00Z",
"refresh_token_expired_at": null
}

Key points:

  • refresh_token does not rotate — you can reuse it within its validity period
  • The old user_grant token is immediately invalidated after refresh
  • If refresh_token_expired_at is null, the refresh token never expires

Step 7: Merchant Revokes Authorization​

Merchants can revoke authorization at any time from the Wonder App. When this happens:

  • All active sessions for that merchant are immediately invalidated
  • Your API calls will return TOKEN_REVOKED (401) or GRANT_REVOKED (409)
  • You should clean up the stored tokens for that merchant and show a "Reconnect" prompt

Recommended behavior: when you receive a revocation error, mark the merchant's connection as "disconnected" and prompt them to go through the QR authorization flow again.

Error Handling​

HTTP StatusError CodeScenarioRecommended Action
401INVALID_CLIENTInvalid client_id / secretCheck credentials
401INVALID_TOKENApp token expired / invalidGet a new app token
404QRCODE_NOT_FOUNDserver_code doesn't exist or belongs to another appCreate a new QR code
409QRCODE_NOT_PENDINGQR already processed (success/rejected/expired)Create a new QR code
409GRANT_REVOKEDMerchant revoked authorizationDisconnect merchant, prompt re-auth
401TOKEN_REVOKEDUser token was revokedUse refresh_token, or prompt re-auth
429RATE_LIMITEDRate limit hitBack off and retry (see Retry-After header)

Security Best Practices​

  1. client_secret stays on the server — never embed it in frontend code, mobile apps, or logs
  2. Use HTTPS for all API calls, in all environments
  3. Cache tokens — reuse app tokens and user tokens until they expire
  4. Encrypt stored tokens — refresh_token and user_grant tokens should be encrypted at rest
  5. Stop polling on terminal state — don't keep polling after SUCCESS / REJECTED / EXPIRED
  6. Handle revocation gracefully — provide a clear "Reconnect Wonder Account" flow in your UI

Checklist​

  • You have received client_id and client_secret from Wonder
  • Your backend can get an app token via /api/v1/biz/token
  • Your backend can create QR codes and render the content field
  • Your frontend displays the QR code with app name and scopes
  • Your backend polls QR status and handles all states (PENDING / SUCCESS / REJECTED / EXPIRED)
  • On SUCCESS, you save token, refresh_token, and business_id mapped to the merchant
  • Token refresh works without merchant interaction
  • Revocation errors (TOKEN_REVOKED / GRANT_REVOKED) are handled gracefully
  • Production credentials and base URL are configured correctly