CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/3ds-test-flow-reference

Cross-gateway, protocol-level reference for 3-D Secure (3DS 2.x) test coverage. Covers the EMVCo frictionless / challenge / not-applicable flow paths, SCA under EU PSD2, and the per-PAN test cards for Stripe, Adyen, and Braintree. The gateway-specific wrappers (adyen-test-mode / stripe-test-cards-and-webhooks / braintree-test-cards) compose this skill for single-gateway work, so use one of those for a single-gateway query. Use this skill when designing multi-gateway 3DS test coverage, auditing a 3DS redirect round-trip, or investigating a challenge-flow regression that is not gateway-specific.

70

Quality

88%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Medium

Suggest reviewing before use

Overview
Quality
Evals
Security
Files
name:
3ds-test-flow-reference
description:
Cross-gateway, protocol-level reference for 3-D Secure (3DS 2.x) test coverage. Covers the EMVCo frictionless / challenge / not-applicable flow paths, SCA under EU PSD2, and the per-PAN test cards for Stripe, Adyen, and Braintree. The gateway-specific wrappers (adyen-test-mode / stripe-test-cards-and-webhooks / braintree-test-cards) compose this skill for single-gateway work, so use one of those for a single-gateway query. Use this skill when designing multi-gateway 3DS test coverage, auditing a 3DS redirect round-trip, or investigating a challenge-flow regression that is not gateway-specific.

3ds-test-flow-reference

Overview

3-D Secure (3DS) is the EMVCo-specified card-authentication protocol. Per emvco.com, 3DS 2.x is the current spec (cite by stable ID: EMV 3-D Secure Protocol Specification v2.x); 3DS 1.0 is deprecated.

When to use

  • Designing test coverage for a payment integration handling 3DS.
  • Auditing existing 3DS flow implementation.
  • Investigating "customer says 3DS challenge looked broken" reports.
  • PR review of payment-redirect flows.

The three flow outcomes

Per EMVCo 3DS 2.x spec:

OutcomeCustomer experienceLiability
FrictionlessNo challenge; issuer authenticates based on risk signalsShifts to issuer (in many regions)
ChallengeCustomer prompted (SMS, biometric, app)Shifts to issuer on success
Not applicable3DS not invoked (non-EU; merchant-initiated)Stays with merchant

The merchant's job: send all available data to the gateway; the gateway + issuer + 3DS server decide the flow.

SCA under PSD2

Per European Banking Authority RTS on SCA: since September 2019 (enforcement gradual through 2021), EU card payments require two-factor SCA (two of knowledge / possession / inherence). Exemptions: low-value (< €30), recurring, trusted-beneficiary, merchant-initiated - each with tracking requirements.

Per-gateway test cards

Per-PAN 3DS test cards for Stripe, Adyen, and Braintree, each with its source doc, are in references/gateway-test-cards.md. The core Stripe cards used in the assertions below: 4000 0000 0000 3055 (frictionless) and 4000 0027 6000 3184 (challenge).

Test flow surface

1. Merchant calls Authorize / PaymentIntent / Charge
2. Gateway returns "requires_action" / "RedirectShopper" / "PAYER_ACTION_REQUIRED"
   per payment-flow-states-reference
3. Frontend redirects user to issuer-hosted 3DS challenge
4. User completes challenge (or auto-completes for frictionless)
5. Redirect back to merchant return URL
6. Merchant confirms PaymentIntent (or equivalent)
7. Gateway returns final state (succeeded / failed)

Test surface per step:

StepTest
1Initiate with each test card; assert state
2Frontend handles each gateway's "requires action" key correctly
3Redirect URL is built with the gateway-provided client secret / token
4Issuer-hosted page is reachable (manual + Playwright e2e)
5Return URL handler parses the response correctly
6Confirm call is idempotent (per payment-flow-states-reference)
7Final state matches expected per test card

Frictionless vs challenge - testable assertions

test('frictionless authentication', async () => {
  // Card 4000 0000 0000 3055 in Stripe test mode
  const intent = await stripe.paymentIntents.create({
    amount: 1000, currency: 'eur',
    payment_method: 'pm_card_threeDSecure2Supported',
    confirm: true,
  });
  expect(intent.status).toBe('succeeded');  // No challenge needed
});

test('challenge flow', async () => {
  // Card 4000 0027 6000 3184
  const intent = await stripe.paymentIntents.create({
    amount: 1000, currency: 'eur',
    payment_method: 'pm_card_threeDSecure2Required',
    confirm: true,
    return_url: 'https://example.com/return',
  });
  expect(intent.status).toBe('requires_action');
  expect(intent.next_action.type).toBe('redirect_to_url');
});

Anti-patterns

Anti-patternWhy it failsFix
Skip 3DS in testsEU regulations require it; you'll discover broken at launchPer-gateway 3DS card battery
Test only happy pathChallenge failure path also mattersTest failure + cancel mid-challenge
No return-URL handler testRace conditions on redirect-back lostTest the full round-trip
Hardcoded redirect URL in production codeLocalhost in prodPer-environment config
Treat requires_action as failureIt's the normal mid-flow stateHandle explicitly
3DS 1 still in code pathsDeprecated since 2022Remove and test
One test for all gatewaysEach handles 3DS slightly differentlyPer-gateway
Sync expectation on async flowConfirm is asyncWait for webhook

Limitations

  • EMVCo 3DS spec is paywalled. Reference by stable ID; rely on gateway docs for test cards.
  • Issuer-hosted challenge UIs vary. Bank-specific design; test via e2e against gateway test mode.
  • SCA exemptions are policy + technical. Code must claim the right exemption + log the decision.
  • 3DS data fields are extensive. Each gateway has its own field names; per-gateway docs are authoritative.

References

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/3ds-test-flow-reference badge