Are you looking for test card numbers?

Would you like to contact support?

Point-of-sale icon

Tax-free shopping with secure card sharing

Learn about tax-free shopping and how to support it.

Tax-Free Shopping (TFS) allows international travelers to claim back the sales tax or Value-Added Tax (VAT) on their purchases from a Tax-Free Shopping provider (TFS provider) such as Global Blue. A TFS provider is an organization that refunds the shopper and takes care of the paperwork with the authorities. We can share the shopper's encrypted card number with the TFS provider so that it is easier for the provider to refund the shopper on their card. This is referred to as secure card sharing.

Supporting TFS drives sales from international shoppers because of its convenience, and can be a great source of data about international shoppers. To offer TFS to your shoppers, your POS app needs to support it.

You can offer TFS with secure card sharing if you are located in a country that allows VAT refunds and where our point-of-sale solution is supported: the European Union, Iceland, Norway, Singapore, Switzerland, and the United Kingdom.

TFS is not supported on standalone Adyen terminals.

How it works

The role of Adyen in the tax-free flow is limited. We pass on information from the terminal to your POS app, and from the terminal to the TFS provider for secure card sharing.
However, secure card sharing requires consent from the shopper. There are two ways to ask for the shopper's consent on the terminal:

  • Ask for consent automatically: you only have to a make a card acquisition request, and a payment request.
  • Ask for consent manually: you make a card acquisition request, an input request to ask the shopper for their consent, and a payment request. You also need to implement logic to decide, based on the input response, whether your payment request should trigger secure card sharing.

After you have set up TFS:

  1. You make a card acquisition request to get the issuer country code and the Bank Identification Number (BIN) of the shopper's card.
  2. Your POS app uses the issuer country code and the BIN to determine whether the purchase qualifies for a tax refund.
  3. If the purchase qualifies for TFS, you make a payment with taxfree.indicator set to true.
  4. The terminal automatically shows a screen asking the shopper for consent to share their encrypted card number with the TFS provider. This makes it easier for the TFS provider to refund on the card.
    • If the shopper agrees, we send a notification to the TFS provider with the shopper's encrypted card number.
    • If the shopper doesn't agree, we automatically change the taxfree.indicator from your payment request to false so that no card sharing happens. The shopper can still claim the refund with the TFS provider.
  5. After the shopper completes the payment, you give the shopper the tax-free form issued by the POS app, and a receipt.

With the tax-free form and the receipt, the shopper can claim the VAT refund from the TFS provider. As part of this process, the shopper has to take the tax-free form to the customs desk at the airport or border, to get export validation. Customs will place a customs stamp on the form, or use an electronic system for validation. The shopper then needs to send or hand in the (electronically) validated form to the TFS provider.

Notifications to your TFS provider

If the shopper agrees to share their card details, we can send a Send card number to <TFS provider> notification to your TFS provider. This makes it easier for your TFS provider to refund the shopper on their card. This notification includes:

  • The PSP reference of the transaction.
  • The shopper's encrypted card number.
  • The BIN of the shopper's card.

Whether we send this notification, is determined through the taxfree.indicator flag in the payment request. If the notification is sent, you see this on the transaction in your Customer Area. The POS tile of the transaction includes:

  • Tax Free Indicator: true
  • Tax Free Identifier: the ID that the TFS provider returned to acknowledge the card details.

The shopper can claim the tax refund regardless of whether we notified the TFS provider of the shopper's encrypted card number.

Set up tax-free shopping

Before you begin offering TFS to your shoppers:

  1. Contact a TFS provider to set up a tax-free process.

  2. To set up your POS app for TFS support, contact the provider of your POS system.

  3. Contact your Adyen account manager or the POS Support Team to:

  4. Enable receiving the issuer country:
    1. In your Customer Area, go to Developers > API URLs.
    2. On the Additional data settings tab, select Issuer country.

Step 1: Acquire card details

