Skip to main content

Refunds

Process refunds using the SDK's Refunds facade.

The Refunds facade handles refund processing for completed payments.

Basic Usage​

<?php

use Paysera\CheckoutSdk\SdkFacadeBuilder;
use Paysera\CheckoutSdk\Entity\PaymentApiCredentials;

// Initialize and authenticate
$sdk = (new SdkFacadeBuilder())->build();
$sdk->getAuthorizationFacade()->authorize(
new PaymentApiCredentials('client-id', 'client-secret')
);

$refundsFacade = $sdk->getRefundsFacade();

// Build refund request
$refundRequest = $refundsFacade->buildRefundOrderRequest([
'refund' => [
'refund_urls' => [
'success_url' => 'https://example.com/refund/success',
'failure_url' => 'https://example.com/refund/failure',
'callback_url' => 'https://example.com/webhooks/refund',
],
],
'refund_details' => [
'order_id' => 'paysera-order-uuid',
'reference' => 'REFUND-12345',
'refund_amount' => 2500, // 25.00 EUR in cents
'currency' => 'EUR',
'payer' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
'reason' => 'Customer requested refund',
],
]);

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

echo "Refund ID: " . $refundResponse->getRefundId() . "\n";
echo "Status: " . $refundResponse->getStatus() . "\n";

Refund Request Parameters​

$refundRequest = $refundsFacade->buildRefundOrderRequest([
// Required: Refund URLs
'refund' => [
'refund_urls' => [
'success_url' => 'https://example.com/refund/success',
'failure_url' => 'https://example.com/refund/failure',
'callback_url' => 'https://example.com/webhooks/refund',
],
// Optional: Metadata for tracking
'metadata' => [
'platform' => 'my-cms',
'platform_version' => '2.0.0',
'plugin_name' => 'paysera-plugin',
'plugin_version' => '1.0.0',
],
],
// Required: Refund details
'refund_details' => [
'order_id' => 'paysera-order-uuid', // Paysera order ID
'reference' => 'REFUND-12345', // Your refund reference
'refund_amount' => 2500, // Amount in cents
'currency' => 'EUR', // ISO 4217 currency
'payer' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
// Optional
'reason' => 'Customer requested refund',
'refund_method' => [
'key' => 'bank_transfer', // Optional: specific refund method
],
],
]);

Refund Response​

$response = $refundsFacade->initiateRefundOrder($refundRequest);

// Refund details
$refundId = $response->getRefundId();
$status = $response->getStatus(); // 'processing', 'completed', 'failed'
$amount = $response->getAmount(); // int (cents)
$currency = $response->getCurrency();
$reference = $response->getReference();
$createdAt = $response->getCreatedAt(); // DateTimeImmutable or null

Full vs Partial Refunds​

Full Refund​

// Original order: 25.00 EUR (2500 cents)
$refundRequest = $refundsFacade->buildRefundOrderRequest([
'refund' => [
'refund_urls' => [
'success_url' => 'https://example.com/refund/success',
'failure_url' => 'https://example.com/refund/failure',
'callback_url' => 'https://example.com/webhooks/refund',
],
],
'refund_details' => [
'order_id' => $order->paysera_order_id,
'reference' => 'REFUND-' . $order->id,
'refund_amount' => 2500, // Full amount
'currency' => 'EUR',
'payer' => [
'name' => $customer->name,
'email' => $customer->email,
],
'reason' => 'Order cancelled',
],
]);

$response = $refundsFacade->initiateRefundOrder($refundRequest);

Partial Refund​

// Original order: 100.00 EUR (10000 cents)
// Refund one item: 30.00 EUR (3000 cents)

$refundRequest = $refundsFacade->buildRefundOrderRequest([
'refund' => [
'refund_urls' => [
'success_url' => 'https://example.com/refund/success',
'failure_url' => 'https://example.com/refund/failure',
'callback_url' => 'https://example.com/webhooks/refund',
],
],
'refund_details' => [
'order_id' => $order->paysera_order_id,
'reference' => 'REFUND-' . $order->id . '-1',
'refund_amount' => 3000, // Partial amount (30.00 EUR)
'currency' => 'EUR',
'payer' => [
'name' => $customer->name,
'email' => $customer->email,
],
'reason' => 'Item returned',
],
]);

$response = $refundsFacade->initiateRefundOrder($refundRequest);

Multiple Partial Refunds​

// Original: 100.00 EUR
// First refund: 30.00 EUR (already processed)
// Second refund: 20.00 EUR

$refundRequest = $refundsFacade->buildRefundOrderRequest([
'refund' => [
'refund_urls' => [
'success_url' => 'https://example.com/refund/success',
'failure_url' => 'https://example.com/refund/failure',
'callback_url' => 'https://example.com/webhooks/refund',
],
],
'refund_details' => [
'order_id' => $order->paysera_order_id,
'reference' => 'REFUND-' . $order->id . '-2',
'refund_amount' => 2000, // 20.00 EUR
'currency' => 'EUR',
'payer' => [
'name' => $customer->name,
'email' => $customer->email,
],
'reason' => 'Second item returned',
],
]);

// Remaining refundable: 50.00 EUR

