Refund Abuse Integration Guide

What is refund abuse?

Refund abuse occurs when a customer uses your returns policy so much that it becomes unprofitable.

One of the most common types of refund fraud is when a customer receives an order, but claims it was never delivered and requests a refund. Determined refund fraudsters can even make a business out of exploiting returns policies.

Ravelin can identify behaviour that looks like refund abuse and provide recommendations on whether you should issue refunds.

How to integrate to prevent refund abuse

Request a refund recommendation

If you want a recommendation on whether to issue a refund, send the details of the refund to the Refund API Endpoint, with the query parameter score=refundRequest added to the URL.

For example, the URL should be: https://api.ravelin.com/v2/refund?score=refundRequest

refund.status must be set to OPEN

If the refund is for specific items, send these in the refund.items array. refundReason must be provided for each item. If the refund is not for specific items, set the refund.refundReason value instead.

We strongly recommend sending all fields marked as important, however the more fields you can populate the better our recommendation.

An example request showing all the refund fields we recommend is shown below:

{
    "timestamp": 1512828988826,
    "customer": {
        "customerId":"abc-123-ZYZ"
    },
    "order": {
      "orderId": "abcde12345-ZXY"
    },
    "refund":{
        "refundId": "abc-123-GDFS",
        "refundRequestTime": 1636629706000,
        "status": "OPEN",
        "initiatedBy": "CUSTOMER",
        "type": "REFUND",
        "claim": "FULL",
        "policy": "TERMS_AND_CONDITIONS",
        "amount": 1000,
        "currency": "GBP",
        "nonRefundableAmount": 250,
        "issuedTo": "ORIGINAL_PAYMENT_METHOD",
        "items": [
          {
            "name": "Midi Dress",
            "quantity": 1,
            "price": 1000,
            "currency": "GBP",
            "sku": "1234AAB",
            "category": "Clothing",
            "subcategory": "Dresses",
            "brand": "Trendy Threads",
            "refundReason": "ITEM_NOT_RECEIVED"
          }
        ]
      },
    "device": {
      "deviceId": "df020f51-5ebb-4901-82cf-96299225754b"
    }
  }

Act on the refund recommendation

The data.action field in the response will contain Ravelin’s recommendation.

You should either allow or prevent the refund depending on the data.action value.

The possible values are:

  • ALLOW - Issue the refund to the customer
  • PREVENT - Do not issue the refund to the customer

It is also possible to configure additional action values, such as MANUAL_REVIEW, which can be used to mark suspicious customers for investigation by your fraud team. We will work with you directly if there are additional action values you want to implement.

The data.source field in the response will tell you what generated the refund recommendation. This can be used for further investigation and analysis.

An example response is shown below.

{
    "status": 200,
    "timestamp": 1637933719,
    "data": {
        "customerId": "abc-123-ZYZ",
        "action": "PREVENT",
        "source": "RULE",
        "rules": {
            "triggered": [
              {
                "triggered": true,
                "description": "Customer is a serial complainer",
                "state": "active",
                "action": "PREVENT"
              }
            ]
          }
    }
}

Send the outcome of refund requests

You should send us the details and outcome of all refund requests, regardless of whether you requested a refund recommendation.

This allows us to learn the behaviour of your customers and make better refund recommendations.

Send the refund details to the Refund API Endpoint. Do not add the score=refundRequest query parameter.

An example request is shown below.

{
    "timestamp": 1512828988826,
    "customer": {
        "customerId":"abc-123-ZYZ"
    },
    "order": {
      "orderId": "abcde12345-ZXY",
      "status": {
        "stage": "refunded"
      }
    },
    "refund": {
        "refundId": "abc-123-GDFS",
        "refundRequestTime": 1636629706000,
        "refundIssuedTime": 1636629706000,
        "status": "COMPLETED",
        "initiatedBy": "CUSTOMER",
        "type": "REFUND",
        "claim": "FULL",
        "policy": "TERMS_AND_CONDITIONS",
        "amount": 7000,
        "currency": "GBP",
        "issuedTo": "ORIGINAL_PAYMENT_METHOD",
        "items": [
          {
            "name": "Heart Gold Necklace",
            "quantity": 1,
            "price": 7000,
            "currency": "GBP",
            "sku": "345682KGP",
            "category": "Accessories",
            "subcategory": "Jewellery",
            "brand": "Trendy Threads",
            "refundReason": "CUSTOMER_INCORRECT_ITEM"
          }]
      },
    "transactions":[{
        "transactionId": "pi_3Jd",
        "paymentMethodId": "card_1Ijim5Bvd0CnXcVWpZz66Idb",
        "gateway": "stripe",
        "gatewayReference": "65645tfghdhgd",
        "amount": 7000,
        "currency": "GBP",
        "type": "refund",
        "success": true
      }]
  }

