Point-of-sale icon

NFC tags reading and writing

Read and write data on NFC tags using Adyen payment terminals.

Near Field Communication (NFC) tags can be cards, phones, bracelets, or similar. They can store data and are commonly used for contactless payments, loyalty programs, festival entrance, and more.
By sending a Terminal API card acquisition request to your Adyen payment terminal, you can identify the type of NFC tag, or read and write data to it.
For more complex use cases, you can create a session to present multiple requests as a single user interaction. For example, in a single session, you could identify the NFC tag, read its balance, and write the new balance on the tag.

To understand how you can use this, here are some examples:

  • Enroll shoppers in a loyalty program.
  • Identify a customer when entering an area such as festival grounds or a parking garage.
  • Check the balance and top up NFC tags such as loyalty and transport cards, or festival tags used to pay for drinks.
  • Define multiple behaviours of the NFC tag. For example, use the tag to enter one event and reuse it to pay for drinks at a different event.

Supported products

The following NFC tags can be used on Adyen payment terminals:

  • MIFARE Classic
  • MIFARE DESFire
  • MIFARE Ultralight, Ultralight C, and Ultralight AES

NFC tags are not supported on UX300 and UX410 payment terminals.

How it works

To use NFC tags on Adyen payment terminals, first you need to contact our Support Team to create a configuration for your use case. Usually, this includes:

  • The type of NFC tag you want to use, for example MIFARE Classic.
  • For NFC tags that have keys, you need to supply the keys you want to use to access data.

After the configuration is created:

  1. Make a card acquisition request to identify, read, or write to an NFC tag.

    For more complex use cases that require multiple requests, see Create a session.

  2. The terminal prompts the user to present their NFC tag or NFC-enabled card.
  3. The user presents their NFC tag by tapping, inserting, or swiping.
  4. The terminal makes one or multiple read/write requests to the NFC tag.
    While this is going on, the terminal shows the loading screen: One moment or Keep card inserted if the customer inserted their NFC-enabled card into the terminal.

Identify the type of NFC tag

You can use the card acquisition request to get the unique identifier and the type of the NFC tag. You can use this identifier, for example, to check if the NFC tag exists in your system or if it is still valid.

  1. Create an Operation JSON object with Operation.Type: NFCReadUID:

  2. Encode the Operation JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request to a Terminal API endpoint, specifying:

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. Specify:

      Parameter Required Description
      ProtocolVersion -white_check_mark- 3.0
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- CardAcquisition
      MessageType -white_check_mark- Request
      ServiceID -white_check_mark- Your unique ID for this request, consisting of 1-10 alphanumeric characters. Must be unique within the last 48 hours for the terminal (POIID) being used.
      SaleID -white_check_mark- Your unique ID for the POS system component to send this request from.
      POIID -white_check_mark- The unique ID of the terminal to send this request to. Format: [device model]-[serial number].
    • CardAcquisitionRequest.SaleData:

      Parameter Required Description
      SaleTransactionID -white_check_mark- An object with:
      • TransactionID: your reference to identify the transaction. We recommend using a unique value.
      • TimeStamp: date and time of the request in UTC format.
      SaleToPOIData The Base64-encoded Operation JSON object.
    • CardAcquisitionRequest.CardAcquisitionTransaction:

      Parameter Required Description
      TotalAmount The transaction amount. You can omit the TotalAmount field when getting the UID of the NFC tag.
      If you omit TotalAmount, you still have to include an empty CardAcquisitionTransaction field in the request.

    The example below shows a card acquisition request to identify the NFC tag:

  4. In the AdditionalResponse of the CardAcquisitionResponse note:

    • NFC.uid: the unique identifier of the NFC tag.
    • NFC.variant: the type of the NFC tag, for example mf_classic for MIFARE Classic.

    After you have identified the NFC tag, the loading screen continues to show until you send an enable service request` (see the next step).

  5. To stop the loading screen, make a POST request to a Terminal API endpoint, specifying:

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. Specify:

      Parameter Required Description
      ProtocolVersion -white_check_mark- 3.0
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- EnableService
      MessageType -white_check_mark- Request
      ServiceID -white_check_mark- Your unique ID for this request, consisting of 1-10 alphanumeric characters. Must be unique within the last 48 hours for the terminal (POIID) being used.
      SaleID -white_check_mark- Your unique ID for the POS system component to send this request from.
      POIID -white_check_mark- The unique ID of the terminal to send this request to. Format: [device model]-[serial number].
    • EnableServiceRequest with:

      Parameter Required Description
      TransactionAction -white_check_mark- AbortTransaction.
      DisplayOutput Optional object to show your own message and an 'Approved' icon (green check mark) or a 'Declined' icon (red cross).
      If you omit DisplayOutput, the terminal shows Canceled, a red cross , and Transaction canceled.

    The following example is the basic request, without the DisplayOutput object, to stop the card acquisition flow.

Read data

NFC tags have multiple parts that can store data. Because the data structure is different for every NFC tag type, the request needs to specify where to read the data from. For some tags, you need to include the key to read data from a sector, aid, orpage.
Select a tab to see the parameters that you need to specify to read data from MIFARE Classic, MIFARE DESFire, or MIFARE Ultralight.

  1. Create an Operation JSON object and specify:

    Parameter Required Description
    Type -white_check_mark- NFCRead: reads data from the specified sector.
    Variant -white_check_mark- MifareClassic: the type of NFC tag.
    NFCData -white_check_mark-
    If you leave the NFCData array empty, the terminal will try to read all sectors.
    An array with:
    • sector: specifies what sector on MifareClassic to read from.
    • keyType: specifies what key is used to access a sector, a (default) or b.
      You cannot use keyType: b on Android terminals due to a limitation from Castles.
  2. Encode the Operation JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request to a Terminal API endpoint, specifying:

    • CardAcquisitionRequest.SaleData:

      Parameter Required Description
      SaleTransactionID -white_check_mark- An object with:
      • TransactionID: your reference to identify the transaction. We recommend using a unique value.
      • TimeStamp: date and time of the request in UTC format.
      SaleToPOIData The Base64-encoded Operation JSON object.
    • CardAcquisitionRequest.CardAcquisitionTransaction:

      Parameter Required Description
      TotalAmount The transaction amount. You can omit the TotalAmount field when reading the NFC tag.
      If you omit TotalAmount, you still have to include an empty CardAcquisitionTransaction field in the request.

    The example below shows a card acquisition request to read data from sectors 1 and 2 on a MifareClassic NFC tag:

  4. When the card acquisition succeeds, in the AdditionalResponse of the CardAcquisitionResponse note:

    • NFC.data: the data from the specified sectors.
      • For a single sector, the data shows as NFC.data=afafafafafafafafafa....
      • For multiple sectors, the data from each sector shows as value of the NFC.data for that sector, for example: NFC.data.S01=afafafafafafafafafa, NFC.data.S02=2222222222222222.
    • NFC.status: the status of the read action for every sector, for example NFC.status=s01.OKs02.OKS03.AUTH_ERROR.
    • NFC.uid: the unique identifier of the NFC tag.
    • NFC.variant: the type of the NFC tag, in this case MIFARE Classic.

    After you have read the specified sectors on the NFC tag, the loading screen continues to show until you send an
    enable service request` (see the next step).

  5. To stop the loading screen, make an enable service request to a Terminal API endpoint, specifying:

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. This includes:

      Parameter Required Description
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- EnableService
    • EnableServiceRequest with:

      Parameter Required Description
      TransactionAction -white_check_mark- AbortTransaction.
      DisplayOutput Optional object to show your own message and an 'Approved' icon (green check mark) or a 'Declined' icon (red cross).
      If you omit DisplayOutput, the terminal shows Canceled, a red cross , and Transaction canceled.

    The following example is the basic request, without the DisplayOutput object, to stop the card acquisition flow.

