This guide explains how to integrate Mercado Pago payment gateway with the Laravel Payments package.
Mercado Pago is a leading payment platform in Latin America that supports multiple payment methods including:
Since Mercado Pago SDK is already included in the package dependencies, you just need to ensure you have the Laravel Payments package installed and configured.
Add your Mercado Pago credentials to your .env file:
MERCADOPAGO_ACCESS_TOKEN=your_access_token_here
MERCADOPAGO_PUBLIC_KEY=your_public_key_here
MERCADOPAGO_TEST_MODE=true
MERCADOPAGO_COUNTRY=MX
MERCADOPAGO_WEBHOOK_SECRET=your_webhook_secret_here
MERCADOPAGO_RETURN_URL=https://yoursite.com/payment/success
MERCADOPAGO_CALLBACK_URL=https://yoursite.com/mercadopago/callback
You can obtain these credentials from your Mercado Pago developer dashboard.
For development/testing:
MERCADOPAGO_TEST_MODE=true
MERCADOPAGO_ACCESS_TOKEN=TEST-1234567890-abcdef
MERCADOPAGO_PUBLIC_KEY=TEST-public-key
For production:
MERCADOPAGO_TEST_MODE=false
MERCADOPAGO_ACCESS_TOKEN=APP_USR-1234567890-abcdef
MERCADOPAGO_PUBLIC_KEY=APP_USR-public-key
You also need to add the configuration to your config/services.php:
'mercadopago' => [
'access_token' => env('MERCADOPAGO_ACCESS_TOKEN'),
'public_key' => env('MERCADOPAGO_PUBLIC_KEY'),
'test_mode' => env('MERCADOPAGO_TEST_MODE', true),
'country' => env('MERCADOPAGO_COUNTRY', 'MX'),
'webhook_secret' => env('MERCADOPAGO_WEBHOOK_SECRET'),
'return_url' => env('MERCADOPAGO_RETURN_URL'),
'callback_url' => env('MERCADOPAGO_CALLBACK_URL'),
],
use Mdiqbal\LaravelPayments\Facades\Payment;
$paymentRequest = [
'amount' => 100.00,
'currency' => 'MXN',
'email' => 'customer@example.com',
'transaction_id' => 'TXN' . time(),
'redirect_url' => 'https://yoursite.com/payment/callback',
'customer' => [
'name' => 'Juan Pérez',
'phone' => '5512345678',
'address' => '123 Main St',
'city' => 'Mexico City',
'country' => 'MX',
'postal_code' => '06000',
'identification' => [
'type' => 'DNI',
'number' => '12345678'
]
],
'metadata' => [
'order_id' => 'ORD123456',
'user_id' => 789
]
];
$payment = Payment::gateway('mercadopago')->pay($paymentRequest);
This will return a payment URL that you need to redirect the user to:
if ($payment['success']) {
// Store preference_id for later verification
session(['mercadopago_preference_id' => $payment['preference_id']]);
// Redirect to Mercado Pago payment page
return redirect($payment['payment_url']);
}
// routes/web.php
Route::get('/payment/success', [PaymentController::class, 'success']);
Route::post('/mercadopago/callback', [MercadoPagoController::class, 'callback']);
// app/Http/Controllers/MercadoPagoController.php
use Mdiqbal\LaravelPayments\Facades\Payment;
class MercadoPagoController extends Controller
{
public function callback(Request $request)
{
// Parse callback data
$gateway = Payment::gateway('mercadopago');
$callbackData = $gateway->parseCallback($request);
// Process the webhook
$result = $gateway->verify($callbackData);
if ($result['success']) {
$transactionId = $result['transaction_id'];
$status = $result['status'];
if ($status === 'completed') {
// Update order status
$order = Order::where('transaction_id', $transactionId)->first();
if ($order) {
$order->status = 'paid';
$order->paid_at = now();
$order->payment_method = $result['payment_method_id'];
$order->mercado_pago_payment_id = $result['payment_id'];
$order->save();
}
}
}
return response()->json(['status' => 'received']);
}
public function success(Request $request)
{
// Handle successful return from payment page
// Note: Always rely on callback for final status confirmation
return view('payment.success');
}
}
// First, get the preference ID (stored during initialization)
$preferenceId = session('mercadopago_preference_id');
$verification = Payment::gateway('mercadopago')->verify($preferenceId);
if ($verification['success']) {
$status = $verification['status'];
if ($status === 'completed') {
// Payment was successful
$paymentId = $verification['payment_id'];
$amount = $verification['amount'];
$currency = $verification['currency'];
}
}
$refundData = [
'payment_id' => 'MERCADO_PAGO_PAYMENT_ID',
'amount' => 50.00, // Optional - omit for full refund
'reason' => 'Customer requested refund'
];
$refund = Payment::gateway('mercadopago')->refund($refundData);
$customerData = [
'email' => 'customer@example.com',
'first_name' => 'Juan',
'last_name' => 'Pérez',
'phone' => [
'area_code' => '55',
'number' => '12345678'
],
'identification' => [
'type' => 'DNI',
'number' => '12345678'
],
'description' => 'Premium customer',
'metadata' => [
'internal_id' => 123,
'segment' => 'premium'
]
];
$customer = Payment::gateway('mercadopago')->createCustomer($customerData);
// Search by external reference
$results = Payment::gateway('mercadopago')->searchTransactions([
'external_reference' => 'TXN123456',
'limit' => 20
]);
// Search with multiple filters
$results = Payment::gateway('mercadopago')->searchTransactions([
'payment_type_id' => 'credit_card',
'payment_method_id' => 'visa',
'status' => 'approved',
'date_created_from' => '2024-01-01T00:00:00Z',
'date_created_to' => '2024-12-31T23:59:59Z',
'limit' => 50
]);
$paymentId = '1234567890';
$status = Payment::gateway('mercadopago')->getTransactionStatus($paymentId);
$subscriptionData = [
'plan_id' => 'PREAPPROVAL_PLAN_ID',
'payer_email' => 'customer@example.com',
'back_url' => 'https://yoursite.com/subscription/return',
'reason' => 'Monthly Premium Subscription',
'external_reference' => 'SUB-' . time(),
'auto_recurring' => [
'frequency' => 1,
'frequency_type' => 'months',
'transaction_amount' => 99.99,
'currency_id' => 'MXN'
]
];
$subscription = Payment::gateway('mercadopago')->createSubscription($subscriptionData);
$subscriptionId = 'SUBSCRIPTION_ID';
$result = Payment::gateway('mercadopago')->cancelSubscription($subscriptionId);
Mercado Pago uses webhooks to notify your application about payment status changes.
Configure your webhook URL in the Mercado Pago dashboard
Create a route to handle webhooks:
// routes/web.php
Route::post('/mercadopago/webhook', [MercadoPagoWebhookController::class, 'handleWebhook']);
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mdiqbal\LaravelPayments\Facades\Payment;
class MercadoPagoWebhookController extends Controller
{
public function handleWebhook(Request $request)
{
$gateway = Payment::gateway('mercadopago');
$callbackData = $gateway->parseCallback($request);
// Process the webhook
$result = $gateway->verify($callbackData);
if ($result['success']) {
$eventType = $result['event_type'];
$transactionId = $result['transaction_id'];
$paymentId = $result['payment_id'];
switch ($eventType) {
case 'payment.approved':
$this->handleApprovedPayment($result);
break;
case 'payment.pending':
$this->handlePendingPayment($result);
break;
case 'payment.rejected':
case 'payment.cancelled':
$this->handleFailedPayment($result);
break;
case 'payment.refunded':
$this->handleRefundedPayment($result);
break;
case 'payment.in_mediation':
$this->handleDisputedPayment($result);
break;
case 'chargeback.created':
$this->handleChargeback($result);
break;
}
}
// Always return 200 OK to acknowledge receipt
return response()->json(['status' => 'received']);
}
protected function handleApprovedPayment($data)
{
// Update order status
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'paid';
$order->paid_at = now();
$order->payment_method = $data['payment_method_id'];
$order->mercado_pago_payment_id = $data['payment_id'];
$order->merchant_info = array_merge($order->merchant_info ?? [], $data['merchant_info']);
$order->save();
// Send confirmation email
Mail::to($order->customer_email)->send(new PaymentConfirmation($order));
}
}
protected function handlePendingPayment($data)
{
// Payment is being processed
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'processing';
$order->save();
}
}
protected function handleFailedPayment($data)
{
// Log failed payment
Log::warning('Mercado Pago payment failed', [
'transaction_id' => $data['transaction_id'],
'payment_id' => $data['payment_id'],
'event_type' => $data['event_type']
]);
// Update order status
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'failed';
$order->save();
}
}
protected function handleRefundedPayment($data)
{
// Process refund
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'refunded';
$order->refunded_at = now();
$order->save();
}
}
protected function handleDisputedPayment($data)
{
// Payment is in mediation/dispute
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'disputed';
$order->disputed_at = now();
$order->save();
}
}
protected function handleChargeback($data)
{
// Handle chargeback
Log::warning('Mercado Pago chargeback received', [
'chargeback_id' => $data['chargeback_id']
]);
}
}
pay() to create a preference and get checkout URLverify() to confirm payment statusMercado Pago uses HMAC SHA256 signatures for webhook security:
ts={timestamp},v1={hash}webhook_secret is configuredThe Mercado Pago gateway provides detailed error messages:
$payment = Payment::gateway('mercadopago')->pay($paymentRequest);
if (!$payment['success']) {
$error = $payment['error'];
$message = $error['message'];
$code = $error['code'];
// Handle error based on type
if ($code === 'PAYMENT_FAILED') {
// Payment initialization failed
}
}
PAYMENT_FAILED - Payment initialization failedVERIFICATION_FAILED - Transaction verification failedREFUND_FAILED - Refund processing failedWEBHOOK_FAILED - Webhook processing failedINVALID_CURRENCY - Unsupported currencyINVALID_REQUEST - Invalid request parametersUse test credentials for development:
MERCADOPAGO_TEST_MODE=true
Use these test card details for testing:
Mercado Pago processes payments in local currencies for each supported country:
Mercado Pago primarily serves Latin America:
Mercado Pago implements rate limits to prevent abuse:
pay() - Initialize a payment and create preferenceverify($payload) - Verify webhook payloadverify($paymentId) - Verify a transaction statusrefund() - Process a refund (full or partial)getTransactionStatus() - Get transaction statuscreateCustomer() - Create a customer recordsearchTransactions() - Search transactions with filterscreateSubscription() - Create a recurring subscriptioncancelSubscription() - Cancel an active subscriptionparseCallback() - Parse webhook parameters from requestgetSupportedCurrencies() - Get supported currenciesgetGatewayConfig() - Get gateway configurationgetPaymentMethodsForCountry() - Get payment methods for a country// If you have multiple Mercado Pago accounts for different countries
$gateway = Payment::gateway('mercadopago', [
'access_token' => 'country_specific_token',
'country' => 'BR',
'public_key' => 'country_public_key'
]);
$paymentRequest = [
'amount' => 100.00,
'currency' => 'MXN',
'email' => 'customer@example.com',
'transaction_id' => 'TXN' . time(),
'items' => [
[
'title' => 'Product 1',
'quantity' => 2,
'unit_price' => 25.00,
'currency_id' => 'MXN'
],
[
'title' => 'Product 2',
'quantity' => 1,
'unit_price' => 50.00,
'currency_id' => 'MXN'
]
],
'redirect_url' => 'https://yoursite.com/payment/callback'
];
$payment = Payment::gateway('mercadopago')->pay($paymentRequest);
// Configure installments in the payment request
$paymentRequest = [
// ... other fields
'payment_methods' => [
'installments' => 12, // Max number of installments
'excluded_payment_methods' => [
['id' => 'amex'] // Exclude American Express
],
'excluded_payment_types' => [
['id' => 'debit_card'] // Exclude debit cards
]
]
];
Since Mercado Pago supports native recurring payments:
// Create a preapproval plan first
$planData = [
'reason' => 'Monthly Premium Subscription',
'auto_recurring' => [
'frequency' => 1,
'frequency_type' => 'months',
'transaction_amount' => 99.99,
'currency_id' => 'MXN'
],
'back_url' => 'https://yoursite.com/subscription/return',
'external_reference' => 'PLAN-' . time()
];
$subscription = Payment::gateway('mercadopago')->createSubscription($planData);
if ($subscription['success']) {
// Redirect user to subscribe
return redirect($subscription['init_point']);
}
class PaymentController extends Controller
{
public function initiate(Request $request)
{
$payment = Payment::gateway('mercadopago')->pay($request->all());
if ($payment['success']) {
// Store payment information in session
session([
'mercadopago_preference_id' => $payment['preference_id'],
'transaction_id' => $payment['transaction_id'],
'amount' => $payment['amount']
]);
return redirect($payment['payment_url']);
}
return back()->with('error', 'Failed to initialize payment');
}
public function success(Request $request)
{
// User returned from Mercado Pago after payment
// Wait for webhook confirmation before updating order
return view('payment.processing');
}
}
For Mercado Pago-specific support:
For Laravel Payments package support:
How can I help you explore Laravel packages today?