Not refunding point-of-sale payments often?
You can also issue refunds from your Customer Area.
If you want to return funds to a shopper after a payment has been Approved, you need to refund the payment.
We offer two types of refund:
- Referenced refund: A
ReversalRequest
. Referenced refunds are connected to the original payment, using a unique identifier of that payment. - Unreferenced refund: A
PaymentRequest
withPaymentType
Refund. Unreferenced refunds need to be manually reconciled, and let you return any amount to any card presented in-store. You can use this for example to issue a refund to someone who did not make the original payment, such as a gift recipient.
You may want to support one type of refund, or both.
Referenced refunds offer the following advantages over unreferenced refunds:
- Simpler reconciliation: A refund can be matched against a payment, using the
pspReference
. - Lower risk of return fraud: A payment can't be refunded multiple times, or for an amount exceeding 100% of the payment value.
- Support for non-card payment methods, such as Alipay and WeChat Pay.
- Cross-channel refunds: Refunds can be issued in-store, from your call center, or your fulfillment center.
When a refund has been processed, it is deducted from your in-process funds, and will appear in your shopper's account within a few days.
-
Refunding BankAxept payments requires an additional contract with the BankAxept scheme and is only possible in stores where the shopper has made a purchase.
-
The following card schemes only support unreferenced refunds.
- Dankort
- Interac
Referenced refunds
When you make a point of sale payment, we connect the shopper's payment details to the payment. In the response we return the transaction identifier of the payment in the format tenderReference.pspReference
. To make a referenced refund, you specify this transaction identifier in your refund request. This allows us to validate the refund against the original payment, to ensure that it hasn't already been refunded.
For a full refund using the same terminal that was used in the original payment, it is sufficient to specify the tenderReference
, because all information is available in that terminal. In all other cases the .pspReference
is required.
Depending on what the shopper is returning, you can either make a:
- Full refund: Returns the total value of the purchase to the shopper.
- Partial refund: Returns part of the purchase to the shopper. For example, when a shopper wishes to return one of the items they purchased.
You can also make multiple partial refunds. These can be useful when, for example, a shopper is returning several items at different times.
To see what your options are if you want to make a refund when your internet connection is down, refer to Refunding an offline payment.
Make a referenced refund
Full and partial refund requests are very similar, but there are a few additional parameters for a partial refund.
-
Make a POST request to a Terminal API endpoint, to initiate a referenced refund for the full amount or a partial amount. Select a tab to see the parameters that you need to specify.
-
MessageHeader
: The standardSaleToPOIRequest.MessageHeader
object. This includes:Parameter Required Description MessageClass
Service MessageCategory
Reversal
-
ReversalRequest
: The request body with:Parameter Required Description OriginalPOITransaction.POITransactionID
An object with: TransactionID
: Transaction identifier of the original payment that you want to refund. See the allowed formats.TimeStamp
: Date and time of the original transaction in UTC format.
ReversalReason
MerchantCancel To specify the original
TransactionID
, use one of the following formats:
Format Example tenderReference.pspReference
7JLX001566393198001.851556019495143C Recommended format
0000000000000000.pspReference
0000000000000000.851556019495143C This has 16 zeroes instead of a tender reference.
.pspReference
.851556019495143C This works from terminal software version 1.44. Don't forget the leading dot (.).
tenderReference
7JLX001566393198001 Only for a full refund using the same terminal as the original payment.
This example shows how you would make a full refund.
{ "SaleToPOIRequest":{ "MessageHeader":{ "ProtocolVersion":"3.0", "MessageClass":"Service", "MessageCategory":"Reversal", "MessageType":"Request", "SaleID":"POSSystemID12345", "ServiceID":"0207111107", "POIID":"V400m-324688179" }, "ReversalRequest":{ "OriginalPOITransaction":{ "POITransactionID":{ "TransactionID":"7JLX001566393198001.851556019495143C", "TimeStamp":"2019-03-07T10:11:04+00:00" } }, "ReversalReason":"MerchantCancel" } } }
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.REVERSAL ); messageHeader.setMessageType( MessageType.REQUEST ); messageHeader.setSaleID(saleID); messageHeader.setServiceID(serviceID); messageHeader.setPOIID(POIID); saleToPOIRequest.setMessageHeader(messageHeader); ReversalRequest reversalRequest = new ReversalRequest(); OriginalPOITransaction originalPOITransaction = new OriginalPOITransaction(); TransactionIdentification pOITransactionID = new TransactionIdentification(); pOITransactionID.setTransactionID(transactionID); pOITransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); originalPOITransaction.setPOITransactionID(pOITransactionID); reversalRequest.setOriginalPOITransaction(originalPOITransaction); reversalRequest.setReversalReason( ReversalReasonType.MERCHANT_CANCEL ); saleToPOIRequest.setReversalRequest(reversalRequest); terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest);
-
MessageHeader
: The standardSaleToPOIRequest.MessageHeader
object. This includes:Parameter Required Description MessageClass
Service MessageCategory
Reversal
-
ReversalRequest
: The request body with:Parameter Required Description OriginalPOITransaction.POITransactionID
An object with: TransactionID
: Transaction identifier of the original payment that you want to refund. See the allowed formats.TimeStamp
: Date and time of the original transaction in UTC format.
ReversalReason
MerchantCancel ReversedAmount
The amount being returned to the shopper in the partial refund. SaleData.SaleToAcquirerData
The currency of the refund, in the format currency=EUR. This must match the currency used to make the original payment. SaleData.SaleTransactionID
An object with: TransactionID
: Your unique reference for the partial refund. In your Customer Area and Adyen reports, this will show as the merchant reference for the transaction.TimeStamp
: Date and time of the partial refund in UTC format.
To specify the original
TransactionID
, use one of the following formats:
Format Example tenderReference.pspReference
7JLX001566393198001.851556019495143C Recommended
0000000000000000.pspReference
0000000000000000.851556019495143C This has 16 zeroes instead of a tender reference.
.pspReference
.851556019495143C This works from terminal software version 1.44. Don't forget the leading dot (.).
tenderReference
7JLX001566393198001 Only for a full refund using the same terminal as the original payment.
This example shows how you would make a partial refund of 6.00 EUR.
{ "SaleToPOIRequest":{ "MessageHeader":{ "ProtocolVersion":"3.0", "MessageClass":"Service", "MessageCategory":"Reversal", "MessageType":"Request", "SaleID":"POSSystemID12345", "ServiceID":"0207111108", "POIID":"V400m-324688179" }, "ReversalRequest":{ "OriginalPOITransaction":{ "POITransactionID":{ "TransactionID":"pZtU001251034513719.881566567357804C", "TimeStamp":"2019-08-12T15:40:03+00:00" } }, "ReversalReason":"MerchantCancel", "ReversedAmount":6.00, "SaleData":{ "SaleToAcquirerData":"currency=EUR", "SaleTransactionID":{ "TimeStamp":"2019-08-15T12:00:00+00:00", "TransactionID":"27911" } } } } }
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.REVERSAL ); messageHeader.setMessageType( MessageType.REQUEST ); messageHeader.setSaleID(saleID); messageHeader.setServiceID(serviceID); messageHeader.setPOIID(POIID); saleToPOIRequest.setMessageHeader(messageHeader); ReversalRequest reversalRequest = new ReversalRequest(); OriginalPOITransaction originalPOITransaction = new OriginalPOITransaction(); TransactionIdentification pOITransactionID = new TransactionIdentification(); pOITransactionID.setTransactionID(transactionID); pOITransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); originalPOITransaction.setPOITransactionID(pOITransactionID); reversalRequest.setOriginalPOITransaction(originalPOITransaction); reversalRequest.setReversalReason( ReversalReasonType.MERCHANT_CANCEL ); reversalRequest.setReversedAmount(6.0); SaleData saleData = new SaleData(); saleData.setSaleToAcquirerData("currency=EUR"); TransactionIdentification saleTransactionID = new TransactionIdentification(); saleTransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); saleTransactionID.setTransactionID(transactionID); saleData.setSaleTransactionID(saleTransactionID); reversalRequest.setSaleData(saleData); saleToPOIRequest.setReversalRequest(reversalRequest); terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest);
-
-
When your full or partial refund request is processed, check the result.
-
If successful, the
ReversalResponse
contains:POIData.POITransactionID.TransactionID
: ThePSP reference
for this refund request.Response.Result
: Success
-
If unsuccessful, the
ReversalResponse
contains:Response.Result
: FailureAdditionalResponse
: Contains amessage
explaining why the request failed.
{ "SaleToPOIResponse":{ "ReversalResponse":{ "Response":{ "Result":"Failure", "AdditionalResponse":"tid=24687685&...&message=Original%20pspReference%20required%20for%20this%20operation&posAmountCashbackValue=0&posEntryMode=UNKNOWN&posAuthAmountValue=0", "ErrorCondition":"NotAllowed" } }, ... } } }
-
For a complete list of fields you can pass and receive for a referenced refund, see the ReversalRequest API reference and the ReversalResponse API reference.
We attempt to issue the refund asynchronously, and inform you whether this is successful with a webhook notification. If successful, the refund is issued to the shopper's account.
Unreferenced refunds
If you are using unreferenced refunds, we highly recommend that your cash register system is able to reconcile a refund against an original purchase. This reduces your risk of return fraud (a payment being refunded multiple times), and human error (store staff enter the wrong refund amount).
It's not possible to make an unreferenced refund to a QR code payment method, such as Alipay and WeChat Pay. To refund these payment methods you must make a referenced refund.
Before you begin
Before you can make unreferenced refunds, some configuration is needed.
-
Contact our POS Support Team team and:
- Ask them to enable unreferenced refunds for your merchant account.
-
Optionally ask them to set a refund delay so you have time to cancel an unreferenced refund when necessary.
Make an unreferenced refund
For an unreferenced refund to a gift card, you need to specify more parameters than we describe here. Refer to Make a refund to a gift card.
-
Make a POST request to a Terminal API endpoint, specifying:
-
MessageHeader
: The standardSaleToPOIRequest.MessageHeader
object. This includes:Parameter Required Description MessageClass
Service MessageCategory
Payment
-
PaymentRequest
: The request body with:Parameter Required Description SaleData.SaleTransactionID
An object with: TransactionID
: Your unique reference for the refund. In your Customer Area and Adyen reports, this will show as the merchant reference for the transaction.TimeStamp
: Date and time of the refund in UTC format.
PaymentTransaction.AmountsReq
An object with: Currency
: The transaction currency.RequestedAmount
: The amount being refunded to the card.
PaymentData.PaymentType
Refund
The example below shows how you would refund 10.99 EUR to a card.
{ "SaleToPOIRequest":{ "MessageHeader":{ "ProtocolVersion":"3.0", "MessageClass":"Service", "MessageCategory":"Payment", "MessageType":"Request", "SaleID":"POSSystemID12345", "ServiceID":"12392", "POIID":"V400m-324688179" }, "PaymentRequest":{ "SaleData":{ "SaleTransactionID":{ "TimeStamp":"2019-04-23T12:15:01+00:00", "TransactionID":"YOUR_REFUND_REFERENCE" } }, "PaymentTransaction":{ "AmountsReq":{ "Currency":"EUR", "RequestedAmount":10.99 } }, "PaymentData":{ "PaymentType":"Refund" } } } }
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(); TransactionIdentification saleTransactionID = new TransactionIdentification(); saleTransactionID.setTimeStamp(DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar())); saleTransactionID.setTransactionID(transactionID); saleData.setSaleTransactionID(saleTransactionID); paymentRequest.setSaleData(saleData); PaymentTransaction paymentTransaction = new PaymentTransaction(); AmountsReq amountsReq = new AmountsReq(); amountsReq.setCurrency("EUR"); amountsReq.setRequestedAmount( BigDecimal.valueOf(10.99) ); paymentTransaction.setAmountsReq(amountsReq); paymentRequest.setPaymentTransaction(paymentTransaction); PaymentData paymentData = new PaymentData(); paymentData.setPaymentType( PaymentType.REFUND ); paymentRequest.setPaymentData(paymentData); saleToPOIRequest.setPaymentRequest(paymentRequest); terminalAPIRequest.setSaleToPOIRequest(saleToPOIRequest);
The refund request is routed to the terminal.
-
-
Present the card to the terminal, and follow the instructions on the terminal screen.
If your refund request is successfully received:
- Approved is displayed on the terminal screen.
- You receive a JSON response with:
Result
: SuccessPOIData.POITransactionID.TransactionID
: The transaction identifier for this refund request.
{ "SaleToPOIResponse": { "PaymentResponse": { "POIData": { "POITransactionID": { "TimeStamp": "2019-06-03T15:49:36.000Z", "TransactionID": "4rVu001559576976000.881559576981487A" }, ... }, ... "Response": { "Result": "Success", ... } }, ... } }
For a complete list of fields you can pass and receive for an unreferenced refund, see the PaymentRequest API reference and the PaymentResponse API reference.
We attempt to issue the refund asynchronously, and inform you whether this is successful with a webhook notification. If successful, the refund is issued to the card presented to the terminal.
Refund notifications
When we receive your refund request, we send you a webhook notification to inform you whether this request was processed. This notification contains:
eventCode
: REFUNDpspReference
: Matches theTransactionID
of the refund request.-
success
: Indicates whether the refund request was processed:- true: We processed your refund request. This usually results in the refund being issued.
- false: We did not process your refund request. This means you do not have sufficient in-process funds to issue the refund. You should increase your in-process funds, and request the refund again.
In rare cases, after your refund request is processed you can receive a notification indicating that this refund:
An overview of failed and reversed refunds is available in your Payment accounting report.
Refund failed
Although rare, a refund can fail after we have sent it for processing to the card scheme or third-party processor. When a refund fails, you receive a notification webhook with:
eventCode
: REFUND_FAILEDoriginalReference
: ThepspReference
of the payment you are refunding.pspReference
: Our unique identifier for the refund that failed.
Most of the time Adyen can fix the issue, so that the shopper will eventually receive the funds. Sometimes, however, you need to take action yourself. To learn why a refund can fail and what, if anything, you need to do in each case, refer to Failed captures and refunds.
Refunded reversed
If we try and refund a payment, and the shopper's account is no longer valid (for example, they have closed their account), we send you a notification that contains:
eventCode
: REFUNDED_REVERSEDoriginalReference
: ThepspReference
of the failed refund request.success
: true
This means that the funds have been returned to Adyen, and are back in your merchant account.