Issuing refunds

Learn how to use the Refunds API to issue full and partial refunds

The Refunds API allows you to reimburse customers for product costs, shipping expenses, taxes, duties, and regulatory fees.

Once you configure and create a refund, your integration should be set up to handle refund state changes.

To tailor a refund so that customers are only reimbursed for specific, non-product related expenses, you must specify type.

Refund preconditions

Before submitting a POST/refunds request, make sure you have:

Order fulfilled

The Refunds API should only be used once an order is partially or completely fulfilled.

This is due to the fact that refunds can only be created on order's with charge captures[] in a state of complete. To be notified of this state change event, you can subscribe to order.charge.capture.complete.

If you'd like to cancel a charge before it's captured, use the Fulfillments API. For details, refer to Capturing and cancelling payment charges.

Once a capture is complete, Digital River increases availableToRefundAmount.

Order
{
    "id": "219965900336",
    ...
    "items": [
        {
            "id": "145174060336",
            ...
            "availableToRefundAmount": 27.01,
            ...
        }
    ],
    "payment": {
        "charges": [
            {
                "id": "07f84c7b-6412-4f75-b0fa-562958ad0b76",
                ...
                "captures": [
                    {
                        "id": "d9202f29-6ec9-40d7-a6bc-73510b4e030b",
                        ...
                        "amount": 27.01,
                        "state": "complete"
                    }
                ],
                ...
            }
        ],
        ...
   "availableToRefundAmount": 27.01
   ...
}

Available to refund amount

Prior to submitting a POST/refunds, you can make a GET/orders/{id}to determine whether the amount you're requesting is less than or equal to what's available.

A 200 OK response contains an availableToRefundAmount at both the order-level and the item-level.

The value of availableToRefundAmount reflects both pending and completed refunds.

Order
{
    "id": "219496900336",
    ...
    "items": [
        {
            "id": "144659950336",
            ...
            "availableToRefundAmount": 15.0,
            ...
        },
        {
            "id": "144659940336",
            ...
            "availableToRefundAmount": 27.01,
            ...
        }
    ],
    ...
    "availableToRefundAmount": 42.01,
    ...
}

At a high level, Digital River calculates these values by using the following formula:

availableToRefundAmount = charge(s)amount captured − (completed refunds amount + pending refunds amount)

If you submit a POST/refunds whose amount is greater than the order's availableToRefundAmount, or whose items[].amount is greater than that items[].availableToRefundAmount, then a 400 Bad Request is thrown:

Error
{
    "type": "bad_request",
    "errors": [
        {
            "code": "invalid_parameter",
            "parameter": "amountRequested",
            "message": "The requested refund amount is greater than the available amount."
        }
    ]
}

Available to refund amount: order-level

At the order-level, availableToRefundAmount reflects the unrefunded portion of an order's capturedAmount. This availableToRefundAmount may include product prices and any expenses related to shipping, duties, fees, and assessed taxes.

Order
{
    "id": "235507650336",
    ...
    "capturedAmount": 27.01,
    ...
    "availableToRefundAmount": 13.5,
    ...
}

Available to refund amount: item-level

In orders, items[].availableToRefundAmount reflects the unrefunded portion of that line item's captured amount plus its tax.amount. It doesn't include expenses related to shipping, duties, or fees.

If you'd like to refund shipping costs associated with an order's individual items[], then you can determine what customers paid by accessing items[].shipping.amount and items[].shipping.taxAmount.

In the following example, note that each line item's availableToRefundAmount excludes its shipping.amount and shipping.taxAmount.

Order
{
    "id": "237455490336",
    ...
    "items": [
        {
            ...
            "amount": 10.0,
            ...
            "tax": {
                ...
                "amount": 0.8
            },
            ...
            "availableToRefundAmount": 10.8,
            ...
            "shipping": {
                "amount": 2.5,
                "taxAmount": 0.2
            }
        },
        {
            ...
            "amount": 10.0,
            ...
            "tax": {
               ...
                "amount": 0.8
            },
            ...
            "availableToRefundAmount": 10.8,
            ...
            "shipping": {
                "amount": 2.5,
                "taxAmount": 0.2
            }
        }
    ],
    ...
}

Currency

A POST/refunds requires currency. After you submit this request, Digital River doesn't perform a currency conversion. As a result, the value you pass must be the same as the associated order's currency. If they're different, a 400 Bad Request is thrown:

Error
{
    "type": "bad_request",
    "errors": [
        {
            "code": "invalid_parameter",
            "parameter": "currency",
            "message": "Currency EUR is not supported."
        }
    ]
}

Configuring and creating refunds

Prior to submitting a POST/refunds request, specific preconditions must exist. Once satisfied, you can request a variety of refunds. The following sections provide information on how to reimburse:

Product refunds

You can issue product refunds at the order level and the line item level.

Order level product refunds

If you'd like to refund an order's product costs, then ensure that the body of your POST/refunds:

  • Omits type

  • Either (1) sets percent to a value in the range of 0.01 to 100.00 inclusive or (2) sets amount to a value that's less than or equal to the order's availableToRefundAmount

curl --location --request POST 'https://api.digitalriver.com/refunds' \
...
--data-raw '{
    "orderId": "219974470336",
    "currency": "USD",
    "percent": 100
}'

If you set percent to 100 or amount to the order's availableToRefundAmount, then Digital River attempts to reimburse the full availableToRefundAmount.

If you pass any lower percent or amount, then an order's product costs, shipping expenses, duties, and taxes are proportionally refunded.

Product refund requests do not reimburse regulatory fees. To do that, you must set type to fees in a separate POST/refunds

curl --location --request POST 'https://api.digitalriver.com/refunds' \
...
--data-raw '{
    "orderId": "219975310336",
    "currency": "USD",
    "percent": 50
}'

Line-item level product refunds

If you'd like to refund an items[] product costs, then ensure that the body of your POST/refunds:

  • Omits items[].type

  • Specifies an items[].quantity that is less than or equal to the corresponding items[].quantity in the order

  • Either (1) sets items[].percent to a value in the range of 0.01 to 100.00, inclusive or (2) sets items[].amount to a value that's less than or equal to that items[].availableToRefundAmount.

If you configure the request this way, Digital River will attempt to refund the specified quantity of items[] by the given percent or amount.

curl --location --request POST 'https://api.digitalriver.com/refunds' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_test_ea6f3e97c3f94b33b58e39d1c013f364' \
--header 'Cookie: incap_ses_1326_1638494=K9W9dx/1/jBJpcH6UeZmEjJ1PGIAAAAAX3ucdBSpIP2TivcLZF6BfQ==; incap_ses_6522_1638494=KObzY36wLi46+CuXdsyCWgXRPGIAAAAAMZkNGsosFQyIXLdAZCcVCQ==; nlbi_1638494_1914372=0JbidDMPHxouUSQviXZ5LAAAAABdyRk6JGyNhyte+5r6KG2E; nlbi_1638494_2412637=aTFZdFXsLCM1uYBtiXZ5LAAAAAB+sX1j1Jq8H0j4gBwzktpi; visid_incap_1638494=S/ubAWkHTB+qbgwhFj7HgMXaSWEAAAAAQUIPAAAAAADUpxCyuVm7HqaQDmEdHhTm; visid_incap_2137235=YibR5HAMTw28J10M4p6Omyb+vWEAAAAAQUIPAAAAAADLOejzH/0oGlmJVXjAkh4Q; visid_incap_2223514=75hpreQFTEeUoJQziq2pkFOccWEAAAAAQUIPAAAAAADefDtHFYydOaHYPCytcnKS' \
--data-raw '{
    "orderId": "220821660336",
    "currency": "USD",
    "items": [
        {
            "itemId": "146111340336",
            "quantity": "2",
            "percent": 100
        },
        {
            "itemId": "146111350336",
            "quantity": "4",
            "percent": 50
        }
    ]
}'

Along with product costs, line item-level refund requests using this configuration proportionally reimburse that items[].tax.amount. They do not, however, refund any shipping, regulatory fee, importer tax, or duty amounts (along with taxes assessed on those amounts) that are associated with that items[].

To get those expenses back, you could submit a separate POST/refunds that specifies items[].type.

