Search

Are you looking for test card numbers?

Would you like to contact support?

Marketpay icon

Platforms for point of sale

Process point-of-sale payments on behalf of your sub-merchants.

Adyen for Platforms enables you to onboard point-of-sale (POS) sub-merchants and accept in-store payments. To make this happen, you need to:

Before you begin

Before you start processing point-of-sale payments for your sub-merchants, you need to:

Subscribe to store notifications

When you add a store for your sub-merchant, we send a notification to let you know the store has been set up. To ensure you receive this notification:

  1. Make a POST request to the /createNotificationConfiguration endpoint, specifying:

    • active: true.
    • description: Your text to recognize the notification subscription by.
    • An eventConfigs array with eventType: ACCOUNT_HOLDER_STORE_STATUS_CHANGE and includeMode: INCLUDE.
    • notifyURL: Endpoint on your server where you'll receive the notifications.
    • hmacSignatureKey: Use this parameter if you want us to protect notifications with HMAC signatures. It specifies the HMAC key that we'll use to calculate the HMAC signatures of the notifications we send to the notifyURL. On your end you'll need to verify the signatures of the incoming notifications.
    • notifyUsername and notifyPassword: Username and password that we need for basic authentication with the server where you'll receive the notifications.
    • sslProtocol: Protocol used to secure the communication, such as TLS or SSL.
    /createNotificationConfiguration request
    {
      "configurationDetails":{
        "active": true,
        "description":"YOUR_UNIQUE_DESCRIPTION",
        "eventConfigs":[
          {
            "eventType":"ACCOUNT_HOLDER_STORE_STATUS_CHANGE",
            "includeMode":"INCLUDE"
          }
        ],
        "notifyURL":"https://www.merchant-domain.com/notification-handler",
        "hmacSignatureKey":"c6c608e9d2ec95889cb9757e6beb4956c187f44cf7bb7297b16880c0310af7ff",
        "notifyUsername":"YOUR_USERNAME",
        "notifyPassword":"YOUR_PASSWORD",
        "sslProtocol":"SSL"
      }
    }
    Response
    {
      "pspReference": "8515681150749298",
      "configurationDetails": {
        "active": true,
        "description": "YOUR_UNIQUE_DESCRIPTION",
        "eventConfigs": [
          {
            "eventType": "ACCOUNT_HOLDER_STORE_STATUS_CHANGE",
            "includeMode": "INCLUDE"
          }
        ],
        "notificationId": 20337,
        "notifyURL": "https://www.merchant-domain.com/notification-handler",
        "sslProtocol": "SSLInsecureCiphers"
      }
    }
  2. Make sure your server accepts the notifications.
  3. Use the notificationId you receive in the response to test the notification.

Add a store to the account holder

If you're onboarding a point-of-sale sub-merchant, you need to provide us with the details of your sub-merchant's store. We will then set up the store to accept the configured payment methods. When we finish configuring the store, we send you a notification.

Currently the store will get the payment methods configured at the platform level.

