Building a checkout that a human can use is table stakes. Building a checkout that a browser‑based AI agent can safely use, under PSD2/SCA, is a different sport. The AI must navigate payment options, survive 3DS2 challenges, respect user consent boundaries, retry when issuers demand SCA, avoid replay and double charges, minimize PII, and leave an auditable trail. And it has to do all this in hostile conditions: flaky ACS pages, divergent browser APIs, PSP idiosyncrasies, and fraud/adversarial behavior.
This article lays out a pragmatic blueprint for a PSD2/SCA‑safe, agent‑capable checkout using the Payment Request API, tokenized cards, 3DS2 challenge orchestration, SCA‑aware retries, idempotent orders, PII minimization, audit trails, and human‑in‑the‑loop (HITL) escalation. We focus on web stacks, with pointers to Apple Pay, Google Pay, and Secure Payment Confirmation (SPC) for WebAuthn‑backed SCA.
1) Why agentic checkout is different
AI shopping agents add constraints that traditional UX patterns never had to consider:
- Deterministic control paths: Agents must follow machine‑interpretable flows, not ad‑hoc DOM hacks.
- Bounded autonomy: Agents cannot silently push funds; SCA and consent require either a user gesture or a previously granted mandate (e.g., MIT with initial SCA).
- Robustness over presentation: Issuer ACS pages vary; agents must handle iframes, redirects, and timeouts without brittle selectors.
- Verifiable provenance: Every automated action must be attributable, reversible, and auditable.
- Privacy discipline: Agents should not over‑collect addresses, emails, or PANs they don’t need.
Your goal isn’t to bypass SCA; it’s to build a compliant path that agents can repeatedly, safely traverse with minimal human interruptions, escalating only when required by regulation or risk.
2) Standards and building blocks
- Payment Request API (PR API): Browser‑native API to request payment and shipping/contact data. Reduces form friction and grants a structured interface that’s friendlier to agents.
- Secure Payment Confirmation (SPC): A W3C specification that layers WebAuthn on top of PR API to provide low‑friction SCA using a bound authenticator. Great for repeat purchases and issuer/merchant wallets.
- Apple Pay JS and Google Pay: Wallets with built‑in authentication; strong alignment with SCA. Google Pay can integrate via PR API or its own JS SDK; Apple Pay uses ApplePaySession.
- 3‑D Secure 2.x (3DS2): Risk‑based authentication mandated by PSD2. Flows: frictionless (no challenge) vs challenge (CReq/CRes in an iframe). Supports decoupled auth in 2.2.
- Tokenization: Network tokens (via wallets) or PSP tokens (card‑on‑file) reduce raw PAN handling and scope. Essential for PII minimization.
- PSD2/RTS rules and exemptions: Low‑value, TRA, recurring, corporate exemptions, trusted beneficiaries, and secure corporate processes. Soft declines require SCA‑aware retries.
3) Architecture overview: a layered, agent‑capable pipeline
Think in layers to contain complexity and make agent flows robust:
-
Offer discovery and quotes
- Expose a machine‑readable quote endpoint and a Payment Options Descriptor that lists supported methods (cards via PSP + 3DS2, Apple Pay, Google Pay, SPC, PayPal, etc.).
- Produce an ephemeral order token binding cart contents, amounts, currency, and shipping options.
-
Payment orchestration
- Select the route: SPC, network wallet, card+3DS2. Keep a unified abstraction over PSPs.
- Always generate idempotency keys for payment intents.
-
Authentication and challenge router
- 3DS2 server and challenge viewer (iframe), SPC fallback, wallet flows. Handle decoupled and out‑of‑band cases.
-
Authorization and capture
- Separate auth from capture when shipping/stock validation requires it. Apply partial capture rules.
-
Observability and audit
- Structured events for every decision and state change; WORM‑like retention and redaction.
-
Human‑in‑the‑loop
- Controlled escalation path with clear evidence, replayable state, and approve/deny ergonomics.
-
Privacy and compliance
- PII minimization, retention schedules, token vault boundaries, PAN‑free logs.
4) State machine and typical sequences
Core states for a single checkout attempt:
- draft_quote -> quoted -> payment_method_selected -> payment_intent_created -> authentication_required?
- If frictionless: authenticated -> authorized -> captured -> success
- If challenged: challenge_presented -> challenge_completed -> authorized -> captured -> success
- If SCA soft decline: sca_retry_pending -> authentication_flow -> authorized
- If fail/timeouts: aborted or requires_human_review
A typical 3DS2 sequence:
- Create payment intent (PSP) with amount, currency, and customer and indicate desire for 3DS.
- If ARes says frictionless (or PSP returns authentication data), proceed to authorization.
- If challenge required, render ACS challenge in iframe; wait for CRes or timeout.
- Once authenticated, retry authorization if needed.
An SPC sequence:
- Check that credential is registered for the instrument. Present PaymentRequest with securePaymentConfirmation.
- On user presence (touch/biometric), the browser returns an assertion that satisfies SCA.
- Use assertion to authenticate and then authorize the payment.
5) Payment Request API for humans and agents
PR API reduces DOM scraping risk and standardizes data capture. Use it to gather shipping/contact details and invoke wallets or SPC when possible.
Example: collecting a shipping address and offering SPC and cards via your PSP.
html<script> async function payWithPaymentRequest({ total, merchantInfo, instrument, fallback }) { if (!window.PaymentRequest) throw new Error('Payment Request API not supported'); const supported = [ // Secure Payment Confirmation if available { supportedMethods: 'secure-payment-confirmation', data: { rpId: merchantInfo.rpId, // e.g., 'shop.example' instrument: { displayName: instrument.displayName, // e.g., 'Visa •••• 1234' icon: instrument.icon }, challenge: instrument.challenge, // server-provided random challenge (ArrayBuffer) credentialIds: instrument.credentialIds, // ArrayBuffer[] // optionally payeeOrigin, networkData per spec } }, // Fallback to your PSP card method via basic-card is deprecated; use your PSP JS or wallet instead. // Provide Google Pay via PR if integrated, else your own SDK flow. fallback && fallback.googlePayPR && { supportedMethods: 'https://google.com/pay', data: fallback.googlePayPR } ].filter(Boolean); const details = { total: { label: merchantInfo.label || 'Total', amount: { currency: total.currency, value: total.value } }, displayItems: total.items || [] }; const options = { requestPayerEmail: true, requestShipping: true }; // Request only what you need const request = new PaymentRequest(supported, details, options); // Agents can call canMakePayment/hasEnrolledInstrument to decide path try { const can = request.canMakePayment ? await request.canMakePayment() : null; const has = request.hasEnrolledInstrument ? await request.hasEnrolledInstrument() : null; console.log('canMakePayment:', can, 'hasEnrolledInstrument:', has); } catch (e) { /* non-fatal */ } const response = await request.show(); // requires user gesture for SPC/wallets // Process response with server; do not store PII in logs return { response, abort: () => request.abort() }; } </script>
Notes:
- Do not rely on deprecated basic‑card. Prefer SPC, network wallets, or PSP SDKs.
- For agents, PR API’s canMakePayment/hasEnrolledInstrument helps choose the path before prompting a human or prior credential.
- SPC is the most agent‑friendly way to satisfy SCA with minimal friction if you can pre‑register credentials.
6) Tokenized cards and stored‑credential strategy
Tokenization is your PII minimization and reliability ally:
- Network tokens (Apple Pay, Google Pay) are ideal: no raw PAN, device‑bound, built‑in SCA.
- PSP tokens (card‑on‑file) reduce PCI scope; never log PAN/CVV.
- Align with the card networks’ stored credential framework:
- Initial cardholder‑initiated transaction (CIT) with SCA.
- Subsequent merchant‑initiated transactions (MIT) referencing the mandate are generally out of scope for SCA (under PSD2), but must be flagged with appropriate indicators and reference to initial SCA transaction.
Implementation guidance:
- On first save, run a CIT with SCA and mark the credential with network/PSP extensions indicating permission for future MIT.
- Always include stored_credential indicators with network‑specific fields (e.g., for Visa/MC) when charging later.
- Scope tokens to merchant account and keep a per‑customer alias mapping in your token vault.
7) 3DS2 orchestration that agents can survive
3DS2 splits into risk assessment and, if needed, a challenge. Your job is to make both predictable for automation while remaining standards‑compliant.
Server flow outline:
- Integrate with a 3DS Server (often your PSP provides one). Provide device data, amount, and message version (2.2 if available for decoupled auth and frictionless improvements).
- If ARes indicates challenge, return challenge parameters (creq, acsUrl, threeDSServerTransID) to the browser.
- Present challenge in an iframe container with SDK‑provided UI; handle size hints (250x400, 390x400, 500x600, 600x400).
- Wait for CRes (postMessage or callback to your server). Handle timeouts (e.g., 5‑10 minutes). Respect issuer flows including OOB to banking apps.
Agent‑friendly challenge viewer:
html<div id='threeDS-container' aria-live='polite'></div> <script> async function present3DSChallenge({ acsUrl, creq }) { const form = document.createElement('form'); form.method = 'POST'; form.action = acsUrl; form.target = 'threeDS-iframe'; const input = document.createElement('input'); input.type = 'hidden'; input.name = 'creq'; input.value = creq; // Base64Url form.appendChild(input); const iframe = document.createElement('iframe'); iframe.name = 'threeDS-iframe'; iframe.width = 500; iframe.height = 600; iframe.style.border = '0'; document.getElementById('threeDS-container').appendChild(iframe); document.body.appendChild(form); form.submit(); // Listen for completion; specifics depend on your PSP SDK window.addEventListener('message', (ev) => { if (ev.data && ev.data.threeDSResult) { // Notify app; do not log sensitive fields console.log('3DS challenge completed'); } }); } </script>
Tips:
- Prefer your PSP’s 3DS2 JavaScript SDK if available; it handles the Method URL (device info), iframe creation, and result posting.
- Always implement a cancel/timeout path that returns control to the agent with a clear state.
- Respect ACS redirects and OOB prompts; surface these to HITL when automation cannot proceed.
8) SCA‑aware retries and soft declines
Under PSD2, issuers may soft‑decline an authorization indicating SCA is required. Your system must parse these signals and retry with 3DS2 or an SCA‑capable method.
Patterns by PSP:
- Stripe: look for decline_code 'authentication_required' or failure_code 'card_declined' with payment_intent.status='requires_action'. Use next_action to complete 3DS.
- Adyen: refusalReason 'Authentication Required', resultCode 'RedirectShopper' or 'IdentifyShopper' leading to 3DS flows.
- Braintree: gateway_rejection_reason='three_d_secure'.
Generic retry loop:
jsasync function authorizeWithScaAwareRetry(intent) { const res = await psp.authorize(intent); if (res.status === 'soft_decline_sca_required' || res.nextAction?.threeDS) { const threeDSData = res.nextAction.threeDS; const auth = await presentChallengeAndReturnAuth(threeDSData); return psp.authorize({ ...intent, threeDSAuth: auth }); } return res; }
Do not blindly loop. Limit to one SCA retry per attempt, attach telemetry, and fail open to HITL when automation cannot proceed.
9) Idempotent orders and replay resistance
Agents are prone to retries due to network flakiness. Idempotency avoids double charges and duplicated orders:
- Generate a stable idempotency key per logical purchase, e.g., hash(agent_session_id + cart_fingerprint + quote_version).
- Use PSP idempotency headers/fields (e.g., 'Idempotency-Key' with Stripe) for authorizations and captures.
- Maintain an order table keyed by client_request_id that enforces a single terminal state.
Example server discipline:
python# pseudocode key = hmac_sha256(secret, f'{agent_session}:{cart_hash}:{quote_id}') exists = db.orders.get_by_idempotency_key(key) if exists: return exists intent = psp.create_payment_intent(amount, currency, idempotency_key=key) # store intent id + key
Also:
- Ensure POST/PATCH endpoints are idempotent by contract and refuse replays after a terminal state.
- Rotate quote/order tokens and embed expiration timestamps to prevent stale actions.
10) PII minimization for agent flows
Collect the least, store the least, log the least.
- Prefer wallet‑ and token‑first flows (Apple Pay/Google Pay/SPC) that abstract PANs.
- Scope PR API options narrowly: requestPayerEmail only if you truly need it; avoid telephone if not needed.
- Vault PII in a segregated service with narrow access; pass around opaque references.
- Never store CVV; never log PAN, CVV, or full addresses. Redact before logs.
- Encrypt at rest and in transit. Enforce data retention windows; auto‑purge after fulfillment + regulatory hold.
- For agents, forbid screenshot/screen recording of sensitive frames; mask form fields in any captured telemetry.
11) Audit trails and provable provenance
Every decision needs a paper trail, especially when an AI acted.
- Event schema fields:
- actor: 'agent' | 'human' | 'system'
- agent_id, model, version, prompt_hash
- session_id, order_token, idempotency_key
- state_before, state_after
- evidence: PSP response codes (redacted), 3DS outcome (Y/N/U/A), challenge path, timings
- consent: SCA method used, exemption applied
- PII pointers: token_ids, no raw values
Example event (illustrative, single quotes to avoid leaking quotes):
json{ 'ts': '2026-01-15T10:22:31Z', 'actor': 'agent', 'agent_id': 'shopbot-42', 'model': 'gpt-ops-2026A', 'action': 'authorize', 'order_token': 'ord_ef_123', 'idempotency_key': 'b5f4...2c', 'sca': { 'method': '3ds2', 'outcome': 'challenge_y', 'version': '2.2.0' }, 'psp': { 'name': 'AcmePSP', 'intent_id': 'pi_abc', 'result': 'approved' }, 'risk': { 'score': 0.32, 'exemption': 'none' }, 'pii_refs': { 'customer_token': 'tok_cus_9', 'card_token': 'tok_card_7' } }
Operationally:
- Route audit events to an append‑only store (e.g., object storage with bucket immutability or a WORM database).
- Sign events (e.g., per‑batch HMAC) for tamper evidence.
- Provide a reviewer UI that renders the timeline, with redactions by default.
12) Human‑in‑the‑loop (HITL) escalation patterns
Design the escalation path for when an agent hits a wall (issuer OOB challenge, repeated soft declines, high risk):
-
Trigger conditions:
- Challenge that requires mobile banking app approval.
- Risk score above threshold; mismatched billing/shipping; unusual device.
- Repeated SCA failures or timeouts.
-
HITL workflow:
- Freeze the order in state 'requires_human_review'; hold inventory optionally.
- Package evidence: order summary, PSP codes, 3DS status, screenshots of ACS (redacted), timelines.
- Offer approve/deny/modify options; allow switching to a different payment method.
- Upon approval, continue the flow (retry SCA if needed); upon denial, cancel and release holds.
-
Guardrails:
- SLA timers to auto‑cancel stale reviews.
- Role‑based access; action‑level logging; dual‑control for refunds/high‑value.
13) Testing and chaos engineering for challenges
3DS2 failures love Fridays. Build a test matrix:
- Simulated issuers/ACS: ensure you can force frictionless, challenge, timeout, OOB, and error codes.
- Browser coverage: Chrome, Safari, Firefox; mobile vs desktop; ITP/ETP privacy modes.
- Wallet variants: Apple Pay on Safari (macOS/iOS), Google Pay on Chrome/Android.
- SPC paths: credential present/absent, rpId mismatch, authenticator locked.
- Network instability: drop postMessage, delay iframe loads, kill the page mid‑challenge.
- PSP behavior: idempotency replay, partial captures, soft decline codes.
Add chaos toggles in staging:
js// Danger: test-only flags ?chaos=acs_timeout ?chaos=psp_soft_decline ?chaos=iframe_blocked
Verify that agents and your orchestration return to a consistent state under each failure.
14) Metrics and alerting
Track health across the funnel:
- PR API availability, canMakePayment success rates.
- Payment method selection distribution (SPC, wallets, 3DS2 card).
- 3DS2: frictionless rate, challenge rate, challenge success rate, timeout rate, decoupled share.
- SCA‑aware retry success and latency.
- Soft decline rate by BIN/issuer/country.
- Idempotency replay rate and prevented duplicates.
- HITL escalation count, approval rate, median time to decision.
- PII incidents (should be zero), redaction coverage.
Alert on:
- Spike in challenges with drop in success.
- ACS timeouts over threshold.
- Increase in soft declines without corresponding SCA retries.
- Repeated charges blocked by idempotency (possible agent loop).
15) Security and abuse considerations
- Consent boundaries: An AI agent must not finalize a payment that requires user presence unless a user explicitly participates (e.g., SPC gesture) or an existing MIT mandate applies.
- Origin hardening: Use COOP/COEP/CSP to prevent clickjacking around challenge iframes; sandbox iframes appropriately per PSP guidance.
- Token handling: Card tokens only in server‑side contexts; never expose vault tokens to the client.
- Secret management: Sign all order tokens/quotes; short TTLs.
- Replay resistance: Nonce on each critical request; HSTS and TLS pinning where feasible.
- Fraud signals: Device fingerprints within privacy constraints; velocity checks; BIN intelligence; shipping risk.
- Privacy by design: Data minimization, DPA readiness, DSAR workflows, deletion APIs.
16) Putting it all together: reference flow
Below is an end‑to‑end sketch focusing on agent compatibility.
mermaidsequenceDiagram participant A as Agent/Browser participant M as Merchant Frontend participant S as Merchant Server participant P as PSP + 3DS Server participant I as Issuer/ACS A->>M: Request checkout (cart) M->>S: GET /quote (cart_hash) S->>S: Create order_token + idempotency_key S-->>M: quote + options (SPC, wallets, 3DS2) M->>A: Present PR API / options alt SPC available A->>M: user gesture -> PR.show (SPC) M->>S: POST /spc-assertion S->>P: authenticate with SPC assertion else 3DS2 card M->>S: POST /payment-intents (idempotency_key) S->>P: create intent with 3DS requested P-->>S: ARes (frictionless or challenge) alt challenge S-->>M: challenge params M->>A: render 3DS iframe A->>I: complete challenge I-->>S: CRes via P end S->>P: authorize (with auth data) end P-->>S: auth approved S->>S: capture (immediate or deferred) S-->>M: success M-->>A: confirmation
And a more concrete, trimmed server pseudocode:
ts// Typescript-like pseudocode async function checkout(req, res) { const { cartHash, paymentPref, agentSession } = req.body; const quote = await createQuote(cartHash); const idemKey = idempotencyKey(agentSession, cartHash, quote.id); if (paymentPref.method === 'spc') { const challenge = randomChallenge(); // send PR API config to client with SPC data (credential IDs, rpId, challenge) return res.json({ quote, spc: { rpId, credentialIds, challenge } }); } // 3DS2 card path const intent = await psp.createIntent({ amount: quote.amount, currency: quote.currency, storedCredential: paymentPref.storedCredential, threeDS: { requestorChallengeInd: '01' }, idempotencyKey: idemKey }); if (intent.nextAction?.threeDS) { return res.json({ quote, threeDS: intent.nextAction.threeDS }); } // frictionless or wallet return res.json({ quote, intentId: intent.id, status: intent.status }); } async function postSpcAssertion(req, res) { const { assertion, quoteId, idemKey } = req.body; const auth = await psp.authenticateWithSpc({ assertion, quoteId, idempotencyKey: idemKey }); const authz = await psp.authorize({ authRef: auth.id, idempotencyKey: idemKey }); return res.json({ status: authz.status }); } async function post3dsResult(req, res) { const { intentId, cres, idemKey } = req.body; // verify CRes via PSP/3DS server const verified = await psp.complete3DS({ intentId, cres }); const authz = await psp.authorize({ intentId, idempotencyKey: idemKey }); return res.json({ status: authz.status }); }
17) Practical checklist
-
Payment methods
- Implement Payment Request API for data capture.
- Offer SPC where possible; register credentials during successful SCA.
- Integrate Apple Pay and Google Pay (PR API or native SDKs) for strong SCA and tokens.
- Provide a card + 3DS2 fallback via your PSP.
-
3DS2
- Use 3DS 2.2; support frictionless, challenge, decoupled.
- Iframe challenge viewer with timeouts and cancel.
- Map issuer/PSP outcomes Y/N/U/A into your state machine.
-
SCA and retries
- Parse soft‑decline semantics; perform exactly one authenticated retry.
- Apply exemptions where allowed (TRA, low‑value) and log rationale.
-
Idempotency
- End‑to‑end idempotency keys across intent, auth, capture.
- Terminal states enforced server‑side; reject replays.
-
Privacy and tokens
- Prefer tokens and wallets; minimize PR API scopes.
- Segregate PII in a vault; encrypt; set retention.
- Zero PII in logs; redaction filters with tests.
-
Audit and HITL
- Structured, signed events with agent provenance.
- Reviewer UI with approve/deny and replayable state.
- SLAs and metrics for escalations.
-
Ops
- Chaos toggles for ACS timeouts and PSP errors.
- Alerts on challenge success rate, soft declines, idempotency replays.
Opinions that save time
- If you can get SPC working for your frequent buyers, do it. Nothing beats WebAuthn‑backed SCA for repeat, low‑friction, PSD2‑compliant payments.
- Don’t ship a custom 3DS2 viewer if your PSP’s SDK is robust; focus on state management, timeouts, and telemetry instead.
- Prefer wallets in the EU for SCA: they collapse many edge cases and return network tokens.
- Keep your order and payment lifecycle decoupled. It simplifies partial capture, refunds, and stock logic under flaky payment flows.
- Treat HITL as a core product surface, not a last‑minute admin page. It will carry your edge cases and customer trust.
References and specs
- Payment Request API: https://www.w3.org/TR/payment-request/
- Secure Payment Confirmation: https://www.w3.org/TR/secure-payment-confirmation/
- 3‑D Secure 2: EMVCo 3DS 2.x (overview): https://www.emvco.com/emv-technologies/3d-secure/
- PSD2 RTS on SCA and secure communication: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32018R0389
- Apple Pay JS: https://developer.apple.com/documentation/apple_pay_on_the_web
- Google Pay API: https://developers.google.com/pay/api/web/overview
- Stripe SCA guide: https://stripe.com/guides/strong-customer-authentication
- Adyen 3DS2 docs: https://docs.adyen.com/online-payments/3d-secure
Closing thought: AI agents do not reduce compliance requirements; they magnify them. A layered, standards‑first checkout with robust 3DS2 handling, SPC, wallets, idempotency, privacy, and HITL gives agents a path to succeed without compromising user consent or regulatory obligations.