To check whether the shopper is eligible for a tax refund, you do a card acquisition.

  1. Make a CardAcquisitionRequest without specifying an amount.

    {
        "SaleToPOIRequest": {
            "MessageHeader": {
                "ProtocolVersion": "3.0",
                "MessageClass": "Service",
                "MessageCategory": "CardAcquisition",
                "MessageType": "Request",
                "SaleID": "POSSystemID12345",
                "ServiceID": "0207111104",
                "POIID": "MX925-284691408"
            },
            "CardAcquisitionRequest": {
                "SaleData": {
                    "SaleTransactionID": {
                        "TimeStamp": "2019-05-17T14:59:09.000Z",
                        "TransactionID": "933"
                    }
                 },
                "CardAcquisitionTransaction": {}
            }
        }
    }
    String saleID = "YOUR_CASH_REGISTER_ID";
    String serviceID = "YOUR_UNIQUE_ATTEMPT_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.CARD_ACQUISITION );
    messageHeader.setMessageType( MessageType.REQUEST );
    messageHeader.setSaleID(saleID);
    messageHeader.setServiceID(serviceID);
    messageHeader.setPOIID(POIID);
    saleToPOIRequest.setMessageHeader(messageHeader);
    
    CardAcquisitionRequest cardAcquisitionRequest = new CardAcquisitionRequest();
    SaleData saleData = new SaleData();
    TransactionIdentification saleTransactionID = new TransactionIdentification();
    saleTransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()));
    saleTransactionID.setTransactionID(transactionID);
    saleData.setSaleTransactionID(saleTransactionID);
    cardAcquisitionRequest.setSaleData(saleData);
    
    CardAcquisitionTransaction cardAcquisitionTransaction = new CardAcquisitionTransaction();
    cardAcquisitionRequest.setCardAcquisitionTransaction(cardAcquisitionTransaction);
    saleToPOIRequest.setCardAcquisitionRequest(cardAcquisitionRequest);
    terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest);
  2. When you receive the response, get the following details.

    • POIData.POITransactionID.TimeStamp and POIData.POITransactionID.TransactionID: you need these details later in your payment request.

    • CardData.CardCountryCode: The issuer country code, for example 528. Also included in the AdditionalResponse.

    • From the AdditionalResponse (you receive either a string of key-value pairs or a Base64 string that you need to decode to get a JSON object):
      • cardIssuerCountryId: the issuer country code, for example 528. Also included in CardData.CardCountryCode.
      • cardBin: the BIN of the shopper's card, for example 541333.
    Card acquisition response
    {
        "SaleToPOIResponse": {
            "CardAcquisitionResponse": {
                "POIData": {
                    "POITransactionID": {
                        "{hint:For use in the CardAcquisitionReference of the payment request}TimeStamp": "2019-05-17T14:59:08.000Z{/hint}",
                        "{hint:For use in the CardAcquisitionReference of the payment request}TransactionID": "jM6S001558097948000{/hint}"
                    }
                },
                "SaleData": {
                    "SaleTransactionID": {
                        "TimeStamp": "2019-05-17T14:59:09.000Z",
                        "TransactionID": "933"
                    }
                },
                "PaymentInstrumentData": {
                    "CardData": {
                        "{hint:Issuer country code}CardCountryCode{/hint}": "528",
                        "MaskedPan": "541333*****9999",
                        "PaymentBrand": "mc",
                        "SensitiveCardData": {
                            "ExpiryDate": "0228"
                    },
                    "PaymentInstrumentType": "Card"
                },
                "Response": {
                    "{hint:Contains the BIN and the issuer country code}AdditionalResponse{/hint}": "...cardBin=541333...cardIssuerCountryId=528...",
                    "Result": "Success"
                }
            },
            "MessageHeader": {...}
        }
    }
  3. Pass the issuer country code and the card BIN to your POS app for the eligibility check.

The POS app either handles the eligibility check locally or through the TFS provider, and informs your store staff of the result.

Step 2: Make a payment

If the purchase qualifies for TFS, you make a payment request with a reference to the card acquisition, and include the taxfree.indicator flag set to true.

If the purchase does not qualify for TFS, you omit taxfree.indicator or set it to false.

  1. Make a PaymentRequest with:

    Parameter Description
    PaymentData.CardAcquisitionReference Data from the card acquisition:
    • TimeStamp: the time stamp returned in the POIData.POITransactionID of the card acquisition response.
    • TransactionID: the transaction ID returned in the POIData.POITransactionID of the card acquisition response.
    SaleData.SaleToAcquirerData The taxfree.indicator flag set to true.

    Pass the SaleToAcquirerData in one of the following formats:

    • Option 1: a JSON object converted to a Base64-encoded string.

      {
          "additionalData": {
              "taxfree.indicator": "true"
          }
      }

      After encoding, pass this in your request:

      "SaleToAcquirerData": "ewogICAgImFkZGl0aW9uYWxEYXRhIjogewogICAgICAgICJ0YXhmcmVlLmluZGljYXRvciI6IHRydWUKICAgIH0KfQ=="
    • Option 2: a form-encoded key-value pair:
      "SaleToAcquirerData": "taxfree.indicator=true"

    The example below shows how you would make a payment request with taxfree.indicator set to true.

    {
        "SaleToPOIRequest":{
            "MessageHeader":{
                "ProtocolVersion":"3.0",
                "MessageClass":"Service",
                "MessageCategory":"Payment",
                "MessageType":"Request",
                "SaleID":"POSSystemID12345",
                "ServiceID":"0207111104",
                "POIID":"MX925-284691408"
            },
            "PaymentRequest":{
                "SaleData":{
                    "SaleToAcquirerData":"taxfree.indicator=true",
                    "SaleTransactionID":{
                        "TransactionID":"27908",
                        "TimeStamp":"2019-03-07T10:11:04+00:00"
                    }
                },
                "PaymentTransaction":{
                    "AmountsReq":{
                        "Currency":"EUR",
                        "RequestedAmount":176.99
                    }
                },
                "PaymentData":{
                    "CardAcquisitionReference":{
                        "{hint:The POITransactionID.TransactionID of the card acquisition}TransactionID":"jM6S001558097948000{/hint}",
                        "{hint:The POITransactionID.TimeStamp of the card acquisition}TimeStamp":"2019-05-17T12:59:08.000Z{/hint}"
                    }
                }
            }
        }
    }
    String saleID = "YOUR_CASH_REGISTER_ID";
    String serviceID = "YOUR_UNIQUE_ATTEMPT_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.setSaleID(saleID);
    messageHeader.setServiceID(serviceID);
    messageHeader.setPOIID(POIID);
    saleToPOIRequest.setMessageHeader(messageHeader);
    
    PaymentRequest paymentRequest = new PaymentRequest();
    SaleData saleData = new SaleData();
    saleData.setSaleToAcquirerData("TaxFreeIndicator=True");
    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(176.99) );
    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);

    The terminal asks the shopper for their consent for secure card sharing.

    Terminal confirmation screen

    The shopper provides their answer and completes the transaction.

  2. When you receive the payment response, note the following in the AdditionalResponse:
    • If the shopper gave consent, the taxfree.indicator flag remains set to true.
    • If the shopper did not give consent, the taxfree.indicator flag is automatically changed to false.
      Payment response
      {
      "SaleToPOIResponse": {
          "MessageHeader": {...},
          "PaymentResponse": {
              "POIData": {
                  "POIReconciliationID": "1000",
                  "POITransactionID": {
                      "TimeStamp": "2022-03-14T08:13:47.000Z",
                      "TransactionID": "jM6S001558097948000.GH4R4RBFJGXXGN82"
                  }
              },
              "PaymentReceipt": [...],
              "PaymentResult": {...},
              "Response": {
                  "AdditionalResponse": "...taxfree.indicator=true...",
                  "Result": "Success"
              },
              "SaleData": {...}
          }
      }
      }