Write data

NFC tags have multiple parts that can store data. Because the data structure is different for every NFC tag type, the request needs to specify the sector, aid, or page where to write the data to.
Select a tab to see the parameters that you need to specify to write data to MIFARE Classic, MIFARE DESFire, or MIFARE Ultralight.

  1. Create an Operation JSON object and specify:

    Parameter Required Description
    Type -white_check_mark- NFCWrite: writes data to the specified sector.
    Variant -white_check_mark- MifareClassic: specifies the type of NFC tag used.
    NFCData -white_check_mark-
    If you leave the NFCData array empty when trying to write, the response will return the UID and the variant but will not write to the card.
    An array specifying:
    • sector: specifies the sector to write to.
    • keyType: specifies what key is used to write to a sector, a (default) or b.
      You cannot use keyType: b on Android terminals due to a limitation from Castles.
    • data: data to write in the specified sector.
  2. Encode the Operation JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request to a Terminal API endpoint, specifying:

    • CardAcquisitionRequest.SaleData:

      Parameter Required Description
      SaleTransactionID -white_check_mark- An object with:
      • TransactionID: your reference to identify the transaction. We recommend using a unique value.
      • TimeStamp: date and time of the request in UTC format.
      SaleToPOIData The Base64-encoded Operation JSON object.
    • CardAcquisitionRequest.CardAcquisitionTransaction:

      Parameter Required Description
      TotalAmount The transaction amount. You can omit the TotalAmount field when writing to the NFC tag.
      If you omit TotalAmount, you still have to include an empty CardAcquisitionTransaction field in the request.

    The example below shows a card acquisition request to write data to sectors 1 and 2 on a MifareClassic NFC tag:

  4. When the card acquisition succeeds, in the AdditionalResponse of the CardAcquisitionResponse note:

    • NFC.data: the data written to the specified sectors.
      • For a single sector, the data shows as NFC.data=afafafafafafafafafa....
      • For multiple sectors, the data from each sector shows as value of the NFC.data for that sector, for example: NFC.data.S01=afafafafafafafafafa, NFC.data.S02=2222222222222222.
    • NFC.status: the status of the write action for every sector, for example NFC.status=s01.OKs02.OKS03.AUTH_ERROR.
    • NFC.uid: the unique identifier of the NFC tag.
    • NFC.variant: the type of the NFC tag, in this case MIFARE Classic.

    After you write to the specified sectors on the NFC tag, the loading screen continues to show until you send an
    enable service request (see the next step).

  5. To stop the loading screen, make an enable service request to a Terminal API endpoint, specifying:

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. This includes:

      Parameter Required Description
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- EnableService
    • EnableServiceRequest with:

      Parameter Required Description
      TransactionAction -white_check_mark- AbortTransaction.
      DisplayOutput Optional object to show your own message and an 'Approved' icon (green check mark) or a 'Declined' icon (red cross).
      If you omit DisplayOutput, the terminal shows Canceled, a red cross , and Transaction canceled.

    The following example is the basic request, without the DisplayOutput object, to stop the card acquisition flow.

