Skip to main content

Payment Orders

Create and manage payment orders - the core transaction record in Paysera Checkout.

Payment orders represent the core transaction record in Paysera Checkout. This guide covers creating and managing orders via the API.

Overview​

A payment order contains:

  • Transaction amount, currency, and your merchant reference (your order ID)
  • Optional redirect URLs for success/failure/callback
  • Optional metadata and merchant_data key-value pairs
  • Optional splits describing how the funds should be distributed (see Split Payments)
note

redirect_urls.* fields are optional in the create-order request. Provide them per environment if you need browser redirects after payment.

Amount Format

All amounts in the API use minor currency units (e.g., cents for EUR, pennies for GBP):

  • Request: integer (e.g., 2500 for €25.00)
  • Response: Long/integer format (e.g., 2500 for €25.00)

Endpoints​

For complete request/response schemas and examples, see the Endpoint Reference:

MethodEndpointDescription
POST/merchant-order/integration/v1/ordersCreate an order
GET/merchant-order/integration/v1/orders/{id}Get a single order
GET/merchant-order/integration/v1/ordersList orders
PATCH/merchant-order/integration/v1/orders/{id}Update an order before payment
info

Order state is pushed to your callback_url via webhooks (recommended for real-time updates). You can also read order state directly with GET /orders/{id} and GET /orders — useful for reconciliation and on-demand lookups. See Webhooks.

No idempotency key

Order creation does not support an idempotency key, and purchase.reference is not enforced unique — retrying POST /orders creates a second order (the server-generated order_id is the unique key). Guard retries on your side (e.g. track the order_id you got back, or check before retrying). Refunds, by contrast, take an idempotency-key header — see Refunds.

Order Statuses​

StatusCodeDescription
Pending Paymentpending_paymentOrder created, awaiting payment
PaidpaidOrder fully paid
CanceledcanceledOrder was canceled*
ClosedclosedOrder closed, no further operations*

*canceled and closed statuses are defined but not yet fully implemented.

See Payment Statuses Reference for complete status documentation.

Splits​

If the merchant has enabled automatic splits or payer tips on the project, attach a splits array to the order to control how the funds are distributed. The merchant's share, fixed shares, and percentages all come from the project's policy template — your integration only sends the per-order pieces. See the Split Payments concept page for the full picture, including how to register beneficiaries.

Activation required

Automatic splits and payer tips are not enabled by default. The merchant must request activation through Paysera customer support before the splits field can be used. Until the project is configured, requests with splits are rejected (for example with intermediary_account_required).

There are two per-order mechanisms: splits[] (fill variadic slots / add tips, referencing registered beneficiaries by id) and split_overrides (restate the whole policy for one order — and the only path that accepts a recipient account directly). Discover your policy's slot_ids and roles via GET /checkout-project/integration/v1/project/split-policy.

splits[] entries reference a registered beneficiary by id:

FieldTypeRequiredDescription
splits[].typestring✓variadic (the per-order recipient for the policy's variadic slot) or tip (an extra tip recipient)
splits[].beneficiary_idstring (UUID)✓Id of the registered beneficiary to pay
splits[].slot_idstring (UUID)Variadic slot to bind to. Required only when the policy has more than one variadic slot; ignored for tips
Payment-link lifetime

Orders with splits require a non-expiring payment link. Pass lifetime: 0 when creating the payment link.

Example: variadic​

The policy template has a variadic slot — your integration points it at the per-order recipient's beneficiary (the marketplace seller, courier, freelancer, etc.):

"splits": [
{ "type": "variadic", "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000" }
]

Example: tip​

Payer tips are enabled. The customer's bill follows the policy template; one entry routes any tip the payer adds to the recipient's beneficiary:

"splits": [
{ "type": "tip", "beneficiary_id": "550e8400-e29b-41d4-a716-446655440000" }
]

Per-order rule overrides​

When the project has per-order overrides enabled, send a split_overrides block instead to restate the whole policy for a single order — change each slot's rule and recipient without altering the saved policy. Override recipients may be a registered beneficiary_id or a Paysera account paid directly (no registration). See Per-order rule overrides for the full rules.

"split_overrides": {
"slots": [
{ "slot_id": "MERCHANT_SLOT_ID", "type": "remainder" },
{ "slot_id": "VARIADIC_SLOT_ID", "type": "fixed", "value": 1500,
"recipient": { "account": "EVP5210018145214" } }
]
}

Code Examples​

Splits not shown below

The samples in this section create an order without splits — the most common entry point. If you need automatic splits or payer tips, take any sample below and add a splits array next to purchase in the request body, using one of the shapes documented in the Splits section above. Don't copy-paste these samples if your project has a policy that requires a variadic entry — the API will return variadic_entry_required.

<?php

function createPaymentOrder(string $accessToken, array $orderData): array
{
$ch = curl_init('https://api.paysera.com/merchant-order/integration/v1/orders');

curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $accessToken,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'redirect_urls' => [
'success_url' => $orderData['success_url'],
'failure_url' => $orderData['failure_url'],
'callback_url' => $orderData['callback_url'],
],
'purchase' => [
'reference' => $orderData['reference'],
'amount' => $orderData['amount'], // Integer, already in minor units
'currency' => $orderData['currency'],
],
]),
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode !== 201) {
throw new Exception('Failed to create order: ' . $response);
}