Refund types

If you want to only reimburse specific, non-product related costs, such as shipping, duties, regulatory fees, and taxes, your POST/refunds must specify type at either the order-level or the line item-level.

When issuing refunds on product costs, make sure you omit type in your POST/refunds request.

curl --location --request POST 'https://api.digitalriver.com/refunds' \
...
--data-raw '{
    "orderId": "222671410336",
    "currency": "USD",
    "type": "shipping",
    "percent": 75
}'

When type is shipping, fees, or duty, any taxes assessed on those particular components of the order are also refunded.

For details on how to configure a refund request when type is tax or importer_tax, refer to Refunding taxes.

Refunding taxes

In POST/refunds, by setting type to tax, you can reimburse just the tax component of an order. You can't, however, partially refund taxes using type.

The Refunds API doesn't support tax-only reimbursements at the items[] level.

If you submit a POST/refunds that specifies a type of tax, then either (1) percent must be 100 or (2) amount must be equal to the unrefunded portion of an order's totalTax. If either of these conditions are not met, then a 400 Bad Request is returned.

Error
{
    "type": "bad_request",
    "errors": [
        {
            "code": "invalid_parameter",
            "parameter": "percentRequested",
            "message": "Only full tax refunds are supported."
        }
    ]
}

When refunding taxes, we recommend you use percent instead of amount. This is because an order's totalTax doesn't always reflect how much refundable tax exists. Previous refunds may have reduced what's available.

In these cases, if you retrieve an order's totalTax, and use that value to set amount in a POST/refunds, then a 400 Bad Request is returned.

Error
{
    "type": "bad_request",
    "errors": [
        {
            "code": "invalid_parameter",
            "parameter": "amountRequested",
            "message": "The requested refund amount is greater than the available amount."
        }
    ]
}

After you successfully submit a POST/refunds with a type of tax, Digital River attempts to reclaim the unrefunded portion of an order's totalTax.

curl --location --request POST 'https://api.digitalriver.com/refunds' \
...
--data-raw '{
    "orderId": "219965900336",
    "currency": "USD",
    "type": "tax",
    "percent": 100
}'

Refunding shipping

You also have the ability to issue refunds only on shipping costs. To do this, set type to shipping and specify an amount or percent.

We recommend using percent instead of amount. This is because an order's totalShipping doesn't always reflect the actual refundable shipping costs. Previous refunds may have reduced what's available.

In these cases, if you retrieve totalShipping, and use that value to set amount in a POST/refunds, then a 400 Bad Request is returned.

Depending on the value you assign percent, passing a type of shipping fully or partially refunds both an order's shippingChoice.amount and shippingChoice.tax

curl --location --request POST 'https://api.digitalriver.com/refunds' \
...
--data-raw '{
    "orderId": "220527740336",
    "currency": "USD",
    "type": "shipping",
    "percent": 100
}'

Handling refund state changes

After submitting a refund request, we recommend that your integration be set up to listen for the following events:

Pending refunds

When a POST/refunds is successful, Digital River sets the refund's state to pending and creates a refund.pending event.

This state indicates that Digital River and the payment processor have all the information they need to initiate the refund process.

A 201 Created response code doesn't signify that the bank approved the refund. It only means that your refund request was successfully sent. To be notified of approved refunds, listen for the refund.complete event.

In refunds that have a state of pending , refundedAmount is always 0.0.

{
    "id": "07b04605-0089-4789-af46-4cefb34786ef",
    "type": "refund.pending",
    "data": {
        "object": {
            "id": "re_252eb3f4-81b2-4576-aabd-6af2df248e99",
            "amount": 13.51,
            "createdTime": "2022-03-17T16:17:54Z",
            "currency": "USD",
            "items": [],
            "orderId": "219966860336",
            "refundedAmount": 0.0,
            "state": "pending",
            "liveMode": false
        }
    },
    "liveMode": false,
    "createdTime": "2022-03-17T16:17:54.93644Z",
    "webhookIds": [
        "bbac1929-580c-4629-b648-4c096b1a104a",
        "6d7055fc-b3b6-42fb-97a8-2443386199fb"
    ],
    "digitalriverVersion": "2021-12-13"
}