Create a session

If your use case requires multiple read/write actions on an NFC tag, you can create a session to make multiple requests part of the same user interaction. While the session is in progress, the terminal shows the One moment screen.

To start the session you need to add Session.Type: Begin in the SaleToPOIData of your first identify, read, or write request. You then send multiple requests and, to finish the session, you add Session.Type: End in the SaleToPOIData of the last request. All requests that belong to the session must contain the same Session.Id in the SaleToPOIData.
When the session ends, the terminal continues to show the loading screen until you send an enable service request.

Here's an example of a session that consists of three card acquisition requests:

Step 1: Start a session and identify the NFC tag type

When starting the session, you need specify the Id to connect the requests into a single session. Here's example of how identify the tag and start the session:

  1. Create a JSON object with:

    Parameter Required Description
    Session.Type -white_check_mark- Begin: starts the session.
    Session.Id -white_check_mark- Your unique reference of the session.
    Session.Timeout -white_check_mark-
    • How long the One moment screen is shown on the terminal display, in milliseconds.
    • If there is no end session request, how long before the session data is deleted and new requests can be sent.
    Operation.Type -white_check_mark- NFCReadUID: returns the NFC tag identifier and type.
  2. Encode the JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request with the Base64-encoded string in the SaleToPOIData.

Step 2: Send read data request with Session.Id

Make sure to include the Session.Id specified in the SaleToPOIData of the first request. Here's example of how to read data from MIFARE Classic as part of the session:

  1. Create a JSON object with:

    Parameter Required Description
    Session.Id -white_check_mark- Your unique reference of the session, specified in the first request.
    Operation.Type -white_check_mark- NFCRead: reads data from the specified sector.
    Operation.Variant -white_check_mark- MifareClassic: the type of NFC tag.
    Operation.NFCData -white_check_mark-
    If you leave the NFCData array empty, the terminal will try to read all sectors.
    An array with:
    • sector: specifies what sector on MifareClassic to read from.
    • keyType: specifies what key is used to access a sector, a (default) or b.
      You cannot use keyType: b on Android terminals due to a limitation from Castles.
  2. Encode the JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request with the Base64-encoded string in the SaleToPOIData.
    The example below shows how to read data from MifareClassic NFC tag as part of the session:

Step 3: Write data to the NFC tag and end the session

In the final request of the interaction, make sure to include the same Session.Id and Session.Type: End. Here's example of how to write data to MIFARE Classic and end the session:

  1. Create a JSON object with:

    Parameter Required Description
    Session.Type -white_check_mark- End: ends the session.
    Session.Id -white_check_mark- Your unique reference of the session.
    Operation.Type -white_check_mark- NFCWrite: reads data from the specified sector.
    Operation.Variant -white_check_mark- MifareClassic: the type of NFC tag.
    Operation.NFCData -white_check_mark-
    If you leave the NFCData array empty when trying to write, the response will return the UID and the variant but will not write to the card.
    An array with:
    • sector: specifies what sector on MifareClassic to read from.
    • keyType: specifies what key is used to access a sector, a (default) or b.
      You cannot use keyType: b on Android terminals due to a limitation from Castles.
  2. Encode the JSON object to Base64. You will pass the resulting string in SaleData.SaleToPOIData.

  3. Make a card acquisition request with the Base64-encoded string in the SaleToPOIData.
    The example below shows how to write data MifareClassic NFC tag and end the session:

    After you end the session, the loading screen continues to show until you send an enable service request (see the
    next step).

  4. To stop the loading screen, make an enable service request to a Terminal API endpoint, specifying:

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. This includes:

      Parameter Required Description
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- EnableService
    • EnableServiceRequest with:

      Parameter Required Description
      TransactionAction -white_check_mark- AbortTransaction.
      DisplayOutput Optional object to show your own message and an 'Approved' icon (green check mark) or a 'Declined' icon (red cross).
      If you omit DisplayOutput, the terminal shows Canceled, a red cross , and Transaction canceled.

    The following example is the basic request, without the DisplayOutput object, to stop the card acquisition flow.

See also