Pular para o conteúdo
MeWeb

Desenvolvedores

Checkout personalizado

O lojista que já tem gateway ou adquirente próprio pluga a própria API de cobrança no MeWeb. O checkout continua sendo o do MeWeb, transparente, sem redirecionar o comprador para lugar nenhum: quem muda é apenas quem processa o dinheiro por trás. A configuração fica em Configurações → Pagamentos → “API própria (custom)”, no admin da loja; esta página é o contrato que o seu servidor precisa cumprir.

Limites deliberados

  • Sem split de pagamento. O MeWeb envia uma cobrança com um valor; a divisão entre recebedores, se existir, é problema do seu lado.
  • Sem cofre de cartão no MeWeb. O token do cartão é o que você devolver, e ele não é reusado entre pedidos: cada compra tokeniza de novo.
  • Sem antifraude do MeWeb. Análise de risco, chargeback, conciliação financeira e responsabilidade pela transação são inteiramente do lojista.

Os endpoints que você expõe

Três operações mais um ping de sanidade, todas sob o baseUrl que você cadastra no admin. O baseUrl é obrigatoriamente HTTPS; http:// só é aceito em ambiente de desenvolvimento.

OperaçãoChamada
Criar cobrançaPOST {baseUrl}/charges
Estornar (total ou parcial)POST {baseUrl}/refunds
Consultar status de uma cobrançaGET {baseUrl}/charges/{providerTransactionId}
Teste de conexão do adminGET {baseUrl}/charges/ping

Headers de toda requisição do MeWeb

headers de saída
Content-Type: application/json
X-MeWeb-Signature: t=<unixSeconds>,v1=<hex(hmac_sha256(secret, "<t>.<rawBody>"))>
X-MeWeb-Tenant: <tenantId>
Idempotency-Key: <ULID>        # só na criação de cobrança
User-Agent: MeWeb/1.0

O secret da assinatura é gerado pelo MeWeb na configuração do provedor. O Idempotency-Key acompanha apenas a criação de cobrança e é igual ao id do corpo: se a mesma chave chegar duas vezes, devolva a cobrança já criada em vez de criar outra.

Criar cobrança

O corpo é o formato canônico do MeWeb, não o de nenhum PSP. Valores em centavos, datas em ISO 8601 UTC. Traduza para o formato do seu gateway do seu lado.

POST {baseUrl}/charges
{
  "id": "01JBX9Q2M7ZK5C0W8V6H3T4RQ1",
  "tenantId": "01JBX9K7Q2M4ZC0W8V6H3T4RQ0",
  "orderRef": "01JBX9K7Q2M4ZC0W8V6H3T4RQ2",
  "amountCents": 15990,
  "currency": "BRL",
  "method": "credit_card",
  "installments": 3,
  "customer": {
    "name": "Maria Souza",
    "email": "maria@exemplo.com.br",
    "document": "12345678909",
    "phone": "+5511999998888"
  },
  "billingAddress": {
    "cep": "01310100",
    "street": "Avenida Paulista",
    "number": "1000",
    "complement": "conj. 42",
    "district": "Bela Vista",
    "city": "São Paulo",
    "state": "SP",
    "country": "BR"
  },
  "card": { "token": "tok_abc123" },
  "metadata": { "source": "meweb-checkout" }
}
CampoTipoDescrição
idstring (ULID)Id da cobrança no MeWeb: sirva-se dele para conciliar. É o mesmo valor do header Idempotency-Key.
tenantIdstring (ULID)Identificador da loja no MeWeb, o mesmo do header X-MeWeb-Tenant e da URL do webhook.
orderRefstring (ULID)Referência do pedido que originou a cobrança.
amountCentsintegerValor total em CENTAVOS: 15990 é R$ 159,90. Nunca decimal.
currencystringSempre "BRL".
methodenumpix · credit_card · boleto.
installmentsintegerNúmero de parcelas escolhido pelo comprador (1 quando à vista).
customer.namestringNome completo informado no checkout.
customer.emailstringE-mail do comprador.
customer.documentstringCPF ou CNPJ, apenas dígitos.
customer.phonestringTelefone em formato E.164.
billingAddressobject | nullEndereço de cobrança (cep, street, number, complement, district, city, state, country). Vem null quando a loja não coletou endereço.
cardobjectSó quando method é credit_card: { "token": "..." } com o token gerado pelo seu gateway.
pixobjectSó quando method é pix: { "expiresInSeconds": 3600 }.
boletoobjectSó quando method é boleto: { "dueDate": "2026-08-25T00:00:00.000Z" } (ISO 8601 UTC).
metadataobjectPares livres enviados pelo MeWeb. Devolva-os no seu registro se quiser.
  • card só vai quando method é credit_card. Para pix o corpo traz "pix": { "expiresInSeconds": 3600 }; para boleto, "boleto": { "dueDate": "2026-08-25T00:00:00.000Z" }.
  • billingAddress pode vir null quando a loja não coletou endereço; não assuma que ele existe.

