API de Mercado Pago con Node.js y TypeScript

La API de Mercado Pago desde Node.js y TypeScript: SDK oficial o fetch, credenciales, pagos, preferencias, idempotencia y errores, con código real.

MC
Martin Coll

La API de Mercado Pago es una API REST en https://api.mercadopago.com que se autentica con un access token en el header Authorization: Bearer …. Todo lo demás —Checkout Pro, pagos, webhooks— sale de ahí. Esta guía cubre lo que necesitás para integrarla bien desde un backend Node.js con TypeScript, con el código que usamos en producción en useDeploy.

Credenciales: de prueba y de producción

En el panel de desarrolladores de Mercado Pago cada aplicación tiene dos juegos de credenciales: de prueba y de producción. Cada juego trae una public key (para el frontend, si usás Bricks) y un access token (solo backend).

Reglas que no conviene romper:

  • El access token va en una variable de entorno del servidor (MERCADO_PAGO_ACCESS_TOKEN). En Next.js, nunca con prefijo NEXT_PUBLIC_: ese prefijo lo manda al navegador.
  • Probá con las credenciales de prueba y con usuarios de prueba, no con tu cuenta real.
  • El secret para validar webhooks es otro valor distinto; lo ves en la configuración de Webhooks de la aplicación.

¿SDK oficial o fetch directo?

El SDK oficial es el paquete mercadopago de npm (versión 3.6.1 al 4 de noviembre de 2026). Expone clases como MercadoPagoConfig, Payment y Preference, valida firmas de webhooks y agrega solo el header de idempotencia en los POST.

import { MercadoPagoConfig, Preference } from 'mercadopago';

const client = new MercadoPagoConfig({
  accessToken: process.env.MERCADO_PAGO_ACCESS_TOKEN!,
  options: { timeout: 15_000 },
});
const preference = await new Preference(client).create({ body: { /* … */ } });

En useDeploy elegimos fetch directo, por tres motivos concretos:

  1. Usamos dos endpoints: crear una preferencia y leer un pago. No hace falta una dependencia entera para eso.
  2. Tipos propios y chicos. Declaramos solo los campos que leemos, así un cambio en la respuesta que no nos afecta no rompe el build.
  3. Control del timeout y de los errores, con AbortSignal.timeout y un Result tipado en vez de excepciones.

Si vas a usar muchos productos de Mercado Pago (Bricks, suscripciones, órdenes), el SDK te ahorra trabajo. Si es Checkout Pro y webhooks, fetch alcanza.

Leer un pago

Es la llamada que más vas a hacer, porque el webhook solo te avisa que algo pasó (más sobre eso en webhooks de Mercado Pago):

const MP_API_BASE = 'https://api.mercadopago.com';

interface MpPayment {
  readonly id: number;
  readonly status: string; // 'approved' | 'rejected' | 'cancelled' | 'pending' | …
  readonly transaction_amount: number; // unidades MAYORES: 199.5 = $199,50
  readonly currency_id: string; // 'ARS', 'MXN', 'BRL', …
  readonly external_reference?: string;
  readonly metadata?: Record<string, unknown>;
}

export async function getPayment(paymentId: string): Promise<MpPayment> {
  const res = await fetch(`${MP_API_BASE}/v1/payments/${paymentId}`, {
    headers: { Authorization: `Bearer ${process.env.MERCADO_PAGO_ACCESS_TOKEN}` },
    signal: AbortSignal.timeout(15_000),
  });
  if (!res.ok) throw new Error(`GET /v1/payments/${paymentId} → ${res.status}`);
  return (await res.json()) as MpPayment;
}

Dos detalles que causan bugs reales:

  • Los montos van en unidades mayores. transaction_amount: 199.5 son $199,50. Si tu base guarda centavos (como debería), convertí en el borde: Math.round(amount * 100) al leer y cents / 100 al crear.
  • external_reference es tu puente. Es el id de tu sistema (usuario, orden) que mandaste al crear la preferencia. Con eso sabés a quién acreditar, sin confiar en nada que venga del navegador.

Crear una preferencia (Checkout Pro)

POST /checkout/preferences devuelve un init_point, la URL de pago a la que redirigís al usuario. El flujo completo —qué campos mandar, por qué el precio tiene que salir del servidor y cómo tratar la vuelta— está en Checkout Pro de Mercado Pago en Next.js.

Idempotencia

Los POST que crean cosas (por ejemplo, un pago con POST /v1/payments) aceptan el header X-Idempotency-Key: si un timeout te hace reintentar, la misma clave evita que se cree dos veces. El SDK oficial lo agrega solo; con fetch lo ponés vos:

await fetch(`${MP_API_BASE}/v1/payments`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
    'X-Idempotency-Key': orderId, // estable por intento lógico, no un random por request
  },
  body: JSON.stringify(payload),
});

Del otro lado también hace falta idempotencia: Mercado Pago puede mandarte el mismo webhook varias veces, así que tu escritura en la base tiene que tolerar duplicados (un upsert sobre el id del pago resuelve casi todo).

Errores y timeouts

  • Poné timeout a cada llamada (15 s en nuestro caso). Sin timeout, una API lenta te deja requests colgados.
  • Ante un 4xx, logueá el cuerpo de la respuesta: Mercado Pago explica ahí qué campo falló.
  • Ante un 5xx o un timeout en un webhook, respondé con error para que Mercado Pago reintente; no marques nada como pagado sin haber leído el pago.

Dónde poner todo esto

La parte más cara de integrar Mercado Pago no es la API; es dejarla bien encapsulada. En useDeploy todo vive en un solo adapter detrás de un puerto de billing, así el resto de la app no sabe si cobra con Mercado Pago o con Polar (cómo funciona ese puerto). Si estás eligiendo cómo cobrar en pesos, este post compara las opciones.

Preguntas frecuentes

¿Necesito el SDK oficial para usar la API? No. Es una API REST con autenticación Bearer; fetch alcanza. El SDK suma comodidad (clases tipadas, validador de firmas, idempotencia automática).

¿Cómo pruebo sin plata real? Con las credenciales de prueba de tu aplicación y usuarios de prueba. Nunca mezcles credenciales de prueba con una cuenta real.

¿Sirve para suscripciones? Mercado Pago tiene una API de suscripciones (preapproval). El adapter de useDeploy hoy hace pagos únicos con Checkout Pro y no implementa suscripciones; si se las pedís, devuelve un error explícito.

¿En qué moneda cobro? La que define cada ítem con currency_id (ARS, MXN, BRL…). Usá la moneda local de tu cuenta de Mercado Pago; cada país opera con la suya.


useDeploy trae este adapter de Mercado Pago ya integrado —Checkout Pro, webhooks con firma verificada e idempotencia— junto con auth, organizaciones y el resto de la base de un SaaS. Mirá la integración.