To use this flow, asking for consent automatically must be disabled for your account.

You can also ask for the shopper's consent manually. This flow is different from the automatic flow because it includes a separate input request to ask the shopper's consent. This way, you can customize the text shown on the terminal screen.

  1. Make a card acquisition request as described in Step 1 of the automatic flow.
    The POS app either handles the eligibility check locally or through the TFS provider, and informs your store staff of the result.
  2. If the purchase qualifies for TFS, ask the shopper for consent to share their encrypted card number with your TFS provider. Depending on your POS app and TFS provider, you can collect the shopper's agreement on the POS app.
    To ask consent with an input request on the terminal:

    1. Make a Confirmation input request with:

      • OutputContent.OutputText: an array of four Text fields containing:

        • A title. For example: Tax-free shopping
        • An explanation and a question. This text can be long and is scrollable. For example:
          You are eligible for tax-free shopping. Would you like the tax-free provider to refund you on your card?
        • A "No" button on the left of the display.
        • A "Yes" button on the right of the display.
      • InputData.MaxInputTime: (Integer) Time-out in seconds. This is the time that the shopper gets to answer the question.
    2. When you receive the input response, check the InputResult for the shopper's answer:

      • Input.ConfirmedFlag: true indicates the shopper gives their consent.
      • Input.ConfirmedFlag: false indicates the shopper doesn't give their consent.
    3. Pass the shopper's answer to the POS app.
  3. Make a payment request as described in Step 2 of the automatic flow and make sure you include:
    • taxfree.indicator set to true if the shopper agreed to sharing their card details with the TFS provider.
    • taxfree.indicator set to false, or omitted, if you did not ask the shopper's consent or if the shopper did not agree.

Testing the tax-free flow

In most cases, your TFS provider organizes an end-to-end test between themselves, Adyen, you, and your POS system provider.

In general, to test the happy flow, you need to use a card with a BIN that is eligible for tax refunds. Otherwise, the TFS provider returns a message that a tax refund is not possible.

  1. Make sure we configured Send card number to <TFS provider> notifications for your test account.
  2. Discuss with your TFS provider which card numbers (PAN) on your test card you are allowed to use for tax refund testing. (The PAN includes the BIN.)
  3. During the card acquisition step, use one of the allowed test card PANs and the associated currency.
    The POS app should now show that the card is eligible for a tax refund. This depends on how the eligibility check is set up with your TFS provider and POS system.
  4. Send a payment request with taxfree.indicator set to true, and complete the payment on the terminal. Note the PSP reference of the transaction.
  5. Issue a tax-free form from your POS app.
  6. In your Customer Area, look up the transaction by PSP reference and check that the POS tile contains an ID in the Tax Free Identifier field. If this field is empty, contact your TFS provider to discover what went wrong.

See also