Resposta esperada

HTTP 200 ou 201, corpo JSON:

resposta
{
  "providerTransactionId": "ch_9f3c21",
  "status": "APPROVED",
  "pixQrCode": null,
  "pixCode": null,
  "boletoUrl": null,
  "boletoLine": null,
  "expiresAt": null,
  "failureCode": null,
  "failureMessage": null
}
  • providerTransactionId (obrigatório) é o id da cobrança no seu sistema. É por ele que o MeWeb consulta status, estorna e casa o webhook.
  • status é o seu status cru: o MeWeb traduz pelo statusMap.
  • Pix: devolva pixCode (copia-e-cola) e, se tiver, pixQrCode. O expiresAt vai em ISO 8601.
  • Boleto: devolva boletoUrl (PDF) e boletoLine (linha digitável).
  • Recusa: devolva um status que mapeie para failed, mais failureCode e failureMessage. O MeWeb nunca mostra a failureMessage crua ao comprador: ela vira uma mensagem em pt-BR acionável; o valor cru serve ao seu log e à sua investigação.

Como o MeWeb chama

terminal
curl -X POST "https://pagamentos.sua-loja.com.br/charges" \
  -H "Content-Type: application/json" \
  -H "X-MeWeb-Signature: t=1770000000,v1=5257a869e7ec..." \
  -H "X-MeWeb-Tenant: 01JBX9K7Q2M4ZC0W8V6H3T4RQ0" \
  -H "Idempotency-Key: 01JBX9Q2M7ZK5C0W8V6H3T4RQ1" \
  -H "User-Agent: MeWeb/1.0" \
  -d '{
    "id": "01JBX9Q2M7ZK5C0W8V6H3T4RQ1",
    "tenantId": "01JBX9K7Q2M4ZC0W8V6H3T4RQ0",
    "orderRef": "01JBX9K7Q2M4ZC0W8V6H3T4RQ2",
    "amountCents": 15990,
    "currency": "BRL",
    "method": "pix",
    "installments": 1,
    "customer": {
      "name": "Maria Souza",
      "email": "maria@exemplo.com.br",
      "document": "12345678909",
      "phone": "+5511999998888"
    },
    "billingAddress": null,
    "pix": { "expiresInSeconds": 3600 },
    "metadata": { "source": "meweb-checkout" }
  }'

Estornar

POST {baseUrl}/refunds
POST {baseUrl}/refunds

{ "providerTransactionId": "ch_9f3c21", "amountCents": 5000 }

Estorno parcial é simplesmente um amountCents menor que o total da cobrança.

resposta
{ "refundId": "re_77a1", "status": "refunded" }

Aqui o status aceita refunded · pending · failed, apenas esses três e sempre canônicos: o estorno não passa pelo statusMap. failed faz a operação inteira falhar no MeWeb, sem estado intermediário.

Consultar status

GET {baseUrl}/charges/{providerTransactionId}
GET {baseUrl}/charges/ch_9f3c21

{ "providerTransactionId": "ch_9f3c21", "status": "APPROVED" }

É a rota usada pela reconciliação: um job roda a cada 15 minutos sobre as cobranças que estão pending há mais de 10 minutos e pergunta ao seu servidor o estado real.

Teste de conexão

GET {baseUrl}/charges/ping
GET {baseUrl}/charges/ping

