Authenticate with Secure Payment Confirmation Stay organized with collections Save and categorize content based on your preferences.
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.
Let's walk through the authentication flow:
- A customer provides their payment credentials (such as credit cardinformation) to the merchant.
- 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.
- If an authentication is needed, the merchantdetermines whether the browser supports SPC.
- If the browser does not support SPC, proceed with the existingauthentication flow.
- 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.
- The user confirms and authenticates the amount and the destination of thepayment by unlocking the device.
- The merchant receives a credential from the authentication.
- The RP receives the credential from the merchant and verifies itsauthenticity.
- The RP sends the verification results to the merchant.
- The merchant shows the user a message to indicate if the payment wassuccessful or unsuccessful.
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:
PublicKeyCredentialRequestOptions
- Otherpayment specific parameters on the merchant's platform.
Here's the parameters you should provide to the payment method'sdata
property,SecurePaymentConfirmationRequest
.
Parameter | Description |
---|---|
rpId | The hostname of the RP origin as RP ID. |
challenge | A random challenge that prevents replay attacks. |
credentialIds | An 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. |
payeeOrigin | The origin of the payee. In the above mentioned scenario, it's the merchant's origin. |
instrument | A 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. |
timeout | Timeout to sign the transaction in milliseconds |
extensions | Extensions 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 aPaymentResponse
object 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"}}}
type
property usespayment.get
instead ofpublic-key
. Thismay require some tweaking tothe existing WebAuthnlibraries,if you choose to use one of them.- The
rp
matches the RP's origin. - The
topOrigin
matches the top-level origin that the RP expects (themerchant's origin in the example above). - The
payeeOrigin
matches the origin of the payee that should have beendisplayed to the user. - The
total
matches the transaction amount that should have been displayedto the user. - The
instrument
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
- Read the overview ofSecure Payment Confirmation
- Learn aboutregistration with Secure Payment Confirmation
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.