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
metadataandmerchant_datakey-value pairs - Optional
splitsdescribing how the funds should be distributed (see Split Payments)
redirect_urls.* fields are optional in the create-order request. Provide them per environment if you need browser redirects after payment.
All amounts in the API use minor currency units (e.g., cents for EUR, pennies for GBP):
- Request: integer (e.g.,
2500for €25.00) - Response: Long/integer format (e.g.,
2500for €25.00)
Endpoints​
For complete request/response schemas and examples, see the Endpoint Reference:
| Method | Endpoint | Description |
|---|---|---|
| POST | /merchant-order/integration/v1/orders | Create an order |
| GET | /merchant-order/integration/v1/orders/{id} | Get a single order |
| GET | /merchant-order/integration/v1/orders | List orders |
| PATCH | /merchant-order/integration/v1/orders/{id} | Update an order before payment |
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.
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​
| Status | Code | Description |
|---|---|---|
| Pending Payment | pending_payment | Order created, awaiting payment |
| Paid | paid | Order fully paid |
| Canceled | canceled | Order was canceled* |
| Closed | closed | Order 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.
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:
| Field | Type | Required | Description |
|---|---|---|---|
splits[].type | string | ✓ | variadic (the per-order recipient for the policy's variadic slot) or tip (an extra tip recipient) |
splits[].beneficiary_id | string (UUID) | ✓ | Id of the registered beneficiary to pay |
splits[].slot_id | string (UUID) | Variadic slot to bind to. Required only when the policy has more than one variadic slot; ignored for tips |
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​
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
- JavaScript
- Python
- Kotlin
- Go
- C#
<?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
async function createPaymentOrder(accessToken, orderData) {
const response = await fetch(
'https://api.paysera.com/merchant-order/integration/v1/orders',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
redirect_urls: {
success_url: orderData.successUrl,
failure_url: orderData.failureUrl,
callback_url: orderData.callbackUrl,
},
purchase: {
reference: orderData.reference,
amount: orderData.amount, // Integer, already in minor units
currency: orderData.currency,
},
}),
}
);
if (!response.ok) {
throw new Error(`Failed to create order: ${await response.text()}`);
}
return response.json();
}
// Usage - amount is in minor units (cents)
const order = await createPaymentOrder(accessToken, {
successUrl: 'https://your-site.paysera.net/success',
failureUrl: 'https://your-site.paysera.net/failure',
callbackUrl: 'https://your-site.paysera.net/webhook',
reference: 'ORDER-12345',
amount: 2500, // €25.00 in cents
currency: 'EUR',
});
console.log('Order ID:', order.order_id);
// Note: To get a payment URL, create a payment link using this order_id
import requests
def create_payment_order(access_token: str, order_data: dict) -> dict:
response = requests.post(
'https://api.paysera.com/merchant-order/integration/v1/orders',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
},
json={
'redirect_urls': {
'success_url': order_data['success_url'],
'failure_url': order_data['failure_url'],
'callback_url': order_data['callback_url'],
},
'purchase': {
'reference': order_data['reference'],
'amount': order_data['amount'], # Integer, already in minor units
'currency': order_data['currency'],
},
},
)
response.raise_for_status()
return response.json()
# Usage - amount is in minor units (cents)
order = create_payment_order(access_token, {
'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',
})
print(f"Order ID: {order['order_id']}")
# Note: To get a payment URL, create a payment link using this order_id
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import kotlinx.serialization.json.*
suspend fun createPaymentOrder(accessToken: String, orderData: OrderData): JsonObject {
val client = HttpClient.newHttpClient()
val requestBody = buildJsonObject {
putJsonObject("redirect_urls") {
put("success_url", orderData.successUrl)
put("failure_url", orderData.failureUrl)
put("callback_url", orderData.callbackUrl)
}
putJsonObject("purchase") {
put("reference", orderData.reference)
put("amount", orderData.amount) // Integer, already in minor units
put("currency", orderData.currency)
}
}
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.paysera.com/merchant-order/integration/v1/orders"))
.header("Authorization", "Bearer $accessToken")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toString()))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 201) {
throw Exception("Failed to create order: ${response.body()}")
}
return Json.parseToJsonElement(response.body()).jsonObject
}
// Usage - amount is in minor units (cents)
val order = createPaymentOrder(accessToken, OrderData(
successUrl = "https://your-site.paysera.net/success",
failureUrl = "https://your-site.paysera.net/failure",
callbackUrl = "https://your-site.paysera.net/webhook",
reference = "ORDER-12345",
amount = 2500, // €25.00 in cents
currency = "EUR"
))
println("Order ID: ${order["order_id"]}")
// Note: To get a payment URL, create a payment link using this order_id
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type OrderRequest struct {
RedirectURLs RedirectURLs `json:"redirect_urls"`
Purchase Purchase `json:"purchase"`
}
type RedirectURLs struct {
SuccessURL string `json:"success_url"`
FailureURL string `json:"failure_url"`
CallbackURL string `json:"callback_url"`
}
type Purchase struct {
Reference string `json:"reference"`
Amount int64 `json:"amount"`
Currency string `json:"currency"`
}
func createPaymentOrder(accessToken string, orderData OrderRequest) (map[string]interface{}, error) {
jsonData, _ := json.Marshal(orderData)
req, _ := http.NewRequest("POST",
"https://api.paysera.com/merchant-order/integration/v1/orders",
bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != 201 {
return nil, fmt.Errorf("failed to create order: %s", string(body))
}
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
// Usage - amount is in minor units (cents)
func main() {
order, _ := createPaymentOrder(accessToken, OrderRequest{
RedirectURLs: RedirectURLs{
SuccessURL: "https://your-site.paysera.net/success",
FailureURL: "https://your-site.paysera.net/failure",
CallbackURL: "https://your-site.paysera.net/webhook",
},
Purchase: Purchase{
Reference: "ORDER-12345",
Amount: 2500, // €25.00 in cents
Currency: "EUR",
},
})
fmt.Printf("Order ID: %s\n", order["order_id"])
// Note: To get a payment URL, create a payment link using this order_id
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
public class PayseraClient
{
private readonly HttpClient _httpClient;
public PayseraClient(string accessToken)
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
}
public async Task<JsonDocument> CreatePaymentOrderAsync(OrderData orderData)
{
var request = new
{
redirect_urls = new
{
success_url = orderData.SuccessUrl,
failure_url = orderData.FailureUrl,
callback_url = orderData.CallbackUrl
},
purchase = new
{
reference = orderData.Reference,
amount = orderData.Amount, // Integer, already in minor units
currency = orderData.Currency
}
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(
"https://api.paysera.com/merchant-order/integration/v1/orders",
content);
var responseBody = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new Exception($"Failed to create order: {responseBody}");
}
return JsonDocument.Parse(responseBody);
}
}
// Usage - amount is in minor units (cents)
var client = new PayseraClient(accessToken);
var order = await client.CreatePaymentOrderAsync(new OrderData
{
SuccessUrl = "https://your-site.paysera.net/success",
FailureUrl = "https://your-site.paysera.net/failure",
CallbackUrl = "https://your-site.paysera.net/webhook",
Reference = "ORDER-12345",
Amount = 2500, // €25.00 in cents
Currency = "EUR"
});
Console.WriteLine($"Order ID: {order.RootElement.GetProperty("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​
| Parameter | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Order 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
}
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​
| Parameter | Type | Description |
|---|---|---|
status[] | string | Filter by one or more order statuses |
amount, amount_gte, amount_lte | integer | Exact / range filter on amount (minor units) |
amount_paid, amount_paid_gte, amount_paid_lte | integer | Exact / range filter on amount paid |
currency[] | string | Filter by one or more ISO 4217 currencies |
created_at_gte, created_at_lte | integer | Created-at range (Unix seconds) |
q | string | Free-text search over reference (max 255 chars) |
cursor | string | Pagination cursor |
limit | integer | Page 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.
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​
| Parameter | Type | Required | Description |
|---|---|---|---|
reference | string | No | Merchant reference (max 255 chars) |
amount | integer | No | New amount in minor units (e.g. 3000 for €30.00) |
currency | string | No | New 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"
}
Related Documentation​
- Payment Links - Create payment links for orders
- Webhooks - Handle payment notifications
- Payment Statuses Reference - Complete status reference