Entregas

Serviços externos

Entregue um benefício por um sistema seu, que recebe o pedido de entrega por webhook e confirma pela API.

Um serviço externo é um sistema seu (ou de terceiros) que entrega algo que a MinePag não entrega sozinha: uma licença em outro sistema, uma conta em um painel, uma cópia personalizada de um arquivo. A MinePag pede a entrega com o evento delivery.requested e o serviço confirma o resultado pela API.

O serviço de packs com marca d'água é um exemplo pronto de serviço externo.

Como funciona

A MinePag pede a entrega

Quando o pagamento é confirmado, a MinePag envia delivery.requested só para o serviço escolhido no benefício. O envio é assinado no padrão Standard Webhooks.

O serviço responde rápido

O serviço confere a assinatura e responde com um status 2xx em até 10 segundos. A entrega fica Em processamento no painel.

O serviço confirma pela API

Depois de entregar, o serviço chama POST /api/v1/deliveries/{operationId}/complete. Se não conseguir, chama POST /api/v1/deliveries/{operationId}/fail. A entrega passa para Entregue, Nova tentativa agendada ou Tentativas esgotadas.

Se o serviço não confirmar em 24 horas, a MinePag considera a tentativa perdida e envia um novo delivery.requested para a mesma entrega, até 6 tentativas.

Configurar

Crie uma chave secreta

Em Desenvolvedores › Chaves de API, crie uma chave secreta (sk_…) com o escopo deliveries:write. O serviço usa essa chave para confirmar as entregas. Guarde a chave só no servidor do serviço. Veja Autenticação.

Cadastre o serviço

Em Entregas › Serviços externos, clique em Adicionar serviço. O formulário já vem com o tipo Serviço externo de entrega e os eventos delivery.requested, entitlement.revoked e order.refunded marcados.

  • URL: endereço HTTPS público do serviço. Endereços locais, internos ou IPs privados são recusados.
  • Descrição (opcional): nome para reconhecer o serviço no painel.

Clique em Criar endpoint e copie o Segredo de assinatura (whsec_…). Você pode vê-lo de novo na página do serviço; cada visualização fica nos logs de auditoria.

Use no produto

Em Catálogo › Produtos, abra o produto, adicione o benefício Serviço externo e escolha o serviço.

Teste

Na página do serviço, use Testar envio para conferir se ele responde e se a assinatura confere. Depois, faça uma compra com o gateway em Sandbox.

O evento delivery.requested

{
  "id": "0d4a3b8e-6f1c-4a7e-9b2d-5c8f1e3a7b90",
  "type": "delivery.requested",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-12T14:30:00.000Z",
  "projectId": "7c1e9f40-2b3d-4c5a-8e6f-9a0b1c2d3e4f",
  "data": {
    "delivery": {
      "operationId": "5f2b8c1d-3e4a-4b6c-9d7e-8f0a1b2c3d4e",
      "entitlementId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
      "channel": "webhook",
      "kind": "grant",
      "attempt": 1,
      "orderId": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
      "orderItemId": "6e7f8a9b-0c1d-4e2f-9a3b-4c5d6e7f8a9b",
      "productId": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
      "benefitId": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
      "quantity": 1
    },
    "customer": {
      "id": "4d5e6f7a-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
      "email": "cliente@exemplo.com",
      "name": "Ana Souza"
    },
    "benefit": {
      "id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
      "type": "external_webhook",
      "config": { "endpointId": "8e9f0a1b-2c3d-4e4f-9a5b-6c7d8e9f0a1b" }
    }
  }
}
CampoPara que serve
delivery.operationIdIdentifica a entrega. Use na URL de confirmação e para não entregar duas vezes.
delivery.attemptNúmero da tentativa. Um novo delivery.requested da mesma entrega chega com o mesmo operationId e outra tentativa.
delivery.kindgrant (primeira entrega) ou resend (reenvio).
delivery.entitlementIdO acesso liberado do cliente. É o que chega em entitlement.revoked quando o acesso termina.
delivery.productId e delivery.quantityO que foi comprado e quantas unidades.
customerQuem comprou.

