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}/refundsRequires the auth headers. Supplying an
X-Idempotency-Key is recommended so retries don't
create duplicate refunds.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
amount | string | no | Defaults to the full remaining amount if omitted |
reason | string | yes | Why you're refunding |
merchantRef | string | no | Your own reference for the refund |
Rules
- The source payment must be
COMPLETED. amountmust 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
| Status | Meaning |
|---|---|
INITIATED | Created and queued with the provider |
PROCESSING | The provider accepted the refund request |
COMPLETED | The provider settled the refund |
FAILED | The 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
COMPLETEDand increments itsrefundTotal. 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