mp-facil
v0.2.0
Published
Biblioteca Node.js simples para criar cobranças, consultar pagamentos e interpretar webhooks do Mercado Pago.
Maintainers
Readme
MP Facil
Uma biblioteca ESM para Node.js que deixa o básico do Mercado Pago direto: criar uma cobrança, mandar o cliente para o checkout, consultar o pagamento e entender webhooks sem ficar atravessando a resposta crua da API.
A lib usa os endpoints oficiais:
- criar cobrança simples:
POST /checkout/preferences - criar Pix direto:
POST /v1/payments - consultar pagamento:
GET /v1/payments/{id} - webhook: evento
payment, com validação opcional dex-signature - alternativa ao webhook: loop local com memória em JSON
Instalação
npm install mp-facilRequer Node.js 18 ou superior.
import { MPFacil } from 'mp-facil';Neste repositório de teste:
import { MPFacil } from './src/index.js';Primeiro pagamento
Imagine uma lojinha vendendo uma assinatura chamada "Clube do Café".
import { MPFacil } from 'mp-facil';
const mp = new MPFacil({
apiKey: process.env.MP_ACCESS_TOKEN,
webhookUrl: 'https://minhaloja.com/webhooks/mercado-pago'
});
const cobranca = await mp.criarCobranca({
produto: 'Clube do Café - Plano Mensal',
id: 'assinatura-cafe-mensal',
preco: 39.9
});
if (!cobranca.ok) {
console.log(cobranca.mensagem);
process.exit(1);
}
console.log('Envie o cliente para:', cobranca.dados.link);Retorno esperado:
{
ok: true,
dados: {
id: '123456789-abcdef',
referencia: 'assinatura-cafe-mensal',
link: 'https://www.mercadopago.com.br/checkout/...',
linkProducao: 'https://www.mercadopago.com.br/checkout/...',
linkTeste: 'https://sandbox.mercadopago.com.br/checkout/...',
criadoEm: '2026-05-15T10:00:00.000Z',
modoTeste: false,
bruto: {}
}
}Qual Função Usar?
| Quero... | Use | O que recebo |
| --- | --- | --- |
| Mandar o cliente para o Checkout Pro do Mercado Pago | criarCobranca | Link de checkout (dados.link). |
| Gerar Pix copia e cola/QR Code direto na minha tela | criarPix | ID real do pagamento, QR Code, copia e cola e link Pix. |
| Criar assinatura recorrente com link de pagamento Pix | criarPixRecorrente | Link de assinatura (dados.link) e ID da assinatura. |
| Dividir pagamento em modelo marketplace | criarCobrancaDividida | Link de checkout com marketplace_fee. |
| Ver status uma vez | consultarCobranca | Status normalizado do pagamento. |
| Ver status da assinatura | consultarAssinatura | Status normalizado da assinatura recorrente. |
| Verificar até pagar/recusar | acompanharCobranca | Loop local com memória em JSON. |
Diferença importante:
criarCobrancacria uma preferência e retorna um link;criarPixcria um pagamento real e já retornadados.id, que pode ser consultado ou acompanhado por loop.criarPixRecorrentecria uma assinatura no Mercado Pago e retorna um link para o cliente concluir o pagamento inicial.
Preferência Não É Pagamento
Esse detalhe é pequeno, mas salva tempo.
Quando você chama:
const cobranca = await mp.criarCobranca({
produto: 'Clube do Café',
id: 'pedido-123',
preco: 39.9
});o campo abaixo é o ID da preferência de checkout:
cobranca.dados.idEle serve para montar/identificar o link de checkout, mas não é o ID do pagamento real. Por isso, isto aqui não é o fluxo correto:
await mp.consultarCobranca(cobranca.dados.id);Para consultar status, você precisa do ID do pagamento:
await mp.consultarCobranca(paymentId);Esse paymentId aparece depois que o comprador passa pelo checkout, normalmente por:
- webhook do Mercado Pago;
- URL de retorno (
success,failure,pending); - parâmetros como
payment_idoucollection_id.
Resumo rápido:
| Valor | De onde vem | Para que serve |
| --- | --- | --- |
| cobranca.dados.id | resposta de criarCobranca | ID da preferência/link de checkout. |
| cobranca.dados.link | resposta de criarCobranca | URL para enviar o comprador ao Mercado Pago. |
| payment_id / collection_id | retorno do checkout ou webhook | ID real do pagamento, usado em consultarCobranca e acompanharCobranca. |
| pagamento.referencia | id que você enviou ao criar cobrança | Seu ID interno de pedido/cobrança. |
Criando o cliente
Você pode usar o nome curto:
const mp = new MPFacil({
apiKey: 'APP_USR...',
appId: '123456789',
webhookUrl: 'https://minhaloja.com/webhook'
});Ou os aliases mantidos para quem prefere nomes mais explícitos:
import { MercadoPago, MercadoPagoSimplificado } from 'mp-facil';Campos aceitos no construtor:
| Campo | Aliases | Obrigatório | O que faz |
| --- | --- | --- | --- |
| apiKey | api_key, accessToken, access_token | Sim | Access Token do Mercado Pago. |
| appId | id_app, applicationId, application_id | Não | ID da aplicação. Hoje é validado e guardado, mas não é enviado em cobranças simples. |
| webhookUrl | webhook, urlWebhook, notificationUrl, notification_url | Não | URL enviada como notification_url ao criar cobrança. |
| webhookSecret | segredoWebhook, secret, webhook_secret | Não | Segredo usado para validar x-signature nos webhooks. |
| sandbox | modoTeste | Não | Liga modo teste desde o construtor. Padrão: false. |
| baseUrl | base_url | Não | Troca a URL base da API. Útil para testes. |
| fetch | fetchImpl | Não | Injeta uma implementação de fetch. Útil para testes. |
| memoryLoopPath | pastaMemoryLoop, memory_loop_path | Não | Pasta onde os loops de consulta ficam salvos. Padrão: memory loop. |
| autoResumeLoops | autoRetomarLoops | Não | Retoma automaticamente loops salvos quando o cliente é criado. Padrão: true. |
Se apiKey não for informada, o construtor lança erro API_KEY_AUSENTE.
Modo Teste
Por padrão, a lib trabalha em produção:
const mp = new MPFacil({ apiKey: 'APP_USR...' });Para tratar as ações como teste:
const mp = new MPFacil({ apiKey: 'APP_USR...' }).modoTeste(true);Também existe o alias em inglês:
mp.testMode(true);modoTeste(true) retorna a própria instância, então dá para encadear. Para desligar:
mp.modoTeste(false);Quando o modo teste está ligado:
criarCobrancaretornasandbox_init_pointemdados.link;criarCobrancaadicionametadata.mp_facil_modo_teste = trueao payload da preferência;criarCobrancaretornadados.modoTeste: true;consultarCobrancaretornadados.modoTeste: true.
Importante: a lib não troca suas credenciais sozinha. Use credenciais de teste do Mercado Pago quando quiser uma operação realmente isolada.
criarCobranca / createCharge
Cria uma preferência do Checkout Pro. É o caminho mais simples para criar uma cobrança usando só produto, referência interna e preço.
const resultado = await mp.criarCobranca({
produto: 'Workshop JavaScript Sem Drama',
id: 'workshop-js-2026',
preco: 89.9,
quantidade: 1,
emailPagador: '[email protected]',
urlSucesso: 'https://minhaloja.com/obrigado',
urlFalha: 'https://minhaloja.com/tentar-novamente',
urlPendente: 'https://minhaloja.com/aguardando',
metadados: {
campanha: 'maio-2026'
}
});Campos aceitos:
| Campo | Aliases | Obrigatório | Enviado para o Mercado Pago |
| --- | --- | --- | --- |
| produto | product, productName, nome, title | Sim | items[0].title |
| id | produtoId, productId, externalReference, external_reference | Não | items[0].id e external_reference. Se ausente, a lib gera um UUID. |
| preco | price, valor, amount | Sim | items[0].unit_price |
| quantidade | quantity | Não | items[0].quantity. Padrão: 1. |
| moeda | currency, currencyId, currency_id | Não | items[0].currency_id. Padrão: BRL. |
| webhookUrl | webhook, urlWebhook, notificationUrl, notification_url | Não | notification_url. Sobrescreve o webhook do cliente. |
| successUrl | urlSucesso, success_url | Não | back_urls.success |
| failureUrl | urlFalha, failure_url | Não | back_urls.failure |
| pendingUrl | urlPendente, pending_url | Não | back_urls.pending |
| payerEmail | emailPagador, payer_email | Não | payer.email |
| metadata | metadados | Não | metadata |
Retorno com sucesso:
{
ok: true,
dados: {
id: 'PREFERENCE_ID',
referencia: 'workshop-js-2026',
link: 'https://www.mercadopago.com.br/checkout/...',
linkProducao: 'https://www.mercadopago.com.br/checkout/...',
linkTeste: 'https://sandbox.mercadopago.com.br/checkout/...',
criadoEm: '2026-05-15T10:00:00.000Z',
modoTeste: false,
bruto: {}
}
}Campos de dados:
| Campo | Significado |
| --- | --- |
| id | ID da preferência gerada pelo Mercado Pago. |
| referencia | Sua referência interna, vinda de external_reference. |
| link | URL que você deve abrir para o comprador. Usa sandbox quando modoTeste(true) está ativo. |
| linkProducao | URL de checkout de produção retornada pela API. |
| linkTeste | URL de checkout sandbox retornada pela API. |
| criadoEm | Data de criação retornada pelo Mercado Pago. |
| modoTeste | true se a instância está em modo teste. |
| bruto | Resposta original da API para casos em que você precisa investigar algo. |
Retorno com erro:
{
ok: false,
erro: 'PRECO_INVALIDO',
mensagem: 'O preco precisa ser maior que zero'
}criarCobrancaDividida / createSplitCharge
Cria uma cobrança com split de pagamento no modelo marketplace 1:1 do Mercado Pago.
Antes de usar, entenda a limitação oficial: no Checkout Pro, o split não envia dinheiro para duas contas aleatórias informadas por ID. O fluxo é:
- conta A: vendedor, dono do
access_tokenOAuth usado para criar a preferência; - conta B: marketplace/plataforma, que recebe uma comissão via
marketplace_fee; - a comissão do Mercado Pago é descontada do vendedor antes da comissão do marketplace.
Exemplo: produto de R$ 100,00, vendedor fica com 80% nominal e marketplace com 20%.
const cobranca = await mp.criarCobrancaDividida({
produto: 'Ingresso - Noite do Jazz',
id: 'pedido-jazz-001',
preco: 100,
split: {
contaA: {
accessToken: 'ACCESS_TOKEN_OAUTH_DO_VENDEDOR',
percentual: 80
},
contaB: {
percentual: 20
}
}
});A lib calcula:
marketplace_fee = preco * percentualDaContaB / 100E envia a preferência para:
POST /checkout/preferences
Authorization: Bearer ACCESS_TOKEN_OAUTH_DO_VENDEDORCampos aceitos:
| Campo | Obrigatório | O que faz |
| --- | --- | --- |
| produto, id, preco, etc. | Sim | Mesmos campos de criarCobranca. |
| split.contaA.accessToken | Sim | Token OAuth do vendedor. Também aceita access_token, apiKey ou api_key. |
| split.contaA.percentual | Sim | Percentual nominal do vendedor. |
| split.contaB.percentual | Sim | Percentual da plataforma, enviado como marketplace_fee. |
| marketplace | Não | ID marketplace explícito. Se não informar e houver appId, a lib usa MP-MKT-${appId}. |
Retorno com sucesso:
{
ok: true,
dados: {
id: 'PREFERENCE_ID',
referencia: 'pedido-jazz-001',
link: 'https://www.mercadopago.com.br/checkout/...',
linkProducao: 'https://www.mercadopago.com.br/checkout/...',
linkTeste: 'https://sandbox.mercadopago.com.br/checkout/...',
criadoEm: '2026-05-15T10:00:00.000Z',
modoTeste: false,
split: {
contaA: {
percentual: 80,
valorEstimado: 80
},
contaB: {
percentual: 20,
valor: 20
},
observacao: 'A comissao do Mercado Pago e descontada antes da comissao do marketplace.'
},
bruto: {}
}
}Campos importantes:
| Campo | Significado |
| --- | --- |
| dados.split.contaA.valorEstimado | Valor antes de considerar taxas do Mercado Pago. A API desconta taxas do vendedor conforme regras oficiais. |
| dados.split.contaB.valor | Valor enviado como marketplace_fee. |
| dados.bruto | Resposta original do Mercado Pago. |
Erros comuns:
{
ok: false,
erro: 'SPLIT_PERCENTUAL_INVALIDO',
mensagem: 'As porcentagens do split precisam somar 100'
}Quando usar:
- marketplace com vendedores conectados por OAuth;
- plataforma que cobra comissão;
- cobrança simples via Checkout Pro.
Quando não usar:
- dividir um mesmo pagamento entre duas contas arbitrárias só por e-mail/ID;
- mandar parte do valor para conta bancária externa;
- criar split com mais de um vendedor no mesmo pagamento;
- substituir repasses financeiros manuais fora do Mercado Pago.
criarPix / createPix
Cria um pagamento Pix direto pela API de pagamentos. Diferente de criarCobranca, aqui a resposta já é um pagamento real e pode trazer QR Code, Pix copia e cola e link de pagamento.
Use essa função quando você quer mostrar o Pix dentro da sua própria aplicação, sem mandar o cliente primeiro para uma página de checkout.
const pix = await mp.criarPix({
produto: 'Clube do Café - Plano Mensal',
id: 'pedido-pix-001',
preco: 39.9,
emailPagador: '[email protected]'
});
if (!pix.ok) {
console.log(pix);
} else {
console.log('Pix copia e cola:', pix.dados.copiaECola);
console.log('QR Code base64:', pix.dados.qrCodeBase64);
console.log('Link Pix:', pix.dados.ticketUrl);
}Exemplo com metadados e loop:
const pix = await mp.criarPix({
produto: 'Clube do Café - Plano Mensal',
id: 'pedido-pix-cafe',
preco: 39.9,
emailPagador: '[email protected]',
metadados: {
idUser: 'user-123'
}
});
if (pix.ok) {
await mp.acompanharCobranca(pix.dados.id, {
intervaloMs: 5000,
duracaoMs: 30 * 60 * 1000
});
}Campos aceitos:
| Campo | Aliases | Obrigatório | Enviado para o Mercado Pago |
| --- | --- | --- | --- |
| produto | product, productName, nome, title | Sim | description |
| id | produtoId, productId, externalReference, external_reference | Não | external_reference. Se ausente, a lib gera UUID. |
| preco | price, valor, amount | Sim | transaction_amount |
| quantidade | quantity | Não | Multiplica o valor final. Padrão: 1. |
| emailPagador | payerEmail, payer_email | Sim | payer.email |
| webhookUrl | webhook, urlWebhook, notificationUrl, notification_url | Não | notification_url. |
| metadata | metadados | Não | metadata |
| idempotencyKey | chaveIdempotencia | Não | Header X-Idempotency-Key. Se ausente, a lib gera uma chave simples. |
Por que emailPagador é obrigatório? Porque a API de pagamento Pix do Mercado Pago exige payer.email no payload. Esse e-mail não precisa ser usado como identificador interno do seu usuário; para isso, prefira id e metadados.
Payload oficial usado pela lib:
{
transaction_amount: 39.9,
description: 'Clube do Café - Plano Mensal',
payment_method_id: 'pix',
external_reference: 'pedido-pix-001',
payer: {
email: '[email protected]'
}
}Retorno com sucesso:
{
ok: true,
dados: {
id: 123456789,
status: 'pending',
statusDetalhado: 'pending_waiting_transfer',
situacao: 'pendente',
valor: 39.9,
moeda: 'BRL',
descricao: 'Clube do Café - Plano Mensal',
referencia: 'pedido-pix-001',
metadados: {},
metodoPagamento: 'pix',
tipoPagamento: 'bank_transfer',
qrCode: '000201...',
qrCodeBase64: 'iVBORw0KGgo...',
ticketUrl: 'https://www.mercadopago.com.br/payments/...',
copiaECola: '000201...',
expiraEm: '2026-05-16T10:00:00.000Z',
criadoEm: '2026-05-15T10:00:00.000Z',
atualizadoEm: '2026-05-15T10:00:00.000Z',
modoTeste: false,
bruto: {}
}
}Campos principais:
| Campo | Significado |
| --- | --- |
| dados.id | ID real do pagamento. Pode ser usado em consultarCobranca e acompanharCobranca. |
| dados.copiaECola | Código Pix copia e cola. É o mesmo valor de qrCode. |
| dados.qrCode | Código Pix completo retornado pela API. |
| dados.qrCodeBase64 | Imagem do QR Code em base64, quando retornada pela API. |
| dados.ticketUrl | Link renderizado do Pix, quando retornado pela API. |
| dados.situacao | Normalmente começa como pendente até o pagamento ser aprovado. |
Como mostrar no front:
res.json({
pagamentoId: pix.dados.id,
copiaECola: pix.dados.copiaECola,
qrCodeBase64: pix.dados.qrCodeBase64,
ticketUrl: pix.dados.ticketUrl
});No HTML, se qrCodeBase64 vier preenchido:
<img src="data:image/png;base64,SEU_QR_CODE_BASE64" alt="QR Code Pix">E para copia e cola:
<textarea readonly>SEU_CODIGO_PIX_COPIA_E_COLA</textarea>Depois de criar o Pix, você pode consultar uma vez:
const pagamento = await mp.consultarCobranca(pix.dados.id);Ou acompanhar por loop:
await mp.acompanharCobranca(pix.dados.id, {
intervaloMs: 5000,
duracaoMs: 30 * 60 * 1000
});Erros comuns:
{
ok: false,
erro: 'EMAIL_PAGADOR_AUSENTE',
mensagem: 'Informe o email do pagador para gerar Pix'
}Arquivo de teste local:
node testePIX.jsO arquivo testePIX.js cria um Pix, imprime copiaECola, qrCodeBase64, ticketUrl e inicia o loop usando pix.dados.id.
Observações:
- em modo teste, use credenciais de teste e comprador de teste do Mercado Pago;
sandbox: truemarca a resposta como teste na lib, mas não troca suas credenciais automaticamente;- o Pix começa normalmente como
pendente; - use webhook ou loop para saber quando virou
pago; - não confunda
pix.dados.idcom ID de preferência: no Pix direto,pix.dados.idjá é ID real de pagamento.
criarPixRecorrente / createRecurringPix
Cria uma assinatura recorrente no Mercado Pago usando o endpoint oficial de assinaturas (POST /preapproval) e retorna o link para o cliente concluir o pagamento inicial.
const assinatura = await mp.criarPixRecorrente({
produto: 'Clube do Cafe - Plano Mensal',
id: 'assinatura-cafe-user-123',
preco: 39.9,
emailPagador: '[email protected]',
urlRetorno: 'https://minhaloja.com/assinatura/ok',
webhookUrl: 'https://minhaloja.com/webhook-assinaturas'
});
if (assinatura.ok) {
console.log('Link da assinatura:', assinatura.dados.link);
}Campos aceitos:
| Campo | Aliases | Obrigatorio | Enviado para o Mercado Pago |
| --- | --- | --- | --- |
| produto | product, productName, nome, title | Sim | reason |
| id | produtoId, productId, externalReference, external_reference | Nao | external_reference. Se ausente, a lib gera um UUID. |
| preco | price, valor, amount | Sim | auto_recurring.transaction_amount |
| quantidade | quantity | Nao | Multiplica o valor final. Padrao: 1. |
| emailPagador | payerEmail, payer_email | Sim | payer_email |
| urlRetorno | returnUrl, backUrl, back_url, successUrl, urlSucesso | Sim | back_url |
| frequencia | frequency | Nao | auto_recurring.frequency. Padrao: 1. |
| tipoFrequencia | frequencyType, frequency_type | Nao | auto_recurring.frequency_type. Padrao: months. |
| dataInicio | startDate, start_date | Nao | auto_recurring.start_date |
| dataFim | endDate, end_date | Nao | auto_recurring.end_date |
| webhookUrl | webhook, urlWebhook, notificationUrl, notification_url | Nao | notification_url. Sobrescreve o webhook do cliente. |
| metadata | metadados | Nao | metadata |
Retorno com sucesso:
{
ok: true,
dados: {
id: 'PREAPPROVAL_ID',
status: 'pending',
situacao: 'pendente',
descricao: 'Clube do Cafe - Plano Mensal',
referencia: 'assinatura-cafe-user-123',
link: 'https://www.mercadopago.com.br/subscriptions/checkout?...',
valor: 39.9,
moeda: 'BRL',
frequencia: 1,
tipoFrequencia: 'months',
webhookUrl: 'https://minhaloja.com/webhook-assinaturas',
modoTeste: false,
bruto: {}
}
}Observacao: essa funcao cria uma assinatura com status: 'pending'. O comprador abre dados.link, escolhe/conclui o pagamento no Mercado Pago, e as proximas cobrancas ficam sob o fluxo de assinaturas do Mercado Pago.
consultarAssinatura / checkSubscription
Consulta uma assinatura recorrente pelo ID retornado em criarPixRecorrente.
const assinatura = await mp.consultarAssinatura('PREAPPROVAL_ID');
if (assinatura.ok && assinatura.dados.situacao === 'ativa') {
console.log('Assinatura ativa:', assinatura.dados.referencia);
}Essa funcao usa o endpoint de assinaturas (GET /preapproval/{id}). Para consultar um pagamento individual gerado pela assinatura, use consultarCobranca com o ID do pagamento.
consultarCobranca / checkCharge
Consulta um pagamento real pelo ID de pagamento do Mercado Pago.
const pagamento = await mp.consultarCobranca('123456789');
if (pagamento.ok && pagamento.dados.situacao === 'pago') {
console.log('Pode liberar o pedido:', pagamento.dados.referencia);
}De Onde Vem o paymentId?
O paymentId não nasce no momento em que você cria a preferência. Ele nasce quando o Mercado Pago cria um pagamento a partir daquele checkout.
Um fluxo comum com back_urls:
const cobranca = await mp.criarCobranca({
produto: 'Clube do Café',
id: 'pedido-123',
preco: 39.9,
urlSucesso: 'https://minhaloja.com/pagamento/volta',
urlFalha: 'https://minhaloja.com/pagamento/volta',
urlPendente: 'https://minhaloja.com/pagamento/volta'
});Depois do checkout, o Mercado Pago pode redirecionar para uma URL parecida com:
https://minhaloja.com/pagamento/volta?payment_id=123456789&status=approved&external_reference=pedido-123No backend, você pega esse ID e consulta a API:
app.get('/pagamento/volta', async (req, res) => {
const paymentId = req.query.payment_id || req.query.collection_id;
if (!paymentId) {
return res.status(400).send('Pagamento sem ID.');
}
const pagamento = await mp.consultarCobranca(paymentId);
if (pagamento.ok && pagamento.dados.situacao === 'pago') {
await liberarPedido(pagamento.dados.referencia);
}
res.send('Pagamento recebido. Pode fechar esta página.');
});O front pode capturar esse parâmetro e enviar ao backend, mas o backend sempre deve confirmar com consultarCobranca. Nunca libere pedido só porque o front disse que pagou.
Retorno com sucesso:
{
ok: true,
dados: {
id: 123456789,
status: 'approved',
statusDetalhado: 'accredited',
situacao: 'pago',
valor: 89.9,
moeda: 'BRL',
descricao: 'Workshop JavaScript Sem Drama',
referencia: 'workshop-js-2026',
metadados: {
idUser: 'user-123'
},
metodoPagamento: 'pix',
tipoPagamento: 'bank_transfer',
aprovadoEm: '2026-05-15T10:05:00.000Z',
criadoEm: '2026-05-15T10:01:00.000Z',
atualizadoEm: '2026-05-15T10:05:00.000Z',
modoTeste: false,
bruto: {}
}
}Campos de dados:
| Campo | Significado |
| --- | --- |
| id | ID do pagamento no Mercado Pago. |
| status | Status original da API, como approved, pending, rejected. |
| statusDetalhado | Detalhe original, como accredited ou motivos de recusa. |
| situacao | Status amigável da lib: pago, pendente, recusado ou desconhecido. |
| valor | Valor da transação. |
| moeda | Moeda retornada pela API. |
| descricao | Descrição do pagamento, quando existir. |
| referencia | Sua referência interna. |
| metadados | Objeto enviado em metadata ao criar a cobrança. Útil para idUser, plano, campanha, etc. |
| metodoPagamento | Método, como pix, visa, master. |
| tipoPagamento | Tipo, como credit_card, debit_card, bank_transfer. |
| aprovadoEm | Data de aprovação, quando existir. |
| criadoEm | Data de criação. |
| atualizadoEm | Data da última atualização. |
| modoTeste | true se a instância está em modo teste. |
| bruto | Resposta original da API. |
Mapeamento de situacao:
| Mercado Pago status | MP Facil situacao |
| --- | --- |
| approved | pago |
| authorized | pendente |
| in_process | pendente |
| pending | pendente |
| rejected | recusado |
| cancelled | recusado |
| refunded | recusado |
| charged_back | recusado |
| qualquer outro | desconhecido |
interpretarWebhook / parseWebhook
Valida e interpreta o corpo de um webhook sem consultar o pagamento na API.
const evento = mp.interpretarWebhook({
body: req.body,
query: req.query,
headers: req.headers
});
if (!evento.ok) {
return res.status(400).json(evento);
}
console.log('Pagamento citado no webhook:', evento.dados.id);Entrada esperada:
{
body: {
type: 'payment',
action: 'payment.updated',
data: {
id: '123456789'
}
},
query: {
'data.id': '123456789'
},
headers: {
'x-request-id': '...',
'x-signature': 'ts=...,v1=...'
}
}Retorno com sucesso:
{
ok: true,
dados: {
id: '123456789',
tipo: 'payment',
acao: 'payment.updated',
evento: 'payment.updated',
headers: {},
query: {},
corpo: {}
}
}Campos de dados:
| Campo | Significado |
| --- | --- |
| id | ID do pagamento citado no webhook. |
| tipo | Tipo do evento. A lib aceita apenas payment. |
| acao | Ação enviada pelo Mercado Pago, quando existir. |
| evento | acao quando existir, senão tipo. |
| headers | Headers normalizados para minúsculas. |
| query | Query recebida. |
| corpo | Body recebido. |
Se webhookSecret estiver configurado, a assinatura é validada com x-signature, x-request-id e data.id. Se algo não bater, o retorno será:
{
ok: false,
erro: 'ASSINATURA_INVALIDA',
mensagem: 'Assinatura do webhook invalida'
}processarWebhook / handleWebhook
Interpreta o webhook, consulta o pagamento na API e dispara eventos com a resposta normalizada do pagamento.
mp.on('pago', (dados) => {
console.log('Liberar acesso ao pedido:', dados.referencia);
console.log('Usuario:', dados.metadados.idUser);
});
mp.on('recusado', (dados) => {
console.log('Avisar cliente para tentar outro meio:', dados.id);
});
mp.on('pendente', (dados) => {
console.log('Ainda aguardando:', dados.id);
});
mp.on('erro', (erro) => {
console.error('Webhook não processado:', erro.mensagem);
});
app.post('/webhook', async (req, res) => {
const resultado = await mp.processarWebhook({
body: req.body,
query: req.query,
headers: req.headers
});
res.status(resultado.ok ? 200 : 400).json(resultado);
});Retorno:
- se o webhook for inválido, retorna o mesmo formato de erro de
interpretarWebhook; - se a consulta do pagamento falhar, retorna o erro de
consultarCobranca; - se der certo, retorna o mesmo formato de sucesso de
consultarCobranca.
Eventos emitidos:
| Evento | Quando acontece | Payload |
| --- | --- | --- |
| pago | status vira approved | dados normalizados de pagamento. |
| pendente | status vira authorized, in_process ou pending | dados normalizados de pagamento. |
| recusado | status vira rejected, cancelled, refunded ou charged_back | dados normalizados de pagamento. |
| erro | webhook inválido, falha na consulta ou status desconhecido | objeto de erro normalizado. |
Alternativa ao Webhook: Loop de Consulta
Às vezes webhook é chato no começo: ambiente local sem túnel, servidor que dorme, configuração ainda em teste. Para esses casos, a lib tem um loop que consulta o pagamento até ele ficar pago ou recusado.
mp.on('pago', (pagamento) => {
console.log('Pedido liberado:', pagamento.referencia);
});
mp.on('recusado', (pagamento) => {
console.log('Pagamento falhou:', pagamento.statusDetalhado);
});
mp.on('pendente', (pagamento) => {
console.log('Ainda esperando:', pagamento.id);
});
await mp.acompanharCobranca('ID_DO_PAGAMENTO', {
intervaloMs: 5000,
duracaoMs: 10 * 60 * 1000
});Esse recurso é útil quando você já tem o ID do pagamento. Em Checkout Pro, esse ID normalmente aparece no retorno para suas back_urls como payment_id ou collection_id, e também pode aparecer no webhook.
O loop não deve receber cobranca.dados.id, porque esse é o ID da preferência. Use:
await mp.acompanharCobranca(paymentId);e não:
await mp.acompanharCobranca(cobranca.dados.id);Como a memória funciona
Ao iniciar um acompanhamento, a lib cria um JSON local na pasta:
memory loop/Exemplo de arquivo:
{
"id": "123456789",
"pagamentoId": "123456789",
"status": "monitorando",
"intervaloMs": 5000,
"duracaoMs": 600000,
"tentativas": 3,
"modoTeste": false
}Se a aplicação parar antes do pagamento resolver, esse arquivo continua ali. Quando você cria o cliente de novo, a lib tenta retomar automaticamente os loops salvos.
const mp = new MPFacil({
apiKey: process.env.MP_ACCESS_TOKEN
});Se preferir controlar isso manualmente:
const mp = new MPFacil({
apiKey: process.env.MP_ACCESS_TOKEN,
autoResumeLoops: false
});
await mp.retomarLoops();Para trocar a pasta:
const mp = new MPFacil({
apiKey: process.env.MP_ACCESS_TOKEN,
memoryLoopPath: './dados/pagamentos-pendentes'
});acompanharCobranca / watchCharge
Inicia um loop em segundo plano.
const loop = await mp.acompanharCobranca('123456789', {
intervaloMs: 3000,
duracaoMs: 15 * 60 * 1000
});Opções:
| Campo | Aliases | Padrão | O que faz |
| --- | --- | --- | --- |
| intervaloMs | intervalMs, intervalo | 10000 | Tempo entre consultas. |
| duracaoMs | durationMs, duracao, duration | null | Tempo máximo do loop. Se não informar, o loop só para quando pagar ou recusar. |
Retorno:
{
ok: true,
dados: {
id: '123456789',
status: 'monitorando',
intervaloMs: 3000,
duracaoMs: 900000,
tentativas: 0,
modoTeste: false,
arquivo: '/caminho/do/projeto/memory loop/123456789.json'
}
}O loop emite:
| Evento | Quando |
| --- | --- |
| pendente | A consulta voltou pending, in_process ou authorized. O loop continua. |
| pago | A consulta voltou approved. O loop para e remove o JSON. |
| recusado | A consulta voltou rejected, cancelled, refunded ou charged_back. O loop para e remove o JSON. |
| erro | A consulta falhou, a duração acabou ou veio status desconhecido. Erros de consulta não param o loop; duração expirada e status desconhecido param. |
retomarLoops / resumeLoops
Lê os JSONs da pasta memory loop e reinicia os acompanhamentos pendentes.
const retomados = await mp.retomarLoops();Retorno:
{
ok: true,
dados: {
retomados: 2,
ids: ['123456789', '987654321']
}
}cancelarAcompanhamento / cancelWatch
Para um loop manualmente e remove o JSON.
const cancelado = await mp.cancelarAcompanhamento('123456789');Retorno:
{
ok: true,
dados: {
id: '123456789',
cancelado: true
}
}Exemplo com back URL
Um uso comum é começar o loop quando o cliente volta do Mercado Pago:
app.get('/pagamento/volta', async (req, res) => {
const pagamentoId = req.query.payment_id || req.query.collection_id;
if (!pagamentoId) {
return res.status(400).send('Pagamento sem ID para acompanhar.');
}
await mp.acompanharCobranca(pagamentoId, {
intervaloMs: 5000,
duracaoMs: 20 * 60 * 1000
});
res.send('Estamos confirmando seu pagamento. Pode fechar esta página.');
});Nesse fluxo, o front só redireciona o comprador. A confirmação acontece no backend:
- front abre
cobranca.dados.link; - Mercado Pago redireciona para sua
urlSucesso/urlFalha/urlPendente; - backend lê
payment_idoucollection_id; - backend chama
consultarCobrancaou iniciaacompanharCobranca; - backend atualiza seu banco.
Se você fizer uma SPA e o front receber o payment_id, envie esse ID ao backend:
await fetch('/api/confirmar-pagamento', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ paymentId })
});E confirme no backend:
app.post('/api/confirmar-pagamento', async (req, res) => {
const pagamento = await mp.consultarCobranca(req.body.paymentId);
if (!pagamento.ok) {
return res.status(400).json(pagamento);
}
if (pagamento.dados.situacao === 'pago') {
await liberarPedido(pagamento.dados.referencia);
}
res.json(pagamento);
});Formato de Erros
As funções públicas que fazem trabalho retornam objeto, não lançam exceção, exceto o construtor quando falta apiKey.
Formato:
{
ok: false,
erro: 'API_KEY_INVALIDA',
mensagem: 'A api_key informada e invalida',
status: 401,
detalhes: {}
}Campos:
| Campo | Quando aparece | Significado |
| --- | --- | --- |
| ok | sempre | false em erro. |
| erro | sempre | Código interno estável da lib. |
| mensagem | sempre | Mensagem curta para log ou resposta simples. |
| status | quando existe | Status HTTP retornado pela API. |
| detalhes | quando existe | Resposta original da API ou dado útil de depuração. |
Códigos internos:
| Código | Causa comum |
| --- | --- |
| API_KEY_AUSENTE | O cliente foi criado sem apiKey/api_key. |
| API_KEY_INVALIDA | Mercado Pago respondeu 401. |
| APP_ID_INVALIDO | appId foi informado vazio. |
| PAYLOAD_INCOMPLETO | Payload ausente. |
| PRODUTO_AUSENTE | Faltou produto/productName. |
| PRECO_AUSENTE | Faltou preco/price. |
| PRECO_INVALIDO | Preço menor ou igual a zero, ou não numérico. |
| ID_AUSENTE | Consulta chamada sem ID. |
| FALHA_DE_REDE | fetch falhou antes de receber resposta. |
| RESPOSTA_INESPERADA | API retornou erro não mapeado ou JSON inválido. |
| WEBHOOK_INVALIDO | Webhook sem data.id/id ou tipo diferente de payment. |
| ASSINATURA_INVALIDA | Assinatura do webhook ausente ou inválida. |
| STATUS_DESCONHECIDO | Pagamento retornou status fora do mapeamento da lib. |
| LOOP_INTERVALO_INVALIDO | intervaloMs menor ou igual a zero, ou não numérico. |
| LOOP_DURACAO_INVALIDA | duracaoMs menor ou igual a zero, ou não numérico. |
| LOOP_TEMPO_ESGOTADO | O loop chegou ao tempo máximo sem pago ou recusado. |
| LOOP_NAO_ENCONTRADO | Reservado para loops não encontrados em operações futuras. |
| SPLIT_CONTA_A_INVALIDA | Faltou token OAuth da conta A/vendedor. |
| SPLIT_CONTA_B_INVALIDA | Faltou percentual da conta B/marketplace. |
| SPLIT_PERCENTUAL_INVALIDO | Percentuais não somam 100. |
| SPLIT_VALOR_INVALIDO | Comissão calculada ficou fora do valor da cobrança. |
| FREQUENCIA_ASSINATURA_INVALIDA | Frequencia menor ou igual a zero, ou nao numerica, ao criar Pix recorrente. |
| EMAIL_PAGADOR_AUSENTE | Faltou emailPagador ao criar Pix. |
| EMAIL_PAGADOR_INVALIDO | E-mail do pagador não parece válido. |
| URL_RETORNO_AUSENTE | Faltou urlRetorno ao criar Pix recorrente. |
Exemplo Completo com Express
import express from 'express';
import { MPFacil } from 'mp-facil';
const app = express();
const mp = new MPFacil({
apiKey: process.env.MP_ACCESS_TOKEN,
webhookUrl: 'https://minhaloja.com/webhook',
webhookSecret: process.env.MP_WEBHOOK_SECRET
});
app.use(express.json());
app.post('/checkout/cafe', async (req, res) => {
const cobranca = await mp.criarCobranca({
produto: 'Clube do Café - Degustação',
id: `pedido-${Date.now()}`,
preco: 29.9,
emailPagador: req.body.email,
urlSucesso: 'https://minhaloja.com/cafe/obrigado',
urlFalha: 'https://minhaloja.com/cafe/erro',
urlPendente: 'https://minhaloja.com/cafe/aguardando'
});
if (!cobranca.ok) {
return res.status(400).json(cobranca);
}
res.json({
checkout: cobranca.dados.link,
referencia: cobranca.dados.referencia
});
});
mp.on('pago', (pagamento) => {
console.log('Separar café para o pedido:', pagamento.referencia);
});
mp.on('pendente', (pagamento) => {
console.log('Aguardando confirmação:', pagamento.id);
});
mp.on('recusado', (pagamento) => {
console.log('Pagamento não aprovado:', pagamento.statusDetalhado);
});
mp.on('erro', (erro) => {
console.error('Falha no fluxo Mercado Pago:', erro);
});
app.post('/webhook', async (req, res) => {
const resultado = await mp.processarWebhook({
body: req.body,
query: req.query,
headers: req.headers
});
res.status(resultado.ok ? 200 : 400).json(resultado);
});
app.listen(3000);O Que Esta Lib Não Tenta Fazer
Ela não é um SDK completo do Mercado Pago. Ela não cria pagamento transparente com cartão, não gerencia reembolso, carteira, disputa ou toda a gestao avancada de assinaturas. O foco é ser pequena e agradável para o fluxo básico:
- criar uma cobrança por checkout;
- criar Pix direto;
- criar assinatura recorrente com link de pagamento;
- consultar status;
- processar webhook ou acompanhar por loop;
- criar split marketplace 1:1;
- receber erros previsíveis.
Fontes Oficiais Consultadas
- Criar preferência Checkout Pro: https://www.mercadopago.com.mx/developers/en/reference/online-payments/checkout-pro/preferences/create-preference/post
- Criar pagamento Pix: https://docs02.mercadopago.com.br/developers/en/docs/checkout-api-payments/integration-configuration/integrate-pix
- Criar assinatura: https://www.mercadopago.com.br/developers/pt/reference/online-payments/subscriptions/create-preapproval/post
- Assinatura com pagamento pendente: https://www.mercadopago.com.br/developers/pt/docs/subscriptions/integration-configuration/subscription-no-associated-plan/pending-payments
- Obter pagamento por ID: https://www.mercadopago.com.br/developers/pt/reference/online-payments/checkout-pro/get-payment/get
- Validação de webhooks com
x-signature: https://www.mercadopago.com.mx/developers/es/docs/your-integrations/notifications/webhooks - Split Payments 1:1: https://www.mercadopago.com.br/developers/en/docs/split-payments/split-1-1/overview
- Integração de marketplace/split: https://www.mercadopago.com.ar/developers/en/docs/split-payments/split-1-1/integration-configuration/integrate-marketplace
