Skip to main content

Overview

The Webhooks API endpoint receives and validates notifications from Mercado Pago about events such as payment updates, refunds, and other transaction-related activities. This endpoint is always active and is the primary way to receive real-time updates from Mercado Pago.
Unlike other demo endpoints, the webhook endpoint is always active in all environments and is essential for production use.

Receive Webhook

Receives and processes webhook notifications from Mercado Pago.

Endpoint

POST /api/mercadopago/webhooks

Request Headers

x-signature
string
HMAC signature for webhook validation. Format: ts={timestamp},v1={hash}
Required when MERCADOPAGO_WEBHOOK_SECRET is configured. Used to verify the authenticity of the webhook.
x-request-id
string
Unique request identifier from Mercado Pago. Required for signature validation.

Query Parameters

id
string
Resource ID (alternative to data.id in body)
topic
string
Notification topic (alternative to type in body)

Request Body Parameters

action
string
Action that triggered the notification (e.g., “payment.created”, “payment.updated”)
api_version
string
Mercado Pago API version
type
string
Notification type/topic (e.g., “payment”, “merchant_order”)
data
object
Notification data object
live_mode
boolean
Whether the notification is from production (true) or sandbox (false)
user_id
string
Mercado Pago user ID

Request Example

{
  "action": "payment.updated",
  "api_version": "v1",
  "type": "payment",
  "data": {
    "id": "1234567890"
  },
  "live_mode": true,
  "user_id": "987654321"
}

Response

ok
boolean
Indicates if the webhook was received successfully
data
object
Processed webhook information
meta
array
Additional metadata (typically empty)

Response Example

{
  "ok": true,
  "data": {
    "acknowledged": true,
    "validated": true,
    "topic": "payment",
    "resource": "1234567890",
    "payload": {
      "action": "payment.updated",
      "api_version": "v1",
      "type": "payment",
      "data": {
        "id": "1234567890"
      },
      "live_mode": true,
      "user_id": "987654321"
    }
  },
  "meta": []
}

cURL Example

curl --request POST \
  --url http://localhost:8000/api/mercadopago/webhooks \
  --header 'Content-Type: application/json' \
  --header 'x-signature: ts=1234567890,v1=abcdef123456' \
  --header 'x-request-id: unique-request-id' \
  --data '{
    "type": "payment",
    "data": {
      "id": "123"
    }
  }'

Error Responses

Invalid Signature (401)

{
  "ok": false,
  "message": "Invalid webhook signature"
}

General Error (500)

{
  "ok": false,
  "message": "Error message describing what went wrong"
}

Signature Validation

When MERCADOPAGO_WEBHOOK_SECRET is configured, the package validates the webhook signature to ensure authenticity.

Validation Process

  1. Extract ts (timestamp) and v1 (hash) from x-signature header
  2. Get x-request-id from headers
  3. Get resource id from query string or request body data.id
  4. Construct manifest: id={id}&request-id={request-id}&ts={ts}
  5. Compute HMAC-SHA256 hash using webhook secret
  6. Compare computed hash with provided v1 hash
Production Requirement: Always configure MERCADOPAGO_WEBHOOK_SECRET in production to ensure webhook authenticity and prevent unauthorized notifications.

Signature Header Format

x-signature: ts=1234567890,v1=abc123def456...

Implementation Notes

Availability

The webhook endpoint is always active regardless of environment or demo route settings. This is necessary to receive production notifications.

Controller Reference

Implemented in: src/Http/Controllers/Api/WebhookController.php:13 Request validation: src/Http/Requests/StoreWebhookRequest.php:16
  • WebhookService - Service layer for webhook processing and validation

Webhook Topics

Mercado Pago sends notifications for various events:
TopicDescription
paymentPayment created, updated, or cancelled
merchant_orderOrder created or updated
subscriptionSubscription events
point_integration_whPoint of sale integration events

Best Practices

Idempotency: Mercado Pago may send duplicate notifications. Always check if you’ve already processed a notification before taking action.
Async Processing: Process webhook notifications asynchronously using queued jobs. Return a 200 response quickly to avoid timeouts.
Verify Resource: Always fetch the actual resource from Mercado Pago to verify its current state. Don’t rely solely on webhook data for critical operations.
  1. Receive webhook → Return 200 immediately
  2. Validate signature → If configured
  3. Dispatch job → Process asynchronously
  4. Fetch resource → Get current state from Mercado Pago
  5. Update database → Persist the current state
  6. Trigger actions → Send emails, update orders, etc.

Example Production Implementation

use Fitodac\LaravelMercadoPago\Services\WebhookService;
use Fitodac\LaravelMercadoPago\Services\PaymentService;

final class MercadoPagoWebhookController
{
    public function handle(
        Request $request,
        WebhookService $webhookService,
        PaymentService $paymentService
    ): JsonResponse {
        // Validate webhook
        $webhook = $webhookService->handle($request);
        
        if (!$webhook['validated']) {
            return response()->json(['error' => 'Invalid signature'], 401);
        }
        
        // Dispatch async job
        if ($webhook['topic'] === 'payment') {
            ProcessPaymentWebhook::dispatch($webhook['resource']);
        }
        
        return response()->json(['ok' => true]);
    }
}

Example Queued Job

use Fitodac\LaravelMercadoPago\Services\PaymentService;

final class ProcessPaymentWebhook implements ShouldQueue
{
    public function __construct(private string $paymentId) {}
    
    public function handle(PaymentService $paymentService): void
    {
        // Fetch current payment state
        $payment = $paymentService->get($this->paymentId);
        
        // Check if already processed
        $localPayment = Payment::where('mercadopago_id', $this->paymentId)->first();
        
        if ($localPayment && $localPayment->status === $payment['status']) {
            return; // Already processed
        }
        
        // Update local database
        $localPayment->update([
            'status' => $payment['status'],
            'status_detail' => $payment['status_detail'],
        ]);
        
        // Trigger business logic based on status
        if ($payment['status'] === 'approved') {
            // Send confirmation email, fulfill order, etc.
        }
    }
}

Configuration

Required Environment Variables

# Required for webhook validation
MERCADOPAGO_WEBHOOK_SECRET=your_webhook_secret_here

Configuring in Mercado Pago

  1. Go to your Mercado Pago dashboard
  2. Navigate to DevelopersWebhooks
  3. Add your webhook URL: https://your-domain.com/api/mercadopago/webhooks
  4. Copy the Secret and set it as MERCADOPAGO_WEBHOOK_SECRET
  5. Select the events you want to receive

Testing Webhooks

Local Testing

  1. Use a tunneling service (ngrok, expose, etc.) to expose your local server:
ngrok http 8000
  1. Configure the public URL in Mercado Pago’s webhook settings
  2. Trigger test events from Mercado Pago dashboard or by performing test transactions

Manual Testing

curl --request POST \
  --url http://localhost:8000/api/mercadopago/webhooks \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "payment",
    "data": {"id": "123"}
  }'
When testing locally without MERCADOPAGO_WEBHOOK_SECRET, the webhook will be processed but validated will be false.

Build docs developers (and LLMs) love