Handle Refunds
Learn how to process full and partial refunds programmatically via the API or through the merchant dashboard.
Refund Types
Full Refund
Refund the entire transaction amount back to the customer. The transaction status changes to REFUND_SUCCESS.
Partial Refund
Refund a portion of the transaction. Multiple partial refunds can be issued until the full amount is refunded.
Refund Processing Flow
Create Refund
Call POST /api/v1/refund/create with transaction ID and amount
Validation
System validates transaction status, refund amount, and merchant balance
Async Processing
Refund queued for processing at PSP (status: REFUND_INIT)
Webhook Notification
Webhook sent with final result: refund.succeeded or refund.failed
Refunds are Asynchronous
processing. This does not mean the refund is complete. Always listen for webhook events to confirm the final result. Create a Refund
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
transactionId | string | Required | ULID of the original transaction |
refundAmount | number | Required | Amount to refund (min: $0.10, max: remaining refundable) |
reason | string | Optional | Reason for refund (max 255 characters) |
API Request
curl -X POST https://api.epayse.com/api/v1/refund/create \
-H "Content-Type: application/json" \
-H "X-Api-Key-Id: your_api_key" \
-H "X-Signature: generated_hmac_signature" \
-H "X-Timestamp: 1700000000" \
-H "X-Nonce: unique_nonce_value" \
-d '{
"transactionId": "01kbkzs3pdcdkjvsq2xb2j0ej3",
"refundAmount": 50.00,
"reason": "Customer request"
}'Response
{
"status": "SUCCESS",
"message": "Refund created successfully",
"data": {
"id": "01kbm1234abcd5678efgh9012",
"transaction_id": "01kbkzs3pdcdkjvsq2xb2j0ej3",
"amount": 50.00,
"currency": "USD",
"status": "processing",
"reason": "Customer request",
"created_at": "2024-01-15T10:30:00Z",
"provider_fee_amount": 2.50,
"provider_fee_percentage": 5.0,
"total_provider_fee": 2.50,
"internal_fee_amount": 1.00,
"internal_fee_percentage": 2.0,
"total_internal_fee": 1.00,
"total_fee": 3.50,
"completed_at": null,
"provider_created_at": "2024-01-15T10:30:00Z"
}
}Check Refund Status
Poll the refund status using the GET endpoint (prefer webhooks for real-time updates):
curl -X GET https://api.epayse.com/api/v1/refund/01kbm1234abcd5678efgh9012 \
-H "X-Api-Key-Id: your_api_key" \
-H "X-Signature: generated_hmac_signature" \
-H "X-Timestamp: 1700000000" \
-H "X-Nonce: unique_nonce_value"PHP Integration Example
<?php
$apiKey = 'your_api_key';
$secretKey = 'your_secret_key';
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
// Full refund
$payload = json_encode([
'transactionId' => '01kbkzs3pdcdkjvsq2xb2j0ej3',
'refundAmount' => 100.00, // Full transaction amount
'reason' => 'Customer request',
]);
// Generate HMAC-SHA256 signature
$signature = hash_hmac('sha256', $timestamp . $nonce . $payload, $secretKey);
$ch = curl_init('https://api.epayse.com/api/v1/refund/create');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key-Id: ' . $apiKey,
'X-Signature: ' . $signature,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
],
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($response['status'] === 'SUCCESS') {
$refundId = $response['data']['id'];
// Store refund ID - wait for webhook to confirm completion
echo "Refund initiated: $refundId (status: processing)";
}Refund Status Lifecycle
| Status | Description | Final? |
|---|---|---|
REFUND_INIT | Refund created, queued for processing at PSP | No |
REFUND_SUCCESS | Full refund completed successfully | Yes |
REFUND_PARTIAL_SUCCESS | Partial refund completed (more refunds possible) | Yes |
REFUND_FAIL | Refund rejected by PSP | Yes |
Webhook Events
Listen for refund webhook events to track the final result:
// Webhook event: refund.succeeded
{
"event": "refund.succeeded",
"timestamp": "2024-01-15T10:31:00Z",
"data": {
"id": "01kbm1234abcd5678efgh9012",
"transaction_id": "01kbkzs3pdcdkjvsq2xb2j0ej3",
"amount": 50.00,
"currency": "USD",
"status": "REFUND_SUCCESS",
"reason": "Customer request",
"completed_at": "2024-01-15T10:31:00Z"
}
}
// Webhook event: refund.failed
{
"event": "refund.failed",
"timestamp": "2024-01-15T10:31:30Z",
"data": {
"id": "01kbm1234abcd5678efgh9012",
"transaction_id": "01kbkzs3pdcdkjvsq2xb2j0ej3",
"amount": 50.00,
"currency": "USD",
"status": "REFUND_FAIL",
"reason": "Customer request",
"message": "Insufficient funds at PSP",
"completed_at": "2024-01-15T10:31:30Z"
}
}Validation Rules
Minimum $0.10, maximum is the lesser of remaining refundable amount or merchant available balance. Max 2 decimal places.
Must exist in your merchant account and have status SUCCESS. Cannot refund PENDING, FAIL, CANCEL, or EXPIRE transactions.
Your merchant account must have sufficient available balance. Refunds deduct from your balance immediately upon creation.
Testing Refunds
Use these sandbox test cards to simulate refund scenarios:
| Card Number | Result | Use Case |
|---|---|---|
4000 0000 0000 5126 | Success | Full refund processed successfully |
4000 0000 0000 5423 | Partial | Partial refund processed successfully |
Dashboard Refunds
Related Resources
Explore the full API documentation for refund endpoint specifications.
