Refund Payments

Refunding payments automatically after a payment exception scenario occurs

In the case where a payment exception scenario has taken place, Layer1 provides the option to automatically refund the cryptocurrency for merchants who do not want to hold these cryptocurrencies, due to any compliance issues, or for merchants that do not want to internally process any payments that have fallen outside of the agreed upon payment rules.

🚧

An Auto Refund does not automatically return the funds to the sender's address. Instead, it automatically generates a payout and sends a URL via webhook. This URL should be forwarded to the end user, allowing them to provide their address and accept the refund.

How a refund works

Refunds function as an automatically generated payout request, which needs to be sent to the end user to complete. In this case, the end user will need to enter their desired wallet address that they want the funds to be sent to, and for them to confirm the payout.

Refunding payment exception scenarios

As stated on the previous pages, the three payment exception scenarios to consider are:

  1. Over payments
  2. Under payments
  3. Late payments

These three scenarios are handled separately on a Merchant ID (MID) level. Which means that each MID that is created can be set up to automatically initiate the refund process for one or more of these scenarios. Each Scenario handles the refund slightly differently, as explained below.

📘

Please contact Merchant Support or your Integration Manager to have the refund options enabled.

Refunding over payments

When an over payment has occurred, the status of the transaction is updated from PROCESSING to COMPLETE. This COMPLETE status should be seen as a standard successful payment on the merchant's side.

The original payment amount requested in the invoice is credited to the merchant's wallet as normal, but the additional amount of overpaid cryptocurrency goes into a holding account so that there is no impact on a merchant wallet balance.

This additional amount is then the total amount that will be returned back to the sender as an automatically generated payout.

Refunding under payments

When an under payment has occurred, the status of the transaction is updated from PROCESSING to UNDERPAID. This UNDERPAID status should be marked as an unsuccessful payment on the merchant's side.

The entire payment amount is put into a holding account, so that it is not credited to the merchant's wallet.

The full payment amount of the underpayment is then the total amount that will be returned back to the sender as an automatically generated payout.

Refunding late payments

In the rare occurrence that a late payment has occurred, the status of the transaction is updated from PENDING to EXPIRED. This EXPIRED status should be marked as an unsuccessful payment on the merchant's side.

The entire payment amount is put into a holding account, so that it is not credited to the merchant's wallet.

The full payment amount of the late payment is then the total amount that will be returned back to the sender as an automatically generated payout.

The refund flow

The ideal flow of an automatically generated refund is given as follows:

  1. The payment request is created for an end user by the merchant.
  2. The payment link returned from BVNK is forwarded to the end user by the merchant.
  3. The end user completes the payment by sending through funds to the given address.
  4. The total funds are either too low, too high, or received late, triggering the appropriate refund.
  5. The merchant is sent a webhook from BVNK, telling them the amount to refund and the URL that it can be claimed from.
  6. The merchant notifies the end user of the URL.
  7. The end user navigates to the URL and enters their wallet address.
  8. Once BVNK receives this address, the displayed amount of cryptocurrency is sent through, less the fees.
  9. The merchant receives a relevant webhook that updates the success of the refund payout.
  10. The process is complete.

Expiring a refund

In the event that the end user never entered their wallet address and confirms the refund within the expiry limit, the refund will become EXPIRED. The default expiry limit for a refund is set to 3 months.

When the limit is reached, the total amount of the refund will move from the held account into the merchants MID wallet.

A further webhook will be sent to the merchant to indicate that the refund has expired and the amounts have been credited to the appropriate wallet.

Refund webhooks

BVNK will automatically initiate the refund process, which in effect acts as a payout transaction.

The merchant will receive a refundInitiated event webhook. The appropriate important fields to pay attention to in this webhook are:

Webhook fieldDescription
event: refundIntiatedWill indicate that the refund has been intiated.
data.uuid: xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxThe UUID of the refund, different to the linked payment in.
data.type: OUTThe type of a refund will always be OUT
data.subType: merchantRefundThe subtype indicated it is a Merchant Refund
data.status: PENDINGThe status of a refund, can become EXPIRED or COMPLETE
data.redirectUrl: https://pay.bvnk.com/payout?uuid=The redirect URL to send to the end user so they can enter their address and claim the refund.

