Skip to main content

Payments

Read the payment records associated with your orders.

A payment represents a single attempt to pay an order through a chosen payment method. Use these endpoints to look up a payment or list payments for your project.

Amount Format

All amounts are in minor currency units (e.g., cents for EUR) and returned as integers (e.g., 2500 for €25.00).

Get a Payment​

GET /payment-executor/integration/v1/payments/{id}

Retrieves a single payment by its identifier. The payment must belong to your project.

Request​

curl https://api.paysera.com/payment-executor/integration/v1/payments/{id} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Path Parameters​

ParameterTypeRequiredDescription
idUUIDYesPayment identifier

Response​

{
"id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"order_id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"order_reference": "ORDER-12345",
"project_id": "your-project-id",
"amount": 2500,
"currency": "EUR",
"status": "settled",
"payment_method_id": "3f9a1c20-7b4d-4e8a-9c1f-5d6e7a8b9c0d",
"payment_method_key": "swedbank",
"payment_method_name": "Swedbank",
"payment_method_type": "banklink",
"purpose": "Order #12345",
"original_amount": null,
"original_currency": null,
"exchange_rate": null,
"external_payment_id": "EXT-998877",
"created_at": 1736433500,
"updated_at": 1736433570
}

Response Fields​

FieldTypeDescription
idUUIDPayment identifier
order_idUUIDAssociated order identifier
order_referencestringMerchant reference of the order
project_idstringProject the payment belongs to
amountintegerAmount in minor units
currencystringISO 4217 currency
statusstringPayment status
payment_method_idstringPayment method identifier
payment_method_keystringPayment method key (e.g., swedbank)
payment_method_namestringHuman-readable payment method name
payment_method_typestringPayment method type (e.g., banklink)
purposestringPayment purpose — the merchant-supplied payment_details.purpose if the link carried one, otherwise the generated default narrative. See Payment purpose
original_amountintegerOriginal amount before conversion (nullable)
original_currencystringOriginal currency before conversion (nullable)
exchange_ratenumberApplied exchange rate (nullable)
external_payment_idstringIdentifier in the external provider system
created_atintegerCreation Unix timestamp (seconds)
updated_atintegerLast update Unix timestamp (seconds)

List Payments​

GET /payment-executor/integration/v1/payments

Returns a cursor-paginated list of payments for your project. Filters can be combined freely.

Request​

curl "https://api.paysera.com/payment-executor/integration/v1/payments?order_id=ORDER_ID&size=20" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Filter Parameters​

ParameterTypeDescription
order_idUUIDFilter by order
statusstringFilter by one or more payment statuses (repeat the parameter: status=settled&status=failed)
payment_method_idstringFilter by payment method id (repeatable)
payment_method_keystringFilter by payment method key (repeatable)
currencystringFilter by ISO 4217 currency (repeatable)
amount, amount_gte, amount_lteintegerExact / range filter on amount (minor units)
created_at_gte, created_at_lteintegerCreated-at range (Unix seconds)
qstringPrefix search over order_reference — matches references that start with the value (max 255 chars)
Repeat the plain parameter name

Repeatable filters use the plain repeated form (status=settled&status=failed). The bracket form status[]=settled is not recognized — like any unknown parameter it is silently ignored, so the list comes back unfiltered.

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), amount, currency, status, order_reference and payment_method_key.

Response​

{
"items": [
{
"id": "0c2d4e6a-1b3c-4d5e-9f8a-2b4c6d8e0f12",
"amount": 2500,
"currency": "EUR",
"status": "settled",
"payment_method_id": "019493f8-f999-7000-0000-0000000000d1",
"payment_method_key": "swedbank",
"payment_method_name": "Swedbank",
"order_id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"order_reference": "ORDER-12345",
"project_id": "your-project-id",
"created_at": 1736433500,
"updated_at": 1736433560,
"external_payment_id": "019fdb2e-eec2-756c-91f4-8623333f7b24",
"purpose": "Payment for order #ORDER-12345 at My Shop",
"original_amount": null,
"original_currency": null,
"exchange_rate": null
}
],
"_metadata": {
"has_next": true,
"has_previous": false,
"total": null,
"cursors": {
"after": "MDE5ZmRiMmUtZWNlMi03ZmVjLWFkNzMtYTFmYTUwYzFlMWZi",
"before": "MDE5ZmRjMjgtYmYxYy03ZDYxLThlZGItOWExNzQ0MjkyYmI0"
}
}
}

