A 3DS2 result of Y does not mean you are protected against fraud chargebacks. The value that decides that is the ECI the card network sends back in the authorisation response, and the network can downgrade it. Stripe only began exposing that response ECI as a separate field on 10 June 2026, so many integrations still store the request ECI and assume liability has shifted when it has not.
This guide covers what happens after authentication. The SCA exemptions guide on this site explains how to ask for a frictionless outcome. This one explains how to read what you get back: every transStatus value, the reason codes worth branching on, Visa and Mastercard ECI codes, and the field names at four PSPs. The pages that rank for these queries today are short reference stubs, and none of them connects status to ECI to liability. That connection is the useful part.
What Is the Difference Between 3DS2 Frictionless and Challenge Flow?
In a frictionless flow, the issuer's Access Control Server (ACS) looks at the data you sent (device fingerprint, billing address, transaction history, amount) and approves authentication without the cardholder doing anything. The cardholder never sees an issuer screen.
In a challenge flow, the ACS decides it needs proof from the cardholder. That can be a one-time passcode, a banking-app approval or biometrics. You render the issuer's UI in an iframe on the web or through the 3DS SDK in an app, and the cardholder completes it there.
The merchant never makes that choice. You can say what you would prefer through threeDSRequestorChallengeInd, but the ACS decides. Your integration has to handle both paths every time, plus a third: decoupled authentication, where the cardholder approves later on another device and you find out asynchronously.
How Does the 3DS2 Message Flow Work?
The protocol has four message pairs. You will see their names in PSP logs.
| Message | Direction | When |
|---|---|---|
| AReq / ARes | 3DS Server → DS → ACS and back | Every authentication. The ARes either finishes the flow (frictionless) or asks for more. |
| CReq / CRes | Browser or SDK ↔ ACS | Challenge only. The cardholder interacts here. |
| RReq / RRes | ACS → DS → 3DS Server | After a challenge or decoupled flow. Carries the final result. |
| PReq / PRes | 3DS Server → DS | Cache refresh of card ranges and 3DS Method URLs. |
Before the AReq, browser flows run the 3DS Method: a hidden iframe that lets the ACS fingerprint the device. You report how it went in threeDSCompInd: Y (completed), N (did not complete) or U (no 3DS Method URL for this card range). Cardinal and Nuvei both document a 10-second ceiling, after which you send N and continue. Global Payments recommends closing the iframe after about 3 seconds. Waiting the full 10 seconds is a choice you make at checkout, and it slows every customer for a small gain in frictionless rate.
Three identifiers come back on every transaction: threeDSServerTransID, acsTransID and dsTransID. The dsTransID is the one the scheme knows about and the one you want in dispute evidence.
3DS2 transStatus Values Explained
transStatus is the single-letter outcome. Where it appears tells you as much as its value does.
| Value | Meaning | Appears in | Final? |
|---|---|---|---|
Y | Authenticated | ARes, RReq, final CRes | Yes |
N | Not authenticated, transaction denied | ARes, RReq, final CRes | Yes |
U | Authentication could not be performed (technical or other problem) | ARes, RReq | Yes |
A | Attempt: not authenticated, but proof of attempt generated | ARes, RReq | Yes |
R | Rejected by issuer; do not authorise | ARes, RReq | Yes |
C | Challenge required | ARes | No |
D | Decoupled authentication confirmed | ARes, final CRes | No |
I | Informational only; requestor's challenge preference acknowledged | ARes | Yes |
S | Challenge using Secure Payment Confirmation (2.3.1+) | ARes | No |
C, D and I were added in 2.2.0. S needs messageVersion 2.3.1 or higher. If SPC cannot run, the issuer falls back to C. EMVCo's whitepaper limits the final CRes to Y, N or D, so a challenge cannot finish with A or U.
I is the one that catches people out. It comes back when you sent a data-only or exemption request. The issuer has accepted the information but has not authenticated anyone, so treat it as "no 3DS liability" and not as a soft success.
Which transStatusReason Codes Should You Act On?
transStatusReason is required when transStatus is N, U or R. EMVCo defines codes 01 to 27, reserves 28 to 79 for itself and gives 80 to 99 to each Directory Server. The codes worth branching on:
| Code | Meaning | What to do |
|---|---|---|
| 01 | Card authentication failed | Do not retry automatically. Ask for another card. |
| 05 | Expired card | Card update flow |
| 06 / 08 | Invalid card number / no card record | Ask for the card again |
| 11 | Suspected fraud | Hard stop. Feed your fraud model. |
| 13 | Cardholder not enrolled | Retrying will not change it. Decide with your risk team whether to authorise without 3DS. |
| 14 | Transaction timed out at the ACS | One retry is reasonable |
| 22 | ACS technical issue | One retry, then fall back |
| 23 / 24 | Decoupled required but not requested / max time exceeded | Fix your request, or raise threeDSRequestorDecMaxTime |
| 26 | Attempted but not performed by the cardholder | Abandonment. Show the challenge again or offer another method. |
The 80 to 99 range is where scheme differences live. In Adyen's spec, 81 means "ACS timed out" on Visa and "challenge exemption accepted" on Mastercard. If you log these codes without the card brand next to them, you cannot interpret them later.
ECI Values for Visa vs Mastercard in 2026
The Electronic Commerce Indicator (ECI) is what the authorisation message carries. Visa and Mastercard use different numbers for the same outcome, which is why mapping ECI by number alone produces wrong answers.
| Outcome | Visa (and Amex, Discover, JCB, Diners at Checkout.com) | Mastercard |
|---|---|---|
| Fully authenticated | 05 | 02 |
| Attempted | 06 | 01 |
| Not authenticated / failed | 07 | 00 |
| Data Only (no authentication) | none | 04 |
The rows above agree across Stripe and Checkout.com. Beyond them the two PSPs disagree:
- Mastercard
06. Stripe calls it "merchant-based liability". Checkout.com calls it out of scope or SCA-exempt. Both say no liability shift. - Mastercard
07. Stripe says partial shipment or recurring. Checkout.com says a successful recurring transaction secured by 3DS, with liability shift unless it is Apple Pay without a cryptogram. - Mastercard
01and Visa06. Stripe labels these "attempted". Checkout.com describes them as a stand-in service treating the transaction as successful.
Does 3DS2 Attempted Authentication Shift Liability?
Usually yes, with exclusions. This is where most guides stop too early.
An A result produces Visa ECI 06 or Mastercard ECI 01, and both are generally covered. Braintree's status table backs this up: authenticate_attempt_successful reports liabilityShifted: true. But TabaPay's summary of the Visa rules lists exclusions:
- Non-reloadable prepaid cards get no attempts protection.
- If the authorisation goes out without a CAVV, Visa reclassifies the ECI to
07. - In the US, merchants in the 3DS Fraud Monitoring Program are excluded, as are MCCs 4829 (money transfer), 5967, 6051 (quasi-cash and crypto), 6540, 7801, 7802 and 7995 (gambling).
Data Only (Mastercard ECI 04, Stripe result data_share_only, Checkout.com exemption data_share) never shifts liability. It exists to give issuers better data for the authorisation decision. In a Stripe Sessions 2026 talk, Stripe said sharing Data Only signals raised conversion by 3.2% on European transactions over €250, but that is a talk figure, not a published dataset.
Stripe vs Adyen vs Checkout.com vs Braintree: Reading the 3DS Result
Each PSP reports the same protocol outcome under different names, and each one hides something different.
| Stripe | Adyen | Checkout.com | Braintree | |
|---|---|---|---|---|
| Outcome | three_d_secure.result | threeDS2Result.transStatus | 3ds.authentication_response | threeDSecureInfo.status |
| Raw transStatus | No | Yes | Yes | On the nonce only (lookup / authentication) |
| Reason code | result_reason (Stripe's own enum) | transStatusReason | authentication_status_reason (only when not Y) | Nonce only |
| Frictionless or challenge | authentication_flow | Infer from ChallengeShopper vs IdentifyShopper | 3ds.challenged (boolean) | Infer from lookup.transStatus = C |
| ECI | Request and response, separately | threeDS2Result.eci, additionalData.eci | 3ds.eci | eciFlag |
| Liability | Not exposed | additionalData.liabilityShift | Not exposed directly | liabilityShifted, liabilityShiftPossible |
Stripe's result enum is authenticated, attempt_acknowledged, exempted, failed, not_supported, processing_error and data_share_only. Its result_reason values (abandoned, canceled, rejected, bypassed, protocol_error, card_not_enrolled, network_not_supported) are a Stripe summary. You cannot get EMVCo code 11 from Stripe, so a fraud model that relies on "suspected fraud" from the issuer will not get that signal there.
The detail most teams miss is on the Stripe side. payment_method_details.card.three_d_secure.electronic_commerce_indicator is the ECI from authentication, sent in the request. payment_method_details.card.electronic_commerce_indicator is the network's final value. Stripe's own example shows a Visa payment with result: "authenticated" and request ECI 05 that came back as 07:
"card": {
"brand": "visa",
"electronic_commerce_indicator": "07",
"three_d_secure": {
"electronic_commerce_indicator": "05",
"result": "authenticated"
}
}
That payment has no liability shift. Stripe only populated the response field from 10 June 2026 in the API (22 July in Sigma) and did not backfill it, so dashboards built before then cannot see downgrades.
Checkout.com's async shape also matters for state machines. A 3DS payment returns HTTP 202 with status: "Pending" and _links.redirect.href. A completed payment returns 201. If your client treats any 2xx as captured money, a challenged card will look paid before the cardholder has done anything.
What to Do After U, R or a 3DS Timeout
These are my retry rules:
R: do not authorise and do not retry 3DS. The issuer has told you the answer.Nwith reason 01, 11 or 10: stop and ask for another payment method.Uwith reason 14 or 22: retry authentication once. If it fails again, decide by risk. Low-value orders can go to authorisation without 3DS and with liability on you. Checkout.com automates this withattempt_n3dand reports it back as3ds.downgraded: true.Uwith no reason, or a DS timeout: same as above, but alert when one BIN range starts producing it in volume (my rule of thumb is anything above 1 to 2%). That pattern means an ACS outage, not your bug.- Challenge abandoned (reason 26, Stripe
abandoned): do not retry silently. Show a clear "your bank needs to confirm this" screen with one retry button.
1A, Mastercard 65) on in-scope transactions, which sends you back to 3DS. The card decline codes guide covers that loop.
3DS2 Frictionless Rates in the UK and Europe
Most published rates have no traceable source. The "80 to 85% frictionless" figure that ranks on several vendor blogs cites unnamed "industry benchmarks". The data I could trace comes from Ravelin:
- In its 2024 data, the UK ranked 22nd of 37 countries for frictionless rate while ranking first for challenge success and overall 3DS success.
- Its March 2026 report puts UK 3DS success at 95% for 2025, still first. It also says frictionless rates were flat or down in 28 of 37 countries, and links that to issuers turning down more exemption requests.
Opinion: Store the Response ECI and Stop Trusting transStatus
My view is that the response ECI should be the field your systems store as the authentication outcome. transStatus describes what the ACS thought. The response ECI describes what the network accepted, and the network's version is the one that counts in a dispute. I expect most PSPs to follow Stripe in exposing request and response ECI as separate fields within the next year, because merchants fighting fraud disputes need the downgraded value and currently cannot see it.
The draft EMV 3DS v2.4.0 that EMVCo put out for comment in June 2026 (comments closed 1 July) does not change this. Since 2.3.1 in September 2022, EMVCo's changes have focused on Secure Payment Confirmation and out-of-band flows rather than the ECI side.
What This Means for Payments Engineers
1. Persist five fields per authentication: card brand, transStatus, transStatusReason, dsTransID, and the response ECI. On Stripe, read payment_method_details.card.electronic_commerce_indicator, not the one under three_d_secure.
2. Map ECI by brand, never by number. 06 is a liability shift on Visa and none on Mastercard.
3. Treat I and Data Only as unprotected. Keep them out of any "authenticated" metric.
4. Check your MCC before trusting attempts. On Visa in the US, 6051, 4829 and 7995 get no attempts protection.
5. Cap the 3DS Method wait. Measure your frictionless rate at 3, 5 and 10 seconds before keeping the spec maximum.
6. Handle async responses. Checkout.com's 202 Pending, Adyen's ChallengeShopper and decoupled D results all need a pending state that is not "paid".
7. Check you have dropped 2.1.0. Mastercard retired it on 24 September 2024 and Visa on 25 September 2024. Any code path still sending messageVersion: "2.1.0" will be rejected.
Key Takeaways
- Frictionless vs challenge is the issuer's decision. Your job is to handle
Y,N,U,A,R,C,D,Iand, from 2.3.1,S. - Visa and Mastercard use different ECI numbers: 05/06/07 against 02/01/00, plus Mastercard
04for Data Only. Stripe and Checkout.com describe Mastercard 06 and 07 differently. - Attempted authentication shifts liability on both schemes, except for Visa's prepaid, missing-CAVV and US high-risk MCC exclusions.
- The response ECI, not
transStatus, tells you whether liability shifted. Stripe exposes both. Most PSPs do not. - UK issuers challenge more than most, and UK cardholders complete challenges more reliably than anywhere else (95% 3DS success in Ravelin's 2025 data).