Refunds
Process refunds for completed transactions. This section covers how to create and manage refunds through the ZoPay API.
Overview
Refunds allow you to return money to customers for various reasons such as:
- Customer request
- Duplicate payment
- Service not delivered
- Wrong amount charged
- Technical error
Refund Methods
ZoPay determines the refund method automatically based on the original transaction and gateway:
- REVERSAL: Refund by reversing the original transaction through the gateway
- PAYOUT: Refund by creating a new payout to the customer (used when reversal is not available)
Refund Flow
- Identify the original transaction that needs to be refunded
- Create a refund request via
POST /api/v1/refunds - Specify refund amount (full or partial)
- Optionally provide a reason for the refund
- ZoPay processes the refund
- Webhook notification sent when refund completes
- Check refund status via
GET /api/v1/refunds/:id
Refund Statuses
- PENDING: Refund initiated, awaiting processing
- PROCESSING: Refund is being processed
- SUCCESS: Refund completed successfully
- FAILED: Refund failed (check error details)
- CANCELLED: Refund was cancelled
Create Refund
Create a refund for a completed transaction. You can refund the full amount or a partial amount.
Endpoint
1POST /api/v1/refundsRequest Body
1{
2 "transactionId": "txn-uuid",
3 "amount": "5000.00",
4 "reason": "Customer request"
5}Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
transactionId | string (UUID) | Yes | ID of the original transaction to refund |
amount | string | Yes | Refund amount as a decimal string (must not exceed original transaction amount) |
reason | string | No | Reason for refund (max 500 chars). e.g., "Customer request", "Duplicate payment" |
Response
A 201 Created response returns the refund wrapped in a refund object:
1{
2 "refund": {
3 "id": "refund-uuid",
4 "transactionId": "txn-uuid",
5 "amount": "5000.00",
6 "method": "REVERSAL",
7 "status": "PROCESSING",
8 "gatewayReference": null,
9 "reason": "Customer request",
10 "createdAt": "2024-01-15T10:00:00.000Z"
11 }
12}Example Request
1const response = await fetch('https://api.zopay.co/api/v1/refunds', {
2 method: 'POST',
3 headers: {
4 'Content-Type': 'application/json',
5 'x-zo-key': 'your-api-key',
6 'x-zo-timestamp': Math.floor(Date.now() / 1000).toString(),
7 'x-zo-nonce': crypto.randomBytes(16).toString('hex'),
8 'x-zo-origin': 'https://yourdomain.com',
9 'x-zo-signature': signature,
10 'x-zo-version': '1.0'
11 },
12 body: JSON.stringify({
13 transactionId: 'txn-uuid',
14 amount: '5000.00',
15 reason: 'Customer request'
16 })
17});
18
19const { refund } = await response.json();
20console.log('Refund ID:', refund.id);
21console.log('Status:', refund.status);Partial Refunds
You can create multiple partial refunds for a single transaction, as long as the total refunded amount does not exceed the original transaction amount.
Get Refund Status
Check the status of a refund to see if it has been completed, failed, or is still processing.
Endpoint
1GET /api/v1/refunds/:idPath Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Refund ID from the create refund response |
Response
1{
2 "refund": {
3 "id": "refund-uuid",
4 "transactionId": "txn-uuid",
5 "amount": "5000.00",
6 "method": "REVERSAL",
7 "status": "SUCCESS",
8 "gatewayReference": "MTN_REF_123456789",
9 "reason": "Customer request",
10 "createdBy": null,
11 "createdAt": "2024-01-15T10:00:00.000Z",
12 "updatedAt": "2024-01-15T10:01:00.000Z"
13 }
14}Response Fields
| Field | Type | Description |
|---|---|---|
refund.id | string | Unique refund identifier |
refund.transactionId | string | Original transaction ID |
refund.amount | string | Refund amount |
refund.method | string | Refund method (REVERSAL or PAYOUT), determined by the system |
refund.status | string | Refund status (PENDING, PROCESSING, SUCCESS, FAILED) |
refund.gatewayReference | string | null | Reference from the gateway once processed |
refund.reason | string | null | Reason for the refund |
refund.updatedAt | string | Last updated timestamp |
List Refunds
Retrieve a list of all your refunds with optional filtering and pagination.
Endpoint
1GET /api/v1/refundsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (PENDING, PROCESSING, SUCCESS, FAILED, CANCELLED) |
startDate | string | Start date filter (ISO 8601, e.g. "2024-01-01T00:00:00Z") |
endDate | string | End date filter (ISO 8601) |
page | number | Page number (default: 1) |
limit | number | Results per page (default: 50, max: 100) |
Response
1{
2 "refunds": [
3 {
4 "id": "refund-uuid",
5 "transactionId": "txn-uuid",
6 "amount": "5000.00",
7 "method": "REVERSAL",
8 "status": "SUCCESS",
9 "gatewayReference": "MTN_REF_123456789",
10 "reason": "Customer request",
11 "createdAt": "2024-01-15T10:00:00.000Z"
12 }
13 ],
14 "pagination": {
15 "page": 1,
16 "limit": 50,
17 "total": 45
18 }
19}To list refunds for a specific transaction, use GET /api/v1/transactions/:id/refunds (no pagination parameters needed).
Example Request
1// List all refunds
2const response = await fetch('https://api.zopay.co/api/v1/refunds?status=SUCCESS&page=1&limit=50', {
3 method: 'GET',
4 headers: {
5 'x-zo-key': 'your-api-key',
6 'x-zo-timestamp': Math.floor(Date.now() / 1000).toString(),
7 'x-zo-nonce': crypto.randomBytes(16).toString('hex'),
8 'x-zo-origin': 'https://yourdomain.com',
9 'x-zo-signature': signature,
10 'x-zo-version': '1.0'
11 }
12});
13
14const data = await response.json();
15console.log('Total refunds:', data.pagination.total);
16console.log('Refunds:', data.refunds);
17
18// List refunds for a specific transaction
19const txResponse = await fetch('https://api.zopay.co/api/v1/transactions/txn-uuid/refunds', {
20 method: 'GET',
21 headers: { /* auth headers */ }
22});Best Practices
- Store the refund
idfrom the create response for future status checks - REVERSAL is the preferred method and is selected automatically when available
- Provide clear reasons for refunds for audit purposes
- Set up webhooks to receive real-time refund status updates
- Keep track of partial refunds to ensure total doesn't exceed original amount
- Monitor refund success rates and handle failures appropriately