To provide the store details, include a storeDetails array when you create a new account holder or when you update an existing one:

  1. Make a POST request to one of the following endpoints:

    Situation Endpoint Specify
    There is no account holder for the sub-merchant yet. /createAccountHolder The usual parameters to create a business account holder, as well as businessDetails.taxId.
    You want to add a store to an existing account holder for the sub-merchant. /updateAccountHolder accountHolderCode: Your unique reference to the sub-merchant's account holder. Include businessDetails.taxId, unless it is already present as part of businessDetails.
  2. In the request body, include an accountHolderDetails.storeDetails array, specifying:

    • storeReference: Your unique ID for the sub-merchant's store.
    • storeName: The name of the sub-merchant's store. This will be included in the shopper statement.
    • merchantAccount: Your merchant account used for accepting payments.
    • merchantCategoryCode: The merchant category code (MCC) that classifies the business of your sub-merchant.
    • address: The city, country, houseNumberOrName, postalCode, stateOrProvince, and street of the physical store where the sub-merchant will process payments from.
    • fullPhoneNumber: The phone number of the store.

    The next example shows a /createAccountHolder request to create a new account holder with a store.

    /createAccountHolder request
    {
      "accountHolderCode": "YOUR_UNIQUE_ACCOUNT_HOLDER_CODE",
      "accountHolderDetails": {
        "address": {
            "country": "US"
        },
        "businessDetails": {
          "legalBusinessName": "Real Good Restaurant Inc.",
           "taxId": "444455555",
          "shareholders": [
            {
                "name": {
                  "firstName": "Maria",
                  "gender": "FEMALE",
                  "lastName": "Green"
                },
                "address": {
                  "country": "US"
                }
            }
          ]
        },
        "email": "maria@green.com",
        "storeDetails" : [
          {
            "storeReference": "YOUR_SUBMERCHANT_STORE_ID",
            "storeName": "Store Name",
            "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
            "merchantCategoryCode": "7999",
            "address" : {
              "city": "Store City",
              "country": "US",
              "houseNumberOrName": "1",
              "postalCode": "123",
              "stateOrProvince": "CA",
              "street": "Store Street"
            },
            "fullPhoneNumber": "+31201234567"
          }
        ]
      },
      "legalEntity": "Business"
    }

    This example shows how you would add a store to an existing account holder using an /updateAccountHolder call.

    /updateAccountHolder request
    {
      "accountHolderCode": "YOUR_UNIQUE_ACCOUNT_HOLDER_CODE",
      "accountHolderDetails": {
        "storeDetails" : [
          {
            "storeReference": "YOUR_SUBMERCHANT_STORE_ID",
            "storeName": "Store Name",
            "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
            "merchantCategoryCode": "7999",
            "address": {
              "city": "Store City",
              "country": "US",
              "houseNumberOrName": "1",
              "postalCode": "123",
              "stateOrProvince": "CA",
              "street": "Store Street"
             },
            "fullPhoneNumber": "+31201234567"
          }
        ]
      }
    }

    The response contains various account holder details, including the storeDetails showing that the status of the store is Pending.

    Response
    {
       "invalidFields": [],
       ...
       "accountHolderDetails":{
          ...
          "storeDetails" : [
             {
                "address": {
                   "city": "Store City",
                   "country": "US",
                   "houseNumberOrName": "1",
                   "postalCode": "123",
                   "stateOrProvince": "CA",
                   "street": "Store Street"
                   },
                "fullPhoneNumber": "+31201234567",
                "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
                "merchantCategoryCode": "7999",
                "status": "Pending",
                "store": "6db2bdbc-5e77-4388-9a6e-9e653905d3e5",
                "storeReference": "YOUR_SUBMERCHANT_STORE_ID",
                "storeName": "Store Name",
             }
          ],
          ...
       },
       ...
    }
  3. Before processing point-of-sale payments, wait until you receive an ACCOUNT_HOLDER_STORE_STATUS_CHANGE notification showing that the new status of the store is Active.

    Notification
    {
       "eventType":"ACCOUNT_HOLDER_STORE_STATUS_CHANGE",
       "eventDate":"2018-04-23T13:13:44+02:00",
       "executingUserKey":"ws",
       "live":true,
       "pspReference":"12312312313",
       "content":{
          "accountHolderCode":"YOUR_UNIQUE_ACCOUNT_HOLDER_CODE",
          "store": "6db2bdbc-5e77-4388-9a6e-9e653905d3e5",
          "storeReference":"YOUR_SUBMERCHANT_STORE_ID",
          "newStatus":"Active",
          "oldStatus":"Pending",
          "reason":"Test Reason"
       }
    }

Assign a terminal to the store

To process in-store payments, your sub-merchant's store needs to have one or more payment terminals. The process is as follows:

  1. Select a terminal and inform your sub-merchant of the communication requirements.
  2. Order a terminal if you don't have the required terminal in your inventory.
  3. Assign the terminal from your inventory to the store of the sub-merchant in one of the following ways:

    • Assign terminals using your Customer Area
      or
    • Assign terminals in bulk using the Terminal Management API

      The advantage of using the Terminal Management API over the Customer Area, is that API calls enable you to automate assigning terminals and retrieving an overview of terminal assignments.

  4. Configure the terminals in the Customer Area, under Point of Sale > Terminal settings.
  5. Ship the terminal to the physical store address and instruct your sub-merchant to board the terminal.

