Account Linking

Account Linking

Account linking is the one-time flow that authorises your app to charge a ShopBack user. At the end of this flow you receive a paymentToken — a long-lived token scoped to a specific user and your merchant channel. Store it server-side and reuse it for all future payments for that user.

📘

One token per user

If the same user links again, ShopBack returns the same paymentToken. You don't need to update your records.


Flow overview

sequenceDiagram
    autonumber
    participant App as Merchant App
    participant BE as Merchant Backend
    participant SB as ShopBack API

    App->>BE: User taps "Link ShopBack account"
    BE->>SB: POST /tokenized-payment/v1/link-sessions/link
    SB-->>BE: 200 { linkToken, redirectUrl, appToken, expiresAt }
    BE-->>App: redirectUrl + appToken
    App->>SB: Open redirectUrl in in-app browser
    Note over App,SB: Header — X-ShopBack-App-Token: appToken
    Note over SB: User reviews and approves on ShopBack consent page
    SB-->>App: Redirect to callbackUrl?state=<state>&code=<authCode>
    App->>BE: Forward authCode
    BE->>SB: POST /tokenized-payment/v1/link-sessions/token
    SB-->>BE: 200 { paymentToken }
    Note over BE: Store paymentToken against merchantUserId

Step-by-step

1. Initiate a link session

Your backend calls POST /tokenized-payment/v1/link-sessions/link to create a new link session.

Request

POST /tokenized-payment/v1/link-sessions/link
Authorization: Bearer <merchant_jwt>
Content-Type: application/json

{
  "callbackUrl": "https://your-app.com/shopback/callback",
  "state": "csrf-nonce-abc123",
  "merchantUserId": "usr_7829",
  "userHint": {
    "phone": "+6591xxxx67"
  }
}
FieldRequiredDescription
callbackUrlYesHTTPS URL ShopBack redirects to after consent. Must be on the allowlist for your merchant channel.
stateYesA CSRF nonce you generate. ShopBack echoes it back in the redirect so you can verify the response originates from ShopBack.
merchantUserIdYesYour opaque, stable identifier for this user. Must match the merchantUserId you'll use in payment requests.
userHint.phoneOne of phone/email requiredA pre-redacted hint displayed on the ShopBack consent page to confirm identity.
userHint.emailOne of phone/email requiredAs above.

Response

{
  "linkToken": "550e8400-e29b-41d4-a716-446655440000",
  "redirectUrl": "https://pay.shopback.com/link",
  "appToken": "eyJhbGciOiJIUzI1NiJ9...",
  "expiresAt": "2026-06-24T10:20:00.000Z"
}
FieldDescription
linkTokenIdentifies this session. Use it to poll session status if needed.
redirectUrlThe ShopBack consent page URL. Open this in your in-app browser.
appTokenA short-lived JWT. Pass as X-ShopBack-App-Token header when opening the in-app browser.
expiresAtSession TTL — 20 minutes from creation. Start the flow before this time.

2. Open the consent page in your in-app browser

Open redirectUrl inside your app using an in-app browser (iOS: SFSafariViewController, Android: Custom Tabs). Pass appToken as a custom HTTP header:

X-ShopBack-App-Token: <appToken>
⚠️

Do not open in a standard WebView

ShopBack's consent page requires cookies and secure storage that standard WebViews may block. Use SFSafariViewController or Chrome Custom Tabs.

ShopBack will display the user's linked payment method and prompt them to authorise your app.


3. Handle the redirect back to your app

Once the user approves (or declines), ShopBack redirects the in-app browser to your callbackUrl:

https://your-app.com/shopback/callback
  ?state=csrf-nonce-abc123
  &code=a1b2c3d4e5f6

In your app:

  1. Verify state matches what you sent in step 1 (CSRF check).
  2. Extract the code (auth code). It expires in 60 seconds — swap it immediately.
  3. Close the in-app browser and pass code to your backend.
⚠️

Auth code is single-use and expires in 60 seconds

Do not store or display it. Send it straight to your backend for the token swap.


4. Exchange the auth code for a payment token

Your backend calls POST /tokenized-payment/v1/link-sessions/token with the one-time code.

Request

POST /tokenized-payment/v1/link-sessions/token
Authorization: Bearer <merchant_jwt>
Content-Type: application/json

{
  "code": "a1b2c3d4e5f6"
}

Response

{
  "paymentToken": "pt_sg_7829_a1b2c3d4e5f6..."
}

Store paymentToken server-side mapped to your merchantUserId. This token is:

  • Long-lived — no expiry under normal circumstances.
  • Reusable — use it for every subsequent pre-auth or immediate charge for this user.
  • Revocable — call POST /tokenized-payment/v1/tokens/unlink to revoke it if the user disconnects.

Unlinking

To revoke a user's payment token (e.g. if they request disconnection):

POST /tokenized-payment/v1/tokens/unlink
Authorization: Bearer <merchant_jwt>
Content-Type: application/json

{
  "paymentToken": "pt_sg_7829_a1b2c3d4e5f6..."
}
📘

Unlink is blocked if a pre-auth is open

If the user has an AUTHORIZED pre-auth, the unlink request returns 400. Capture or void the pre-auth first.


Next step

With your paymentToken, proceed to: