Are you looking for test card numbers?

Would you like to contact support?

Default icon

Referenced refunds

Enable cross-channel returns with simple reconciliation and better fraud protection.

Customers appreciate the possibility of cross-channel returns, allowing them to return an ecommerce purchase to a physical store, or return their in-store purchase by sending it to your distribution center. With referenced refunds, you can offer cross-channel returns with a minimum of operational burden.

Simpler reconciliation

On our unified payments platform we assign a unique identifier to each transaction. This identifier is known as the PSP reference. When you issue a referenced refund, you specify the PSP reference of the original payment. In this way, you can match each refund against a payment, regardless of the sales channel. You'll have the complete audit trail of a payment including any full or partial refunds.

Better fraud protection

A referenced refund returns the funds to the payment method that was used for the original payment, not to the card the shopper is presenting in the store or as cash. This helps combat various return fraud types like returning stolen merchandise, receipt fraud, and cross-retailer returns.

Also, when using referenced refunds, payments can't be refunded multiple times, or for an amount exceeding 100% of the payment value.

Synchronize your back-end systems

A requirement for cross-channel returns is that you have access to transaction information of all sales channels. For referenced refunds, this transaction information must include the PSP reference.

Ideally you have a central database where you store information from both ecommerce and point-of-sale transactions. If you store the ecommerce and point-of-sale transaction information separately on different systems, these systems must be able read each other's data.

Processing refunds

Refunds are not processed synchronously. When you send an API request for a referenced refund, the response confirms we received the request. Then we try to process the refund asynchronously, and will inform you of the outcome through a webhook.

When we have processed a refund, the amount is deducted from your in-process funds, and appears in your shopper's account within a few days.

By default, refunds are deducted from the merchant account that processed the original payment. For example, if a sale took place on MerchantAccount_ECOM and the product is returned to MerchantAccount_POS, the funds are taken from the ECOM account.

If you want to process cross-channel returns between multiple merchant accounts, contact our Support Team to enable refunds on the company level. This will allow you to use the /payments/paymentPspReference/refunds endpoint to submit returns for any merchant account under your company account.

Refund options

To issue a full or partial referenced refund, you have these options:

  • Terminal API reversal request: Your cash register sends a Terminal API ReversalRequest to the payment terminal. The request includes the PSP reference of the original in-store or ecommerce purchase. The terminal generates a receipt, and the funds are returned to the original card or other payment method without the need for the customer to present a card to the payment terminal.

  • Server-to-server refund: You make a POST request to the /payments/{paymentPspReference}/refunds endpoint, where paymentPspReference is the PSP reference of the original in-store or ecommerce purchase.

  • Refund from your Customer Area. This is a good option if you don't issue refunds often. We describe this in the Account section of our documentation.

Terminal API reversal request

When you make an in-store payment, the Terminal API response returns 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.

However, the tenderReference is generated by the payment terminal, and is missing for an ecommerce payment. In that case you include only the PSP reference, in the format .pspReference.

You can make a:

  • Full refund to return the total value of the purchase to the shopper.
  • Partial refund to return part of the purchase to the shopper. For example, when a shopper returns one of the items they purchased. You can also make multiple partial refunds. For example, when a shopper returns several items at different times.

Select a tab to see the parameters that you need to specify for a full referenced refund, or a partial referenced refund.

