Applies to: Web Payments SDK
Learn how to add Strong Customer Authentication to the card payment.
You can add Strong Customer Authentication (SCA) to the card payment integration you built using the quickstart project sample in Web Payments SDK Quickstart to integrate the SDK into your application.
The following steps show how to add code to the application you created from the quickstart project sample. If you haven't created an application using the quickstart, you need to do so before completing these steps.
An example of the frontend SCA code can be found in the GitHub Web Payments SDK Quickstart repository.
Your production application should collect the billing address of the buyer. Although providing an empty billingContact
is acceptable, by providing as much billing contact information as possible, you increase the chances of a successful authentication. This example uses an object that's declared with a hard-coded billing address. Your application might collect a billing address at payment time or, if the buyer is a customer on the seller Square account, from the buyer's customer record.
Note
The Web Payments SDK produces a payment token that can be used to make a payment with the presented card, to store the card on file, or to perform both operations in a single checkout. These operations are represented by three intents, respectively: CHARGE
to make a payment, STORE
to store the card (save the card on file), and CHARGE_AND_STORE
to perform both operations. Currently, the capability to use the CHARGE_AND_STORE
intent is in beta.
The code in the following steps adds the billing contact values needed by SCA (with the payments.verifyBuyer function) to verify the authenticity of the card holder:
Add the following function after the
tokenize
function in your script tag:async function verifyBuyer(payments, token) { const verificationDetails = { amount: '1.00', /* collected from the buyer */ billingContact: { addressLines: ['123 Main Street', 'Apartment 1'], familyName: 'Doe', givenName: 'John', email: '[email protected]', countryCode: 'GB', phone: '3214563987', state: 'LND', city: 'London', }, currencyCode: 'GBP', intent: 'CHARGE', }; const verificationResults = await payments.verifyBuyer( token, verificationDetails ); return verificationResults.token; }The function creates a ChargeVerifyBuyerDetails object that provides the buyer and purchase details needed by 3DS. To verify a card to be stored on file, create a StoreVerifyBuyerDetails object instead.
Update the call to
handlePaymentMethodSubmission
in thecardButton
click handler to include a third argument oftrue
.cardButton.addEventListener('click', async function (event) { // add 3rd argument of `true` handlePaymentMethodSubmission(event, card, true); });Update the
handlePaymentMethodSubmission
function in theDOMContentLoaded eventListener
function to have a third parametershouldVerify
and a call to theverifyBuyer
function.Important
SCA should be called for all customer-initiated transactions, including digital wallet payments. If the seller doesn't have SCA called for digital wallet payments, the transactions might be declined due to lack of authentication.
async function handlePaymentMethodSubmission( event, paymentMethod, shouldVerify = false /* 3DS needs to run (shouldVerify = true) for card payments, digital wallets, or storing a card on file. Set shouldVerify = false for all other payment methods. */ ) { // Add the following lines after the `tokenize` call let verificationToken; if (shouldVerify) { verificationToken = await verifyBuyer( payments, token ); } console.debug('Verification Token:', verificationToken); //TODO: Add the verification token in Step 2.2 const paymentResults = await createPayment(token); // handling the paymentResults. }The full version of the
handlePaymentMethodSubmission
function should look like the following:async function handlePaymentMethodSubmission( event, paymentMethod, shouldVerify = false ) { event.preventDefault(); try { // disable the submit button as we await tokenization and make a payment // request. cardButton.disabled = true; const token = await tokenize(paymentMethod); let verificationToken; if (shouldVerify) { verificationToken = await verifyBuyer( payments, token ); } console.debug('Verification Token:', verificationToken); //TODO: Add the verification token in Step 2.2 const paymentResults = await createPayment(token); displayPaymentResults('SUCCESS'); console.debug('Payment Success', paymentResults); } catch (e) { cardButton.disabled = false; displayPaymentResults('FAILURE'); console.error(e.message); } }
Test the application
Navigate to http://localhost:3000/ in your browser.
Use one of the test cards from the following table with a challenge type of "Modal with Verification Code":
Brand Numbers CVV Challenge type Verification code Visa 4800 0000 0000 0004 111 No Challenge N/A Mastercard 5222 2200 0000 0005 111 No Challenge N/A Discover EU 6011 0000 0020 1016 111 No Challenge 123456 Visa EU 4310 0000 0020 1019 111 Modal with
Verification Code123456 Mastercard 5248 4800 0021 0026 111 Modal with
Verification Code123456 Mastercard EU 5500 0000 0020 1016 111 Modal with
Verification Code123456 American Express EU 3700 000002 01014 1111 Modal with
Verification Code123456 Visa 4811 1100 0000 0008 111 No Challenge with Failed Verification N/A After you submit, the following window appears where you enter the verification code:
When 3DS has verified the buyer, the verification token is logged to the Developer Console.
Important
Your application should always attempt to create a payment with a verification token when a token is returned. It should also be prepared to respond to a payment failure in a small number of cases where a payment is rejected despite receiving a buyer verification token.
Update the
createPayment
function to take theverificationToken
parameter.// verificationToken can be undefined, as it doesn't apply to all payment // methods. async function createPayment(token, verificationToken) { const bodyParameters = { locationId, sourceId: token, }; if (verificationToken !== undefined) { bodyParameters.verificationToken = verificationToken; } const body = JSON.stringify(bodyParameters); // Same as in the cards example. const paymentResponse = await fetch('/payment', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body, }); if (paymentResponse.ok) { return paymentResponse.json(); } const errorBody = await paymentResponse.text(); throw new Error(errorBody); }Add the verification token parameter to the
createPayment
function call in theDOMContentLoaded eventListener
.//TODO: Add the verification token in Step 2.2 const paymentResults = await createPayment( token, verificationToken //new parameter );On your backend, modify your payment object to include a
verificationToken
. If you're using the Square example server, this is done for you as shown in the following code from server.js:const payment = { idempotencyKey, locationId: payload.locationId, sourceId: payload.sourceId, amountMoney: { amount: '100', // the expected amount is in cents ($1.00.) currency: 'USD', }, }; // VerificationDetails is part of Secure Card Authentication. // This part of the payload is highly recommended (and required for some // countries) for 'unauthenticated' payment methods like Cards. if (payload.verificationToken) { payment.verificationToken = payload.verificationToken; } const { result, statusCode } = await square.paymentsApi.createPayment( payment )
Test the application
Navigate to http://localhost:3000/ in your browser.
Choose the Pay $1.00 button.
You see the SCA window. The payment succeeds on successfully completing the challenge.