Skip to main content
Secure Payment Confirmation (SPC) lets a cardholder approve a specific transaction (a given amount and payee) with a passkey. The approval is signed and tamper-evident: the amount, currency and payee are bound into the assertion, so it proves the cardholder authorised that payment, not just that they authenticated. SPC is part of EMV 3-D Secure (3DS) 2.3.1. It replaces an OTP or out-of-band challenge in the 3DS step-up with a one-tap biometric. Authsignal is the FIDO engine behind your Access Control Server (ACS): it enrolls payment passkeys and verifies the signed transaction. Your ACS keeps the 3DS message rails and the authentication value it returns to the scheme.

Where Authsignal fits

In SPC the ACS is the WebAuthn relying party (RP). It owns credential enrollment (the issuer domain), stores the public key, issues the challenge, and cryptographically verifies the assertion. This is the reverse of non-SPC FIDO, where the merchant is the RP and the ACS only consumes the attestation as a risk signal it does not verify. Authsignal provides that relying party. It slots in wherever your ACS makes an authentication decision, and never touches the PAN or the 3DS message rails.
The transaction is set server-side (when you track the action) and validated server-side by Authsignal. The cardholder’s browser only renders what the server issued, and nothing client-supplied is trusted for the amount or payee.

Prerequisites

  • EMV 3DS 2.3.1, which defines the SPC data elements. The ACS advertises SPC support for a card range with acsInfoInd = 07.
  • A platform authenticator registered with the WebAuthn payment extension: Touch ID, Windows Hello, or a Google Password Manager passkey on Android. Authsignal marks a credential payment-capable only when the browser returns a successful payment extension result at registration.
  • Browser support: SPC runs on Chromium browsers (Chrome on macOS, Windows, and Android). Feature-detect with PaymentRequest.securePaymentConfirmationAvailability() and fall back on Safari and Firefox.
  • A Permissions-Policy of payment=*, publickey-credentials-get=* on whichever page runs the ceremony (the ACS challenge iframe, or the merchant page).

The two SPC flows

EMV 3DS defines two ways to run SPC. Authsignal’s role, and its API calls, are identical in both; only where the browser ceremony runs and how the data travels in the 3DS messages differ.
For an ACS integration, lead with the ACS-initiated flow: it needs no merchant-side work and keeps the ceremony, and the passkey relying party, entirely on the issuer side.

Enrollment

Enroll one payment passkey per user, per device. It works across all of the cardholder’s cards: the specific card is a per-transaction value (see step 1), not bound to the credential. This matches the SPC model, where the relying party supplies the instrument at authentication time and the issuer keeps the card-to-credential mapping. Enrollment is owned by the ACS and typically offered as an uplift in a prior authenticated session (for example right after a successful OTP step-up). Authsignal forces a platform authenticator with required user verification and the payment extension, and marks the credential payment-capable.
1

Generate an enrollment token (server SDK)

Pass the token to the browser.
2

Create the passkey (browser SDK)

paymentInstrument: { displayName } is optional: it sets a default label for the single-card case. Multi-card issuers omit it and set the card per transaction instead.
On a platform that can’t enroll a payment credential, retry signUp without the flag to enroll a standard passkey, or fall back to another method.

Authenticating a payment

The ACS uses three Authsignal calls per transaction: set the transaction, fetch the SPC request data, and verify the assertion. In the ACS-initiated flow the ACS makes all three server-side and runs the ceremony in its iframe; in the requestor-initiated flow the SPC request data is handed to the merchant in ARes and the assertion comes back in the second AReq. Client REST calls use the Client API base https://api.authsignal.com/v1/client with Authorization: Bearer <token> (regional bases available: au., eu., ca.). The <token> is the action token returned by track.

1. Set the transaction (server SDK)

Set the amount, payee, and the card for this transaction on the action’s custom data. These values are the source of truth Authsignal validates the signed assertion against, and the client never supplies them.
Pass the returned token to the client calls that follow.

2. Fetch the SPC request data (client REST)

Request authentication options with securePaymentConfirmation: true. The response carries the rpId, challenge, the payment-capable credentialIds, and the server-set payment. The ACS uses this to run the ceremony in its iframe, or delivers it to the merchant in ARes.
payment.instrumentDisplayName is the card for this transaction (from track). Use it as the instrument in the ceremony. paymentInstruments (a credential-level label) is still returned for the single-card default, but the per-transaction value takes precedence.
rpId, challenge, allowCredentials[].id and payment are first-class fields in the response, not hidden implementation detail. Carry the challengeId across to the verify call in step 4.

3. Run the SPC ceremony (browser)

The page that runs the ceremony (the ACS challenge iframe, or the merchant page) builds a PaymentRequest from the request data and shows the native “Confirm your purchase” dialog. The amount and payee come from the server-set payment.
Secure Payment Confirmation dialog showing Example Merchant, Visa ending 1234, and USD $42.00

The native Secure Payment Confirmation dialog. The browser renders the payee, instrument and amount, and binds them into the signed assertion.

The signed clientDataJSON (type: "payment.get") binds the amount, currency and payee.

4. Verify the assertion (client REST)

The ACS verifies the assertion against the transaction set at track. Authsignal checks the WebAuthn signature and that the signed payment matches the server-set amount, currency and payee, so a tampered client can’t bind a valid signature to a different transaction.
A mismatch (wrong amount or payee) or an invalid signature is rejected here with 401.

5. Confirm the result (server SDK)

The ACS then computes the authentication value (CAVV/AAV) and ECI and returns the 3DS result.

EMV 3DS field mapping

How the Authsignal steps sit in each flow. Authsignal’s calls are the same; the 3DS message carriage differs. Field names follow the EMV 3DS 2.3.1 specification. Confirm the exact carriage of the SPC request data and assertion against your 3DS Server and scheme profile.

Feature detection and fallback

SPC only runs on Chromium browsers (Chrome on macOS, Windows, and Android). On Safari and Firefox the ACS must fall back, and must never dead-end the cardholder. Detect support before attempting the ceremony:
The recommended ladder for a 3DS step-up is SPC → standard passkey → OTP/OOB, and you can offer SPC enrollment as an uplift after the fallback succeeds.

First-party alternative (browser SDK)

For a merchant-initiated, first-party web flow (not split across 3DS messages), the @authsignal/browser SDK wraps the client calls in steps 2 to 4 into one method:
The amount and payee still come from the server (set at track), and there is no payment argument. For a split 3DS ceremony, the SDK also exposes runPaymentConfirmation({ rpId, challenge, credentialIds, payment }), which runs only the browser ceremony against request data you already hold.
Authsignal never stores or sees the card number; the instrument is a display label and icon only.