Skip to main content

Overview

The RefundService class handles full and partial refunds of approved payments. Refunds return money to the customer and update the payment status accordingly. Source: src/Services/RefundService.php:9
Only payments with approved status can be refunded. Refund availability depends on the payment method and time elapsed since approval.

Constructor

public function __construct(
    private MercadoPagoClientFactory $clientFactory,
) {}

Dependencies

clientFactory
MercadoPagoClientFactory
required
Factory for creating and configuring MercadoPago SDK clients. Automatically injected by Laravel’s service container.

Methods

create()

Creates a full or partial refund for an approved payment.
public function create(string $paymentId, array $payload = []): mixed
Source: src/Services/RefundService.php:15

Parameters

paymentId
string
required
The MercadoPago payment ID to refund (must be an approved payment)
payload
array
default:"[]"
Optional refund configuration.
Implementation detail (src/Services/RefundService.php:21-28): When amount is present in the payload, the service calls refund() with the specific amount. Otherwise, it calls refundTotal() for a complete refund.

Returns

response
mixed
MercadoPago refund object containing:

Usage

use Fitodac\LaravelMercadoPago\Services\RefundService;

$service = app(RefundService::class);

// Full refund - omit amount or pass empty array
$refund = $service->create('123456789');
// or
$refund = $service->create('123456789', []);

$refundId = data_get($refund, 'id');
$refundedAmount = data_get($refund, 'amount');

Full vs Partial Refunds

Full Refund

Refunds the entire payment amount:
// Original payment: $100.00
$refund = $refundService->create($paymentId);
// Refunds: $100.00
After a full refund:
  • Payment status changes to refunded
  • Customer receives the complete payment amount
  • No additional refunds can be processed

Partial Refund

Refunds a portion of the payment:
// Original payment: $100.00
$refund = $refundService->create($paymentId, ['amount' => 30.00]);
// Refunds: $30.00, Remaining: $70.00
Partial refund rules:
  • You can process multiple partial refunds
  • Total refunds cannot exceed original payment amount
  • Payment status may change depending on total refunded

Common Patterns

Refund with validation

Validate payment status before refunding:
use Fitodac\LaravelMercadoPago\Services\PaymentService;
use Fitodac\LaravelMercadoPago\Services\RefundService;

$paymentService = app(PaymentService::class);
$refundService = app(RefundService::class);

// Check payment status
$payment = $paymentService->get($paymentId);

if (data_get($payment, 'status') !== 'approved') {
    throw new \Exception('Only approved payments can be refunded');
}

// Process refund
$refund = $refundService->create($paymentId);

Refund with reason tracking

Store refund details in your database:
use Fitodac\LaravelMercadoPago\Services\RefundService;
use App\Models\Refund;

$refund = app(RefundService::class)->create($paymentId, [
    'amount' => 50.00,
]);

Refund::create([
    'order_id' => $orderId,
    'mercadopago_payment_id' => $paymentId,
    'mercadopago_refund_id' => data_get($refund, 'id'),
    'amount' => data_get($refund, 'amount'),
    'reason' => request()->input('reason'),
    'processed_by' => auth()->id(),
    'processed_at' => now(),
]);

Automatic refund on order cancellation

use Fitodac\LaravelMercadoPago\Services\RefundService;

public function cancelOrder(Order $order)
{
    if ($order->status === 'paid' && $order->mercadopago_payment_id) {
        try {
            $refund = app(RefundService::class)
                ->create($order->mercadopago_payment_id);
            
            $order->update([
                'status' => 'refunded',
                'refund_id' => data_get($refund, 'id'),
            ]);
        } catch (\Exception $e) {
            \Log::error('Refund failed during order cancellation', [
                'order_id' => $order->id,
                'payment_id' => $order->mercadopago_payment_id,
                'error' => $e->getMessage(),
            ]);
            
            throw new \Exception('Could not process refund');
        }
    }
    
    $order->update(['status' => 'cancelled']);
}

Calculate refundable amount

Check how much can still be refunded:
use Fitodac\LaravelMercadoPago\Services\PaymentService;

$payment = app(PaymentService::class)->get($paymentId);

$totalAmount = data_get($payment, 'transaction_amount');
$refundedAmount = data_get($payment, 'transaction_amount_refunded', 0);
$refundableAmount = $totalAmount - $refundedAmount;

if ($refundableAmount > 0) {
    // Can still refund up to $refundableAmount
}

Error Handling

MercadoPagoConfigurationException
exception
Thrown when:
  • MercadoPago SDK is not installed
  • Required SDK classes are not found
  • SDK methods are unavailable
MercadoPagoException
exception
Thrown by the MercadoPago SDK for API errors:
  • Invalid credentials
  • Payment not found
  • Payment not refundable (wrong status)
  • Refund amount exceeds available amount
  • Refund window expired
  • Network errors

Error handling example

use Fitodac\LaravelMercadoPago\Exceptions\MercadoPagoConfigurationException;

try {
    $refund = $refundService->create($paymentId, $payload);
    
    return response()->json([
        'success' => true,
        'refund_id' => data_get($refund, 'id'),
    ]);
} catch (MercadoPagoConfigurationException $e) {
    return response()->json(['error' => 'Service misconfigured'], 500);
} catch (\Exception $e) {
    $message = $e->getMessage();
    
    if (str_contains($message, 'not found')) {
        return response()->json(['error' => 'Payment not found'], 404);
    }
    
    if (str_contains($message, 'status')) {
        return response()->json(['error' => 'Payment cannot be refunded'], 422);
    }
    
    \Log::error('Refund failed', [
        'payment_id' => $paymentId,
        'error' => $message,
    ]);
    
    return response()->json(['error' => 'Refund processing failed'], 500);
}

Refund Limitations

Refund availability depends on several factors:
  • Payment method: Some methods have limited refund windows
  • Time elapsed: Refunds may not be available after a certain period
  • Payment status: Only approved payments can be refunded
  • Amount: Partial refunds cannot exceed remaining balance

Typical refund windows

  • Credit cards: Up to 180 days
  • Debit cards: Up to 90 days
  • Cash payments: Varies by provider
Check MercadoPago documentation for specific refund policies by payment method.

PaymentService

Create and retrieve payments

WebhookService

Handle refund notifications

Additional Resources

Refunds Guide

Complete guide to processing refunds

MercadoPago API Documentation

Official refunds API reference

Build docs developers (and LLMs) love