Menu

Card-On-File Payments

This guide explains how to implement card-on-file payments, allowing you to offer a one-click checkout experience to your customers using secure tokens.


Card-on-file payments can be combined with any other payment options, except for interactive options such as 3D Secure.

First payment

When a customer visits your website or app for the first time, you must take their card details and receive a token from Worldpay either usingyour own formor thetemplate form.

Remember to set Worldpay.reusable = true so we can store the card details on your behalf. You will need to securely store the token in your database in order to take subsequent payments from this customer using the reusable token.

Note: card-on-file payments andrecurring paymentsboth use reusable tokens, but only recurring payments have orderType set as RECURRING. For card-on-file payments orderType is ECOM.

Subsequent payments

For repeat purchases, customers do not have to re-supply their card details. However, as Worldpay is unable to determine whether or not a customer has already shopped with you, your website will need the capacity to do this. Typically, this is done through user sign-in.

Once you have identified the customer and retrieved their reusable token, you can implement the one-click checkout process in three steps:

  1. Get the stored card details
  2. Present the one-click checkout form
  3. Make the payment from your server

1. Get the stored card details

To remind your customer which card will be charged, you should show them the masked card number and card type. You can obtain these details by making the following call to our server:

Copied!
curl https://api.worldpay.com/v1/tokens/<your-reusable-token&gt;    -H "Authorization: your-test-service-key" 
    -H "Content-type: application/json"

The response will be as follows:

{
    "token":"your-persistent-token",
    "paymentMethod": {
        "type":"ObfuscatedCard",
        "name":"Shopper name",
        "expiryMonth":10,
        "expiryYear":2015,
        "cardType":"VISA",
        "maskedCardNumber":"**** **** **** 1111"
    }
}
$worldpay = new Worldpay('your-test-service-key');

try {
    $cardDetails = $worldpay->getStoredCardDetails('your-reusable-token');

    $name = $cardDetails['name'];
    $expiryMonth = $cardDetails['expiryMonth'];
    $expiryYear = $cardDetails['expiryYear'];
    $cardType = $cardDetails['cardType'];
    $maskedCardNumber = $cardDetails['maskedCardNumber'];

} catch (WorldpayException $e) {
    echo 'Error code: ' . $e->getCustomCode() . '

    HTTP status code:' . $e->getHttpStatusCode() . '

    Error description: ' . $e->getDescription()  . '

    Error message: ' . $e->getMessage();
} catch (Exception $e) {
    echo 'Error message: '. $e->getMessage();
}
WorldpayRestClient restClient = new WorldpayRestClient("https://api.worldpay.com/v1", "your-test-service-key");
try
{
    TokenResponse cardDetails = restClient.GetTokenService().Get("your-reusable-token");

    Console.WriteLine("Name: " + cardDetails.paymentMethod.name);
    Console.WriteLine("ExpiryMonth: " + cardDetails.paymentMethod.expiryMonth);
    Console.WriteLine("ExpiryYear: " + cardDetails.paymentMethod.expiryYear);
    Console.WriteLine("CardType: " + cardDetails.paymentMethod.cardType);
    Console.WriteLine("MaskedCardNumber: " + cardDetails.paymentMethod.maskedCardNumber);
}
catch (WorldpayException e)
{
    Console.WriteLine("Error code:" + e.apiError.customCode);
    Console.WriteLine("Error description: " + e.apiError.description);
    Console.WriteLine("Error message: " + e.apiError.message);
}
worldpay = Worldpay.new('your-test-service-key')

begin

  response = worldpay.getStoredCardDetails('your-reusable-token')

  if (response.class.to_s=="Hash")
    puts "Name: " + response['name']
    puts "Expiry Month: " + response['expiryMonth'].to_s
    puts "Expiry Year: " + response['expiryYear'].to_s
    puts "Card Type: " + response['cardType']
    puts "Masked Card Number: " + response['maskedCardNumber']
  else
    puts "There was a problem"
  end

rescue Exception => e
  raise e
end
WorldpayRestClient restClient = new WorldpayRestClient("your-test-service-key");

try {
    TokenResponse tokenResponse = restClient.getTokenService().get("your-reusable-token");
    CardResponse cardResponse = (CardResponse) tokenResponse.getPaymentMethod();

    System.out.println("Name: " + tokenResponse.getPaymentMethod().getName());
    System.out.println("Expiry Month: " + cardResponse.getExpiryMonth());
    System.out.println("Expiry Year: " + cardResponse.getExpiryYear());
    System.out.println("Card Type: " + cardResponse.getCardType());
    System.out.println("Masked Card Number: " + cardResponse.getMaskedCardNumber());
} catch (WorldpayException e) {
    System.out.println("Error code: " + e.getError().getCustomCode());
    System.out.println("Error description: " + e.getError().getDescription());
    System.out.println("Error message: " + e.getError().getMessage());
}

2. Present the one-click checkout form

You now have to present the one-click checkout form to your customer. You can do this with or without asking for the customer's Card Security Code.

The one-click checkout form without asking for Card Security Code looks as follows:

Copied!
<form action="your-complete.page" id="payment-form" method="post">
<p>
    We will charge £5 to the following card:
</p>
<div class="form-row">
    <label>
        <span>
            CARD
        </span>
        <input type="text" value="  ** 1111" />
    </label>
</div>
<div>
    <input type="submit" value="Place Order" />
</div>
</form>

Due to Card Scheme security rules, Worldpay are not allowed to store the Card Security Code. If Card Security Code check in theRisk Settingspage is set to on (recommended) you must obtain the Card Security Code each time and update the token for each subsequent order.

The one-click checkout form with request for Card Security Code looks as follows:

Copied!
<!DOCTYPE html>
<html>
   <head>
      <title>Card on File Example</title>
      <script src="https://cdn.worldpay.com/v1/worldpay.js"></script&gt;
   </head>
   <body>
      <form action="your-complete.page" id="payment-form" method="post">
         <input data-worldpay="token" type="hidden" value="your-reusable-token" />
         <div class="form-row">
            <label>
            <span>
            CARD
            </span>
            <input type="text" value="  ** 1111" /> 
            </label>
         </div>
         <div class="form-row">
            <label>
            <span>
            CVC
            </span>
            <input data-worldpay="cvc" size="4" type="text" value="" /> 
            </label>
         </div>
         <div>
            <input type="submit" value="Place Order" />
         </div>
      </form>
      <script type="text/javascript">
         Worldpay.setClientKey("your-test-client-key");

         var form = document.getElementById('payment-form');
         Worldpay.useForm(form, function(status, response) {
             if (response.error || status!=200) {
                 Worldpay.handleError(form, document.getElementById('payment-errors'), {message:status});
             } else {
                 form.submit();
             }
         }, true);
      </script>
   </body>
</html>

The true as the last argument to Worldpay.useForm indicates that this is a reusable token.

3. Make the payment from your server

You can nowcomplete the paymentsfrom your server using the reusable token.

Suggested next steps: