Skip to main content

threeDSChallenge

const threeDSChallenge: (options) => Promise<ThreeDSFrameResult>;

Renders a 3DS challenge in an iframe inside your checkout and resolves when the cardholder is done.

The challenge is shown in-context: the iframe goes into the container you provide, sized per the EMVCo challengeWindowSize the authentication response echoed back. The SDK adds no overlay, no dialog and no styling of its own — the surrounding UI is yours.

Two ways to know the cardholder is done, and a call needs at least one. completion waits for a postMessage from the page the flow lands on, which is why completion.origins is required — only those exact origins can end the wait — the Purse 3DS challenge included: its notification endpoint answers with a page that posts the completion. resolveAfterLoads waits for the iframe to navigate instead, for a provider whose result page posts nothing.

Note the challenge outcome is authoritative server-side. Whatever this resolves with, confirm the payment through your backend before fulfilling: an abandoned or timed-out challenge is settled server-side too, so status: 'timeout' does not mean the payment failed.

Parameters​

ParameterType
optionsThreeDSChallengeOptions

Returns​

Promise<ThreeDSFrameResult>

Always resolves. status is completed, timeout or aborted.

Throws​

SdkError If the input is invalid. Possible errors include:

Error Code
THREEDS_INVALID_URL
THREEDS_INVALID_INPUT
CONTAINER_NOT_FOUND

Examples​

Purse 3DS, from the `authentication.challenge_data` blob of the `POST /v2/payments` answer

const { status, data } = await threeDSChallenge({
challengeData, // forwarded verbatim from your backend
container: 'checkout-3ds-slot',
completion: {
// The Purse API origin your payments are created against — ask Purse for the one matching
// your environment. A message from any other origin is ignored.
origins: [PURSE_API_ORIGIN],
match: (data) => (data as { type?: string }).type === 'purse:3ds:challenge-completed',
},
});
// `data` carries `result` (`SETTLED` | `PENDING` | `REJECTED`) and `payment_id`; confirm the
// payment through your backend either way.

EMVCo challenge, from the authentication response

const { status, data } = await threeDSChallenge({
acsUrl,
creq,
challengeWindowSize, // pass through — EMVCo requires the announced size
container: 'checkout-3ds-slot',
completion: { origins: ['https://api.purse.tech'] },
});

A provider posting the result back through your own return page

await threeDSChallenge({
url: stepUpUrl,
fields: { JWT: accessToken, MD: merchantData },
container: challengeSlot,
completion: {
origins: [window.location.origin],
match: (data) => (data as { MessageType?: string }).MessageType === 'stepUpComplete',
},
});

See​

SecureFieldsErrors