Os cabeçalhos webhook-id, webhook-timestamp e webhook-signature acompanham cada envio. Como verificar a assinatura em cada linguagem está em Webhooks.

Responda antes de processar

A MinePag espera a resposta por até 10 segundos. Responda 2xx assim que verificar a assinatura e processe em segundo plano. Se o serviço responder com erro ou não responder, o mesmo evento é reenviado depois de 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas e 24 horas.

Confirmar a entrega

POST /api/v1/deliveries/{operationId}/complete
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "externalReference": "licenca_123",
  "downloadUrl": "https://servico.sualoja.com.br/p/licenca_123",
  "expiresAt": "2026-12-31T23:59:59Z",
  "metadata": { "plano": "anual" }
}
CampoRegra
externalReferenceObrigatório. Identificador da entrega no seu sistema, até 255 caracteres. Aparece no detalhe da entrega no painel.
downloadUrlOpcional. Link HTTPS de até 2.048 caracteres.
expiresAtOpcional. Data ISO 8601 com fuso em que o link deixa de valer.
watermarkedOpcional. true quando o link entrega cópias com marca d'água por comprador. Veja o aviso de faturamento abaixo.
metadataOpcional. Até 20 chaves com textos (até 500 caracteres), números ou booleanos.

A resposta traz o estado final:

{ "data": { "operationId": "5f2b8c1d-3e4a-4b6c-9d7e-8f0a1b2c3d4e", "status": "succeeded" } }

Confirmar de novo uma entrega já concluída devolve a mesma resposta, sem duplicar nada. Confirmar uma entrega cancelada ou com tentativas esgotadas devolve 409 com o código delivery_already_finalized.

Quando a confirmação conta como entrega com marca d'água

Só a primeira entrega (kind: grant) confirmada com downloadUrl e watermarked: true conta uma unidade de "entregas com marca d'água" na sua fatura (pedidos em Sandbox não contam). Um link comum, sem marca d'água, não conta, e reenvios também não. Veja Preços.

Reenviar uma entrega

Para pedir de novo a entrega ao serviço (por exemplo, depois de trocar a chave que assina os links), use Reenviar ao serviço externo no detalhe da entrega, no painel, ou chame:

POST /api/v1/deliveries/{operationId}/resend
Authorization: Bearer sk_live_…
Idempotency-Key: 6f1c2a9e-…

A chave precisa do escopo deliveries:write. A MinePag cria uma entrega nova com kind: resend e envia um novo delivery.requested ao serviço. Só acessos ativos podem ser reenviados.

Informar uma falha

POST /api/v1/deliveries/{operationId}/fail
Authorization: Bearer sk_live_…
Content-Type: application/json

{
  "code": "product_not_configured",
  "message": "Produto sem arquivo vinculado.",
  "retryable": true
}
CampoRegra
codeObrigatório. Letras minúsculas, números e _, até 64 caracteres.
messageObrigatório. Até 500 caracteres. Aparece para a loja no detalhe da entrega.
retryabletrue agenda uma nova tentativa, com um novo delivery.requested. false (padrão) manda a entrega direto para Tentativas esgotadas e avisa a loja.
externalReferenceOpcional, como na confirmação.

A resposta traz status: failed quando uma nova tentativa foi agendada e dead_letter quando as tentativas se esgotaram. Informar a falha de uma entrega já concluída ou cancelada devolve 409 com o código delivery_already_finalized.

Exemplo de serviço em Node.js

Um serviço mínimo com Express que verifica a assinatura, responde na hora e confirma pela API. Ele usa só node:crypto para a assinatura e o fetch nativo do Node 18 ou mais recente.

server.mjs
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const MINEPAG_API = 'https://minepag.com.br/api/v1';
const API_KEY = process.env.MINEPAG_SECRET_KEY; // sk_… com escopo deliveries:write
const WEBHOOK_SECRETS = process.env.MINEPAG_WEBHOOK_SECRETS.split(' '); // whsec_…; dois durante uma rotação

