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
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.
domain may include a locale path — nike.com/sg and nike.com/jp are distinct merchants.
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:
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 method | Fired when |
|---|---|
onCheckoutReady | The customer reached a payable checkout. See Detect checkout. |
onCheckoutExited | The checkout state ended. |
onSessionExpired | The session credential expired mid-flow (see below). |
onError | Session 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.
| Property | Description |
|---|---|
checkoutDetection | URL patterns plus the DOM conditions that must hold before a checkout counts as payable. Drives onCheckoutReady. Optional: omit it and checkout is never detected. |
paymentStatusDetection | How 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.