Make a split payment

To initiate an in-store payment, you make a Terminal API call. In the PaymentRequest you provide the split payment data in the SaleToAcquirerData, in URL-escaped or Base64-encoded format. This credits payments directly to a sub-merchant's balance.

To make the payment:

  1. Make a POST request to a Terminal API endpoint, specifying:

    • MessageHeader: The standard SaleToPOIRequest.MessageHeader object, explained in Terminal API fundamentals:
      Parameter Required Description
      ProtocolVersion -white_check_mark- 3.0
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- Payment
      MessageType -white_check_mark- Request
      SaleID -white_check_mark- Your unique ID for the cash register.
      ServiceID -white_check_mark- Your unique ID for this request, consisting of 1-10 alphanumeric characters. Must be unique within the last 48 hours.
      POIID -white_check_mark- Unique ID of the terminal that you want to route this request to. Format: [device model]-[serial number].
    • PaymentRequest: The request body. This must include:

      Parameter Required Description
      SaleData.SaleTransactionID -white_check_mark- An object with:
      • TransactionID: Your unique reference for this request. In your Customer Area and Adyen reports, this will show as the merchant reference for the transaction.
      • TimeStamp: Date and time of the request in UTC format.
      SaleData.SaleToAcquirerData Provides the split payment data as concatenated key-value pairs separated by an ampersand (&) character, or in Base64-encoded format. See the tables below for details.
      PaymentTransaction.AmountsReq -white_check_mark- An object with:
      • Currency: The transaction currency.
      • RequestedAmount: The transaction amount.
    • To provide the split payment data in PaymentRequest.SaleData.SaleToAcquirerData, use the following fields:

      Field Description Example
      split.api Version of the Split API: 1. split.api=1
      split.totalAmount Amount to be split, in minor units. Must be equal to the transaction amount and to the sum of the split amounts. split.totalAmount=62000
      split.currencyCode Currency of the amount to be split. split.currencyCode=EUR
      split.nrOfItems Number of times you will split the payment. Must match the number of split items in the request. split.nrOfItems=2

      Specify each split as a split.item{item#} starting at 1split.item1split.item2, and so on. Each split item includes:

      Field Description Example
      split.item{item#}.amount Amount of the split, in minor units. split.item1.amount=60000 and split.item2.amount=2000
      split.item{item#}.type Split type. A type of MarketPlace or VAT sends the amount to the account specified. A type of Commission or PaymentFee sends the amount to your liable account. split.item1.type=MarketPlace and split.item2.type=Commission
      split.item{item#}.account Account that will receive the split. This is the accountCode of one of your account holder's accounts or your own liable account. You don't need to specify an account when split.type is Commission.  split.item1.account=8815628270908491
      split.item{item#}.reference The reference for that specific transaction split, which is returned in our reporting. Required if the split.type is MarketPlace.  

    The next example shows how you would split a 620.00 EUR payment into 600.00 EUR to be paid into the sub-merchant account 8815628270908491 and 20.00 EUR commission to be paid to the platform.

    Payment request with split data
    {
      "SaleToPOIRequest":{
        "MessageHeader":{
          "ProtocolVersion":"3.0",
          "MessageClass":"Service",
          "MessageCategory":"Payment",
          "MessageType":"Request",
          "SaleID":"POSSystemID12345",
          "ServiceID":"0207111104",
          "POIID":"V400m-324688179"
        },
        "PaymentRequest":{
          "SaleData":{
            "SaleTransactionID":{
              "TransactionID":"27908",
              "TimeStamp":"2019-03-07T10:11:04+00:00"
            },
            "SaleToAcquirerData": "split.api=1&split.nrOfItems=2&split.totalAmount=62000&split.currencyCode=EUR&split.item1.amount=60000&split.item1.type=MarketPlace&split.item1.account=8815628270908491&split.item1.reference=test1&split.item2.amount=2000&split.item2.type=Commission&split.item2.reference=TestCommission"
          },
          "PaymentTransaction":{
            "AmountsReq":{
              "Currency":"EUR",
              "RequestedAmount":620.00
            }
          }
        }
      }
    }

    The payment request is routed to the terminal, for the shopper to present their card and verify the payment. The payment is then sent to the Adyen payments platform for processing.

  2. Receive the payment result.

    If your integration uses asynchronous cloud communications, you receive the result in a TENDER_FINAL display notification.

    If the payment is successful:

    • Approved is displayed on the terminal screen.
    • You receive a payment response containing:
      • POIData.POITransactionID.TransactionID: Transaction identifier for the payment in the format tenderReference.pspReference. For example, oLkO0012498220087000.981517998282382C.
      • PaymentResponse.Response.Result: Success
      • PaymentResponse.Response.AdditionalResponse: A base64 string. When decoded, this is a JSON object with additional transaction data.
      • PaymentReceipt: Object containing data you can use to generate a receipt.

You can also view the details of a payment in your Customer Area, under Transactions > Payments.

Splitting at capture

Some platforms do not know the final split amounts at the moment the payment request is made. If this applies to your sub-merchant's in-store payments, you need to enable manual capture for in-store payments and follow up each authorised Terminal API payment with a /capture API call. Splitting at capture is also the procedure to use for tipping (gratuity) and partial payments.

Enabling manual capture for in-store payments applies to all your point-of-sale sub-merchants.

Proceed as follows:

  1. Enable manual capture for point-of-sale payments:

    1. Log in to your Customer Area.
    2. Switch to your merchant account.
    3. Go to Account > Settings.
    4. For the POS Capture Delay, select manual.
    5. Select Submit.
  2. Make a payment request to a Terminal API endpoint. You can include split data as described above, but these will be overriden by the capture request you'll do in the next step.

  3. When the payment is authorised, make a POST request to the /capture endpoint, specifying:

    • originalReference: The pspReference returned as part of the transaction identifier in the response to the Terminal API payment request.
    • reference: Optional reference for this capture. This will be shown in your Customer Area and reports.
    • merchantAccount: Your merchant account used for accepting payments.
    • modificationAmount: The value in minor units and currency of the amount being split. Must be the same amount as the original transaction.
    • splits: An array of split items, each including:

      • amount.value: Amount of the split, in minor units.
      • type: Split type. A type of MarketPlace or VAT sends the amount to the account specified. A type of Commission or PaymentFee sends the amount to your liable account.
      • account: Account that will receive the split. This is the accountCode of one of your account holder's accounts or your own liable account. You don't need to specify an account when split.type is Commission.
      • reference: An optional reference which is returned in our reporting. You can use this to refer to that specific transaction split.

    The next example shows how you would capture a 620.00 EUR authorisation with pspReference 981517998282382C, and split it into 600.00 EUR to be paid into the sub-merchant account 8815628270908491 and 20.00 EUR commission to be paid to the platform.

    Capture request with split data
    {
      "originalReference":"981517998282382C",
      "reference":"YOUR_REFERENCE_TO_THIS_CAPTURE",
      "merchantAccount":"YOUR_MERCHANT_ACCOUNT",
      "modificationAmount":{
        "value":62000,
        "currency":"EUR"
      },
      "splits":[
        {
          "amount":{
            "value":60000
          },
          "type":"MarketPlace",
          "account":"8815628270908491",
          "reference":"YOUR_REFERENCE_TO_SPLIT_#1"
        },
        {
          "amount":{
            "value":200
          },
          "type":"Commission",
          "reference":"YOUR_REFERENCE_TO_SPLIT_#2"
        }
      ]
    }

    If the capture request is successfully received, the response contains:

    • response: [capture-received]
    • pspReference: Our unique identifier for this capture request.

    Payment captures are performed asynchronously, and we inform you whether a capture request is sent for processing with a webhook notification. If the capture is successfully sent for processing, this notification contains:

    • eventCode: CAPTURE
    • originalReference: The pspReference of the authorisation.
    • pspReference: Our unique identifier for this capture request.
    • success: true

    If success is false then your capture request failed. Review the reason you received in the notification, fix the issue, and submit the capture request again.

See also