Pesquisar

Are you looking for test card numbers?

Would you like to contact support?

Atenção, esta página não se encontra disponível em Português
Point-of-sale icon

Recurring payments

Tokenize an initial in-store payment for future recurring online payments.

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 additional 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:

  1. You ask the shopper for their consent to tokenize their card for specific future payments.
  2. You make an initial in-store payment, specifying a recurringContract flag and some other parameters to indicate you are entering into a recurring contract with the shopper.
  3. 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.
  4. 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 the shopper's consent, they are more likely to issue and win chargeback cases for payments made using stored card details.

Therefore, 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. However, 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 UK.

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 the AdditionalResponse. In a subsequent recurring payment, you submit it again.
  • Recurring detail reference (recurringDetailReference): You receive this in the AdditionalResponse 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

  1. Enable receiving shopper identifiers.
  2. 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:

  1. 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:

  2. Make a PaymentRequest with:

    Parameter Required Description
    TokenRequestedType Customer. This returns the card alias in the TokenValue field of the response. If you do not include the TokenRequestedType field, the card alias is returned only in the AdditionalResponse.
    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);
  3. When you receive the PaymentResponse, save the following data from the AdditionalResponse (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: Reference to the payment account that's linked to the shopper's card and/or NFC wallet.
    Response including the recurring detail reference (token)
    {
        "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=9914828517650010&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 you use for Terminal API calls.

To initiate an online later payment using a token you created with an in-store payment:

  1. From your server, make a POST /payments request, specifying:

    • The request header with:

      Parameter Required Description
      content-type -white_check_mark- application/json
      x-api-key -white_check_mark- Your API key.
    • The request body with:

      Parameter Required Description
      paymentMethod -white_check_mark- Object that contains the shopper's tokenized payment details.
      storedPaymentMethodId -white_check_mark- The recurringDetailReference returned in the Terminal API payment response for the initial in-store payment.
      shopperReference -white_check_mark- Your unique shopperReference that you created with the initial in-store payment.
      shopperInteraction -white_check_mark- ContAuth
      recurringProcessingModel -white_check_mark- The type of recurring transaction. Allowed 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/v67/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);
  2. If the payment was successful, you'll receive a resultCode of Authorised.

    {
       "pspReference": "8815329842815468",
       "resultCode": "Authorised"
    }

See also