For certain business models you need to be able to make later charges after the shopper's initial point-of-sale transaction. For example:
- To cross-sell products that require regular payments, such as an insurance policy for a product that the shopper bought in your store.
- In hospitality, to charge a guest an extra amount after they have left.
- To complete part of the sale in-store, and complete another part of the sale after goods are delivered to the shopper.
For these situations we offer tokenization:
- You ask the shopper for their consent to tokenize their card for specific future payments.
- You make an initial in-store payment, specifying a recurringContract flag and some other parameters to show you are entering into a recurring contract with the shopper.
- We securely store the shopper's sensitive payment details, generate a token called recurring detail reference to represent these details, and send you the token in the payment response.
- You use the token to make a recurring online payment.
Shopper consent for tokenization
Tokenizing the shopper's card for future payments should be an opt-in process. If you don't ask their consent first, there is an increased chance that shoppers issue and win chargeback cases for payments made using stored card details.
For this reason, when your payment request contains the recurringContract flag, the terminal asks the shopper to authenticate by entering their PIN. Because there are still cards being used that don't have a PIN, our POS Support Team can disable asking for a PIN when a card is tokenized. But it goes against best practices to set the recurringContract flag for every transaction.
If your integration sets the recurringContract flag regardless of whether you intend to enter into a recurring contract, you should fix this as soon as possible.
PSD2 SCA requirements for cardholder authentication
PSD2 is a European regulation requiring strong customer authentication (SCA) to make payments in the European Economic Area (EEA) more secure. PSD2 SCA applies when your business is located in the EEA, Monaco, or the United Kingdom.
When you enter into a recurring contract, PSD2 SCA requires cardholder authentication on the initial transaction. The later transactions are then exempted from cardholder authentication. That means that for the later transactions the card doesn't need to be present and you can use a token instead.
For an initial transaction at the point of sale, cardholder authentication is done through the card and typically the PIN.
Card and shopper identifiers
To be able to make recurring payments you need to save the following identifiers:
- Shopper reference (
shopperReference
): this is your own reference to the shopper. You submit it with the initial payment, and receive it back in theAdditionalResponse
. In a future recurring payment, you submit it again. - Recurring detail reference (
recurringDetailReference
): you receive this in theAdditionalResponse
to the initial payment. It is the token for future online payments.
To get the shopper reference and the recurring detail reference, you need to enable receiving these identifiers.
Before you begin
- Enable receiving shopper identifiers.
- Ask our POS Support Team to check that your account is correctly configured for your tokenization use case.
Make the initial payment
To create a token from an in-store transaction:
-
Collect the shopper's consent to save their payment details for future online recurring payments, and collect any contact details you may require.
You can let your staff enter the information in your cash register software, but you can also make input requests to collect the information on the terminal. For example:- Make a confirmation input request or a signature input request to ask the shopper's consent.
-
Make text input requests to collect the shopper's name and email address, and a digit input request to collect their phone number.
-
Make a
PaymentRequest
with:Parameter Required Description TokenRequestedType
Customer. Returns the card alias in the TokenValue
field of the response. Note that the card alias is always returned in theAdditionalResponse
.SaleData.SaleToAcquirerData
Data to create the token and shopper identifiers (see below). In
SaleData.SaleToAcquirerData
include:
shopperReference
: your unique reference for this shopper (minimum length three characters).shopperEmail
: optional. The shopper's email address, if you collected that in the first step.-
recurringContract
: RECURRING,ONECLICK. This triggers us to create a token for later payments, and store the shopper reference and shopper email on our platform.
Pass the
SaleToAcquirerData
value in one of the following formats:- Option 1: a JSON object converted to a Base64 encoded string. Refer to Add information to a payment.
- Option 2: form-encoded key-value pairs (using & as a separator). For example:
recurringContract=RECURRING,ONECLICK&shopperReference=12345&shopperEmail=S.Hopper@example.com
The format that you use here, will also be the format of the
AdditionalResponse
that you receive.{ "SaleToPOIRequest":{ "MessageHeader":{ "ProtocolVersion":"3.0", "MessageClass":"Service", "MessageCategory":"Payment", "MessageType":"Request", "SaleID":"POSSystemID12345", "ServiceID":"01142", "POIID":"V400m-346403161" }, "PaymentRequest":{ "SaleData":{ "SaleTransactionID":{ "TransactionID":"12420", "TimeStamp":"2020-01-07T14:15:25.114Z" }, "SaleToAcquirerData":"recurringContract=RECURRING,ONECLICK&shopperReference=YOUR_UNIQUE_SHOPPER_ID&shopperEmail=S.Hopper@example.com", "TokenRequestedType":"Customer" }, "PaymentTransaction":{ "AmountsReq":{ "Currency":"EUR", "RequestedAmount":24.98 } } } } }
String serviceID = "YOUR_UNIQUE_ATTEMPT_ID"; String saleID = "YOUR_CASH_REGISTER_ID"; String POIID = "YOUR_TERMINAL_ID"; String transactionID = "YOUR_UNIQUE_TRANSACTION_ID"; SaleToPOIRequest saleToPOIRequest = new SaleToPOIRequest(); MessageHeader messageHeader = new MessageHeader(); messageHeader.setProtocolVersion("3.0"); messageHeader.setMessageClass( MessageClassType.SERVICE ); messageHeader.setMessageCategory( MessageCategoryType.PAYMENT ); messageHeader.setMessageType( MessageType.REQUEST ); messageHeader.setServiceID(serviceID); messageHeader.setSaleID(saleID); messageHeader.setPOIID(POIID); saleToPOIRequest.setMessageHeader(messageHeader); PaymentRequest paymentRequest = new PaymentRequest(); SaleData saleData = new SaleData(); saleData.setSaleToAcquirerData("recurringContract=RECURRING,ONECLICK&shopperReference=YOUR_UNIQUE_SHOPPER_ID&shopperEmail=s.hopper@example.com"); saleData.setTokenRequestedType( TokenRequestedType.CUSTOMER ); TransactionIdentification saleTransactionID = new TransactionIdentification(); saleTransactionID.setTransactionID(transactionID); saleTransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); saleData.setSaleTransactionID(saleTransactionID); paymentRequest.setSaleData(saleData); PaymentTransaction paymentTransaction = new PaymentTransaction(); AmountsReq amountsReq = new AmountsReq(); amountsReq.setCurrency("EUR"); amountsReq.setRequestedAmount( BigDecimal.valueOf(24.98) ); paymentTransaction.setAmountsReq(amountsReq); paymentRequest.setPaymentTransaction(paymentTransaction); PaymentData paymentData = new PaymentData(); TransactionIdentification cardAcquisitionReference = new TransactionIdentification(); cardAcquisitionReference.setTransactionID(transactionID); cardAcquisitionReference.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); paymentData.setCardAcquisitionReference(cardAcquisitionReference); paymentRequest.setPaymentData(paymentData); saleToPOIRequest.setPaymentRequest(paymentRequest); terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest);
-
When you receive the
PaymentResponse
, save the following data from theAdditionalResponse
(you may need to Base64-decode first) in your back-end system:recurring.recurringDetailReference
: the token representing the shopper's payment method, for use in online recurring payments.recurring.shopperReference
: your unique reference for this shopper that you specified in the request.
Optionally also keep the following card and shopper identifiers, for easier shopper recognition:
shopperEmail
: the shopper's email address, if you specified that in the request.alias
: the card alias.PaymentAccountReference
: a reference to the payment account that is linked to the shopper's card and/or NFC wallet.
{ "SaleToPOIResponse": { "PaymentResponse": { "POIData": {...}, "SaleData": {...}, "PaymentReceipt": [...], "PaymentResult": { ... "PaymentInstrumentData": { "CardData": { "EntryMode": [ "Contactless" ], "{hint:This is the card alias, not the token for future payments}PaymentToken{/hint}": { "TokenRequestedType": "Customer", "TokenValue": "M469509594859802" }, "PaymentBrand": "mc", "MaskedPan": "541333 **** 9999", "SensitiveCardData": { "CardSeqNumb": "83", "ExpiryDate": "0228" } }, "PaymentInstrumentType": "Card" }, "AmountsResp": { "AuthorizedAmount": 24.98, "Currency": "EUR" }, "Response": { "Result": "Success", "AdditionalResponse": "...&alias=M469509594859802...recurring.recurringDetailReference=7219687191761347&recurring.shopperReference=YOUR_UNIQUE_SHOPPER_ID...&shopperReference=YOUR_UNIQUE_SHOPPER_ID&shopperEmail=S.Hopper%40gmail.com..." } }, "MessageHeader": {...} } }
Note that the
PaymentToken
object contains the card alias. You can't use this for making payments. It is intended only for recognizing the card.
The recurring detail reference and the shopper reference are now stored on the plataforma de pagamentos da Adyen, as well as the shopper's email address if you specified that in the request.
Make a recurring online payment
In this step you make a request to the Adyen back end directly. This is not a Terminal API request to either the terminal itself or the Cloud endpoint for the terminal. You need to generate an API key to authenticate your request to the back end.
If you are using cloud-based communications, you can use the existing API key that you use for Terminal API requests.
To make an online payment using a token you created with an in-store payment:
-
From your server, make a POST /payments request, specifying:
-
The request header with:
Parameter Required Description content-type
application/json x-api-key
Your API key. -
The request body with:
Parameter Required Description paymentMethod
Object that contains the shopper's tokenized payment details. storedPaymentMethodId
The recurringDetailReference
returned in the Terminal API payment response for the initial in-store payment.shopperReference
Your unique shopperReference
that you created with the initial in-store payment.shopperInteraction
ContAuth recurringProcessingModel
The recurring transaction type. Possible values: - CardOnFile: a one-off payment.
- Subscription: a subscription payment.
- UnscheduledCardOnFile: an automatic top-up or other non-fixed schedule contract payment.
curl https://checkout-test.adyen.com/v68/payments \ -H "X-API-key: [Your API Key here]" \ -H "Content-Type: application/json" \ -d '{ "amount":{ "value":2000, "currency":"USD" }, "paymentMethod":{ "type":"scheme", "{hint:The recurringDetailReference}storedPaymentMethodId{/hint}":"7219687191761347" }, "reference":"YOUR_ORDER_NUMBER", "shopperInteraction": "ContAuth", "recurringProcessingModel": "Subscription", "merchantAccount":"YOUR_MERCHANT_ACCOUNT", "shopperReference":"YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j" }'
# Set your X-API-KEY with the API key from the Customer Area. adyen = Adyen::Client.new adyen.api_key = "YOUR X-API-KEY" response = adyen.checkout.payments({ :amount => { :currency => "USD", :value => 2000 }, :reference => "YOUR_ORDER_NUMBER", :paymentMethod => { :type => "scheme", :storedPaymentMethodId => "7219687191761347" }, :shopperInteraction => ContAuth, :recurringProcessingModel => Subscription, :shopperReference => "YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j", :merchantAccount => "YOUR_MERCHANT_ACCOUNT" })
// Set your X-API-KEY with the API key from the Customer Area. Config config = new Config(); config.setApiKey("Your X-API-KEY")); Client client = new Client(config); Checkout checkout = new Checkout(client); PaymentsRequest paymentsRequest = new PaymentsRequest(); Amount amount = new Amount(); amount.setCurrency("USD"); amount.setValue(2000L); paymentsRequest.setAmount(amount); paymentsRequest.setReference("YOUR_ORDER_NUMBER"); paymentsRequest.setPaymentMethod(new HashMap<String, String>()); paymentsRequest.putPaymentMethodItem("type","scheme"); paymentsRequest.putPaymentMethodItem("storedPaymentMethodId", "7219687191761347"); paymentsRequest.setShopperReference("YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j"); paymentsRequest.setMerchantAccount("YOUR_MERCHANT_ACCOUNT"); paymentsRequest.setShopperInteraction("ContAuth"); paymentsRequent.setRecurringProcessingModel("Subscription"); PaymentsResponse response = checkout.payments(paymentsRequest);
// Set your X-API-KEY with the API key from the Customer Area. $client = new \Adyen\Client(); $client->setXApiKey("YOUR X-API-KEY"); $service = new \Adyen\Service\Checkout($client); $params = array( "amount" => array( "currency" => "USD", "value" => 2000 ), "reference" => "YOUR_ORDER_NUMBER", "paymentMethod" => array( "type" => "scheme", "storedPaymentMethodId" => "7219687191761347" ), "returnUrl" => "https://your-company.com/checkout/", "shopperInteraction" => "ContAuth", "recurringProcessingModel" => "Subscription", "merchantAccount" => "YOUR_MERCHANT_ACCOUNT", "shopperReference" => "YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j" ); $result = $service->payments($params);
#Set your X-API-KEY with the API key from the Customer Area. adyen = Adyen.Adyen() adyen.client.xapikey = 'YOUR_X-API-KEY' result = adyen.checkout.payments({ 'amount': { 'value': 2000, 'currency': 'USD' }, 'reference': 'YOUR_ORDER_NUMBER', 'paymentMethod': { 'type': 'scheme', 'storedPaymentMethodId': '7219687191761347' }, 'shopperInteraction': 'ContAuth', 'recurringProcessingModel': 'Subscription', 'shopperReference': 'YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j', 'merchantAccount': 'YOUR_MERCHANT_ACCOUNT' })
// Set your X-API-KEY with the API key from the Customer Area. var client = new Client ("YOUR_X-API-KEY", Environment.Test); var checkout = new Checkout(client); var amount = new Model.Checkout.Amount("USD", 2000); var details = new Model.Checkout.DefaultPaymentMethodDetails{ Type = "scheme", StoredPaymentMethodId = "7219687191761347" }; var paymentsRequest = new Model.Checkout.PaymentRequest { Reference = "YOUR_ORDER_NUMBER", Amount = amount, MerchantAccount = "YOUR_MERCHANT_ACCOUNT", ShopperInteraction = "ContAuth", RecurringProcessingModel = "Subscription", ShopperReference = "YOUR_UNIQUE_SHOPPER_ID_IOfW3k9G2PvXFu2j", PaymentMethod = details }; var paymentResponse = checkout.Payments(paymentsRequest);
// Set your X-API-KEY with the API key from the Customer Area. const {Client, Config, CheckoutAPI} = require('@adyen/api-library'); const config = new Config(); // Set your X-API-KEY with the API key from the Customer Area. config.apiKey = '[API_KEY]'; config.merchantAccount = '[YOUR_MERCHANT_ACCOUNT]'; const client = new Client({ config }); client.setEnvironment("TEST"); const checkout = new CheckoutAPI(client); checkout.payments({ amount: { currency: "USD", value: 2000 }, paymentMethod: { type: 'scheme', storedPaymentMethodId: '7219687191761347' }, reference: "YOUR_ORDER_NUMBER", merchantAccount: config.merchantAccount, shopperInteraction: "ContAuth", recurringProcessingModel: "Subscription" }).then(res => res);
-
-
If the payment was successful, you'll receive a
resultCode
of Authorised.{ "pspReference": "NC6HT9CRT65ZGN82", "resultCode": "Authorised" }