Skip to main content
ShadhinPay Docs

Refunds

Refund a completed payment in full or in part, and track the refund's status.

Refund money back to a customer against a completed payment. Refunds are asynchronous — you create one, and it settles with the provider over a short period.

Create a refund

POST /api/v1/payments/{paymentId}/refunds

Requires the auth headers. Supplying an X-Idempotency-Key is recommended so retries don't create duplicate refunds.

Request body

FieldTypeRequiredNotes
amountstringnoDefaults to the full remaining amount if omitted
reasonstringyesWhy you're refunding
merchantRefstringnoYour own reference for the refund

Rules

  • The source payment must be COMPLETED.
  • amount must be greater than zero, and the sum of all refunds can't exceed the original payment. Over-refunding is rejected.
  • Concurrent refund requests are serialised so you can't accidentally double-refund.

Example

curl -X POST https://api.shadhinpay.pay/api/v1/payments/PAY_20260517_K8X3M2/refunds \
  -H "Client-Id: HM_4f8c2b1a" \
  -H "Business-Id: HB_71c4a09e" \
  -H "X-Api-Key: sk_live_aB3kC9...9xQ2" \
  -H "X-Idempotency-Key: refund-7831-1" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "500.00",
    "reason": "Customer cancelled one item",
    "merchantRef": "rf-7831"
  }'

Response — 202 Accepted

{
  "status": "success",
  "data": {
    "refundId": "REF_20260517_M4N1J7",
    "paymentId": "PAY_20260517_K8X3M2",
    "amount": "500.00",
    "status": "INITIATED",
    "reason": "Customer cancelled one item",
    "merchantRef": "rf-7831",
    "initiatedAt": "2026-05-17T10:05:00Z",
    "completedAt": null
  }
}

202 Accepted means the refund is queued — it isn't settled yet. Track it via status or webhooks.

Refund statuses

StatusMeaning
INITIATEDCreated and queued with the provider
PROCESSINGThe provider accepted the refund request
COMPLETEDThe provider settled the refund
FAILEDThe provider rejected the refund

When a refund completes you receive a payment.refunded webhook; payment.refund_failed fires if it fails. See Webhooks.

Full vs partial

  • A full refund moves the payment to REFUNDED.
  • A partial refund leaves the payment COMPLETED and increments its refundTotal. You can issue further partial refunds up to the original amount.

Get / list refunds

GET /api/v1/payments/{paymentId}/refunds            # list refunds for a payment
GET /api/v1/payments/{paymentId}/refunds/{refundId} # one refund

Next steps

On this page