Skip to main content

Create a shopping session

Every shopping journey starts with a session. Your backend creates it and receives a single-use exchange token; your app hands that token to the SDK, which redeems it for a session-scoped credential and opens the merchant.

Create the session from your backend​

info

For security reasons, sessions must be created from your backend (server-to-server) using your OAuth access token. The exchange token is single-use with a 60-second TTL — mint it when the customer opens the merchant, not in advance.

Call the Create in-app shopping session API with the merchant domain:

Sample request

{
"domain": "nike.com/sg",
"tenant_user_ref": "CUST0001"
}

Sample response

{
"session_id": "01927d8a-9c3e-7b7a-9f4e-1d2a3b4c5d6e",
"exchange_token": "8f4a2b1c9d3e7f5a6b8c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a",
"expires_at": "2026-07-08T13:00:00Z",
"merchant": {
"id": "9b2f4e1c-6d8a-4f3b-8c5e-7a1b2c3d4e5f",
"domain": "nike.com/sg",
"name": "Nike",
"automation": {
"checkout_detection": {
"urls": ["https://www.nike.com/**/checkout**"],
"ready": [
{ "selector": "button[data-attr='placeOrder']", "state": "enabled" }
]
},
"payment_status_detection": {
"timeout_ms": 60000,
"outcomes": [
{ "status": "succeeded", "urls": ["https://www.nike.com/**/orders/**"] },
{ "status": "requires_action", "ready": [{ "selector": "#otp-input", "state": "visible" }] },
{ "status": "failed", "ready": [{ "selector": ".payment-error", "state": "visible" }] }
]
}
}
}
}

Forward session_id, exchange_token, the merchant identity and the whole automation.checkout_detection and automation.payment_status_detection objects to your app. Keep session_id on your backend too; you will need it to create the transaction later.

tip

domain may include a locale path — nike.com/sg and nike.com/jp are distinct merchants.

Merchant configuration is owned by Open Fabric

Open Fabric maintains automation for each onboarded merchant and updates it when a merchant changes their checkout. Treat it as opaque, forward it verbatim, and do not cache it across sessions. A session started with a stale configuration can miss a checkout or misread a payment outcome.

Start the session​

One call attaches the SDK to the WebView that will host the merchant, registers your listener, and starts the session with the values received from your backend:

WebViewHolder.kt
val session: InAppShoppingSession = OfInAppShoppingSdk.startSession(
webView = webView,
configuration = InAppSessionConfiguration(
sessionId = sessionId,
merchantId = merchantId,
merchantName = merchantName,
merchantUrl = "https://www.nike.com/sg",
exchangeToken = exchangeToken,
// both from the merchant.automation response, passed through unchanged
checkoutDetection = checkoutDetection,
paymentStatusDetection = paymentStatusDetection,
),
listener = object : CheckoutListener {
override fun onCheckoutReady() { /* show your payment hint */ }
override fun onCheckoutExited() { /* hide the hint */ }
override fun onSessionExpired(sessionId: String) {
// create a fresh session via your backend and restart
}
override fun onError(error: OpenFabricError) { /* show error, offer retry */ }
},
)

There is no "session started" callback. The SDK reports only failures, so your screen can move straight to browsing once startSession returns.

CheckoutListener methodFired when
onCheckoutReadyThe customer reached a payable checkout. See Detect checkout.
onCheckoutExitedThe checkout state ended.
onSessionExpiredThe session credential expired mid-flow (see below).
onErrorSession start failed (expired or already-used exchange token, network error).

Under the hood, the SDK generates an RSA-2048 keypair on the device (the private key never leaves it), redeems the exchange token for a session-scoped token valid for 3 hours, and loads merchantUrl into the WebView. It never mints or holds any other credential.

PropertyDescription
checkoutDetectionURL patterns plus the DOM conditions that must hold before a checkout counts as payable. Drives onCheckoutReady. Optional: omit it and checkout is never detected.
paymentStatusDetectionHow the SDK reads the merchant's own verdict after the payment is submitted. Optional: omit it and every run reports PaymentStatus.UNCONFIRMED. See Payment outcome.

Handle expiry and teardown​

  • Expiry: if the session outlives its 3-hour credential, the SDK fires onSessionExpired(sessionId) — either proactively before a call, or on a rejected request. Create a fresh session via your backend and restart the flow; the exchange token cannot be reused.
  • Teardown: call session.close() when the customer dismisses the shopping screen, and destroy the WebView. Run one session at a time: start a second session before closing the first and the two will contend over the same SDK state. The SDK does not currently reject it, so this is yours to enforce.

Next: Capture and approve the order.