In both flows, mounting the 3D Secure Element is what runs the authentication. Just creating a session is not enough for the cardholder or the bank to complete the authorization — mount the element for every session with
auth_status: ACTION_REQUIRED and let onSuccess/onFailure drive the next step.Choose a Flow
Both flows are official in Rinne. There is no platform-level preferred flow.
Backend Endpoint Mapping
Shared Frontend Setup
checkout.html
checkout.ts
Session-First Flow
1
Create 3DS session on backend
Send the encrypted card number and plain expiry (month/year) from
mountedCard.values to your backend. The Card Element encrypts the card number client-side; expiry values are not encrypted.2
Run authentication in the 3DS element
Create
threeDSecure once and call mount(tdsSessionId). Mounting starts the authentication — the element resolves frictionless flows silently and renders the issuer’s challenge when one is required.3
Create transaction after authentication
On
onSuccess, call your backend and include the authenticated session ID.Backend session request example
/api/... routes are your backend: they forward these payloads to the Rinne endpoints in Backend Endpoint Mapping with your API key. See 3D Secure authentication for the backend contract.
Transaction-First Flow
In this flow, your backend transaction status determines whether frontend should run 3DS.
1
Create transaction and branch by status
Backend creates the transaction and returns the initial status.Use
require_3ds: true when you want deterministic AWAITING_3DS entry in transaction-first.2
Start 3DS only for AWAITING_3DS
If transaction is
AWAITING_3DS, create a 3DS session with the same amount/currency and encrypted card number/expiry.3
Mount challenge when required and authenticate
Branch on
auth_status: mount only for ACTION_REQUIRED, skip for AUTHENTICATED, abort for FAILED.
After challenge success (or if already AUTHENTICATED), backend authenticates the pending transaction with session id using:- Self:
POST /v1/transactions/{transactionId}/authenticate - Merchant:
POST /v1/merchants/{merchantId}/transactions/{transactionId}/authenticate
4
Handle transaction result
Card transactions typically resolve synchronously — the
/authenticate response already contains the final status (APPROVED, AUTHORIZED, or REFUSED). Use this status to drive your UI immediately.Challenge strategy flags (backend)
require_3ds: true: create directly inAWAITING_3DS, so you can enforce 3DS while staying on one transaction-first backend flow.refuse_on_challenge: true: fail fast withREFUSED+status_reason=CHALLENGE_NOT_ALLOWEDwhen a challenge-triggered path would occur.- Use
refuse_on_challengewhen you intentionally do not support challenge UX for a merchant, channel, or transaction segment. - The two flags cannot both be
true.
Transaction-first status branching
If a transaction is initially
PROCESSING and later becomes AWAITING_3DS (for example, soft-decline recovery), run the same transaction-first 3DS steps at that point.Transaction-first orchestration example
runTransactionFirst to your submit handler the same way as runSessionFirst in the session-first flow.
Session ID Mapping
tds_session_id: use inthreeDSecure.mount(tds_session_id)on frontend.id: use asthree_d_secure_session_idin backend transaction create/authenticate calls.
Handle 3DS Statuses
In practice, sessions come back asACTION_REQUIRED or FAILED at creation — even frictionless authentication completes through the SDK element. The API contract allows AUTHENTICATED as a creation-time response for forward compatibility, so always branch on all three statuses.
Frictionless vs challenge
When the element is mounted, the issuer decides whether the transaction can be authenticated silently (frictionless) or needs cardholder interaction (challenge):- Frictionless: authentication completes in the background with no visible UI.
onSuccessfires automatically, often within seconds. - Challenge: an issuer-hosted verification step appears inside the element (OTP, app approval, biometric). Even when that screen tells the cardholder to approve the purchase in their banking app, it is rendered by the element, which also detects the approval — do not replace it with a waiting screen of your own.
onSuccessfires after the user completes it.
onSuccess is the signal to proceed in either case. You can inspect authentication_flow on the backend after the session is authenticated to see which path was taken.
Retry Strategy
Sessions are single-use: after a failure, an expired session, or a rejected/authenticate call, the attempt must restart with a new session. In this guide’s architecture that happens naturally — the submit handler re-enables the button, and runSessionFirst/runTransactionFirst create a fresh session on every attempt.
The SDK automatically cleans up the previous element instance when a new one mounts.