function isSignedByMinePag(headers, body) {
  const id = headers['webhook-id'];
  const timestamp = headers['webhook-timestamp'];
  const signatures = String(headers['webhook-signature'] ?? '').split(' ');
  if (!id || !/^\d{1,12}$/.test(timestamp ?? '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  return WEBHOOK_SECRETS.some(secret => {
    const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
    const expected = createHmac('sha256', key).update(`${id}.${timestamp}.${body}`).digest();
    return signatures.some(entry => {
      if (!entry.startsWith('v1,')) return false;
      const received = Buffer.from(entry.slice(3), 'base64');
      return received.length === expected.length && timingSafeEqual(received, expected);
    });
  });
}

async function callMinePag(path, payload) {
  const response = await fetch(`${MINEPAG_API}${path}`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
  if (response.status === 409) return; // a entrega já tinha sido finalizada
  if (!response.ok) throw new Error(`A MinePag respondeu ${response.status}`);
}

async function handleDeliveryRequested({ delivery, customer }) {
  let license;
  try {
    // Sua lógica. Use delivery.operationId como chave para não emitir duas vezes.
    license = await issueLicense({
      operationId: delivery.operationId,
      entitlementId: delivery.entitlementId,
      productId: delivery.productId,
      quantity: delivery.quantity,
      email: customer.email
    });
  } catch {
    await callMinePag(`/deliveries/${delivery.operationId}/fail`, {
      code: 'license_error',
      message: 'Não foi possível emitir a licença.',
      retryable: true
    });
    return;
  }
  await callMinePag(`/deliveries/${delivery.operationId}/complete`, { externalReference: license.id });
}

const app = express();

app.post('/webhooks/minepag', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString('utf8');
  if (!isSignedByMinePag(req.headers, body)) return res.sendStatus(401);
  res.sendStatus(202);

  const event = JSON.parse(body);
  if (event.type === 'delivery.requested') {
    handleDeliveryRequested(event.data).catch(error => console.error('Falha na entrega', error));
  }
  if (event.type === 'entitlement.revoked') {
    revokeLicense(event.data.entitlement.id).catch(error => console.error('Falha na revogação', error));
  }
});

app.listen(3000);

issueLicense e revokeLicense são funções do seu sistema. Em produção, coloque os eventos em uma fila em vez de processar na mesma requisição, para não perder trabalho se o processo reiniciar. Se o serviço cair antes de confirmar, a MinePag pede a entrega de novo depois de 24 horas.

Leia o corpo bruto

A assinatura é calculada sobre o corpo exatamente como foi enviado. Se o seu framework converter o JSON antes de você verificar, a assinatura não confere. No Express, use express.raw só nessa rota.

Revogação

A MinePag não envia delivery.requested para revogar. Quando o acesso do cliente termina (reembolso total, contestação, cancelamento, fim da assinatura ou revogação manual), ela envia entitlement.revoked para os endpoints inscritos nesse evento, com entitlement.id igual ao delivery.entitlementId recebido na entrega. Use esse identificador para desfazer a entrega no seu sistema.

O evento order.refunded traz fullyRefunded. Um reembolso parcial não revoga acessos.

Situações do serviço

SituaçãoO que significa
AtivoO serviço está recebendo os envios.
Com falhasO serviço falhou em 3 envios seguidos. O proprietário e os administradores da loja recebem uma notificação.
DesativadoO serviço falhou 20 vezes seguidas por mais de 3 dias, ou alguém o desativou. As entregas para ele ficam com nova tentativa a cada hora até você reativá-lo.

Em Entregas › Serviços externos, cada serviço mostra as entregas aguardando confirmação, as entregues em 30 dias e as com falha. Se o serviço for removido, as entregas que dependem dele vão para Tentativas esgotadas.

Acompanhar pela API

Para conferir o estado das entregas a partir do seu sistema, use Listar entregas e Consultar entrega com uma chave de escopo deliveries:read. Endpoints de webhook comuns também podem assinar delivery.completed e delivery.failed.

Nesta página