EPaySe
Guide

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

1

Create Refund

Call POST /api/v1/refund/create with transaction ID and amount

2

Validation

System validates transaction status, refund amount, and merchant balance

3

Async Processing

Refund queued for processing at PSP (status: REFUND_INIT)

4

Webhook Notification

Webhook sent with final result: refund.succeeded or refund.failed

Create a Refund

Request Parameters

ParameterTypeRequiredDescription
transactionIdstring
Required
ULID of the original transaction
refundAmountnumber
Required
Amount to refund (min: $0.10, max: remaining refundable)
reasonstring
Optional
Reason for refund (max 255 characters)

API Request

Bash
Create Refund (cURL)
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

JSON
Success Response (200)
{
  "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):

Bash
Get Refund Details (cURL)
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
Create Refund (PHP)
<?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

StatusDescriptionFinal?
REFUND_INIT
Refund created, queued for processing at PSPNo
REFUND_SUCCESS
Full refund completed successfullyYes
REFUND_PARTIAL_SUCCESS
Partial refund completed (more refunds possible)Yes
REFUND_FAIL
Refund rejected by PSPYes

Webhook Events

Listen for refund webhook events to track the final result:

JSON
Refund Webhook Events
// 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

Amount

Minimum $0.10, maximum is the lesser of remaining refundable amount or merchant available balance. Max 2 decimal places.

Transaction

Must exist in your merchant account and have status SUCCESS. Cannot refund PENDING, FAIL, CANCEL, or EXPIRE transactions.

Balance

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 NumberResultUse Case
4000 0000 0000 5126
Success
Full refund processed successfully
4000 0000 0000 5423
Partial
Partial refund processed successfully

Related Resources

Explore the full API documentation for refund endpoint specifications.