Authenticate with Secure Payment Confirmation

Merchants can use Secure Payment Confirmation (SPC) as part of a strong customer authentication (SCA) process for a given credit card or bank account. WebAuthn performs the authentication (frequently through biometrics). WebAuthn must be registered in advance, which you can learn about inRegister a Secure Payment Confirmation.

How a typical implementation works

The most common use for SPC is when a customer makes a purchase on a merchant'ssite, and the credit card issuer or bank requires payer authentication.

Authentication workflow.

Let's walk through the authentication flow:

  1. A customer provides their payment credentials (such as credit cardinformation) to the merchant.
  2. The merchant asks the payment credential's corresponding issuer or bank(relying party or RP) if the payer needs a separate authentication. Thisexchange might happen, for example, withEMV® 3-D Secure.
    • If the RP wishes the merchant to use SPC, and if the user has previouslyregistered, the RP responds with a list of credential IDs registered bythe payer and a challenge.
    • If an authentication is not needed, the merchant can continue tocomplete the transaction.
  3. If an authentication is needed, the merchantdetermines whether the browser supports SPC.
    • If the browser does not support SPC, proceed with the existingauthentication flow.
  4. The merchant invokes SPC. The browser displays a confirmation dialog.
    • If there are no credential IDs passed from the RP, fall back to theexisting authentication flow.After a successful authentication,consider usingSPC registrationto streamline future authentications.
  5. The user confirms and authenticates the amount and the destination of thepayment by unlocking the device.
  6. The merchant receives a credential from the authentication.
  7. The RP receives the credential from the merchant and verifies itsauthenticity.
  8. The RP sends the verification results to the merchant.
  9. The merchant shows the user a message to indicate if the payment wassuccessful or unsuccessful.
Key point: To quickly support an initial SPC experiment, this API was designed atopexisting implementations of the Payment Request and Payment Handler APIs.There is now general agreement to explore a design of SPC independent ofPayment Request. We expect (without a concrete timeline) that SPC will moveaway from its payment request origins.For developers, this should improve feature detection, invocation, and otheraspects of the API.

Feature detection

To detect whether SPC is supported on the browser, you can send a fake call tocanMakePayment().

Copy and paste the following code to feature detect SPC on a merchant's website.