In the even that the refund is not claimed and has expired, a statusChanged event webhook is sent to the merchant. The important fields in this webhook are:

Webhook fieldDescription
event: statusChangedWill indicate that the refund has had its status changed.
data.uuid: xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxThe matching UUID of the refund..
data.subType: merchantRefundThe subtype indicated it is a Merchant Refund
data.status: EXPIREDThe final state of the expired payment

Webhook Examples:

{
  "source": "payment",
  "event": "refundInitiated",
  "data": {
    "uuid": "9485895b-c4a3-4885-bcdc-XXXXXXXXXX",
    "merchantDisplayName": "Merchant Name",
    "merchantId": "00a4af9d-ad4b-42d5-bec4-XXXXXXXXXX",
    "dateCreated": 1689264437804,
    "expiryDate": 1689869237804,
    "quoteExpiryDate": null,
    "acceptanceExpiryDate": null,
    "quoteStatus": "TEMPLATE",
    "reference": "REFUND-XXXXX",
    "type": "OUT",
    "subType": "merchantRefund",
    "status": "PENDING",
    "displayCurrency": {
      "currency": "EUR",
      "amount": 24.58,
      "actual": 0
    },
    "walletCurrency": {
      "currency": "ETH",
      "amount": 0.01421,
      "actual": 0
    },
    "paidCurrency": {
      "currency": null,
      "amount": 0,
      "actual": 0
    },
    "feeCurrency": {
      "currency": "EUR",
      "amount": 0.49,
      "actual": 0.49
    },
    "displayRate": null,
    "exchangeRate": null,
    "address": null,
    "returnUrl": "",
    "redirectUrl": "https://pay.sandbox.bvnk.com/payout?uuid=9485895b-c4a3-4885-bcdc-XXXXXXXXXX",
    "transactions": [],
    "refund": {
      "uuid": "309d82fd-e1d4-4354-9015-XXXXXXXXXX",
      "merchantDisplayName": "Merchant Name",
      "merchantId": "00a4af9d-ad4b-42d5-bec4-XXXXXXXXXX",
      "dateCreated": 1689264326000,
      "expiryDate": 1689265526000,
      "quoteExpiryDate": 1689265526000,
      "acceptanceExpiryDate": 1689264363000,
      "quoteStatus": "ACCEPTED",
      "reference": "reference",
      "type": "IN",
      "subType": "merchantPayIn",
      "status": "COMPLETE",
      "displayCurrency": {
        "currency": "EUR",
        "amount": 10,
        "actual": 10
      },
      "walletCurrency": {
        "currency": "EUR",
        "amount": 10,
        "actual": 10
      },
      "paidCurrency": {
        "currency": "ETH",
        "amount": 0.00579,
        "actual": 0.02
      },
      "feeCurrency": {
        "currency": "EUR",
        "amount": 0.1,
        "actual": 0.1
      },
      "displayRate": {
        "base": "ETH",
        "counter": "EUR",
        "rate": 1727.070973981676
      },
      "exchangeRate": {
        "base": "ETH",
        "counter": "EUR",
        "rate": 1727.071668
      },
      "address": {
        "address": "0x434c932fc5e324204c80c1fb15f3a7XXXXX",
        "tag": null,
        "protocol": "ETH",
        "uri": "ethereum:0x434c932fc5e324204c80c1fb15f3a7XXXXX?value=5.79015E+15",
        "alternatives": []
      },
      "returnUrl": "",
      "redirectUrl": "https://pay.sandbox.bvnk.com/payin?uuid=309d82fd-e1d4-4354-9015-XXXXXXXXXX",
      "transactions": [
        {
          "dateCreated": 1689264367000,
          "dateConfirmed": 1689264429000,
          "hash": "0x034612080ab226a5c3254b521f30dafd7df30dd1c1fc35723cf6XXXXX",
          "amount": 0.02,
          "risk": {
            "level": "LOW",
            "resourceName": "UNKNOWN",
            "resourceCategory": "UNKNOWN",
            "alerts": []
          },
          "networkFeeCurrency": "ETH",
          "networkFeeAmount": 0.0000315,
          "sources": [
            "0x4b8b57aa7a92ea959c92aa32a72bbXXXXX3"
          ],
          "displayRate": {
            "base": "ETH",
            "counter": "EUR",
            "rate": 1727.070973981676
          },
          "exchangeRate": {
            "base": "ETH",
            "counter": "EUR",
            "rate": 1727.071668
          }
        }
      ],
      "refund": null,
      "refunds": []
    },
    "refunds": []
  }
}
{
  "source": "payment",
  "event": "statusChanged",
  "data": {
    "uuid": "9485895b-c4a3-4885-bcdc-XXXXXXXXXX",
    "merchantDisplayName": "Merchant Name",
    "merchantId": "00a4af9d-ad4b-42d5-bec4-XXXXXXXXXX",
    "dateCreated": 1689264438000,
    "expiryDate": 1689264510403,
    "quoteExpiryDate": 1689275310395,
    "acceptanceExpiryDate": 1689264540395,
    "quoteStatus": "ACCEPTED",
    "reference": "REFUND-XXXXX",
    "type": "OUT",
    "subType": "merchantRefund",
    "status": "EXPIRED",
    "displayCurrency": {
      "currency": "EUR",
      "amount": 24.58,
      "actual": 0
    },
    "walletCurrency": {
      "currency": "EUR",
      "amount": 23.49968,
      "actual": 23.49968
    },
    "paidCurrency": {
      "currency": null,
      "amount": 0,
      "actual": 0
    },
    "feeCurrency": {
      "currency": "EUR",
      "amount": 0.49,
      "actual": 0.49
    },
    "displayRate": null,
    "exchangeRate": {
      "base": "ETH",
      "counter": "EUR",
      "rate": 1653.7602177875
    },
    "address": null,
    "returnUrl": "",
    "redirectUrl": "https://pay.sandbox.bvnk.com/payout?uuid=9485895b-c4a3-4885-bcdc-XXXXXXXXXX",
    "transactions": [],
    "refund": {
      "uuid": "309d82fd-e1d4-4354-9015-XXXXXXXXXX",
      "merchantDisplayName": "Metallica Inc",
      "merchantId": "00a4af9d-ad4b-42d5-bec4-XXXXXXXXXX",
      "dateCreated": 1689264326000,
      "expiryDate": 1689265526000,
      "quoteExpiryDate": null,
      "acceptanceExpiryDate": null,
      "quoteStatus": "TEMPLATE",
      "reference": "XXXXXXXXXX",
      "type": "IN",
      "subType": "merchantPayIn",
      "status": "COMPLETE",
      "displayCurrency": {
        "currency": "EUR",
        "amount": 10,
        "actual": 10
      },
      "walletCurrency": {
        "currency": "EUR",
        "amount": 10,
        "actual": 10
      },
      "paidCurrency": {
        "currency": "ETH",
        "amount": 0.00579,
        "actual": 0.02
      },
      "feeCurrency": {
        "currency": "EUR",
        "amount": 0.1,
        "actual": 0.1
      },
      "displayRate": {
        "base": "ETH",
        "counter": "EUR",
        "rate": 1727.070973981676
      },
      "exchangeRate": null,
      "address": null,
      "returnUrl": "",
      "redirectUrl": "https://pay.sandbox.bvnk.com/payin?uuid=309d82fd-e1d4-4354-9015-XXXXXXXXXX",
      "transactions": [
        {
          "dateCreated": 1689264367000,
          "dateConfirmed": 1689264429000,
          "hash": "0x034612080ab226a5c3254b521f30dafd7df30dd1c1fc35723cf638XXXXX",
          "amount": 0.02,
          "risk": {
            "level": "LOW",
            "resourceName": "UNKNOWN",
            "resourceCategory": "UNKNOWN",
            "alerts": []
          },
          "networkFeeCurrency": "ETH",
          "networkFeeAmount": 0.0000315,
          "sources": [
            "0x4b8b57aa7a92ea959c92aa32a72bb3dXXXXX7"
          ],
          "displayRate": {
            "base": "ETH",
            "counter": "EUR",
            "rate": 1727.070973981676
          },
          "exchangeRate": {
            "base": "ETH",
            "counter": "EUR",
            "rate": 1727.071668
          }
        }
      ],
      "refund": null,
      "refunds": []
    },
    "refunds": []
  }
}