Esc

Refunds

Send all or part of a virtual account deposit back to the customer who paid it.

How refunds work

  • The refund amount is debited from your Gafiapay balance as soon as the refund is accepted. Your balance must cover it.
  • A refund starts as pending. It becomes completed once the money reaches the customer, usually within 1–3 business days.
  • If a refund ends as failed, the amount is credited back to your balance and the payment becomes refundable again.
  • Each payment can have one refund, for the full amount or part of it.
  • Once a refund is created, the original payment's status changes to refunded.
Only one refund per payment

A partial refund uses up the payment's refund. You can't refund the rest of the amount later unless the first refund fails.

StatusMeaning
pendingAccepted and being processed.
completedThe customer has been paid back. completedAt is set.
failedThe refund didn't go through. The amount is back in your balance, and failedAt is set.

Create a refund

Start a refund for a completed deposit. Identify the deposit with one of orderNo or transactionId.

POST /refund/create

POST requests need an x-signature computed over the JSON body and timestamp. See Authentication.

Body

orderNo string one of the two
The deposit's orderNo, from the payment.received webhook.
transactionId string one of the two
The deposit's transaction id, also in the payment.received webhook.
amount number required
Amount to refund in Naira, up to 2 decimal places. Must be greater than 0 and no more than the original payment.
reason string required
Why you're refunding, up to 200 characters. Included in webhooks and the refund record.

The response returns 201. Save refund.orderId: you need it to check the refund's status.

Errors

StatusWhen
400Something in the request is wrong: no deposit reference (or both sent), an invalid amount, no reason, an amount above the original deposit, or a balance too low to cover it. Also returned when the bank rejects the refund.
404No completed payment matches the identifier for your business. This includes payments that have already been refunded.
409Another refund for your business is being processed right now. Retry after a few seconds.
502Couldn't reach the bank. No money was moved, so you can safely retry.
curl -X POST 'https://api.gafiapay.com/api/v1/external/refund/create' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: your_api_key' \
  -H 'x-timestamp: 1678901234567' \
  -H 'x-signature: generated_signature' \
  -d '{
    "orderNo": "PP25101400098765",
    "amount": 1500,
    "reason": "Customer cancelled the order"
  }'
{
  "status": "success",
  "data": {
    "refund": {
      "id": "6710a1c2e4b0f5a3c1d2e3f4",
      "orderId": "REF1760500000123A1B2C3D4",
      "orderNo": "PP25101500012345",
      "amount": 1500,
      "formattedAmount": "1500.00",
      "currency": "NGN",
      "status": "pending",
      "reason": "Customer cancelled the order",
      "originalTransaction": {
        "id": "670f9b11e4b0f5a3c1d2e001",
        "orderNo": "PP25101400098765",
        "amount": 2000
      },
      "createdAt": "2026-10-15T09:20:00.000Z"
    },
    "message": "Refund initiated. The customer will receive funds within 1–3 business days."
  }
}

Get refund status

Get a refund by the orderId returned when you created it. If the refund is still pending, Gafiapay checks with the bank before responding, so the status you get back is current.

GET /refund/status/:orderId

GET requests only need x-api-key and x-timestamp.

Path parameters

orderId string required
The refund's orderId, returned by Create a refund.
curl -X GET 'https://api.gafiapay.com/api/v1/external/refund/status/REF1760500000123A1B2C3D4' \
  -H 'x-api-key: your_api_key' \
  -H 'x-timestamp: 1678901234567'
{
  "status": "success",
  "data": {
    "id": "6710a1c2e4b0f5a3c1d2e3f4",
    "orderId": "REF1760500000123A1B2C3D4",
    "orderNo": "PP25101500012345",
    "amount": 1500,
    "formattedAmount": "1500.00",
    "currency": "NGN",
    "status": "completed",
    "reason": "Customer cancelled the order",
    "originalTransaction": {
      "id": "670f9b11e4b0f5a3c1d2e001",
      "orderNo": "PP25101400098765",
      "amount": 2000
    },
    "createdAt": "2026-10-15T09:20:00.000Z",
    "completedAt": "2026-10-15T11:02:41.000Z"
  }
}

List refunds

List your business's refunds, newest first.

GET /refund/list

Query parameters

page number optional Default 1
Page number.
limit number optional Default 20
Results per page, up to 100.
status string optional
Only return refunds with this status: pending, completed or failed.
curl -X GET 'https://api.gafiapay.com/api/v1/external/refund/list?page=1&limit=20&status=pending' \
  -H 'x-api-key: your_api_key' \
  -H 'x-timestamp: 1678901234567'
{
  "status": "success",
  "data": {
    "refunds": [
      {
        "id": "6710a1c2e4b0f5a3c1d2e3f4",
        "orderId": "REF1760500000123A1B2C3D4",
        "amount": 1500,
        "status": "pending",
        "...": "same fields as Get Refund Status"
      }
    ],
    "pagination": { "page": 1, "limit": 20, "total": 1, "pages": 1 }
  }
}

Refund webhooks

If you've set a webhook URL, Gafiapay sends refund.completed or refund.failed when a refund finishes. Webhooks are signed the same way as payment webhooks; see Payment notifications.

Gafiapay also sends refund.completed when it automatically returns a payment that was made to one of your expired or closed virtual accounts.

refund.completed
{
  "event": "refund.completed",
  "data": {
    "transaction": {
      "id": "6710a1c2e4b0f5a3c1d2e3f4",
      "orderId": "REF1760500000123A1B2C3D4",
      "orderNo": "PP25101500012345",
      "amount": 1500,
      "currency": "NGN",
      "status": "completed",
      "originalTransaction": "670f9b11e4b0f5a3c1d2e001",
      "reason": "Customer cancelled the order",
      "completedAt": "2026-10-15T11:02:41.000Z"
    }
  },
  "timestamp": "1760526161000",
  "signature": "hmac_sha256_signature"
}
GafiaPayGafiaPayDocs
Guide for AI agents Dashboard