v148 · Web APIs · SPC Overview
SPC Overview
What Secure Payment Confirmation is, how the WebAuthn-backed payment dialog works, and where the new getSecurePaymentConfirmationCapabilities() API fits in the merchant integration flow.
what is SPC?
Secure Payment Confirmation (SPC) is a W3C specification that uses a WebAuthn platform authenticator (Touch ID, Face ID, Windows Hello) to authenticate payment transactions. Instead of a bank-redirect 3DS challenge, the user sees a browser-native dialog showing the transaction amount, merchant, and payment instrument — and confirms with biometrics.
Traditional 3DS flow
- Redirect to bank's auth page
- SMS OTP or app approval
- High friction, abandonment risk
- No browser-native UX
- ~10–30 second flow
SPC flow
- Browser-native dialog, no redirect
- Biometric confirmation (Touch ID etc.)
- Low friction, high conversion
- Transaction details shown clearly
- ~2–5 second flow
SPC payment flow
-
1
Capability check (new in Chrome 148) — merchant calls
PaymentRequest.getSecurePaymentConfirmationCapabilities()to verify the device supports SPC - 2 Credential lookup — bank checks if a WebAuthn credential is enrolled for this user and returns a credential ID
-
3
PaymentRequest construction — merchant creates a
PaymentRequestwithsecure-payment-confirmationmethod data, including the credential ID and transaction details - 4 Browser dialog — Chrome shows the SPC dialog with merchant name, amount, and payment instrument. User confirms with biometric
- 5 Signed assertion — browser returns a WebAuthn assertion with the payment details cryptographically bound. Merchant passes this to the bank for verification
- 6 Server verification — bank verifies the assertion signature using the stored public key from step 2. No OTP, no redirect
capabilities API in context
// Step 1: capability check (Chrome 148+)
const caps = await PaymentRequest
.getSecurePaymentConfirmationCapabilities();
if (!caps.spcSupported) {
return startTraditional3DS();
}
// Step 2: fetch enrolled credential from bank
const { credentialId, rpId } = await bank.getCredential(userId);
// Step 3: construct payment request
const request = new PaymentRequest(
[{
supportedMethods: 'secure-payment-confirmation',
data: {
credentialIds: [credentialId],
challenge: cryptoChallenge,
rpId: rpId,
payeeOrigin: 'https://merchant.example',
instrument: { displayName: 'Visa •••• 1234', icon: '/card.png' },
timeout: 60000
}
}],
{ total: { label: 'Total', amount: { currency: 'USD', value: '49.99' } } }
);
// Step 4: show dialog
const response = await request.show();
// Step 5: send assertion to bank
await bank.verifyPayment(response.details);
see also
scenario focus
Select a scenario to focus its rendered example and summary.
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