Refunds
Initiate refunds for settled payments and track their status.
A refund returns funds for a payment, in full or in part. Before creating a refund, check eligibility to learn the remaining refundable amount and whether you must collect the payer's bank details.
All amounts are in minor currency units (e.g., cents for EUR) as integers
(e.g., 2500 for €25.00).
Check Refund Eligibility​
GET /payment-executor/integration/v1/payments/{paymentId}/refund-eligibility
Returns refund eligibility and limits for a payment. Call this before showing a refund form.
Request​
curl https://api.paysera.com/payment-executor/integration/v1/payments/{paymentId}/refund-eligibility \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response​
{
"eligible": true,
"reason": null,
"refunded_amount": 0,
"pending_refunds": 0,
"remaining_refundable": 2500,
"currency": "EUR",
"min_refund_amount": 1,
"max_refund_amount": 2500,
"estimated_fee_amount": 0,
"estimated_fee_currency": "EUR",
"fee_type": "flat",
"requires_manual_payer_data": false
}
Response Fields​
| Field | Type | Description |
|---|---|---|
eligible | boolean | Whether the payment can be refunded |
reason | string | Ineligibility reason code (nullable, e.g. PAYMENT_NOT_SETTLED) |
refunded_amount | integer | Amount already refunded |
pending_refunds | integer | Amount in in-progress refunds |
remaining_refundable | integer | Amount still available to refund |
currency | string | ISO 4217 currency |
min_refund_amount | integer | Minimum allowed refund amount |
max_refund_amount | integer | Maximum allowed refund amount |
estimated_fee_amount | integer | Estimated refund fee |
estimated_fee_currency | string | Fee currency |
fee_type | string | Fee type |
requires_manual_payer_data | boolean | When true, you must collect the payer's IBAN and name |
requires_manual_payer_dataWhen this is true, your UI must collect the payer's IBAN and name and send them as
payer_iban / payer_name when creating the refund.
Create a Refund​
POST /payment-executor/integration/v1/payments/{paymentId}/refunds
Initiates a refund for a payment.
This endpoint requires an Idempotency-Key header. Replaying the same key with the
same payload returns the original response (200); replaying it with a
different payload returns 409. Initial creation returns 201.
Request​
curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{paymentId}/refunds \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3c1e90-1b2a-4d5e-9f8a-0c1d2e3f4a5b" \
-d '{
"amount": 2500,
"currency": "EUR",
"reference": "REFUND-001"
}'
Headers​
| Header | Required | Description |
|---|---|---|
Idempotency-Key | Yes | Unique key that makes the request safe to retry |
Request Parameters​
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | integer | Yes | Amount in minor units (≥ 1) |
currency | string | Yes | ISO 4217 currency |
reference | string | No | Merchant reference (max 255 chars) |
payer_iban | string | No | Payer IBAN (max 34 chars) |
payer_name | string | No | Payer name (max 140 chars) |
payer_iban and payer_name must be provided together. The endpoint returns 422
if the payment is not eligible for refund.
Response​
{
"refund_id": "1a3b5c7d-2e4f-4a6b-8c0d-3e5f7a9b1c2d",
"payment_id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"amount": 2500,
"currency": "EUR",
"status": "initiated",
"idempotency_key": "7f3c1e90-1b2a-4d5e-9f8a-0c1d2e3f4a5b",
"workflow_id": "refund-1a3b5c7d",
"created_at": 1736440000
}
Status values: initiated, processing, completed, failed.
Get a Refund​
GET /payment-executor/integration/v1/payments/{paymentId}/refunds/{refundId}
Retrieves a single refund by its identifier.
Response​
{
"id": "1a3b5c7d-2e4f-4a6b-8c0d-3e5f7a9b1c2d",
"payment_id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"amount": 2500,
"currency": "EUR",
"status": "completed",
"type": "full",
"method": "swedbank",
"reason": "Customer request",
"external_reference": "EXT-REF-55",
"fee_amount": 0,
"fee_currency": "EUR",
"created_at": 1736440000,
"updated_at": 1736440600
}
List Refunds​
GET /payment-executor/integration/v1/payments/{paymentId}/refunds
Returns a cursor-paginated list of refunds for a payment, ordered by created_at
descending.
curl "https://api.paysera.com/payment-executor/integration/v1/payments/{paymentId}/refunds?size=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Filter Parameters​
None — the payment id in the path is the only scope. Pagination parameters are supported as described below.
Pagination Parameters​
The order, payment link, payment and refund list endpoints share the same cursor-based
pagination. Cursors are opaque strings — read them from _metadata.cursors and send
them back unchanged; never build or parse them yourself.
| Parameter | Type | Default | Description |
|---|---|---|---|
size | integer | 10 | Items per page (1–1000). Out-of-range values return 400 invalid_properties. |
after | string | — | Cursor for the next page. Take it from _metadata.cursors.after. |
before | string | — | Cursor for the previous page. Take it from _metadata.cursors.before. Mutually exclusive with after. |
order_direction | string | desc | Sort direction: asc or desc. |
order_by | string | creation time | Field to sort by. Allowed values differ per endpoint — see the endpoint's parameter table. |
include_total_count | boolean | false | Set to true to populate _metadata.total. Off by default because counting is more expensive. |
Every list response has the same envelope:
{
"items": [],
"_metadata": {
"has_next": true,
"has_previous": false,
"cursors": {
"after": "MDE5ZmRiMmUtOWE5NC03ZDVmLThmY2ItNGRhMTQ1MzFkZGQ0",
"before": "MDE5ZmRjMjgtNWQ5YS03NjQ5LWExOWItZDhjNDAzODQ2NTRi"
},
"total": null
}
}
total is null unless you pass include_total_count=true.
To walk the whole collection, keep sending after until has_next is false:
# first page
curl "https://api.paysera.com/merchant-order/integration/v1/orders?size=50" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# next page — reuse _metadata.cursors.after from the previous response
curl "https://api.paysera.com/merchant-order/integration/v1/orders?size=50&after=MDE5ZmRiMmUtOWE5NC03ZDVmLThmY2ItNGRhMTQ1MzFkZGQ0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
The API does not reject parameters it doesn't know. Sending limit or cursor (names
used by some other APIs) does not return an error — the values are silently dropped
and you keep receiving the first page with the default size of 10. If pagination seems
stuck, check the parameter names first.
order_by accepts created_at (default), updated_at and id.
This endpoint sorts on stored column names, not on the field names in the response.
order_by=amount and order_by=status return 400; the stored equivalents are
refund_amount and refund_status. Prefer created_at — the refunds of a single
payment fit in one page in practice.
Response​
{
"items": [
{
"id": "1a3b5c7d-2e4f-4a6b-8c0d-3e5f7a9b1c2d",
"payment_id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"amount": 2500,
"currency": "EUR",
"status": "completed",
"type": "full",
"method": "swedbank",
"reason": "Customer request",
"external_reference": "EXT-REF-55",
"fee_amount": 0,
"fee_currency": "EUR",
"created_at": 1736440000,
"updated_at": 1736440600
}
],
"_metadata": {
"has_next": false,
"has_previous": false,
"total": null,
"cursors": {
"after": null,
"before": null
}
}
}