Use refunds to return funds for payments that have reached P_SETTLED. Refund requests are idempotent and must reference the original payment token.
Create refunds only after the payment reaches P_SETTLED.
Create a refund
curl --request POST "{{base_url}}/v1/aggregators/{{aggregator_id}}/refunds" \
--header "X-SFPY-AGGREGATOR-SECRET-KEY: {{secret_key}}" \
--header "Content-Type: application/json" \
--data '{
"request_id": "{{request_id}}",
"payment_id": "{{payment_token}}",
"amount": 500,
"reason": "DuplicatePayment",
"addtl_info": "Duplicate charge on invoice INV-1024",
"debitor_iban": "PK12ALFH0031001006540005"
}'
Body fields
| Field | Type | Required? | Notes |
|---|
request_id | string | Yes | UUID to guarantee idempotency. |
payment_id | string | Yes | Token from the original payment response. |
amount | integer | Yes | Amount in paisa; cannot exceed the settled payment amount. |
reason | string | Yes | One of TechnicalProblem or DuplicatePayment. |
debitor_iban | string | Yes | IBAN that received the original payment. |
addtl_info | string | Optional | Free-form text for audit logs. |
Statuses
Refunds are asynchronous. The API returns a status field; list filters include R_INITIATED, R_COMPLETED, R_FAILED, and R_CANCELED.
Webhook events to expect
refund.created
refund.completed
refund.failed
refund.canceled
Key endpoints
| Endpoint | Purpose |
|---|
POST /v1/aggregators/{{aggregator_id}}/refunds | Create a refund. |
GET /v1/aggregators/{{aggregator_id}}/refunds | List refunds with filters and pagination. |
GET /v1/aggregators/{{aggregator_id}}/refunds/{{refund_id}} | Read a single refund. |
See also