200 OK
{ "ok": true }

Precisa responder 200 com um corpo JSON qualquer. É o botão “Testar conexão” do admin, e o provedor custom só pode ser ativado com esse teste verde.

statusMap

O MeWeb não adivinha o vocabulário do seu gateway: você declara no admin o mapeamento dos seus status para os canônicos da plataforma.

exemplo
{
  "APPROVED": "approved",
  "DENIED": "failed",
  "WAITING": "pending"
}

Canônicos válidos: pending · authorized · approved · failed · cancelled · refunded · partially_refunded.

A busca é case-insensitive. Status fora do mapa não muda a cobrança: o fato vira log de aviso e a cobrança cai na reconciliação: o MeWeb nunca chuta um estado.

Webhook: você avisando o MeWeb

requisição
POST https://sistema-<sua-loja>.meweb.com.br/api/v1/webhooks/payments/custom/<tenantId>
Content-Type: application/json
X-MeWeb-Signature: t=1770000000,v1=5257a869e7ec...

{ "id": "evt_123", "providerTransactionId": "ch_9f3c21", "status": "APPROVED" }

A URL exata, já com o seu tenantId, é copiável no admin. O webhook é assinado com o mesmo esquema X-MeWeb-Signature e o mesmo segredo das chamadas de saída.

  • id é opcional e serve à idempotência: evento repetido é descartado.
  • Assinatura inválida ou ausente → 401, e nada muda. Deriva de relógio maior que 5 minutos → 401 também.
  • O processamento é idempotente por (providerTransactionId, status).

Verificando e gerando a assinatura

O rawBody tem de ser o corpo bruto recebido, byte a byte, nunca um JSON interpretado e re-serializado: a re-serialização muda os bytes e a verificação falha. É o mesmo esquema dos webhooks da loja.

Node.js: verificar o que chega do MeWeb
const crypto = require('node:crypto');

function verifyMewebSignature(header, rawBody, secret) {
  const parts = Object.fromEntries(
    String(header ?? '')
      .split(',')
      .map((p) => p.split('=').map((s) => s.trim())),
  );
  const t = Number(parts.t);
  if (!Number.isFinite(t)) return false;
  // Janela de 5 minutos contra replay.
  if (Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Node.js: assinar o webhook que você envia
const t = Math.floor(Date.now() / 1000);
const body = JSON.stringify({ providerTransactionId: 'ch_9f3c21', status: 'APPROVED' });
const v1 = crypto.createHmac('sha256', secret).update(`${t}.${body}`).digest('hex');
// header: `X-MeWeb-Signature: t=${t},v1=${v1}`

Timeouts e falhas

  • 15 segundos por chamada, e sem retry automático na criação: retry duplicaria cobrança. Quem retenta é o comprador, até 3 tentativas antes de o checkout sugerir outro método de pagamento.
  • Timeout na criação ⇒ a cobrança fica pending no MeWeb e a reconciliação resolve. Se o seu servidor criou a cobrança mesmo assim, ela será encontrada.
  • Consulta de status e reconciliação retentam com backoff, porque essas são idempotentes por natureza.
  • Qualquer resposta não-2xx ⇒ a cobrança não é criada e o comprador vê a mensagem padrão em pt-BR.
  • Redirects não são seguidos. O baseUrl precisa apontar direto para o seu servidor.
  • O baseUrl passa por validação anti-SSRF: hosts que resolvem para rede interna ou reservada (127.0.0.0/8, 10/8, 172.16/12, 192.168/16, 169.254/16, CGNAT etc.) são recusados.

Segurança

  • O segredo de assinatura é gerado pelo MeWeb, exibido uma única vez e guardado cifrado. Combine um canal seguro para transportá-lo até o seu servidor.
  • O admin tem botão de regerar: o segredo anterior é invalidado na hora e um novo teste de conexão passa a ser exigido.
  • Rejeite assinatura inválida e deriva de relógio maior que 5 minutos. Mantenha o relógio do seu servidor sincronizado por NTP.
  • Nunca envie o PAN do cartão para a API do MeWeb: só o token. Dados de cartão não trafegam nem são armazenados pela plataforma.