return json_decode($response, true);
}

// Usage - amount is in minor units (cents)
$order = createPaymentOrder($accessToken, [
'success_url' => 'https://your-site.paysera.net/success',
'failure_url' => 'https://your-site.paysera.net/failure',
'callback_url' => 'https://your-site.paysera.net/webhook',
'reference' => 'ORDER-12345',
'amount' => 2500, // €25.00 in cents
'currency' => 'EUR',
]);

echo "Order ID: " . $order['order_id'];
// Note: To get a payment URL, create a payment link using this order_id

Reading and Updating Orders​

You can read an order directly and update it before payment. Webhooks remain the recommended push channel for real-time status changes (see Webhooks).

Get an Order​

GET /merchant-order/integration/v1/orders/{id}

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

curl https://api.paysera.com/merchant-order/integration/v1/orders/{id} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Path Parameters​

ParameterTypeRequiredDescription
idUUIDYesOrder identifier

Response​

{
"id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"project_id": "your-project-id",
"amount": 2500,
"currency": "EUR",
"status": "paid",
"reference": "ORDER-12345",
"amount_paid": 2500,
"balance_due": 0,
"callback_url": "https://example.com/webhook",
"success_url": "https://example.com/success",
"fail_url": "https://example.com/failure",
"source": "Integration",
"created_at": 1736433000,
"updated_at": 1736433600
}
Polling vs webhooks

Webhooks remain the recommended way to react to order changes in real time. Use this endpoint for reconciliation or on-demand lookups.

List Orders​

GET /merchant-order/integration/v1/orders

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

Query Parameters​

ParameterTypeDescription
status[]stringFilter by one or more order statuses
amount, amount_gte, amount_lteintegerExact / range filter on amount (minor units)
amount_paid, amount_paid_gte, amount_paid_lteintegerExact / range filter on amount paid
currency[]stringFilter by one or more ISO 4217 currencies
created_at_gte, created_at_lteintegerCreated-at range (Unix seconds)
qstringFree-text search over reference (max 255 chars)
cursorstringPagination cursor
limitintegerPage size

Response​

{
"items": [
{ "id": "a6f2b8e3-…", "amount": 2500, "currency": "EUR", "status": "paid" }
],
"cursor": "eyJpZCI6Li4ufQ==",
"has_more": true
}

Update an Order​

PATCH /merchant-order/integration/v1/orders/{id}

Partially updates an order before payment. At least one field must be provided. Changing amount or currency cancels any active payment link for the order.

warning

Returns 422 if the order is already paid or canceled.

Request​

curl -X PATCH https://api.paysera.com/merchant-order/integration/v1/orders/{id} \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reference": "ORDER-12345-REV2", "amount": 3000, "currency": "EUR" }'

Request Parameters​

ParameterTypeRequiredDescription
referencestringNoMerchant reference (max 255 chars)
amountintegerNoNew amount in minor units (e.g. 3000 for €30.00)
currencystringNoNew currency (ISO 4217)

All fields optional; at least one required.

Response​

{ "order_id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a" }

Error Responses​

Validation Error​

{
"error": "invalid_properties",
"error_description": "Validation failed",
"error_properties": {
"purchase.amount": ["Amount should be greater than 0"]
}
}

Invalid Project​

{
"error": "not_found",
"error_description": "Project not found or access denied"
}

Authentication Failure​

{
"error": "unauthorized",
"error_description": "Authentication is required to access this resource"
}