Mercado Pago — Módulo de Pagos Únicos
Módulo completo para manejar pagos únicos a través de Mercado Pago (Checkout Pro) en un proyecto de Next.js App Router. Sin suscripciones ni facturación: solo checkout, consulta de pagos, cancelación, reembolsos, métodos de pago y webhooks.
Dependencia
Este módulo usa el SDK oficial de Node de Mercado Pago:
mercadopago(v2)
Estructura de archivos
lib/mercadopago/
service.ts Direct Mercado Pago SDK calls. Internal only — don't import
this from components, go through actions.ts or client.ts.
actions.ts Server-side functions ready to import into server components
(or call as Server Actions from client components).
types.ts Shared TypeScript types for preferences, payments, refunds,
payment methods, and webhook payloads.
client.ts The one client-side helper this module needs: redirecting
the browser to the Mercado Pago checkout page.
app/api/webhooks/mercadopago/route.ts
Receives and validates Mercado Pago's payment notifications.
.env.local
Environment variables (see below).
Variables de entorno
| Variable | Dónde encontrarla |
|---|---|
MERCADOPAGO_ACCESS_TOKEN | Mercado Pago Developer Dashboard → Your application → Production/Test credentials → Access Token |
MERCADOPAGO_WEBHOOK_SECRET | Developer Dashboard → Your application → Webhooks → Signature secret |
NEXT_PUBLIC_APP_URL | La URL base pública de tu propia app (por ejemplo https://yourapp.com, o una URL de ngrok/túnel mientras probás webhooks en local) |
Usá credenciales de prueba mientras desarrollás: el sandbox de Mercado Pago te permite simular pagos aprobados/rechazados/pendientes con tarjetas de prueba, sin tocar la API de producción.
Cómo funciona el flujo
- Crear un checkout — llamá a
createCheckoutdeactions.tscon tus ítems yback_urls. DevuelveinitPoint(producción) ysandboxInitPoint(credenciales de prueba). - Redirigir al usuario — desde un client component, pasale esa URL a
redirectToCheckoutenclient.ts. - El usuario paga en la página de checkout alojada por Mercado Pago.
- Mercado Pago redirige de vuelta a la entrada de
back_urlsque coincida con el resultado (success,failureopending), agregando un query parampayment_id. - Vos resolvés el resultado leyendo
payment_idde la URL en esa página y llamando acheckPaymentSuccess/checkPaymentFailure/getPaymentStatusdeactions.ts. Son funciones simples, no páginas generadas: conectalas a la UI de éxito/fallo que ya tengas. - Mercado Pago también llama a tu webhook (
app/api/webhooks/mercadopago/route.ts) de forma independiente a la redirección: tratá al webhook como tu fuente de verdad para cumplir el pedido, y a la redirección solo como la UX de cara al usuario. El usuario puede saltearse la redirección (cerrando la pestaña, etc.); el webhook no.
Notas de nomenclatura (específicas de Mercado Pago)
- "Checkout" corresponde al recurso Preference de Mercado Pago (Checkout Pro).
No hay un paso separado de "crear pago" de nuestro lado: Mercado Pago
crea el recurso Payment real de su lado cuando el usuario paga;
nosotros solo lo leemos (
getPayment,getPaymentStatus). - La cancelación no es un endpoint dedicado: consiste en actualizar el
statusde un pago acancelled, y solo funciona mientras el pago estápendingoin_process. Un pagoapprovedno se puede cancelar, solo reembolsar. - Refund y partial refund usan el mismo endpoint subyacente:
omitir
amounthace un reembolso total, pasarlo hace uno parcial.actions.tslos expone como dos funciones separadas (refundPayment,partialRefundPayment) por claridad. - Payment methods (
getPaymentMethods) lista los métodos habilitados para tu cuenta/país de Mercado Pago; no está atado a un pago específico.
Configuración del webhook
- En el Developer Dashboard, andá a tu application → Webhooks.
- Definí la URL de notificación como
https://yourdomain.com/api/webhooks/mercadopago. - Suscribite al topic
payments. - Copiá el signature secret que aparece ahí en
MERCADOPAGO_WEBHOOK_SECRET.
El route handler verifica el header x-signature en cada request y
rechaza todo lo que no coincida antes de tocar el pago: mirá el
comentario en verifyWebhookSignature de service.ts para ver cómo se
calcula la firma.
Para completar tu propia lógica de negocio (marcar un pedido como pagado, enviar un
email de confirmación, etc.), editá el bloque comentado dentro de
app/api/webhooks/mercadopago/route.ts.
Opcional: una tabla de "orders"
Este módulo solo habla con Mercado Pago: no persiste nada. Si querés registrar las compras en tu propia base de datos, una tabla genérica mínima se ve así:
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
external_reference TEXT UNIQUE NOT NULL, -- pass this as externalReference to createCheckout
mp_payment_id TEXT, -- filled in once the webhook resolves the payment
status TEXT NOT NULL DEFAULT 'pending', -- pending | approved | cancelled | refunded
amount NUMERIC NOT NULL,
currency TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Flujo típico: creá una fila con status = 'pending' justo antes de llamar a
createCheckout, y después actualizá status y mp_payment_id dentro del
handler del webhook una vez que leas el estado real del pago.
Manejo de errores
Cada función de service.ts y actions.ts devuelve un objeto de resultado
en lugar de lanzar errores:
{ success: true, data: T } | { success: false, error: string, code?: string }
Verificá siempre result.success antes de usar result.data.