original_amount, original_currency and exchange_rate are populated only when the payment was converted from another currency.

Resolve a Held Payout Leg (Verification of Payee)​

Split-payment integrations paying external (non-Paysera) recipients may receive a recipient.vop_checked webhook: the recipient's name did not strongly match their IBAN's account holder, and the payout leg is held until you act. There are two ways to release it — confirm the pair as-is, or correct the recipient. Both take the distribution_id from the webhook's data, and both are also available in the Paysera Checkout portal.

Confirm (Pay Anyway)​

POST /payment-executor/integration/v1/payments/{id}/distributions/confirm-vop-mismatch

Records your explicit authorization to pay out despite the name mismatch and dispatches the held leg. {id} is the payment ID. Send the IBAN and name exactly as received in the webhook — the confirmation is pinned to that reviewed pair:

curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/confirm-vop-mismatch \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner GmbH"
}'
ParameterTypeRequiredDescription
distribution_idUUIDYesThe held payout leg, from the webhook's data
beneficiary_ibanstring (max 34)YesThe IBAN you reviewed, exactly as received in the webhook
beneficiary_namestring (max 140)YesThe name you reviewed, exactly as received in the webhook
// Response — 202 Accepted
{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"debit_uuid": "6a1b3c4d-5e9f-4a2b-8c6d-8e0f120c2d4e",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner GmbH",
"outcome": "ACCEPTED"
}

The confirmation applies to that IBAN + name pair only — correcting the recipient afterwards discards it and triggers a fresh check.

Correct the Recipient​

POST /payment-executor/integration/v1/payments/{id}/distributions/correct-beneficiary

Rewrites the held leg's recipient IBAN and name — both are required, so resend the unchanged value when correcting only one. The corrected pair is re-verified with a fresh name check; if it still does not match strongly, a new recipient.vop_checked webhook is sent. Use this when the webhook's beneficiary_name is genuinely wrong rather than merely a weak match:

curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/correct-beneficiary \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH"
}'
ParameterTypeRequiredDescription
distribution_idUUIDYesThe held payout leg, from the webhook's data
beneficiary_ibanstring (max 34)YesCorrected beneficiary IBAN
beneficiary_namestring (max 140)YesCorrected beneficiary name, as held by the receiving bank

The response has the same shape as confirm, carrying the corrected pair.

Response and Errors​

Both endpoints return 202 Accepted — the leg is released or re-verified asynchronously:

FieldTypeDescription
distribution_idUUIDThe resolved payout leg
debit_uuidstringStable identifier of the leg
beneficiary_ibanstringBeneficiary IBAN the resolution applies to
beneficiary_namestringBeneficiary name the resolution applies to
outcomestringWhat was scheduled — see below
outcomeMeaning
ACCEPTEDThe retry was signalled; the leg dispatches (or re-verifies) shortly
ALREADY_RETRYINGA retry for this leg is already running — nothing further to do
MISSING_RETRY_DATAThe leg cannot be retried automatically — contact Paysera support
Error CodeHTTP StatusEndpointMeaning
invalid_properties400BothMissing distribution_id, or blank/oversized IBAN (max 34 chars) or name (max 140 chars) — per-field details in error_properties
forbidden403BothThe payment belongs to another project
payment_not_found404BothPayment not found
vop_confirmation_superseded409ConfirmThe recipient changed since the webhook you are answering — review the latest pair before confirming
vop_mismatch_not_confirmable422ConfirmThe leg is not awaiting a name-check confirmation (already released, corrected, or settled)
vop_beneficiary_not_correctable422CorrectThe leg is not correctable (not held on a name mismatch, or already at the bank)
vop_beneficiary_iban_invalid422CorrectThe corrected IBAN is not a valid IBAN — its structure or checksum does not hold
vop_beneficiary_iban_not_eea422CorrectThe corrected IBAN is valid but belongs to a country outside the European Economic Area
distribution_retry_unavailable503BothThe release could not be scheduled — retry later