Refund Status Validation​

Check if a status is valid:

$status = $refundResponse->getStatus();

if ($refundsFacade->isRefundStatusValid($status)) {
// Valid status
}

Get Available Statuses​

$statuses = $refundsFacade->getRefundStatuses();

foreach ($statuses as $status) {
echo $status->getValue() . "\n";
}
// processing
// completed
// failed

Error Handling​

use Paysera\CheckoutSdk\Exception\IntegrationException;
use Paysera\CheckoutSdk\Exception\ValidationException;

try {
$response = $refundsFacade->initiateRefundOrder($refundRequest);
} catch (ValidationException $e) {
// Invalid parameters
// - Amount exceeds refundable amount
// - Currency mismatch
error_log('Validation error: ' . $e->getMessage());
} catch (IntegrationException $e) {
// API error
// - Order not found
// - Order not in refundable state
error_log('Refund failed: ' . $e->getMessage());
}

Handling Refund Callbacks​

Use processRefundCallback() to handle refund webhooks:

<?php

use Paysera\CheckoutSdk\SdkFacadeBuilder;
use Paysera\CheckoutSdk\Exception\IntegrationException;
use Psr\Http\Message\ServerRequestInterface;

function handleRefundWebhook(ServerRequestInterface $request): void
{
$sdk = (new SdkFacadeBuilder())->build();
$callbacksFacade = $sdk->getCallbacksFacade();

try {
$refundCallback = $callbacksFacade->processRefundCallback($request);

$orderId = $refundCallback->getOrderId();
$reference = $refundCallback->getReference();
$status = $refundCallback->getRefundStatus()->getValue();
$amount = $refundCallback->getAmount(); // int (cents)

if ($status === 'completed') {
// Refund successful - update your records
markRefundCompleted($orderId, $reference, $amount);
} elseif ($status === 'failed') {
// Refund failed - notify admin
notifyRefundFailed($orderId, $reference);
}

http_response_code(200);
echo $callbacksFacade->getCallbackResponseSuccessMessage();

} catch (IntegrationException $e) {
error_log('Refund callback verification failed: ' . $e->getMessage());
http_response_code(401);
}
}

Complete Refund Service Example​

<?php

namespace App\Service;

use Paysera\CheckoutSdk\SdkFacade;
use Paysera\CheckoutSdk\Exception\IntegrationException;
use App\Entity\Order;
use App\Entity\Refund;
use App\Repository\RefundRepository;

class RefundService
{
private SdkFacade $sdk;
private RefundRepository $refunds;

public function __construct(SdkFacade $sdk, RefundRepository $refunds)
{
$this->sdk = $sdk;
$this->refunds = $refunds;
}

public function processRefund(Order $order, int $amountCents, string $reason): Refund
{
// Validate refund is possible
$this->validateRefund($order, $amountCents);

$refundsFacade = $this->sdk->getRefundsFacade();
$reference = 'REFUND-' . $order->id . '-' . time();

// Build request
$request = $refundsFacade->buildRefundOrderRequest([
'refund' => [
'refund_urls' => [
'success_url' => route('refund.success', ['order' => $order->id]),
'failure_url' => route('refund.failure', ['order' => $order->id]),
'callback_url' => route('webhooks.refund'),
],
],
'refund_details' => [
'order_id' => $order->paysera_order_id,
'reference' => $reference,
'refund_amount' => $amountCents,
'currency' => $order->currency,
'payer' => [
'name' => $order->customer_name,
'email' => $order->customer_email,
],
'reason' => $reason,
],
]);

try {
// Process refund
$response = $refundsFacade->initiateRefundOrder($request);

// Create local refund record
$refund = new Refund();
$refund->order_id = $order->id;
$refund->paysera_refund_id = $response->getRefundId();
$refund->reference = $reference;
$refund->amount_cents = $amountCents;
$refund->currency = $order->currency;
$refund->reason = $reason;
$refund->status = $response->getStatus();

$this->refunds->save($refund);

// Update order
$order->total_refunded_cents += $amountCents;
$order->save();

return $refund;

} catch (IntegrationException $e) {
throw new RefundException(
'Failed to process refund: ' . $e->getMessage(),
previous: $e
);
}
}

private function validateRefund(Order $order, int $amountCents): void
{
// Check order is paid
if ($order->status !== 'paid') {
throw new RefundException('Order is not paid');
}

// Check refund amount
$refundable = $order->total_cents - $order->total_refunded_cents;
if ($amountCents > $refundable) {
throw new RefundException(
"Refund amount ($amountCents) exceeds refundable amount ($refundable)"
);
}
}

public function handleRefundWebhook(array $data): void
{
$reference = $data['reference'];
$status = $data['status'];

$refund = $this->refunds->findByReference($reference);
if (!$refund) {
return;
}

$refund->status = $status;
$this->refunds->save($refund);

if ($status === 'completed') {
event(new RefundCompleted($refund));
} elseif ($status === 'failed') {
event(new RefundFailed($refund));
}
}
}

Refund Timeline​

StageTypical Duration
Refund initiatedImmediate
Processing1-3 business days
Funds returned3-10 business days
note

Refund processing times vary by payment method and banking institutions.