Virtual Accounts
Refunds

Refunds

For any payment executed via Volume's system into your Virtual Account, you can return money to initial payer. We will call such payment a Refund.

Rules and constrains of Refunds:

  • Refund cannot be initiated to an arbitrary account. It will always be done to the original account money were payed from
  • Refund can be initiated only after payment is in SETTLED status
  • You can refund entire amount of the original payment or only part of it. Returning only part of original payment will be called Partial Refund
  • You can make multiple Partial Refunds for any payment
  • There can be only one active Refund for any payment. It means that you cannot start next Refund until previous one for this payment is finished
  • Sum of all Refunds for any payment cannot exceed original payment's amount

Payment reference for payments to Virtual Account

There is one important detail about payments being initiated from Volume's components in our system. For payments to non-virtual accounts merchant can specify custom payment reference in Volume's payment component. For payment to virtual accounts this is not allowed. Nature of the system executing payments to virtual account require us to ensure such payments have unique reference across all merchants. For this reason we need to control this value and assign it on our side, hence any value passed as reference in Volume's payment component for payments to Virtual Accounts will be ignored.

Initiating a Refund

You can initiate Refund in two ways:

  • from Merchant Portal
  • using backend API

Initiating Refund in Merchant Portal

In Merchant Portal, in Payments tab, once you have selected payment in SETTLED status, you can initiate Refund by clicking ... (button with three dots) on the right end of Payment line.

PaymentSettled

After pressing Refund button you'll be presented with a dialog allowing to provide Refund amount. Remember that:

  • it can be Partial Refund
  • sum of all Refunds for given Payment cannot exceed the amount of the Payment

RefundDialog

After confirming Refund you will see in Payment's status column information about the amount being refunded.

PaymentRefunding

Once Refund is finished, when you refresh the page, you will see information about refunded amount, and be able to initiate another Refund.

PaymentRefunded

Initiating Refund using backend API

You can also initiate Refund using Volume's API.

Path

{volume-host-url}/api/merchants/{merchantId}/applications/{applicationId}/refunds

Authentication

Authenticated as merchant application with application-id and application-secret passed using corresponding headers : x-application-id and x-application-secret.

Request Body

Example payload

{
    "paymentId": "{{payment-id}}",
    "amount": "3.0"
}

Success Response

Example payload

{
    "refundPaymentId": "6642a2a1-feab-4542-b80d-5fb9d4b060ce",
    "refundedPaymentId": "52d1ebd2-7804-4340-8155-098031a43cd2",
    "createdAtUtc": "2023-08-23T13:14:12.012",
    "status": "IN_PROGRESS",
    "refundRequestDetails": {
        "destination": {
            "type": "SCAN",
            "id": null,
            "iban": null,
            "accountNumber": "17113780",
            "sortCode": "040605",
            "name": "Volume",
            "address": {
                "addressLine1": "King's Charles Street",
                "postTown": "London",
                "addressLine2": null,
                "postCode": "SW1A 2AH",
                "country": "GB",
                "countrySubDivision": null
            },
            "birthdate": null,
            "emailAddress": null,
            "phoneNumber": null,
            "bic": null
        },
        "currency": "GBP",
        "amount": 3.0,
        "reference": "23082313852497KIWP",
        "paymentDate": null
    }
}

Failure Status Codes

Error codes:

Error CodeMeaning
4xxclient errors including bad request, authentication/authorisation, configuration or payment status problems
5xxserver errors

Body example:

{
    "statusCode": 422,
    "statusName": "UNPROCESSABLE_ENTITY",
    "errorMessage": "Payment cannot be refunded in status other than SETTLED, current status=AWAITING_AUTHORIZATION",
    "traceId": "3c008dc140a96dfd",
    "source": "MERCHANT"
}

Example curl

curl --location 'https://api.sandbox.volumepay.io/api/merchants/{merchant-id}/applications/{application-id}/refunds' \
--header 'x-app-name: sandbox | volume' \
--header 'x-application-id: application-id' \
--header 'x-application-secret: application-secret' \
--header 'Content-Type: application/vnd.volume.v0.7+json' \
--header 'Accept: application/vnd.volume.v0.7+json' \
--data '{
    "paymentId": "1a0bf878-11a6-4044-9470-248cf5307fbe",
    "amount": "300.0"
}'

With following values replaced by specific to your configuration:

  • merchant-id
  • application-id
  • applicaiton-secret

Refund status

You can get Refund status in two ways:

  • by checking status in Merchant Portal
  • from information delivered by Refund Webhooks

Payout status has following values:

StatusDescription
IN_PROGRESSpayment is in progress
PROCESSEDpayment is was processed (does not mean delivered)
CANCELLEDpayment was cancelled
FAILEDpayment failed
HELDpayment is held and may require contact with volume for it's further processing
RETURNEDpayment was delivered to destination account and returned

Refund status in Merchant Portal

You can refund status looking at status column of the payment being refunded.

RefundedPayment

Refund status delivered by Webhooks

Once you've configured Refund Webhook URL in Merchant Portal, in Settings->Webhooks And Callbacks section, you will be provided with a Webhook for any status change. It is important to note that Webhooks are delivered in the same way regardless of where you have initiated Refund payment : in Merchant Portal or from the API.

Delivery guarantee

Each webhook is delivered until your endpoint answers with 200 OK.

Body

Webhook calls will be delivered as PUT REST call with following payload

{
  "eventTimeUtc" : "2023-08-24T10:43:41.941648Z",
  "refundedPaymentId" : "17a82031-d8f2-48a1-8815-edff5ac1470d",
  "refundedMerchantPaymentId" : "merchant-payment-id-814853",
  "refundedPaymentAmount" : 25.00,
  "refundId" : "139c9d1d-a1b3-476f-8a7a-0709ebc44a78",
  "refundAmount" : 25.00,
  "refundCurrency" : "GBP",
  "refundReference" : "230824102620056jug",
  "refundStatus" : "PROCESSED",
  "refundStatusDescription" : "<to-be-implemented>",
  "refundWebhookDeliveryAttempt" : 0,
  "destination" : {
    "type" : "SCAN",
    "accountNumber" : "17113780",
    "sortCode" : "040605",
    "name" : "Volume",
    "address" : {
      "addressLine1" : "King's Charles Street",
      "postTown" : "London",
      "postCode" : "SW1A 2AH",
      "country" : "GB"
    }
  }
}

Important headers

Expect this call with following headers:

HeaderValue
Content-Typeapplication/json
Acceptapplication/json
Authorizationsignature of the request calculated as described here

Security

You can verify webhook integrity by checking it's signature passed via Authorization header. Mechanism is identical to signature verification if regular payment webhooks. You can find description here