Prompt and scope
A multi-merchant payment platform wants shoppers to confirm the payee, amount, and currency at checkout and produce cryptographic evidence that a bank or payment service can verify. The team is considering the W3C Secure Payment Confirmation Candidate Recommendation Draft published on July 2, 2026. The merchant page, payment orchestrator, and issuer authentication page may have different origins, while older browsers must keep the existing authentication path.
Design the system from SPC credential registration through assertion verification. Explain how a third party can initiate authentication for a relying party, which fields the server must bind, and how to handle API unavailability, cancellation, duplicate payment, and rollback. The specification is still a draft; do not treat that status as universal browser support.
What the interviewer evaluates
The interviewer wants to see whether you bind the transaction details shown to the user, the order approved by the server, and the authentication assertion to one payment attempt. You should explain the relationship between SPC and WebAuthn and the security implications of cross-origin invocation.
A strong answer also covers the payment Permission Policy, the privacy limits of securePaymentConfirmationAvailability(), credential separation, idempotency keys, risk-based fallback, evidence retention, and a reversible rollout instead of only describing a biometric dialog.
Clarifications to ask first
- Who registers the SPC credential, and is it separate from the login credential?
- What are the origins of the issuer, merchant, and payment orchestrator, and which party is the WebAuthn Relying Party?
- Must the authentication data include amount, payee, currency, order ID, and expiry?
- Can an existing authentication path safely take over when SPC is unavailable or cancelled?
- Which evidence is required for retries, duplicate callbacks, refunds, and disputes?
A 30-second answer
“I would create an immutable server-side payment attempt before authentication, register a payment-specific SPC credential, and restrict the allowed caller origins. The server issues a one-time challenge and order digest; the merchant passes only server-approved payment data to SPC, and the callback is checked against the origin policy, challenge, credential, user verification, and order state. Unavailability or cancellation follows an explicit legacy path, never a success shortcut. State transitions use idempotency keys. I would start with low-risk traffic, measure assertion failures, disputes, and fallback, and disable new SPC attempts while preserving in-flight evidence if anomalies appear.”
Step-by-step solution
Define origins and credential ownership
SPC builds on WebAuthn but permits a third party to trigger a ceremony for a relying party. Use a payment-specific Relying Party ID or payment subdomain so login and payment credentials are not silently interchangeable. Registration accepts only server-issued user and merchant binding tokens. Store the credential ID, origin, creation time, revocation state, and device-binding properties; never trust a browser-supplied Relying Party ID as authorization.
Bind order state to the challenge
Create a payment_attempt containing the order version, amount, currency, payee, challenge, expiry, and idempotency key. Consume a challenge once. An order edit, currency conversion, or payee change creates a new attempt. The amount rendered to the user comes from the same server-approved record or digest; a merchant must not concatenate a value in the browser and call SPC directly.
type PaymentAttempt = {
id: string;
orderVersion: number;
amountMinor: bigint;
currency: string;
payeeOrigin: string;
challenge: Uint8Array;
expiresAt: Date;
status: "created" | "authorizing" | "approved" | "cancelled" | "expired";
};Enforce the cross-origin boundary
The important SPC change is that a third party can use a credential belonging to another relying party. Put the caller, relying party, order, and allowed authentication origins in a server policy. Configure the payment Permission Policy before embedding a payment iframe and validate the top-level origin against a merchant allowlist. After a cross-origin assertion returns, the caller receives only the result needed for that transaction; it must not request arbitrary WebAuthn extensions or access login credentials.
Detect capability and choose a fallback
Prefer PaymentRequest.securePaymentConfirmationAvailability() and record available separately from unavailable reasons; a user agent may return an intentionally vague reason to reduce fingerprinting. Availability still does not prove that a particular credential exists. A risk-based fallback can select SPC, bank authentication, a one-time code, or manual review. Every path rechecks the order and challenge, so an SPC error cannot become a payment success.
Verify the assertion and payment semantics
The server verifies client-data origin, challenge, Relying Party ID, signature, user-verification flag, credential state, and order digest. Use a conditional update to claim the created attempt before charging; duplicate callbacks return the same result. A digest mismatch is a security failure and must not be retried indefinitely. An assertion proves that authentication completed; it does not prove that funds settled.
Handle cancellation, expiry, and faults
Cancellation, no authenticator, a closed browser, and an issuer timeout become distinct terminal states. A retry creates a new attempt and challenge. A gateway timeout must not replay the old challenge: query the idempotency key first, then decide whether the result is pending, successful, or requires human review. Events between the payment service, merchant, and issuer carry versions so an old event cannot overwrite a newer state.
Observe risk and retain evidence
Segment SPC availability, user cancellation, assertion failures, duplicate callbacks, fallback rate, charge latency, and disputes by browser, origin combination, authentication method, and risk tier. Retain an order-digest hash, a non-reversible credential identifier, attempt ID, policy version, and verification result; do not log biometrics or private keys. Rate-limit cross-origin anomalies, repeated credential attempts, and abrupt amount changes.
Stage, test, and roll back
Start with internal merchants and low-risk amounts, then expand by browser, origin, and region. Test cross-origin iframes, missing Permission Policy, user refusal, missing credentials, challenge replay, order edits, duplicate callbacks, gateway timeouts, and refund races. On rollback, stop creating new SPC attempts, let in-flight transactions finish through the state machine, and route new transactions to the legacy path. Keep assertions and order evidence for dispute analysis.
Model high-quality answer
“SPC generates authentication evidence; it is not settlement. I would bind an immutable payment_attempt to the order version, amount, payee, challenge, origin policy, and idempotency key. Payment credentials are separated from login credentials, and Permission Policy limits cross-origin callers. After authentication, the server verifies the WebAuthn assertion and order digest, then conditionally advances the charge state. Unavailability, cancellation, and timeout use explicit fallbacks, while duplicate callbacks return the established result. During a staged rollout I would monitor assertion failures, fallback, disputes, and cross-origin anomalies; rollback stops new SPC attempts and preserves in-flight evidence.”
Common mistakes
- Treating
availableas a credential → the user may still have no usable credential → separate capability detection from credential availability. - Letting the browser choose the amount → the confirmation and charge can diverge → bind a server order digest and challenge.
- Treating a cross-origin call as ordinary login → login credentials or extension data may leak → separate payment credentials and restrict the caller.
- Turning SPC failure into payment success → authentication is confused with settlement → model authentication, charge, and settlement separately.
- Reusing a challenge on retry → an assertion can be replayed → consume once and issue a new challenge per attempt.
- Deleting records during rollback → disputes and duplicate charges become untraceable → freeze new requests and retain in-flight evidence.
Follow-up questions and answers
Follow-up 1: Why not reuse the login passkey?
Payment and login have different threats, origins, and authorization purposes. The specification allows WebAuthn credentials in SPC, but a payment subdomain and separate credentials reduce the login attack surface. If credentials are shared, the assertion purpose, Relying Party, and server checks must be explicit.
Follow-up 2: How do you stop a merchant showing one dollar and charging one hundred?
The merchant UI is not the amount authority. The payment service creates the digest from the order version, the authentication flow carries that digest, and the charge service accepts only the same attempt. Any field change creates a new attempt.
Follow-up 3: What is the risk of returning a detailed availability reason?
Fine-grained reasons can become a device or configuration fingerprint, so a user agent may return unavailable-unknown-reason. The business uses the result for experience selection, never as a user profile, risk score, or authorization fact.
Follow-up 4: Can the payment service call SPC again after a timeout?
First query the charge and authentication state by idempotency key. If the old attempt is unresolved, do not blindly replay it. Cancel or expire it explicitly, then create a new attempt with a fresh challenge and order digest.