Refunds & Disputes

Zanga provides a secure way to process refunds for completed transactions. Refunds are managed through the Zanga Ledger to ensure forensic integrity and correct account balancing.

Refund Eligibility

A refund is only possible under the following circumstances:

  • Completed Payment: The original transaction must have a status of COMPLETED.
  • Sufficient Balance: The merchant must have sufficient funds in their Zanga wallet or the specific transaction must still be in a reversible state (e.g., held in Escrow).
  • Amount Limit: You cannot refund more than the total amount of the original transaction.

1. Requesting a Refund

To request a refund, call the Zanga API from your backend.

POST /v1/refunds

Request Body

FieldTypeRequiredDescription
paymentIntentIdStringYesThe ID of the original payment intent.
requestedByStringYesIdentifier for the person/system requesting the refund.
amountNgweBigIntYesAmount to refund in minimal units.
reasonEnumYesSee Refund Reasons below.
idempotencyKeyUUIDYesUnique ID to prevent duplicate requests.
notesStringNoInternal notes for the refund audit log.

Refund Reasons

When requesting a refund, you must provide one of the following reasons:

  • SERVICE_NOT_RENDERED: The service was not provided to the customer.
  • DUPLICATE_PAYMENT: The customer was charged twice for the same order.
  • FRAUD: The transaction was identified as fraudulent.
  • CUSTOMER_REQUEST: The customer requested a refund (e.g., return of goods).
  • DISPUTE_RESOLVED: A dispute was resolved in favor of the customer.
  • SYSTEM_CORRECTION: Correction for a technical error.

2. Refund Lifecycle

Refunds follow a specific status transition:

  1. PENDING: The refund has been requested and is awaiting review or confirmation from the provider (MTN/Airtel/Card issuer).
  2. COMPLETED: The funds have been successfully moved back to the customer's account/wallet.
  3. REJECTED: The refund was denied (e.g., insufficient merchant funds or carrier rejection).

3. Webhook Notifications

Zanga will send a webhook when a refund status changes of importance to the tenant.

Event: refund.completed

{
  "id": "evt_ref_123...",
  "event": "refund.completed",
  "data": {
    "refundId": "ref_987654",
    "amount": 500,
    "intentId": "int_xyz"
  }
}