When refunding a split payment, you can either deduct the same amount from the same balance accounts as used in the original payment request, or provide new split instructions in the ReversalRequest
. Refunds can only be deducted from the balance accounts to which the payment was credited.
For example, a payment of EUR 620.00 is split between two balance accounts:
- EUR 600.00 is booked to your user's balance account (BA00000000000000000000001).
- EUR 20.00 is booked to your liable balance account as your platform's commission.
To refund this payment, send a Terminal API ReversalRequest
. If you do not provide the split instructions in the SaleData.SaleToAcquirerData
object of your request, the split instructions from the original payment are used for the refund.
We recommend that you always provide split instructions for refunds. That way, you are always in control and can support different refund scenarios, such as partial and multiple partial refunds.
When sending split instructions in a refund request, you must:
- Only use balance accounts that were part of the original split.
- Use the same
reference
values as the original split.
To provide split instructions in your refund request:
Step 1: Gather the data
Gather the following data that you need pass inside SaleToAcquirerData
of the refund request:
Field | Description | Example |
---|
Field | Description | Example |
---|---|---|
split.api |
Version of the Split API: 1. | split.api=1 |
split.totalAmount |
The full transaction mount, in minor units. Must be equal to the sum of the split amounts. |
split.totalAmount=62000 |
split.currencyCode |
Currency of the transaction. | split.currencyCode=EUR |
split.nrOfItems |
The number of splits you want to create. Must match the sum of split items in the request. |
split.nrOfItems=2 |
currency |
Currency of the split amount. Must match the value provided in split.currencyCode . |
currency=EUR |
Every split of the full amount is called a split item, and needs to be specified with a different number, starting at 1.
For every split item, like split.item1
, split.item2
, and so on, include the following:
Field | Description | Example |
---|---|---|
split.item[ITEM_NUMBER].amount |
Amount of each split item, in minor units. You do not need to specify the amount for the fees, since this is not known at the time of payment. | split.item1.amount=62000 split.item2.amount=0 |
split.item[ITEM_NUMBER].type |
Defines the part of the payment you want to book to the specified split.item[ITEM_NUMBER].account .Possible values:
|
split.item1.type=BalanceAccount split.item2.type=Commission |
split.item[ITEM_NUMBER].account |
Account that will receive (or be charged) the split. This is the balanceAccountID of one of your user's balance accounts or your own liable account. You do not need to specify this field when the split.item[ITEM_NUMBER].type is Commission. |
split.item1.account=BA00000000000000000000001 |
split.item[ITEM_NUMBER].reference |
The reference for that specific transaction split, which is returned in our reporting. Required if the split.item[ITEM_NUMBER].type is BalanceAccount. |
split.item1.reference=reference_split_1 split.item2.reference=reference_commission |
split.item[ITEM_NUMBER].description |
The description for that specific transaction split, which is returned in our reporting. | split.item1.description=description_split_1 split.item2.description=description_commission |
currency=EUR split.api=1 split.nrOfItems=2 split.totalAmount=62000 split.currencyCode=EUR split.item1.amount=62000 split.item1.type=BalanceAccount split.item1.account=BA00000000000000000000001 split.item1.reference=reference_split_1 split.item1.description=description_split_1 split.item2.amount=0 split.item2.type=Commission split.item2.reference=reference_commission split.item2.description=description_commission
Step 2: Prepare the data
There are two ways to pass data elements in the SaleToAcquirerData
field of your request:
- Option 1: as form-encoded key-value pairs (using & as a separator).
- Option 2: as a JSON object converted to a Base64-encoded string.
-
Concatenate the key-value pairs from Step 1 into a string by separating them with ampersands (&).
Concatenated key-value pairsExpand viewCopy link to code blockCopy codecurrency=USD&split.api=1&split.nrOfItems=2&split.totalAmount=62000&split.currencyCode=USD&split.item1.amount=62000&split.item1.type=BalanceAccount&split.item1.account=BA00000000000000000000001&split.item1.reference=reference_split_1&split.item1.description=description_split_1&split.item2.amount=0&split.item2.type=Commission&split.item2.reference=reference_commission&split.item2.description=description_commission
-
Pass this string in the
SaleToAcquirerData
object of your request.
The format you choose in your request is the format that will be returned in the AdditionalResponse
. To always receive the AdditionalResponse
in the same format, contact our Support Team
Step 3: Make a refund request
Make a POST request to a Terminal API endpoint, specifying:
-
MessageHeader
: the standardSaleToPOIRequest.MessageHeader
object. Specify:Parameter Required Description ProtocolVersion
3.0 MessageClass
Service MessageCategory
Reversal MessageType
Request ServiceID
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
Your unique ID for the POS system component to send this request from. POIID
The unique ID of the terminal to send this request to. Format: [device model]-[serial number]. -
ReversalRequest
: The request body. This includes:Parameter Required Description OriginalPOITransaction.POITransactionID
An object with: TransactionID
: Transaction identifier of the original payment in the formattenderReference.pspReference
. For example, BV0q001643892070000.VK9DRSLLRCQ2WN82TimeStamp
: date and time of the original payment request in UTC format.
ReversalReason
Set to MerchantCancel. ReversedAmount
The refunded amount.
Specify the amount with a decimal point. Do not use minor units. For example, for an amount of USD 10 specify 10.00 (not 1000).SaleData.SaleToAcquirerData
Provides the split refund data as concatenated key-value pairs separated by an ampersand (&) character, or in Base64-encoded format. See the tables below for details SaleData.SaleTransactionID
An object with: TransactionID
: your reference to identify the refund. We recommend using a unique value per refund.TimeStamp
: date and time of the request in UTC format.
Below is an example Terminal API request to split a refund of EUR 620.00 between the balance accounts on your platform, using key-value pairs.
In this example, we are deducting different amounts from the balance accounts than what was defined in the original payment request.
- EUR 620.00 is debited from BA00000000000000000000001.
- EUR 0.00 is debited from your liable balance account.
{ "SaleToPOIRequest":{ "MessageHeader":{ "MessageCategory": "Reversal", "MessageClass": "Service", "MessageType": "Request", "POIID": "V400m-324688179", "ProtocolVersion": "3.0", "SaleID": "POSSystemID12345", "ServiceID": "207111107" }, "ReversalRequest":{ "OriginalPOITransaction":{ "POITransactionID":{ "TransactionID": "OBV0q001643892070000.VK9DRSLLRCQ2WN82", "TimeStamp": "2022-01-31T12:08:45.004Z" } }, "ReversalReason": "MerchantCancel", "ReversedAmount": "620", "SaleData":{ "SaleToAcquirerData": "currency=EUR&split.api=1&split.nrOfItems=2&split.totalAmount=62000&split.currencyCode=EUR&split.item1.amount=62000&split.item1.type=BalanceAccount&split.item1.account=BA00000000000000000000001&split.item1.reference=reference_split_1&split.item1.description=description_split_1&split.item2.amount=0&split.item2.type=Commission&split.item2.reference=reference_commission&split.item2.description=description_commission", "SaleTransactionID":{ "TimeStamp": "2022-02-03T15:04:14.004Z", "TransactionID": "27911" } } } } }
The refund request is routed to the terminal, where the customer can present their card. The payment terminal shows a waiting screen until you receive a response.
Step 4: Check the result
Response
When you receive the ReversalResponse
, check if we received your request. Note the following:
Response.Result
: Success. This means we received your request. The refund itself will be processed asynchronously.POIData.POITransactionID.TransactionID
: the PSP reference for this refund request.PaymentReceipt
: the generated receipt data. This includes REFUND REQUESTED.-
Response.AdditionalResponse
with:posOriginalAmountValue
: the amount (in minor units) of the original payment.posAuthAmountValue
: the refund amount (in minor units) that we will try to get authorized.-
pspReference
: the PSP reference for this refund request.Response for the reversal request with split instructionsExpand viewCopy link to code blockCopy code{ "SaleToPOIResponse": { "MessageHeader": { "MessageCategory": "Reversal", "MessageClass": "Service", "MessageType": "Response", "POIID": "V400m-324688179", "ProtocolVersion": "3.0", "SaleID": "POSSystemID12345", "ServiceID": "207111107" }, "ReversalResponse": { "POIData": { "POITransactionID": { "TimeStamp": "2022-02-03T15:04:15.004Z", "TransactionID": "TF995R5G6L2GWR82" } }, "PaymentReceipt": [...], "Response": { "AdditionalResponse": "currency=EUR&split.api=1&split.nrOfItems=2&split.totalAmount=62000&split.currencyCode=EUR&split.item1.amount=62000&split.item1.type=BalanceAccount&split.item1.account=BA00000000000000000000001&split.item1.reference=reference_split_1&split.item1.description=description_split_1&split.item2.amount=0&split.item2.type=Commission&split.item2.reference=reference_commission&split.item2.description=description_commission", "Result": "Success", "ReversedAmount": 620 } } } }
If your request failed, the ReversalResponse
contains:
Response.Result
: Failure-
AdditionalResponse
: this includes amessage
explaining why the request failed. For example:- Original pspReference required for this operation: messages like this tell you how to fix the request so you can try again.
- Transaction is already voided: this message tells you that we already received a full reversal request for the original transaction.
Webhook
Wait for the CANCEL_OR_REFUND webhook to learn the outcome. Refunds are always processed asynchronously.