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.
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
After confirming Refund you will see in Payment's status column information about the amount being refunded.
Once Refund is finished, when you refresh the page, you will see information about refunded amount, and be able to initiate another Refund.
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": "7fbd6514-60d1-486e-a0ab-7824766be37c",
"refundedPaymentId": "1bd54f8c-b712-4aba-b524-54780191d705",
"createdAtUtc": "2024-11-07T08:20:54.147685",
"status": "IN_PROGRESS",
"refundRequestDetails": {
"amount": 0.2,
"currency": "GBP",
"reference": "241107081253125ND9",
"destination": {
"type": "SCAN",
"name": "Volume",
"accountNumber": "12345678",
"sortCode": "123456",
"iban": null
}
}
}
Failure Status Codes
Error codes:
Error Code | Meaning |
---|---|
4xx | client errors including bad request, authentication/authorisation, configuration or payment status problems |
5xx | server 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:
Status | Description |
---|---|
IN_PROGRESS | payment is in progress |
PROCESSED | payment is was processed (does not mean delivered) |
CANCELLED | payment was cancelled |
FAILED | payment failed |
HELD | payment is held and may require contact with volume for it's further processing |
RETURNED | payment 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.
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" : "2024-11-05T10:13:43.763860Z",
"applicationId" : "62b36790-f8cd-4764-8e19-2e19ada49cb1",
"refundedPaymentId" : "43c4f70b-5528-4f97-a7a6-225760639bcc",
"refundedMerchantPaymentId" : "merchant-payment-id-328827",
"refundedPaymentAmount" : 0.20,
"refundId" : "412d42be-4ef1-495c-9b87-e241ad185582",
"refundAmount" : 0.20,
"refundCurrency" : "GBP",
"refundReference" : "24110510821242Z43Z",
"refundStatus" : "PROCESSED",
"refundStatusDescription" : "",
"refundWebhookDeliveryAttempt" : 0,
"destination" : {
"type" : "SCAN",
"accountNumber" : "12345678",
"sortCode" : "123456",
"name" : "Volume"
}
}
Important headers
Expect this call with following headers:
Header | Value |
---|---|
Content-Type | application/json |
Accept | application/json |
Authorization | signature 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