Testland
Browse all skills & agents

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.

Install with skills.sh (any agent)

npx skills add testland/qa --skill 3ds-test-flow-reference
View source

3ds-test-flow-reference

Overview

3-D Secure (3DS) is the EMVCo-specified card-authentication protocol. Per emvco.com (opens in new window), 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 (opens in new window): 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

Gateway 3DS test cards

View source (opens in new window)

Gateway 3DS test cards

Per-PAN test cards that drive 3DS challenge, frictionless, and bypass flows in each gateway's test mode. Cross-referenced from 3ds-test-flow-reference. For single-gateway work, prefer the gateway wrapper skills (stripe-test-cards-and-webhooks, adyen-test-mode, braintree-test-cards).

Stripe

Per docs.stripe.com/testing#regulatory-cards (opens in new window):

CardBehaviour
4000 0027 6000 3184Authentication required (challenge)
4000 0025 0000 3155Authentication required (challenge), payment failure after success
4000 0000 0000 3220Authentication required (challenge), payment success
4000 0000 0000 30553DS supported but not required (frictionless)
4242 4242 4242 4242Standard test card (no 3DS)

Adyen

Per docs.adyen.com/development-resources/test-cards-and-credentials/test-card-numbers (opens in new window):

CardBehaviour
4917 6100 0000 00003DS 2 challenge flow
5454 5454 5454 54543DS 2 frictionless
4012 8888 8888 18813DS 1 (deprecated; for migration testing)

Braintree

Per developer.paypal.com/braintree/docs/guides/3d-secure/testing/node (opens in new window):

CardBehaviour
4000 0000 0000 1091Authenticate via standard flow
4000 0000 0000 1109Frictionless
4000 0000 0000 1125Bypass (skipped)

Related skills

adyen-test-mode

Wraps Adyen test-mode patterns: test-API-key initialization, the canonical Adyen test cards (Visa 4111 1111 1111 1111; 5454 5454 5454 5454 3DS frictionless; 4917 6100 0000 0000 3DS 2 challenge), the per-flow result codes (Authorised / Refused / Pending / RedirectShopper / Error), the HMAC-SHA256 webhook validation, and notifications-vs-API duality (Adyen separates synchronous API calls from asynchronous notifications). Use when testing Adyen-integrated code.

braintree-test-cards

Wraps Braintree (PayPal-owned) sandbox testing patterns: sandbox merchant credentials, Drop-in / Hosted Fields client-side patterns, the Transaction lifecycle (submitted_for_settlement → settled), Braintree's distinctive test-card behaviours (specific PANs trigger specific errors), and the webhook verification (Braintree Webhook Parser). Use when testing Braintree-integrated code.

chargeback-flow-test-author

Workflow-driven skill that builds the chargeback / dispute test suite: canonical reason codes (Visa 10.4 fraud, 13.1 services-not-provided; Mastercard MCC 4855), per-gateway dispute APIs (Stripe Disputes, Adyen Chargeback notifications, PayPal Disputes), the evidence-submission flow + window, and disposition outcomes (won / lost / accepted). Use when designing dispute coverage; for refunds use refund-test-matrix-builder, for webhook redelivery + idempotency use payment-webhook-replay, and for the lifecycle state model use payment-flow-states-reference.

payment-flow-states-reference

Pure-reference catalog of payment lifecycle state machines across Stripe, Adyen, PayPal, and Braintree: canonical states (created / requires_action / processing / succeeded / requires_capture / canceled / failed), authorisation vs capture, asynchronous webhook states, and refund / dispute / chargeback transitions. Use when designing tests for payment flows or auditing state-handling code; this is the state model, not a builder - to author suites on it use refund-test-matrix-builder (refunds), chargeback-flow-test-author (disputes), or payment-webhook-replay (webhook replay).

payment-webhook-replay

Workflow-driven skill that builds payment webhook replay + recovery tests. Covers the idempotency contract (every webhook handler must handle redelivery without side effects), the replay simulators (Stripe CLI `stripe trigger`, Adyen Customer Area resend, PayPal Webhook Simulator, Braintree webhook test), the signature-verification gauntlet (HMAC-SHA256 per gateway, expired-timestamps rejection), and the partial-failure recovery scenarios. Use when designing webhook robustness tests.

paypal-sandbox

Wraps PayPal Sandbox testing patterns: sandbox account creation (Business + Personal accounts in developer.paypal.com), the Orders v2 API (create / capture / refund), webhook event simulator (developer.paypal.com webhook simulator), sandbox-account-specific test cards, and the OAuth2 client-credentials flow for sandbox. Use when testing PayPal-integrated code.

pci-dss-scope-reference

Pure-reference catalog of PCI DSS v4.0 scope reduction techniques + the testable scope boundaries. Covers the SAQ levels (A through D, picked by how cardholder data flows), the PAN-storage prohibitions (only first-6 + last-4 retained; nothing else cleartext), the tokenization + hosted-fields scope-reduction patterns (Stripe Elements / Adyen Drop-in / Braintree Hosted Fields keep PAN off your servers), Network-Segmentation as PCI scope-reduction, and the testable behaviours the scope boundary creates. This is the catalog of WHY the boundary matters and what it makes testable - not a checker that verifies a given integration against the standard. Use when designing or auditing the PCI scope of a payment integration.

refund-test-matrix-builder

Workflow-driven skill that builds a refund test matrix from a payment-flow inventory: refund variants (full / partial / multiple-partials / over-refund / refund-on-disputed / refund-on-already-refunded), per-gateway nuances (Stripe, Adyen, PayPal, Braintree refund APIs), and timing variants (immediate / next-day / declined-by-bank), one test case per cell. Use for refund coverage on a new integration; for chargeback / dispute coverage use chargeback-flow-test-author, for webhook redelivery + idempotency use payment-webhook-replay, and for the state model use payment-flow-states-reference.

stripe-subscription-billing-test-author

Builds test suites for Stripe recurring-billing flows: trial-to-paid conversion, proration on plan upgrade and downgrade, dunning on failed renewal, cancel and reactivation, and the full subscription webhook event matrix (invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted, invoice.paid). Uses Stripe Billing test clocks (POST /v1/test_helpers/test_clocks) to time-travel through billing cycles without calendar delay. Distinct from stripe-test-cards-and-webhooks (one-time PaymentIntents) and payment-webhook-replay (idempotency + replay robustness). Does not cover single-event CLI replay or handler idempotency testing (see payment-webhook-replay for those). Use when authoring tests for subscription or recurring-billing integrations.

stripe-test-cards-and-webhooks

Wraps Stripe API testing patterns: test-mode initialization, the canonical test cards (4242 success; 4000 0000 0000 0002 declined; 4000 0027 6000 3184 3DS challenge per 3ds-test-flow-reference), the Stripe CLI webhook flow (`stripe listen --forward-to`), the Stripe CLI fixture commands (`stripe trigger payment_intent.succeeded`), and the webhook signature verification (Stripe-Signature header + HMAC-SHA256). Use when testing Stripe-integrated code.