You can use refund.pending to trigger a notification to customers, informing them that their refund is being processed. Ensure you also update the refund status on their order details page.

Pending information refunds

In some cases, customers must first supply their banking details before payment processors act on a refund request. This is common when the purchase was made with a wire transfer or other delayed payment method.

When the bank requests this additional information, Digital River moves the refund's state to pending_information and creates a refund.pending_information event.

For more information on how to handle this state change, refer to the Refunding asynchronous payment methods page.

Completed refunds

When a refund is successfully processed, Digital River moves its state to succeeded and creates an event with a type of refund.complete.

In the payload, amount is how much you requested be refunded and refundedAmount is the actual reimbursed amount.

{
    "id": "ebd5dcb3-7028-4a78-8efb-3d7ca918e96e",
    "type": "refund.complete",
    "data": {
        "object": {
            "id": "re_252eb3f4-81b2-4576-aabd-6af2df248e99",
            "amount": 13.51,
            "createdTime": "2022-03-17T16:17:54Z",
            "currency": "USD",
            "items": [],
            "orderId": "219966860336",
            "refundedAmount": 13.51,
            "state": "succeeded",
            "liveMode": false,
            "charges": [
                {
                    "id": "b46e2189-fd69-4819-a886-f43c89283bda",
                    "captured": true,
                    "refunded": true,
                    "refunds": [
                        {
                            "createdTime": "2022-03-17T16:22:14Z",
                            "amount": 13.51,
                            "state": "complete"
                        }
                    ],
                    "sourceId": "cca536f3-93ed-40e8-bd79-99a3dcf2edf8"
                }
            ]
        }
    },
    "liveMode": false,
    "createdTime": "2022-03-17T16:23:29.233283Z",
    "webhookIds": [
        "bbac1929-580c-4629-b648-4c096b1a104a",
        "6d7055fc-b3b6-42fb-97a8-2443386199fb"
    ],
    "digitalriverVersion": "2021-12-13"
}

You can use refund.complete to trigger a refund confirmation email to customers. The message (typically an email) should notify them that their refund request was successful and inform them how much they were reimbursed. Ensure you also update the refund status on their order details page.

Failed refunds

If your refund request fails, Digital River moves the refund's state to failed and creates a refund.failed event. In these cases, refundedAmount is 0.0.

For information on how to test refund failures, refer to Testing scenarios.

refund.failed
{
    "id": "73f6350d-bd03-48fc-a1a4-080dc538002a",
    "type": "refund.failed",
    "data": {
        "object": {
            "id": "re_9ed7d5b1-186c-492f-bcb1-a4177d9ceead",
            "amount": 27.01,
            "createdTime": "2022-03-18T13:18:40Z",
            "currency": "USD",
            "items": [],
            "orderId": "220072430336",
            "refundedAmount": 0.0,
            "state": "failed",
            "liveMode": false,
            "charges": [
                {
                    "id": "5bb706f7-fb2d-4b02-906e-f8fb4f10d34a",
                    "captured": true,
                    "refunded": true,
                    "refunds": [
                        {
                            "createdTime": "2022-03-18T13:22:36Z",
                            "amount": 27.01,
                            "state": "failed"
                        }
                    ],
                    "sourceId": "824f65eb-2270-402f-9dee-1346db324070"
                }
            ]
        }
    },
    "liveMode": false,
    "createdTime": "2022-03-18T13:23:33.253931Z",
    "webhookIds": [
        "bbac1929-580c-4629-b648-4c096b1a104a",
        "6d7055fc-b3b6-42fb-97a8-2443386199fb"
    ],
    "digitalriverVersion": "2021-12-13"
}

Some common reasons for a refund failure include:

  • A closed account

  • A frozen account due to suspected fraud

  • An expired credit card

If a refund fails, your integration can attempt to submit another POST/refunds using the same itemId(s) and/or orderId. Alternatively, you can try to manually process the refund in Digital River Dashboard.

Providing a total refunded amount

An order's refundedAmount contains the total amount of all refunds issued on an order. This is a useful value to display to customers on their order details page.

Last updated