Card on file

The "Card on file" functionality allows users to save credit card information for future use instead of repeatedly entering their credit card information.

Cards can be stored during each of the following flows:

  • Charge flow
  • From a client card
  • During checkout

Saving card during a charge flow

Client consent

For the payment gateway application to determine whether the user chose to save the card, the iFrame should listen to the following postMessage that indicates whether the “Client has consented to save this card on file” checkbox is checked or unchecked.

Code snippet for listening to save card checkbox events

window.addEventListener(
        'message',
        (event) => {
          // Save card checked/unchecked event
          if (event.data.type === 'generic' && event.data.func === 'setSaveCard') {
            const saveCard = event.data.params.checked.checked; // boolean
            return;
          }
        })

Charge event

Once the user clicks the Charge button, we will start the process of saving the card, and once done, the Charge will continue as described under the Charge section.

At that point, you want to save the payment details along with the credit card information, and make sure you can reference the credit card information based on the "charge token" - the token you provided via the JavaScript postMessage.

Note that this time, the postMessage emitted when the Charge CTA is clicked will indicate that "save_card: true":

{
  type: 'generic', 
  func: 'getToken', 
  params: {
    amount: number,
    save_card: true,
    pivot_id: string,
    client_id: string,
    currency: string
  } 
}

Also, when save_card is true, you'll need to pass a cardholder_name in the postMessage in addition to the token you'd pass in a charge flow.

window.addEventListener(
        'message',
        (event) => {
           if (event.data.type === 'generic' && event.data.func === 'getToken') {
            // Do some credit card number validations
            const cardValidationErrors = validateCard(); // Can be a function that returns an array of errors
            // if card validations checks error
            if (cardValidationErrors.length > 0) {
              const messageObj = {
                type: 'generic',
                func: 'validationFailure'
              }
              window.parent.postMessage(messageObj, '*')
              // present the errors in the iFrame
              return;
              // If the card checks correctly with no issues
            } else {
              const token = {TOKEN}
              const messageObj = {
                type: 'generic',
                token: {TOKEN}
              }
              // Add cardholder_name is save_card === true
              if (event.data.params.save_card) {
                messageObj['cardholder_name'] = this.cardHolderName;
              }
              window.parent.postMessage(messageObj, '*');
              // Store token and payment details in your database for later reference
              return;
            }
          }
        }
      )

POST /customer

inTandem will send a POST request to {redirect_uri}/customer the first time you're attempting to save a card for a given client. The response should be the client's customer_id.

Following card-on-file attempts for that same client will not trigger another request to /customer.

Request

POST {redirect_uri}/customer

Request body payload

client_id: "jwlubzof7xahdazo"
email: "[email protected]"
name: "first name last name"

Expected response

{customer_id: {CUSTOMER_ID_VALUE}}

📘

customer_id

The customer ID is a unique identifier assigned to a customer by the payment gateway. This identifier is used to represent the customer in the payment gateway's system and is typically used for managing and processing payments, subscriptions, and storing payment information securely.

POST /customer/:token/attach

Next, inTandem will send another POST request to {redirect_uri}/customer/:token/attach.
The "token" param will correspond to the token you initially provided in the Charge event.

POST {redirect_uri}/customer/:token/attach

Request payload:

{
	"customer_id": string,
  "initiator": "business" | "client"
}

PropertyTypeDescription
initiator"business" | "client"Indicates the origin of card-on-file.
"business" - when the card-on-file is initiated from the back office by the business.
"client" - when the card-on-file is initiated from the client portal by the client.

Expected response:

{
    "card": {
        "card_id": "addtkthQ16trAsldSar50v6sLfdmuQ", // card identifier provided by the payment gateway
        "card_info": {
            "card_brand": "visa",
            "last_4": 1217,
            "exp_month": 10,
            "exp_year": 2030,
            "cardholder_name": "John Doe"
        }
    },
    "customer_id": "49v0ud9YhquOoYTczn0PkwdRn9QRb7"
}

That's it. At that point, the card is saved and will appear as an optional payment option for future payments.

Saving card on file from the client card

Saving a card on file from the client card is identical to how you save a card on the Charge flow. On both, inTandem will embed the Payment Processing Page from {redirect_uri}/charge, emit the getToken postMessage when the ADD CTA is clicked (and expecting a following postMessage with a token), followed by a POST request to {redirect_uri}/charge/:token/attach.

For the payment processing page to differentiate between the two flows, when inTandem loads the payment processing page on the client card page, we'll append a query param to the iFrame URL indicating that this is a save-card-only operation and not a charge flow.

{redirect_uri}/charge?save_card=true&pivot_id=BUSINESS_UID

Delete saved cards

Removing saved cards is done by inTandem sending a request to:

POST {api_uri}/customer/:token/detach

Request payload

{
    "customer_id": "49v0ud9YhquOoYTczn0PkwdRn9QRb7"
}

Expected response:

{
    "customer_id": "49v0ud9YhquOoYTczn0PkwdRn9QRb7"
}

Saving card-on-file on checkout

Get payment information from inTandem (make sure that “allow_save_card” is true). If allow_save_card is false, either the client already has 3 cards saved, or the “checkout_vaulting” feature flag is missing for that account.

Saving cards on checkout is done in two steps:

  1. Make sure that the save card feature is available at the account level.
    When sending the GET request to /checkout/:url_key as part of the usual checkout flow, make sure that the value of the “allow_save_card” property is true.

    GET /checkout/:url_key body request:

{
    "status": "OK",
    "data": {
        "amount": "33.0",
        "currency": "USD",
        "name": "John Doe",
        "return_url": "https://clients.meet2know.com/portal/5e3c760d?fromCheckout=true#/loader",
        "email": "[email protected]",
        "client_id": "zlbsfegs493u9zmo",
        "invoice_id": null,
        "allow_save_card": true
    }
}
  1. If “allow_save_card” is true, you can proceed with the checkout flow by appending the customer id and the card information when sending the payment checkout update request.

    PUT https://api.vcita.biz/platform/v1/payment/checkout/ body request:

{
   "url_key": "ijGghOYFKTFClDWJQZSNzkaZDZNQOiksktubFXpXfWmdovxuSz",
   "card":{
       "card_id": "15dsanida54",
       "card_info": {
           "exp_month": "01",
           "exp_year": "2025",
           "last_4": "6666",
           "card_brand": "visa",
           "cardholder_name": "John Doe"
           }
   },
   "customer_id": "49v0ud9YhquOoYTczn0PkwdRn9QRb7",
   "created": "2020-11-26",
   "transaction_id": "12qw34rt56yu",
   "type": "checkout.session.completed"
}

Expected response:

{
    "status": "OK",
    "data": {}
}