Re-send a Returned Payout​

Split-payment integrations paying external (non-Paysera) recipients may receive a recipient.returned webhook: a payout that had already settled was sent back by the recipient's bank, days later. The money is back on your settlement account and that leg is final — but the return can be answered with a new payout leg carrying corrected recipient details.

POST /payment-executor/integration/v1/payments/{id}/distributions/resend-returned

{id} is the payment ID; distribution_id is the returned leg, from the webhook's data. The corrected pair is optional — omit both to send the same details again (useful when the bank returned for a reason that has since been resolved on the recipient's side):

curl -X POST https://api.paysera.com/payment-executor/integration/v1/payments/{id}/distributions/resend-returned \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH"
}'
ParameterTypeRequiredDescription
distribution_idUUIDYesThe returned payout leg, from the webhook's data
beneficiary_ibanstring (max 34)NoCorrected beneficiary IBAN. Omit to keep the one that was returned
beneficiary_namestring (max 140)NoCorrected beneficiary name. Omit to keep the one that was returned
// Response — 202 Accepted
{
"distribution_id": "019efc90-1c44-7a02-8b71-2ce9f0a71b55",
"returned_distribution_id": "019efc71-f2b0-7d51-8f68-1a9f865dcf03",
"return_id": 8412394,
"amount": 4000,
"currency": "EUR",
"beneficiary_iban": "DE89370400440532013000",
"beneficiary_name": "Partner Handels GmbH",
"outcome": "CREATED",
"saved_recipient_updated": true
}
outcomeMeaning
CREATEDA new leg was created and is being verified before dispatch
ALREADY_RESENTThis return was already answered — the response names the existing leg
ALREADY_RETRYINGThe leg exists and its dispatch was already running — nothing further to do

Three things worth knowing before you build against this:

  • At most one re-send per return. The re-send is keyed on the return itself — that is what return_id identifies, the bank's own reference for the reversal. It is unrelated to a refund id, which is a UUID and describes money going back to the payer. Retrying the call is safe: it answers ALREADY_RESENT with the leg that already exists rather than paying twice. Two cases answer differently and neither pays twice either: if that earlier re-send failed without reaching the bank, the call revives that same leg and answers CREATED; and if the earlier re-send was itself returned, the call is refused with payout_not_resendable — answer the newer return.
  • The new leg carries the amount that came back, which under a partial return is less than the original leg carried. Read amount from the response rather than assuming.
  • Correcting the pair also updates your saved recipient — best-effort. When you send a corrected IBAN or name, the registered beneficiary stored under the returned account is rewritten too, so orders created afterwards are addressed to the corrected account. Orders that already exist keep the account they were created with. Read saved_recipient_updated in the response: true means it followed; null means there was nothing to carry over, which includes an ALREADY_RESENT or ALREADY_RETRYING answer, where this call changed no recipient even if you sent a corrected pair; and false means the payout is on its way but your saved recipient still holds the account that bounced, because the registry refused the change or could not be reached — update it yourself via split beneficiaries, or the next order will be addressed to it again. The re-send itself is never refused over this: the money has already moved by then.

Not every return is yours to answer. Only the reason codes that a corrected name and IBAN actually fix — AC01, AC04, AC06 and RR03 — are self-service; a return with any other reason, with no reason code at all, or one that has already been re-sent is refused with 422 payout_not_resendable and is handled by Paysera customer support.

Error CodeHTTP StatusMeaning
invalid_properties400Missing distribution_id, or oversized IBAN (max 34 chars) or name (max 140 chars) — per-field details in error_properties
forbidden403The payment belongs to another project
payment_not_found404Payment not found
payout_not_resendable422The leg does not belong to this payment, is not a returned payout, its reason code is a support case, or the returned amount could not be established
distribution_retry_unavailable503The re-send could not be created — retry later