Here we describe the basic referenced refund request for the full amount. To refund an ecommerce payment, the request is a little different.

  1. Get the TransactionID and the TimeStamp of the original payment. You received these values in the payment response, in the POIData.POITransactionID object.

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

    • MessageHeader: the standard SaleToPOIRequest.MessageHeader object. This includes:
      Parameter Required Description
      ProtocolVersion -white_check_mark- 3.0
      MessageClass -white_check_mark- Service
      MessageCategory -white_check_mark- Reversal
      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 cash register.
      POIID -white_check_mark- The unique ID of the terminal that you want to send this request to. Format: [device model]-[serial number].
    • ReversalRequest: the request body. Specify:
      Parameter Required Description
      OriginalPOITransaction.POITransactionID -white_check_mark- An object with:
      • TransactionID: Transaction identifier of the original payment in the format tenderReference.pspReference. For example, BV0q001643892070000.VK9DRSLLRCQ2WN82
      • TimeStamp: Date and time in UTC format of the original payment. For example, 2000-01-01T00:00:00.000Z
      ReversalReason -white_check_mark- MerchantCancel

    The next example shows how to make a full referenced refund.

    {
        "SaleToPOIRequest":{
            "MessageHeader":{
                "ProtocolVersion":"3.0",
                "MessageClass":"Service",
                "MessageCategory":"Reversal",
                "MessageType":"Request",
                "SaleID":"POSSystemID12345",
                "ServiceID":"0207111107",
                "POIID":"V400m-324688179"
            },
            "ReversalRequest":{
                "OriginalPOITransaction":{
                    "POITransactionID":{
                        "TransactionID":"BV0q001643892070000.VK9DRSLLRCQ2WN82",
                        "TimeStamp":"2022-01-31T12:08:45.004Z"
                    }
                },
                "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);

    The payment terminal shows a waiting screen until you receive the response.

  3. 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 authorised.
      • pspReference: the PSP reference for this refund request.
    Response when we received your full reversal request
    {
        "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": "tid=47069832&transactionType=GOODS_SERVICES&posadditionalamounts.originalAmountValue=1025&giftcardIndicator=false&posAmountGratuityValue=0&pspReference=TF995R5G6L2GWR82&store=YOUR_STOREL&iso8601TxDate=2022-02-03T15%3a04%3a13.0000000%2b0000&posOriginalAmountValue=1025&txtime=16%3a04%3a13&paymentMethod=mc&txdate=03-02-2022&merchantReference=2022-02-03%2016%3a04%3a13&posadditionalamounts.originalAmountCurrency=EUR&transactionReferenceNumber=TF995R5G6L2GWR82&posAuthAmountCurrency=EUR&posAmountCashbackValue=0&posEntryMode=CLESS_CHIP&posAuthAmountValue=1025",
                    "Result": "Success"
                }
            }
        }
    }

    If your request failed, the ReversalResponse contains:

    • Response.Result: Failure
    • AdditionalResponse: this includes a message 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.
  4. Wait for the CANCEL_OR_REFUND webhook to learn the outcome. Refunds are always processed asynchronously. If successful, the refund is issued to the shopper's account.

Refunding an ecommerce payment

An ecommerce payment does have a PSP reference, but the tender reference is missing because it is generated by the terminal. To refund an ecommerce payment, you need to know the PSP reference, the date, and the currency of the original payment.

Compared to a basic full referenced refund, note the following request parameters:
Parameter Required Description
OriginalPOITransaction.POITransactionID -white_check_mark- An object with TransactionID: the PSP reference of the original payment, in the format .pspReference. Don't forget the leading dot (.). For example: .VK9DRSLLRCQ2WN82
SaleData.SaleToAcquirerData -white_check_mark- The currency of the refund, in the format currency=ABC where ABC is the three-letter currency code. This must match the currency of the original payment.
SaleData.SaleTransactionID -white_check_mark- An object with:
  • TransactionID: Your unique reference for the refund. In your Customer Area and Adyen reports, this will show as the merchant reference.
  • TimeStamp: Date and time in UTC format of the refund.
{
  "SaleToPOIRequest":{
    "MessageHeader":{
      "ProtocolVersion":"3.0",
      "MessageClass":"Service",
      "MessageCategory":"Reversal",
      "MessageType":"Request",
      "SaleID":"POSSystemID12345",
      "ServiceID":"207111108",
      "POIID":"V400m-324688179"
    },
    "ReversalRequest":{
      "OriginalPOITransaction":{
        "POITransactionID":{
          "TransactionID":".VK9DRSLLRCQ2WN82",
          "TimeStamp":"2022-01-31T12:08:45.004Z"
        }
      },
      "ReversalReason":"MerchantCancel",
      "SaleData":{
        "SaleToAcquirerData":"currency=EUR",
        "SaleTransactionID":{
          "TimeStamp":"2022-02-03T15:04:14.004Z",
          "TransactionID":"rev-708"
        }
      }
    }
  }
}
When you receive the ReversalResponse, note that the Response.AdditionalResponse includes:
  • posOriginalAmountValue: 0 (zero)
  • posAuthAmountValue: 0 (zero)
