Skip to main content
3D Secure Element renders issuer authentication challenges for transactions that require cardholder verification. You create the element once, mount it with a tds_session_id, and react to success, failure, or technical errors through callbacks.

Create a 3DS Element

Create the element once with callbacks, then call mount(sessionId) whenever you need to authenticate.
const threeDSecure = await rinne.elements.threeDSecure({
  target: '#three-ds-container',
  size: { width: '100%', height: '450px' },
  colorScheme: 'dark',
  locale: 'pt',
  onReady: () => console.log('Challenge is ready'),
  onSuccess: () => console.log('Authentication succeeded'),
  onFailure: (error) => console.log('Authentication failed', error),
  onError: (error) => console.log('3DS technical error', error)
})

await threeDSecure.mount('tds_session_123')

Options

interface ThreeDSecureElementOptions {
  target?: string | HTMLElement   // default: modal overlay
  size?: { width: string; height: string }
  colorScheme?: 'light' | 'dark'  // iframe root color-scheme; match your page theme
  theme?: ThemeOption
  locale?: 'pt' | 'en' | 'es'     // default: 'pt'
  onReady?: () => void
  onSuccess?: () => void
  onFailure?: (error?: { code?: string; message?: string }) => void
  onError?: (error: { code: string; message: string }) => void
}

Callback Semantics

CallbackMeaningTypical action
onReadyChallenge UI is rendered and readyHide loading placeholders
onSuccessAuthentication completed successfullyCall backend to confirm/finalize payment
onFailureAuthentication was attempted but failedShow retry CTA and create a new session
onErrorTechnical error (component/network/runtime)Show fallback path and log diagnostics
const threeDSecure = await rinne.elements.threeDSecure({
  onReady: () => console.log('Challenge is ready'),
  onSuccess: () => confirmOnBackend(transactionId),
  onFailure: (error) => showError(error?.message ?? 'Authentication failed'),
  onError: (error) => showError(`Technical error: ${error.message}`)
})

colorScheme

Use colorScheme to align the 3DS iframe root with your page theme.
const colorScheme =
  document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'

const threeDSecure = await rinne.elements.threeDSecure({
  target: '#three-ds-container',
  colorScheme,
  onSuccess: () => confirmPayment()
})

Theme

theme supports preset strings (clean, minimal, material) and function themes.
const threeDSecure = await rinne.elements.threeDSecure({
  theme: 'material'
})
If you pass theme as an object, the 3DS component currently uses the preset value.

Full Theme Example

const isDark = document.documentElement.classList.contains('dark')

const threeDSTheme = (utils) => ({
  styles: {
    ':root': {
      colorScheme: isDark ? 'dark' : 'light',
      backgroundColor: isDark ? '#020617' : '#ffffff'
    },
    body: {
      margin: 0,
      fontFamily: 'Inter, ui-sans-serif, system-ui, sans-serif'
    },
    ...utils.media('(max-width: 640px)', {
      body: {
        fontSize: 14
      }
    })
  }
})

const threeDSecure = await rinne.elements.threeDSecure({
  target: '#three-ds-container',
  size: { width: '100%', height: '450px' },
  colorScheme: isDark ? 'dark' : 'light',
  theme: threeDSTheme
})
3DS challenge copy and issuer-controlled challenge UI cannot be fully restyled. Use theme, size, and colorScheme to style the container experience around it.

Mount Behavior

  • mount(sessionId) requires a non-empty session ID.
  • If you mount again with a new session ID, the SDK cleans up the previous instance automatically.
  • Callbacks from options are automatically wired for each mount.
3DS sessions expire 1 hour after creation. When a session expires, request a new session ID from your backend and call mount(newSessionId). The SDK automatically attempts to unmount the previous instance, but you should unmount manually for safety.

Backend Contract

Use the frontend 3DS element as an authentication step between your backend session creation and transaction confirmation.
1

Create a 3DS session on your backend

Return both session IDs from your backend:
  • tds_session_id for threeDSecure.mount(...) in the frontend
  • id for three_d_secure_session_id in backend transaction create/authenticate calls
Use one of the supported create-session endpoints based on your context:
  • POST /v1/3ds-sessions (self)
  • POST /v1/merchants/{merchantId}/3ds-sessions (organization on behalf of merchant)
2

Mount the 3DS element

In practice, sessions come back as ACTION_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.
  • FAILED: the card was rejected before the flow could start — surface the error and let the user retry.
  • AUTHENTICATED: authentication already completed — skip mounting and proceed to payment confirmation.
  • ACTION_REQUIRED: mount with tds_session_id. The SDK runs the full 3DS flow internally.
When mounted, the issuer decides the path:
  • Frictionless: the issuer authenticates the transaction silently in the background. No challenge UI is shown. onSuccess fires automatically.
  • Challenge: the issuer presents a verification step inside the element. onSuccess fires after the user completes it.
Your code handles both identically.
if (session.auth_status === 'FAILED') {
  showError(session.failure_reason ?? 'Authentication failed')
  return
}

if (session.auth_status === 'AUTHENTICATED') {
  confirmPayment()
  return
}

await threeDSecure.mount(session.tds_session_id)
// onSuccess/onFailure/onError callbacks drive the next step
3

Handle success/failure callbacks

On onSuccess, create/authenticate the transaction on your backend using the internal session id. On onFailure, create a new session before retrying.

Mount Errors

try {
  const threeDS = await rinne.elements.threeDSecure()
  await threeDS.mount('') // invalid session
} catch (error) {
  console.error('3DS mount failed', error)
}

Unmount

const mounted = await threeDSecure.mount(sessionId)
mounted.unmount()

Common Failure Handling

const threeDSecure = await rinne.elements.threeDSecure({
  onFailure: (error) => {
    showError(error?.message ?? 'Authentication failed')
  },
  onError: (error) => {
    showError(`Technical error: ${error.message}`)
  }
})
locale applies to SDK/component messages. Issuer-hosted challenge copy is controlled by the card issuer.