You can always be in control of funds movements in your platform if you provide split instructions in your refund requests, and you can support different refund scenarios, such as partial and multiple partial refunds.
You can split a refund into:
- The sale amount that is refunded
- Your platform's commission that is now refunded
- The transaction fees incurred by the refund
Requirements
In addition to the general requirements for in-person payments with an Adyen for Platforms integration, take into account the following information.
| Requirement | Description |
|---|---|
| Integration type | A Terminal API integration with payment terminals or with a Mobile solution. The standalone solution does not support adding split instructions to individual referenced refund requests. |
| Webhooks | Listen to the following webhook events: |
| Limitations | Take into account:
|
Make a partial refund with split instructions
The process is the same as for providing split instructions in a Terminal API payment request:
- You gather the split instructions and format these either as a JSON object converted to a Base64-encoded string, or as a string of concatenated key-value pairs.
- Then you pass the resulting string in the
SaleToAcquirerDatafield.
The main difference is that for a refund you need to use a reversal request, not a payment request.
-
Collect the following information:
- From the POIData.POITransactionID object in the Terminal API payment response: The
TransactionIDandTimeStampof the original payment. - From the transfer webhooks related to the original payment: The split instructions of the original payment, specifically the
split.item[ITEM_NUMBER].referencevalues.
- From the POIData.POITransactionID object in the Terminal API payment response: The
-
Gather the split instructions, taking into account the following:
- For the
split.item[ITEM_NUMBER].referencevalues in the reversal request, you must use the same reference values as in the payment that you are refunding. - The amounts that the refund will be split into will be deducted from the relevant balance accounts.
- Refunds can only be deducted from the balance accounts to which the payment was credited.
- For the
-
Format the split instructions in one of the following ways:
- Option 1: as a string of form-encoded key-value pairs (using & as a separator).
- Option 2: as a JSON object converted to a Base64-encoded string.
As an example, we issue a partial refund of USD 40.00 for an original split payment of USD 80.00.
- A refund amount of USD 37.50 is debited from the user's balance account BA00000000000000000000001.
- A commission of USD 2.50 is debited from your liable balance account.
-
Make a Terminal API reversal request specifying:
-
The standard
SaleToPOIRequest.MessageHeaderobject, withMessageClassset to Service andMessageCategoryset to Reversal.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]. -
The ReversalRequest object with:
Parameter Required Description OriginalPOITransaction.POITransactionIDAn object with: TransactionID: The transaction identifier of the original payment in the formattenderReference.pspReference. For example, BV0q001765198054002.CWBC43ZX2VTFWR82TimeStamp: The date and time of the original payment request in UTC format.
ReversalReasonSet to MerchantCancel. ReversedAmountRequired for partial refunds.
The refunded amount. If not included, defaults to the authorized amount of the referenced payment.
Specify the amount as a number (not a string) with a decimal point. Do not use minor units. For example, for an amount of USD 10 specify 10.00 (not 1000).SaleData.SaleToAcquirerDataThe formatted string of split instructions for the refund.
For a partial refund, the string must also include the currency of the partial refund. This must match the currency of the original payment and must be provided in the format"currency":"ABC"(orcurrency=ABCif using key-value pairs), whereABCis the three-letter currency code.SaleData.SaleTransactionIDRequired for partial refunds.
An object with:TransactionID: Your reference to identify the refund. We recommend using a unique value per (partial) refund.TimeStamp: The date and time of the request in UTC format.
The payment terminal shows a waiting screen until you receive a response.
-
-
When you receive the ReversalResponse note the following:
Parameter Description Response.ResultSuccess means we received your request. The refund itself will be processed asynchronously. Response.ReversedAmountThe partial refund amount that we will try to get authorized. POIData.POITransactionID.TransactionIDThe PSP reference for this refund request. PaymentReceiptThe generated receipt data. This includes REFUND REQUESTED. Response.AdditionalResponseThis includes: posOriginalAmountValue: The amount (in minor units) of the original payment.posAuthAmountValue: The refund amount (in minor units) that we will try to get authorized.merchantReference: TheSaleData.SaleTransactionID.TransactionIDfrom the partial refund request.pspReference: The PSP reference for this refund request.
If your request failed, the ReversalResponse.Response contains:
Result: FailureAdditionalResponse: This includes amessageexplaining 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.
-
Wait for the CANCEL_OR_REFUND webhook event to learn the outcome of the refund request. Refunds are always processed asynchronously.
Full refund with split instructions
To refund the full amount of the original payment, your Terminal API reversal request requires less parameters than to refund a partial amount. Other than that, the procedure is the same.
The following example shows a reversal request for a full refund of a USD 80.00 payment, where the split instructions differ from the payment:
- A refund amount of USD 76.00 is debited from the user's balance account BA00000000000000000000001.
- A commission of USD 4.00 is debited from your liable balance account.
As you can see, for a full refund with split instructions the ReversalRequest object only needs these parameters:
| Parameter | Required | Description |
|---|---|---|
OriginalPOITransaction.POITransactionID |
An object with:
|
|
ReversalReason |
Set to MerchantCancel. | |
SaleData.SaleToAcquirerData |
The formatted string of split instructions for the refund. There is not need to add a separate currency parameter. |
The ReversalResponse does not contain a ReversedAmount because the full amount from the payment referenced in the POITransactionID of the reversal request will be refunded.
Track fund movements
To track the status of the funds transfers initiated by a refund:
- Listen to the following webhooks:
- Transfer webhooks: Adyen sends a balancePlatform.transfer.created webhook to inform your server that funds will be credited to balance accounts, and balancePlatform.transfer.updated webhooks after every status change.
- Transaction webhooks: Adyen sends a balancePlatform.transaction.created webhook to inform your server that funds have been credited to a balance account.
- Acknowledge the webhooks. We send these webhooks for every split item in the payment.
- In the payload of the balancePlatform.transfer.updated webhook, note that:
- The
eventarray includes all previous transfer events. - The
sequenceNumberdefines the number of webhooks sent for the transfer, including the current one.
- The
Examples
Your server receives webhooks for each split of the refund amount. The following examples are based on a refund request with split instructions use case.