constisSecurePaymentConfirmationSupported=async()=>{if(!'PaymentRequest'inwindow){return[false,'Payment Request API is not supported'];}try{// The data below is the minimum required to create the request and// check if a payment can be made.constsupportedInstruments=[{supportedMethods:"secure-payment-confirmation",data:{// RP's hostname as its IDrpId:'rp.example',// A dummy credential IDcredentialIds:[newUint8Array(1)],// A dummy challengechallenge:newUint8Array(1),instrument:{// Non-empty display name stringdisplayName:' ',// Transparent-black pixel.icon:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==',},// A dummy merchant originpayeeOrigin:'https://non-existent.example',}}];constdetails={// Dummy shopping detailstotal:{label:'Total',amount:{currency:'USD',value:'0'}},};constrequest=newPaymentRequest(supportedInstruments,details);constcanMakePayment=awaitrequest.canMakePayment();return[canMakePayment,canMakePayment?'':'SPC is not available'];}catch(error){console.error(error);return[false,error.message];}};isSecurePaymentConfirmationSupported().then(result=>{const[isSecurePaymentConfirmationSupported,reason]=result;if(isSecurePaymentConfirmationSupported){// Display the payment button that invokes SPC.}else{// Fallback to the legacy authentication method.}});

Authenticate the user

To authenticate the user, invoke thePaymentRequest.show() method withsecure-payment-confirmation and WebAuthn parameters:

Here's the parameters you should provide to the payment method'sdata property,SecurePaymentConfirmationRequest.

ParameterDescription
rpIdThe hostname of the RP origin as RP ID.
challengeA random challenge that prevents replay attacks.
credentialIdsAn array of credential IDs. In WebAuthn's authentication,allowCredentials property accepts an array ofPublicKeyCredentialDescriptor objects, but in SPC, you only pass a list of credential IDs.
payeeName (optional)Name of the payee.
payeeOriginThe origin of the payee. In the above mentioned scenario, it's the merchant's origin.
instrumentA string fordisplayName and a URL foricon that points to an image resource. An optional boolean (defaults totrue) foriconMustBeShown that specifies an icon must be successfully fetched and shown for the request to succeed.
timeoutTimeout to sign the transaction in milliseconds
extensionsExtensions added to WebAuthn call. You don't have to specify the "payment" extension yourself.

Check out this example code:

// After confirming SPC is available on this browser via a feature detection,// fetch the request options cross-origin from the RP server.constoptions=fetchFromServer('https://rp.example/spc-auth-request');const{credentialIds,challenge}=options;constrequest=newPaymentRequest([{// Specify `secure-payment-confirmation` as payment method.supportedMethods:"secure-payment-confirmation",data:{// The RP IDrpId:'rp.example',// List of credential IDs obtained from the RP server.credentialIds,// The challenge is also obtained from the RP server.challenge,// A display name and an icon that represent the payment instrument.instrument:{displayName:"Fancy Card ****1234",icon:"https://rp.example/card-art.png",iconMustBeShown:false},// The origin of the payee (merchant)payeeOrigin:"https://merchant.example",// The number of milliseconds to timeout.timeout:360000,// 6 minutes}}],{// Payment details.total:{label:"Total",amount:{currency:"USD",value:"5.00",},},});try{constresponse=awaitrequest.show();// response.details is a PublicKeyCredential, with a clientDataJSON that// contains the transaction data for verification by the issuing bank.// Make sure to serialize the binary part of the credential before// transferring to the server.constresult=fetchFromServer('https://rp.example/spc-auth-response',response.details);if(result.success){awaitresponse.complete('success');}else{awaitresponse.complete('fail');}}catch(err){// SPC cannot be used; merchant should fallback to traditional flowsconsole.error(err);}

The.show() function results in aPaymentResponseobject except thedetails contains a public key credential with aclientDataJSON that contains the transaction data(payment)for verification by the RP.

The resulting credential must be transferred cross-origin to the RP andverified.

How the RP verifies the transaction

Verifying the transaction data at the RP server is the most important step inthe payment process.

To verify the transaction data, the RP can follow WebAuthn'sauthentication assertion verification process.In addition, they need toverify thepayment.

An example payload of theclientDataJSON:

{"type":"payment.get","challenge":"SAxYy64IvwWpoqpr8JV1CVLHDNLKXlxbtPv4Xg3cnoc","origin":"https://spc-merchant.glitch.me","crossOrigin":false,"payment":{"rp":"spc-rp.glitch.me","topOrigin":"https://spc-merchant.glitch.me","payeeOrigin":"https://spc-merchant.glitch.me","total":{"value":"15.00","currency":"USD"},"instrument":{"icon":"https://cdn.glitch.me/94838ffe-241b-4a67-a9e0-290bfe34c351%2Fbank.png?v=1639111444422","displayName":"Fancy Card 825809751248"}}}
Caution: Thetype property usespayment.get instead ofpublic-key. Thismay require some tweaking tothe existing WebAuthnlibraries,if you choose to use one of them.
  • Therp matches the RP's origin.
  • ThetopOrigin matches the top-level origin that the RP expects (themerchant's origin in the example above).
  • ThepayeeOrigin matches the origin of the payee that should have beendisplayed to the user.
  • Thetotal matches the transaction amount that should have been displayedto the user.
  • Theinstrument matches the payment instrument details that should havebeen displayed to the user.
constclientData=base64url.decode(response.clientDataJSON);constclientDataJSON=JSON.parse(clientData);if(!clientDataJSON.payment){throw'The credential does not contain payment payload.';}constpayment=clientDataJSON.payment;if(payment.rp!==expectedRPID||payment.topOrigin!==expectedOrigin||payment.payeeOrigin!==expectedOrigin||payment.total.value!=='15.00'||payment.total.currency!=='USD'){throw'Malformed payment information.';}

After all the verification criteria have been passed, the RP can tell themerchant that the transaction is successful.

Next steps

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2022-05-27 UTC.