How it works
- The shopper’s card is tokenized with Elements as usual (
elements.submit(), without callingthreeDS()). - Your 3DS provider authenticates the shopper using its own access to the card — it doesn’t receive anything from Elements.
- Your server charges with the card token plus the evidence your provider returned.
Before you begin
This path requires per-outlet enablement. Without it, the charge is rejected
with
422 urn:radiumone:three-ds:external-auth-not-permitted.Steps
1
Create a session and tokenize the card
Create a tokenization session and mount Elements as usual. Don’t call
radiumone.threeDS() on this path — you’re not using RadiumOne’s 3DS.2
Authenticate with your own 3DS provider
Run your own 3DS integration (AReq/ARes/CReq) however you already do
today. This happens entirely outside RadiumOne. Collect at minimum the
scheme’s ECI, the CAVV (or AAV), and the directory server transaction ID.
3
Charge with the provider's evidence
Send the card token and your provider’s evidence to your server, then
purchase or authorize with the If the charge is rejected, see Handle 3DS failures with your own provider.
three_ds evidence arm. API reference: purchase · API reference: authorize.Handle the result
Any 2xx response is a result you must branch onstatus — never on response_code (that’s the verbatim host/acquirer code; useful for support tickets, not for your app logic).
PENDING means the outcome isn’t known yet — most often after a processor timeout. Don’t assume success or failure. Recover it one of two ways:
- Wait for a webhook (
payment.*,authorization.*,refund.*— see Webhook event types). - Call
GET /v1/transactions/{id}/statusfor a live inquiry against the acquirer.
id yet — a client-side timeout before the first response arrived — replay the same request with the same request_id and body. The replay returns the stored transaction and its id, whatever status it’s reached. Never re-submit with a new idempotency key just because the first attempt is slow — that risks a second charge for the same order.
Errors specific to this path
The two errors you’re most likely to hit on this path arethree-ds:external-auth-not-permitted (your outlet isn’t enabled — see
Before you begin) and gateway:validation-error (a
three_ds field is missing, too long, or an unrecognized key was sent — the
evidence arm rejects unknown keys). For the full 3DS error reference with HTTP
codes and remedies, see
Authentication results.
ECI values
The electronic commerce indicator (ECI) your provider returns determines whether liability shifts to the issuer:Limitations
- No 3DS1. Only 3DS2 evidence is accepted — there’s no
xidfield, so a provider that only supports 3DS1 can’t use this path. - Fields:
cavv(≤64 chars),eci(≤2 chars),ds_transaction_id(≤64 chars),version(≤10 chars). Unknown keys in the same object are rejected. - Merchant-asserted evidence. RadiumOne doesn’t independently verify the CAVV or ECI you submit — it trusts your provider’s result. Make sure your own integration validates the ARes/RReq signature before you submit its fields here.
Server policy
Never use a secret key (
r1sk_…) in browser code, mobile apps, or anywhere a shopper can inspect it. Secret keys belong on your server only.Test your integration
See Test your integration for scenario coverage.Go-live notes
- Confirm enablement for this outlet before relying on this path in production.
- Keep your provider’s evidence validation server-side; never trust values that could originate from the browser.
- Review the go-live checklist before launch.
Next steps
3D Secure overview
Compare this path against RadiumOne’s built-in 3DS.
Security and PCI scope
PCI scope per integration path.