Skip to main content

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.

Amount Format

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​

FieldTypeDescription
eligiblebooleanWhether the payment can be refunded
reasonstringIneligibility reason code (nullable, e.g. PAYMENT_NOT_SETTLED)
refunded_amountintegerAmount already refunded
pending_refundsintegerAmount in in-progress refunds
remaining_refundableintegerAmount still available to refund
currencystringISO 4217 currency
min_refund_amountintegerMinimum allowed refund amount
max_refund_amountintegerMaximum allowed refund amount
estimated_fee_amountintegerEstimated refund fee
estimated_fee_currencystringFee currency
fee_typestringFee type
requires_manual_payer_databooleanWhen true, you must collect the payer's IBAN and name
requires_manual_payer_data

When 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.

Idempotency required

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​

HeaderRequiredDescription
Idempotency-KeyYesUnique key that makes the request safe to retry

Request Parameters​

ParameterTypeRequiredDescription
amountintegerYesAmount in minor units (≥ 1)
currencystringYesISO 4217 currency
referencestringNoMerchant reference (max 255 chars)
payer_ibanstringNoPayer IBAN (max 34 chars)
payer_namestringNoPayer name (max 140 chars)
note

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.

ParameterTypeDefaultDescription
sizeinteger10Items per page (1–1000). Out-of-range values return 400 invalid_properties.
afterstring—Cursor for the next page. Take it from _metadata.cursors.after.
beforestring—Cursor for the previous page. Take it from _metadata.cursors.before. Mutually exclusive with after.
order_directionstringdescSort direction: asc or desc.
order_bystringcreation timeField to sort by. Allowed values differ per endpoint — see the endpoint's parameter table.
include_total_countbooleanfalseSet 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"
Unknown query parameters are ignored

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.

caution

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
}
}
}
  • Payments - Read payment records
  • Activity - Refund events also appear in the payment activity feed