Applies to: Web Payments SDK | Customers API
Learn how to charge a card on file with the Web Payments SDK.
With Web Payments SDK integration, your application can charge a card on file in the following two payment flows for a card on file:
- (Beta) A new default payment flow that involves:
- Generating a payment token. During payment tokenization, Square checks the tokenize request to determine whether buyer verification is needed based on the buyer's information.
- Including the payment token in a Payments API request call to process a payment.
- The existing payment flow that involves:
- Generating a verification token after verifying the buyer.
- Including both the verification token and the card id of the stored card in a Payments API request call to process a payment.
Important
The new payment flow will replace the existing Web Payments SDK card payment acceptance implementation and become the new default card payment flow when Square releases it for General Availability.
When Square releases the new payment flow for General Availability, Square will deprecate the Payments.verifyBuyer() method that performs buyer verification and generates a verification token. Square will provide a migration guide so that you can update your application to take card payments with the new payment flow.
Square recommends updating your application to use the new default payment flow with the Web Payments SDK. However, during Beta, Square will continue to support both payment flows.
Choose one of the following card-on-file payment flows to set up your application to charge a card on file with Web Payments SDK integration.
Before updating your application with the CHARGE
intent, make sure to update your application to support the new payment flow by following the instructions in Take a Card Payment.
In your application:
Initialize a Card object.
Call Card.tokenize() with the
verificationDetails
andcardId
of the stored card.Call CreatePayment with the
token
as thesourceId
.The
Card.tokenize()
method passes the following properties in averificationDetails
object:amount
- The amount of the card payment to be charged.billingContact
- The buyer's contact information for billing.intent
- The transactional intent of the payment.sellerKeyedIn
- Indicates that the seller keyed in payment details on behalf of the customer. This is used to flag a payment as Mail Order / Telephone Order (MOTO).customerInitiated
- Indicates whether the customer initiated the payment.currencyCode
- The three-letter ISO 4217 currency code.
Important
Provide as much buyer information as possible for
billingContact
so that you get more accurate decline rate performance from 3DS authentication.The following code example demonstrates the charge card on file setup.
async function createPaymentWithCardOnFile( token, customerId, ) { const body = JSON.stringify({ locationId, sourceId: token, customerId, idempotencyKey: window.crypto.randomUUID(), }); 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); } // status is either SUCCESS or FAILURE; function displayPaymentResults(status) { const statusContainer = document.getElementById( 'payment-status-container', ); if (status === 'SUCCESS') { statusContainer.classList.remove('is-failure'); statusContainer.classList.add('is-success'); } else { statusContainer.classList.remove('is-success'); statusContainer.classList.add('is-failure'); } statusContainer.style.visibility = 'visible'; } async function tokenize(paymentMethod, sourceId) { const verificationDetails = { amount: '10.00', billingContact: { givenName: 'John', familyName: 'Doe', email: '[email protected]', phone: '3214563987', addressLines: ['123 Main Street', 'Apartment 1'], city: 'London', state: 'LND', countryCode: 'GB', }, currencyCode: 'GBP', intent: 'CHARGE', customerInitiated: true, sellerKeyedIn: false, }; const tokenResult = await paymentMethod.tokenize(verificationDetails, sourceId); if (tokenResult.status === 'OK') { return tokenResult.token; } else { let errorMessage = `Tokenization failed with status: ${tokenResult.status}`; if (tokenResult.errors) { errorMessage += ` and errors: ${JSON.stringify( tokenResult.errors, )}`; } throw new Error(errorMessage); } } document.addEventListener('DOMContentLoaded', async function () { if (!window.Square) { throw new Error('Square.js failed to load properly'); } let payments; try { payments = window.Square.payments(appId, locationId); } catch { const statusContainer = document.getElementById( 'payment-status-container', ); statusContainer.className = 'missing-credentials'; statusContainer.style.visibility = 'visible'; return; } let card; try { card = await payments.card(); } catch (e) { console.error('Initializing Card failed', e); return; } async function handleChargeCardOnFileSubmission( event, card, cardId, customerId, ) { event.preventDefault(); try { // disable the submit button as we await tokenization and make a payment request. cardButton.disabled = true; const token = await tokenize(card, cardId); const paymentResults = await createPaymentWithCardOnFile( token, customerId ); displayPaymentResults('SUCCESS'); console.debug('Payment Success', paymentResults); } catch (e) { cardButton.disabled = false; displayPaymentResults('FAILURE'); console.error(e.message); } } const cardButton = document.getElementById('card-button'); cardButton.addEventListener('click', async function (event) { const customerTextInput = document.getElementById('customer-input'); const cardTextInput = document.getElementById('card-input'); if ( !customerTextInput.reportValidity() || !cardTextInput.reportValidity() ) { return; } const cardId = cardTextInput.value; const customerId = customerTextInput.value; handleChargeCardOnFileSubmission(event, card, cardId, customerId); }); });