That is because at this moment we don't know the original amount of the ecommerce payment, and thus also don't know the refund amount that we will try to get authorised.

Server-to-server refund

To return funds to your customer:

  1. From the AUTHORISATION webhook, get the pspReference of the authorisation you want to refund.
  2. Make a POST request to the /payments/paymentPspReference/refunds endpoint, where paymentPspReference is the pspReference of the authorisation you want to refund.
    In your request, include:
    Parameter Required Description
    merchantAccount -white_check_mark- The name of your merchant account that is used to process the payment.
    amount -white_check_mark- The amount that you want to refund.
    • The value must be the same or, in case of a partial refund, less than the captured amount.
    • The currency must match the currency used in the authorisation.
      reference Your reference for the refund, for example for tagging a partial refund for future reconciliation. The reference parameter is required for GrabPay refunds.
      The next example below shows how you would refund 25.00 EUR on an authorisation that has the pspReference XB7XNCQ8HXSKGK82.
      curl https://checkout-test.adyen.com/checkout/v68/payments/XB7XNCQ8HXSKGK82/refunds \
      -H 'X-API-Key: YOUR_API_KEY' \
      -H 'Content-Type: application/json' \
      -d '{
          "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
          "amount": {
              "value": 2500,
              "currency": "EUR"
          },
          "reference": "YOUR_UNIQUE_REFERENCE"
      }'
      String xApiKey = "YOUR_X-API-KEY";
      Client client = new Client(xApiKey,Environment.TEST);
      Checkout checkout = new Checkout(client);
      var paymentRefundRequest = new CreatePaymentRefundRequest();
      var amount = new Amount();
      amount.setCurrency("EUR");
      amount.setValue(2500L);
      paymentRefundRequest.setAmount(amount);
      paymentRefundRequest.setMerchantAccount("YOUR_MERCHANT_ACCOUNT");
      paymentRefundRequest.setReference("YOUR_UNIQUE_REFERENCE");
      String paymentPspReference = "XB7XNCQ8HXSKGK82";
      var response = checkout.paymentsRefunds(paymentPspReference, paymentRefundRequest);
    • When you receive the /payments/paymentPspReference/refunds response, note:
      • paymentPspReference: the PSP reference of the authorisation you want to refund.
      • pspReference: Adyen's unique reference associated with this refund request.
      {
          "merchantAccount": "YOUR_MERCHANT_ACCOUNT",
          "paymentPspReference": "XB7XNCQ8HXSKGK82",
          "pspReference" : "JDD6LKT8MBLZNN84",
          "reference": "YOUR_UNIQUE_REFERENCE",
          "status" : "received"
      }
    • Wait for the REFUND notification webhook to learn the outcome of the refund request.

    Webhooks

    Refunds are not processed synchronously. When you send a request for a referenced refund, the API response only confirms we received the request.

    We process the refund asynchronously, and inform you of the result through a notification webhook.

    To receive notifications about your refunds, you must set up notification webhooks.

    CANCEL_OR_REFUND webhook

    You receive the outcome of your Terminal API reversal request asynchronously, in a CANCEL_OR_REFUND webhook that includes:

    • eventCode: CANCEL_OR_REFUND.
    • originalReference: the PSP reference of the original payment.
    • pspReference: the pspReference from the reversal response.
    • success: indicates whether the request to reverse the payment was successful. Possible values:
      • true: your request was successful. The modification.action field shows whether the request produced a cancel or a refund. This depends on whether the original payment has already been captured.
      • false: your request failed. The notification includes a reason field with a short description of the problem. Review the reason, fix the issue if possible, and resubmit the reversal request.
    {
       "live":"false",
       "notificationItems":[
          {
             "NotificationRequestItem":{
                "additionalData":{
                   "modification.action": "refund"
                },
                "amount":{
                   "currency": "EUR",
                   "value": 1025
                },
                "eventCode":"CANCEL_OR_REFUND",
                "eventDate":"2022-02-03T15:14:15.004Z",
                "merchantAccountCode":"YOUR_MERCHANT_ACCOUNT",
                "originalReference":"VK9DRSLLRCQ2WN82",
                "paymentMethod":"mc",
                "pspReference":"TF995R5G6L2GWR82",
                "reason":"",
                "success":"true"
             }
          }
       ]
    }

    For more information about the included fields, see the CANCEL_OR_REFUND notification reference.

    REFUND webhook

    You receive the outcome of the refund request asynchronously, in a notification webhook that includes:

    • eventCode: REFUND.
    • pspReference: the pspReference from the response for your refund request.
    • success: indicates whether the refund request was successful. Possible values:
      • true: the refund request was successful. This usually means that the refund was successful. For information on exceptions, refer to REFUND_FAILED webhook, and REFUNDED_REVERSED webhook.
      • false: the refund request failed. The webhook includes a reason field with a short description of the problem. Review the reason, fix the issue if possible, and resubmit the refund request.
    {
       "live":"false",
       "notificationItems":[
          {
             "NotificationRequestItem":{
                "amount":{
                   "currency":"EUR",
                   "value":2500
                },
                "eventCode":"REFUND",
                "eventDate":"2021-11-01T00:19:34+01:00",
                "merchantAccountCode":"YOUR_MERCHANT_ACCOUNT",
                "merchantReference": "YOUR_UNIQUE_REFERENCE",
                "originalReference":"XB7XNCQ8HXSKGK82",
                "paymentMethod":"visa",
                "pspReference":"JDD6LKT8MBLZNN84",
                "reason":"",
                "success":"true"
             }
          }
       ]
    }

    For more information about the included fields, see the REFUND notification reference.

    Failed refund request

    When a refund request fails, you receive a webhook with success: false and the reason why the request failed. The next table shows the most common reason values in the webhook.

    reason Description
    Requested refund amount too high No chargeback or refund has been processed, and the requested refund amount is more than the balance on the payment.
    Already partially refunded, new requested refund amount too high Partial refund(s) has(/have) been processed, and the requested refund amount is more than the balance on the payment.
    Already partially disputed, new requested refund amount too high Partial chargeback(s) has(/have) been processed, and the requested refund amount is more than the balance on the payment.
    Already fully refunded, no balance available for new requested refund Full refund has been processed, and the remaining balance on the payment is 0.
    Partially refunded and partially disputed, no balance available for new requested refund Partial refund(s) and chargeback(s) have been processed, and the requested refund amount is more than the balance on the payment.
    Partial refund(s) and chargeback(s) have been processed, and the balance on the payment is a negative amount.
    Already fully disputed, no balance available for new requested refund Full chargeback has been processed, and the balance on the payment is 0.
    A full chargeback and partial refund(s) have been processed, and the balance on the payment is a negative amount.
    Insufficient in-process funds on account for refunding this payment There is not enough balance on your merchant account to process the refund.
    Transaction hasn't been captured, refund not possible The refund was requested before the transaction was captured. You need to cancel the transaction instead or wait until the transaction is settled.
    The maximum period for this operation has expired The refund was requested past the expiration date permitted by the payment method to process the request.

    Balance on the payment

    The balance on the payment refers to the amount that remains from the original payment. For example, if a transaction has a total of 10 EUR and no refund or chargeback is processed, then the balance on the payment is 10 EUR. After a refund or chargeback of 3 EUR is processed, the remaining balance on the payment is 7 EUR.

    See also