Skip to main content

Refunds

Process full or partial refunds for completed payments.

This guide covers processing refunds for completed payments via the Paysera Checkout API.

Overview​

Refunds allow you to return funds to customers for completed payments. You can process:

  • Full refunds - Return the entire payment amount
  • Partial refunds - Return a portion of the payment amount
Amount Format

All amounts use minor currency units (e.g., cents for EUR):

  • Request: String format (e.g., "1000" for €10.00)
  • Response: Long/integer format (e.g., 1000 for €10.00)

Prerequisites​

Before processing a refund:

  • The original order must be in paid status
  • Sufficient time has passed for the payment to settle (typically 1-2 business days)
  • The refund amount doesn't exceed the original payment amount minus any previous refunds

Endpoint​

MethodEndpointDescription
POST/merchant-order/integration/v1/orders/{order_id}/refundsCreate a refund

Create Refund​

Request​

curl -X POST https://api.paysera.com/merchant-order/integration/v1/orders/ORDER_ID/refunds \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": "1000",
"currency": "EUR",
"reason": "Customer requested refund"
}'

Request Parameters​

ParameterTypeRequiredDescription
amountstringYesRefund amount in minor units as string (e.g., "1000" for €10.00)
currencystringYesISO 4217 currency code (must match original)
reasonstringNoReason for the refund

Response​

{
"id": "b7c8d9e0-1f2a-3b4c-5d6e-7f8a9b0c1d2e",
"orderId": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"status": "pending",
"amount": 1000,
"currency": "EUR",
"reason": "Customer requested refund",
"createdAt": 1736437200
}

Refund Statuses​

StatusDescription
pendingRefund initiated, processing
completedRefund successfully processed
failedRefund failed

Code Examples​

<?php

function createRefund(string $accessToken, string $orderId, int $amountCents, string $currency, ?string $reason = null): array
{
$url = "https://api.paysera.com/merchant-order/integration/v1/orders/$orderId/refunds";

$payload = [
'amount' => (string) $amountCents, // Already in minor units (cents)
'currency' => $currency,
];

if ($reason) {
$payload['reason'] = $reason;
}

$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $accessToken,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);

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

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

return json_decode($response, true);
}

// Full refund - amount in minor units (cents)
$refund = createRefund(
$accessToken,
'order-uuid',
2500, // €25.00 in cents
'EUR',
'Customer requested refund'
);

echo "Refund ID: " . $refund['id'];
echo "Status: " . $refund['status'];

PHP with SDK​

<?php

use Paysera\CheckoutSdk\SdkFacade;

/** @var SdkFacade $sdkFacade */
$refundsFacade = $sdkFacade->getRefundsFacade();

// Build refund request
$refundRequest = $refundsFacade->buildRefundOrderRequest([
'order_id' => 'order-uuid',
'amount' => 2500, // 25.00 EUR in cents
'currency' => 'EUR',
'reason' => 'Customer requested refund',
]);

// Process refund
$refundResponse = $refundsFacade->initiateRefundOrder($refundRequest);

echo "Refund ID: " . $refundResponse->getId();
echo "Status: " . $refundResponse->getStatus();

Partial Refunds​

You can process multiple partial refunds as long as the total doesn't exceed the original payment:

# Original payment: €100.00 (10000 cents)

# First partial refund: €30.00 (3000 cents)
curl -X POST https://api.paysera.com/merchant-order/integration/v1/orders/ORDER_ID/refunds \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": "3000",
"currency": "EUR",
"reason": "Partial refund for returned item"
}'

# Second partial refund: €20.00 (2000 cents)
curl -X POST https://api.paysera.com/merchant-order/integration/v1/orders/ORDER_ID/refunds \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": "2000",
"currency": "EUR",
"reason": "Partial refund for second returned item"
}'

# Remaining refundable: €50.00 (5000 cents)

Refund Webhooks​

When a refund status changes, you'll receive a webhook. Note that refund webhooks follow the same payload structure as other webhooks:

{
"event": {
"name": "order.paid",
"type": "order",
"timestamp": 1736437500
},
"order": {
"id": "a6f2b8e3-5e5f-47d9-b13f-87ed2db2938a",
"projectId": "01990618-2c90-7103-9169-752bdaac7a52",
"status": "paid",
"amount": 2500,
"currency": "EUR",
"reference": "ORDER-12345",
"amountPaid": 2500,
"balanceDue": 0,
"createdAt": 1736433270,
"updatedAt": 1736437500
}
}

Handle order status changes in your webhook handler:

<?php

$orderStatus = $data['order']['status'];
$reference = $data['order']['reference'];

switch ($orderStatus) {
case 'paid':
// Order fully paid - fulfill the order
updateOrderStatus($reference, 'paid');
break;

case 'pending_payment':
// Order awaiting payment
updateOrderStatus($reference, 'awaiting_payment');
break;
}

Error Responses​

Order Not Found​

{
"error": "not_found",
"message": "Order not found"
}

Order Not Completed​

{
"error": "invalid_state",
"message": "Cannot refund order in pending status"
}

Amount Exceeds Refundable​

{
"error": "validation_error",
"message": "Refund amount exceeds refundable amount",
"details": [
{
"field": "amount",
"message": "Maximum refundable amount is 15.00 EUR"
}
]
}

Currency Mismatch​

{
"error": "validation_error",
"message": "Currency must match original payment",
"details": [
{
"field": "amount.currency",
"message": "Original payment was in EUR"
}
]
}

Refund Timeline​

StageTypical Duration
Refund initiatedImmediate
Processing1-3 business days
Funds returned to customer3-10 business days (varies by payment method)
note

Refund processing times vary by payment method and banking institutions. Bank transfers typically take longer than card refunds.