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"
}'
const baseUrl = '{{base_url}}';
const aggregatorId = '{{aggregator_id}}';
const secretKey = '{{secret_key}}';
const res = await fetch(`${baseUrl}/v1/aggregators/${aggregatorId}/refunds`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-SFPY-AGGREGATOR-SECRET-KEY': secretKey,
},
body: JSON.stringify({
request_id: crypto.randomUUID(),
payment_id: '{{payment_token}}',
amount: 500,
reason: 'DuplicatePayment',
addtl_info: 'Duplicate charge on invoice INV-1024',
debitor_iban: 'PK12ALFH0031001006540005',
}),
});
if (!res.ok) {
const error = await res.json();
throw new Error(`Refund failed: ${res.status} ${error.message}`);
}
const payload = await res.json();
console.log('Refund token', payload.data.token);
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