If you previously requested a refund recommendation you do not need to send all the details of the refund again. You can just send refund.refundId and the fields describing the outcome of the refund request.

If you issued the refund, set the following fields:

  • refund.status: "COMPLETED"
  • refund.refundIssuedTime
  • order.status.stage: refunded
  • transaction.type: refund

An example request for an issued refund is shown below.

{
    "timestamp": 1512828988826,
    "customer": {
        "customerId":"abc-123-ZYZ"
    },
    "order": {
      "orderId": "abcde12345-ZXY",
      "status": {
        "stage": "refunded"
      }
    },
    "refund": {
        "refundId": "abc-123-GDFS",
        "refundIssuedTime": 1512828988826,
        "status": "COMPLETED"
    },
    "transactions":[{
        "transactionId": "pi_3Jd",
        "paymentMethodId": "card_1Ijim5Bvd0CnXcVWpZz66Idb",
        "gateway": "stripe",
        "gatewayReference": "65645tfghdhgd",
        "amount": 700,
        "currency": "GBP",
        "type": "refund",
        "success": true
    }]
  }

If you did not issue the refund, set the following fields:

  • refund.status: "DECLINED"
  • refund.declineReason

An example request for a declined refund is shown below.

{
    "timestamp": 1512828988826,
    "customer": {
        "customerId":"abc-123-ZYZ"
    },
    "order": {
      "orderId": "abcde12345-ZXY"
    },
    "refund": {
        "refundId": "abc-123-ZYZ",
        "status": "DECLINED",
        "declineReason": "RAVELIN"
    }
}

Testing Your Integration

After completing integration you should test that you correctly handle our refund recommendation responses.

To help with this we support special email addresses which will return a specific refund recommendation response.

These special email addresses only work when using the API key for your sandbox account.

  • Switch to your sandbox account from the account menu in the Ravelin dashboard.
  • Your sandbox secret API key can be accessed in the API Keys tab of the Developer section of the dashboard.
  • For more details on your API keys see Authentication.

The special email addresses which will force specific refund recommendation responses are shown below:

Customer Email Action Response
qa-force-refund-allow@ravelin.com ALLOW
qa-force-refund-prevent@ravelin.com PREVENT
qa-force-refund-manualreview@ravelin.com MANUAL_REVIEW
qa-force-refund-review@ravelin.com REVIEW

These email addresses should be used in the customer.email field.

An example request which will force an ALLOW action to be returned is shown below:

{
    "timestamp": 1512828988826,
    "customer": {
        "customerId":"abc-123-ZYZ",
        "email":"qa-force-refund-allow@ravelin.com"
    },
    "order": {
      "orderId": "abcde12345-ZXY"
    },
    "refund":{
        "refundId": "abc-123-GDFS",
        "refundRequestTime": 1636629706000,
        "status": "OPEN",
        "initiatedBy": "CUSTOMER",
        "type": "REFUND",
        "claim": "FULL",
        "policy": "TERMS_AND_CONDITIONS",
        "amount": 1000,
        "currency": "GBP",
        "nonRefundableAmount": 250,
        "issuedTo": "ORIGINAL_PAYMENT_METHOD",
        "items": [
          {
            "name": "Midi Dress",
            "quantity": 1,
            "price": 1000,
            "currency": "GBP",
            "sku": "1234AAB",
            "category": "Clothing",
            "subcategory": "Dresses",
            "brand": "Trendy Threads",
            "refundReason": "ITEM_NOT_RECEIVED"
          }
        ]
      },
    "device": {
      "deviceId": "df020f51-5ebb-4901-82cf-96299225754b"
    }
  }

An example testing response containing ALLOW in the data.action field is shown below:

{
    "status": 200,
    "timestamp": 1637933719,
    "data": {
        "customerId": "abc-123-ZYZ",
        "action": "ALLOW",
        "source": "RULE"
    }
}