Skip to main content
Payment preferences are the foundation of MercadoPago’s checkout experience. They define what items your customer is purchasing, where to redirect them after payment, and how to notify your system.

Understanding preferences

A preference contains:
  • Items: Products or services being purchased
  • Payer information: Buyer’s email and optional details
  • URLs: Where to redirect users after payment
  • Notification URL: Where MercadoPago sends webhook notifications
  • External reference: Your internal order/transaction ID

Basic preference creation

<?php

use Fitodac\LaravelMercadoPago\Services\PreferenceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

final class CheckoutPreferenceController
{
    public function store(
        Request $request,
        PreferenceService $preferenceService
    ): JsonResponse {
        $payload = $request->validate([
            'items' => ['required', 'array', 'min:1'],
            'items.*.title' => ['required', 'string'],
            'items.*.quantity' => ['required', 'integer', 'min:1'],
            'items.*.unit_price' => ['required', 'numeric', 'min:0'],
            'payer' => ['sometimes', 'array'],
            'back_urls' => ['sometimes', 'array'],
            'notification_url' => ['sometimes', 'url'],
            'external_reference' => ['sometimes', 'string'],
        ]);

        $preference = $preferenceService->create($payload);

        return response()->json([
            'id' => data_get($preference, 'id'),
            'init_point' => data_get($preference, 'init_point'),
            'sandbox_init_point' => data_get($preference, 'sandbox_init_point'),
        ], 201);
    }
}

Payload structure

Required fields

The minimum payload requires only an items array:
{
  "items": [
    {
      "title": "Product name",
      "quantity": 1,
      "unit_price": 100.50
    }
  ]
}

Complete payload example

Here’s a comprehensive example with all common options:
{
  "items": [
    {
      "title": "Premium Subscription",
      "quantity": 1,
      "unit_price": 99.99,
      "currency_id": "ARS",
      "description": "Monthly premium subscription"
    }
  ],
  "payer": {
    "email": "[email protected]",
    "name": "Ada",
    "surname": "Lovelace",
    "phone": {
      "area_code": "11",
      "number": "12345678"
    },
    "address": {
      "street_name": "Calle Falsa",
      "street_number": 123,
      "zip_code": "1234"
    }
  },
  "back_urls": {
    "success": "https://your-app.com/payments/success",
    "pending": "https://your-app.com/payments/pending",
    "failure": "https://your-app.com/payments/failure"
  },
  "notification_url": "https://your-app.com/api/mercadopago/webhooks",
  "external_reference": "ORDER-12345",
  "expires": true,
  "expiration_date_from": "2026-03-10T09:00:00.000-03:00",
  "expiration_date_to": "2026-03-15T23:59:59.000-03:00",
  "metadata": {
    "user_id": "98765",
    "subscription_plan": "premium"
  }
}

Field validation rules

The package validates preference payloads using these rules:
FieldRuleDescription
itemsrequired, array, min:1At least one item required
items.*.titlerequired, stringItem name
items.*.quantityrequired, integer, min:1Quantity must be positive
items.*.unit_pricerequired, numeric, min:0Price cannot be negative
payersometimes, arrayOptional payer information
back_urlssometimes, arrayOptional redirect URLs
notification_urlsometimes, urlMust be valid URL
external_referencesometimes, stringYour internal reference
expiressometimes, booleanEnable expiration
expiration_date_fromsometimes, dateExpiration start
expiration_date_tosometimes, dateExpiration end
metadatasometimes, arrayCustom data
Validation rules are defined in src/Http/Requests/CreatePreferenceRequest.php

Understanding URLs

Back URLs

These URLs define where users are redirected after payment:
{
  "back_urls": {
    "success": "https://your-app.com/payments/success",
    "pending": "https://your-app.com/payments/pending",
    "failure": "https://your-app.com/payments/failure"
  }
}
  • success: Payment approved
  • pending: Payment pending (e.g., bank transfer)
  • failure: Payment rejected or cancelled
Back URLs are for user experience only. Never rely on them for payment confirmation. Always use webhooks for server-side processing.

Notification URL

The notification URL receives webhook POST requests when payment status changes:
{
  "notification_url": "https://your-app.com/api/mercadopago/webhooks"
}
The package automatically registers a webhook endpoint at /api/mercadopago/webhooks. You can use this URL or create your own custom endpoint.

Response structure

Successful preference creation returns:
{
  "ok": true,
  "data": {
    "id": "1234567890-abc123def456",
    "init_point": "https://www.mercadopago.com/checkout/v1/redirect?pref_id=...",
    "sandbox_init_point": "https://sandbox.mercadopago.com/checkout/v1/redirect?pref_id=...",
    "date_created": "2026-03-10T10:30:00.000-03:00",
    "items": [...],
    "payer": {...},
    "back_urls": {...},
    "external_reference": "ORDER-12345"
  },
  "meta": []
}

Key response fields

  • id: Preference unique identifier
  • init_point: Production checkout URL
  • sandbox_init_point: Testing checkout URL

Common patterns

Linking preferences to orders

Use external_reference to link preferences to your internal orders:
$order = Order::create([...]);

$preference = $preferenceService->create([
    'items' => $order->items->map(fn($item) => [
        'title' => $item->name,
        'quantity' => $item->quantity,
        'unit_price' => $item->price,
    ])->toArray(),
    'external_reference' => "ORDER-{$order->id}",
    'notification_url' => route('mercadopago.webhooks.store'),
]);

Adding metadata

Store custom data for webhook processing:
$preference = $preferenceService->create([
    'items' => [...],
    'metadata' => [
        'user_id' => auth()->id(),
        'subscription_plan' => 'premium',
        'source' => 'web',
    ],
]);

Setting expiration

Create time-limited payment preferences:
$preference = $preferenceService->create([
    'items' => [...],
    'expires' => true,
    'expiration_date_from' => now()->toIso8601String(),
    'expiration_date_to' => now()->addHours(24)->toIso8601String(),
]);

Next steps

Processing Payments

Learn about direct payment processing

Handling Webhooks

Process payment notifications

Build docs developers (and LLMs) love