Documentação
Documentação API
Última atualização: setembro de 2026
Este guia reúne tudo o que precisa para aceitar M-Pesa, mKesh e cartões Visa/Mastercard na pay.co.mz, com os endpoints reais em produção. A integração M-Pesa Moçambique é exclusivamente de produção: não existe ambiente sandbox nem simulação.
1. Como funciona
A API pay.co.mz é REST, aceita e devolve JSON e exige HTTPS em todos os pedidos. Cada cobrança fica pendente até o provedor confirmar; só nessa altura o saldo é creditado. A comissão é sempre 10% do valor da transacção, sem mensalidade.
- URL base de produção: https://pay.co.mz/api/public/v1
- Endpoints disponíveis: POST /charges, GET /charges, POST /payouts
- Cabeçalho obrigatório em pedidos com corpo: Content-Type: application/json
- Corpo limitado a 8 KB; valores em MZN com duas casas decimais
- M-Pesa e cartões/mKesh são exclusivamente de produção — não existe sandbox
2. Autenticação
Cada pedido é validado com quatro elementos: a URL base, a chave de API (Authorization: Bearer), o Wallet ID de 5 dígitos da carteira que recebe o dinheiro (X-Wallet-Id ou campo wallet_id) e o Merchant ID de 10 dígitos da sua conta (X-Merchant-Id, disponível em Definições). Gere as chaves no painel, em Developer, e veja o Wallet ID em Carteiras. A chave começa por sk_live_ e nunca deve ir para código de navegador ou repositórios públicos.
curl https://pay.co.mz/api/public/v1/charges \
-H "Authorization: Bearer sk_live_xxx" \
-H "X-Wallet-Id: 48213" \
-H "X-Merchant-Id: 1234567890" \
-H "Content-Type: application/json"3. Criar uma cobrança
POST /charges. O corpo aceita apenas amount, method, customer_name, customer_contact e wallet_id (campos extra são recusados). O method aceita mpesa, mkesh ou card — e-Mola ainda não tem provedor activo em produção. Recomendamos sempre o cabeçalho Idempotency-Key (mínimo 8 caracteres): repetir o mesmo pedido nunca cria uma segunda cobrança. Resposta 201 quando o provedor aceita de imediato, 202 enquanto fica pendente.
curl https://pay.co.mz/api/public/v1/charges \
-H "Authorization: Bearer sk_live_..." \
-H "X-Merchant-Id: 1234567890" \
-H "X-Wallet-Id: 48213" \
-H "Idempotency-Key: encomenda-1042" \
-H "Content-Type: application/json" \
-d '{
"method": "card",
"amount": 1500.00,
"customer_name": "Ana Cossa",
"customer_contact": "ana@exemplo.mz"
}'
HTTP/1.1 202 Accepted
{
"data": {
"reference": "PAY-48213-A55391CEBA",
"transaction_reference": "PAY-48213-A55391CEBA",
"amount": 1500.00,
"method": "card",
"state": "pending",
"checkout_url": "https://pay.co.mz/checkout/cs_9f2c...",
"source": "api"
}
}4. Cobranças por método
Cada método tem um comportamento próprio. Em baixo encontra a secção dedicada a M-Pesa, mKesh, Visa/Mastercard e e-Mola, com o campo method a usar, o formato do contacto do cliente e o que acontece a seguir.
- M-Pesa — method: "mpesa" — confirmação por PIN no telemóvel
- mKesh — method: "mkesh" — confirmação no telemóvel, acompanhada no checkout
- Visa/Mastercard — method: "card" — checkout seguro com a sua marca
- e-Mola — em breve; ainda sem provedor activo em produção
4.1 M-Pesa (Vodacom)
Use method "mpesa". O customer_contact tem de ser o número no formato 258XXXXXXXXX (84 ou 85). O pedido segue para a Vodacom em produção e o cliente confirma com o PIN; a resposta é 202 e o estado final chega por webhook. Os levantamentos M-Pesa seguem de imediato por B2C.
curl https://pay.co.mz/api/public/v1/charges \
-H "Authorization: Bearer sk_live_..." \
-H "X-Merchant-Id: 1234567890" \
-H "X-Wallet-Id: 48213" \
-H "Idempotency-Key: pedido-1042" \
-H "Content-Type: application/json" \
-d '{"method":"mpesa","amount":1500.00,"customer_contact":"258841234567"}'
HTTP/1.1 202 Accepted
{
"data": {
"reference": "PAY-48213-A55391CEBA",
"amount": 1500.00,
"method": "mpesa",
"state": "pending",
"source": "api"
}
}4.2 mKesh
Use method "mkesh" com o número do cliente (82 ou 83) em 258XXXXXXXXX. O pagamento corre pelo nosso provedor de produção: o cliente confirma no telemóvel e pode acompanhar o estado no checkout_url devolvido. Os levantamentos mKesh ficam pendentes de aprovação do administrador.
curl https://pay.co.mz/api/public/v1/charges \
-H "Authorization: Bearer sk_live_..." \
-H "X-Merchant-Id: 1234567890" \
-H "X-Wallet-Id: 48213" \
-H "Idempotency-Key: pedido-1043" \
-H "Content-Type: application/json" \
-d '{"method":"mkesh","amount":900.00,"customer_contact":"258821234567"}'
HTTP/1.1 202 Accepted
{
"data": {
"reference": "PAY-48213-7C1A0B44E2",
"method": "mkesh",
"state": "pending",
"checkout_url": "https://pay.co.mz/checkout/cs_7c1a..."
}
}4.3 Visa/Mastercard
Use method "card". Encaminhe o cliente para o checkout_url devolvido — é uma página segura com a sua marca. O customer_contact pode ser o e-mail do cliente. É o único método com reembolsos, pedidos no painel e aprovados pelo administrador.
curl https://pay.co.mz/api/public/v1/charges \
-H "Authorization: Bearer sk_live_..." \
-H "X-Merchant-Id: 1234567890" \
-H "X-Wallet-Id: 48213" \
-H "Idempotency-Key: encomenda-1044" \
-H "Content-Type: application/json" \
-d '{"method":"card","amount":1500.00,"customer_name":"Ana Cossa","customer_contact":"ana@exemplo.mz"}'
HTTP/1.1 202 Accepted
{
"data": {
"reference": "PAY-48213-A55391CEBA",
"method": "card",
"state": "pending",
"checkout_url": "https://pay.co.mz/checkout/cs_9f2c..."
}
}4.4 e-Mola (em breve)
O e-Mola ainda não tem provedor activo em produção, por isso method "emola" é recusado com invalid_method. Assim que a ligação estiver disponível, funcionará exactamente como os restantes métodos: mesmo endpoint, mesmos cabeçalhos e o mesmo webhook — só precisa de criar a carteira e-Mola e passar method "emola".
5. Consultar o estado
GET /charges devolve as suas transações mais recentes com o estado actual. O parâmetro limit aceita entre 1 e 100 (por omissão 20). Recomendamos confiar no webhook e usar esta consulta apenas como confirmação ou reconciliação.
curl "https://pay.co.mz/api/public/v1/charges?limit=20" \
-H "Authorization: Bearer sk_live_..."
{
"data": [{
"id": "0f8c...",
"reference": "PAY-48213-A55391CEBA",
"amount": 1500.00,
"fee": 150.00,
"net": 1350.00,
"method": "mpesa",
"status": "pending",
"operator_ref": "DI80LI5BYYM",
"customer_name": "Ana Cossa",
"customer_contact": "258841234567",
"created_at": "2026-09-09T10:12:44.128Z"
}]
}6. Levantamentos (saque)
POST /payouts pede um levantamento da carteira indicada. O Wallet ID é obrigatório (X-Wallet-Id ou wallet_id) tal como o X-Merchant-Id. Em carteiras M-Pesa o pagamento segue de imediato para a Vodacom B2C; em mKesh e cartão/banco o pedido fica pendente de aprovação do administrador. Em alternativa, active o saque automático nas definições da carteira, com uma taxa de 5% sobre o valor líquido.
curl https://pay.co.mz/api/public/v1/payouts \
-H "Authorization: Bearer sk_live_..." \
-H "X-Wallet-Id: 48213" \
-H "X-Merchant-Id: 1234567890" \
-H "Content-Type: application/json" \
-d '{"amount": 900.00}'
HTTP/1.1 201 Created
{
"data": {
"id": "5b21...",
"amount": 900.00,
"fee": 0,
"destination": "258841234567",
"status": "pending",
"state": "processing",
"auto": false,
"created_at": "2026-09-09T10:15:02.441Z"
}
}7. Links de pagamento
Os links de pagamento são criados no painel, em Links, e não por API. Cada link pode ter valor, descrição, data de expiração e ficheiros entregáveis (PDF ou imagem) que são enviados automaticamente ao cliente por e-mail ou SMS assim que o pagamento é confirmado. A página pública do link é pay.co.mz/pay/<slug> e funciona nos quatro métodos activos.
- Criação e gestão: painel /app → Links
- Página pública: https://pay.co.mz/pay/o-seu-link
- Entregáveis: PDF, PNG, JPG ou WEBP até 20 MB, com ligação assinada válida 7 dias
- O envio ao cliente é automático e único por pagamento confirmado
8. Webhooks
Registe o endereço HTTPS do seu servidor em Developer → Webhooks. Enviamos um POST por evento, com até três tentativas até receber uma resposta 2xx. Endereços internos, IP directos e HTTP simples são recusados por segurança.
- payment.succeeded — pagamento confirmado e saldo creditado
- payment.failed — pagamento recusado, cancelado ou expirado
- payout.paid — levantamento concluído no operador
- payout.failed — levantamento recusado pelo operador
- Cabeçalhos: X-Pay-Event, X-Pay-Event-Id e X-Pay-Signature
POST https://o-seu-servidor.com/webhooks/pay
X-Pay-Event: payment.succeeded
X-Pay-Event-Id: 2f1c9d64-...
X-Pay-Signature: t=1757246400,v1=8f3c...
{
"id": "2f1c9d64-...",
"event": "payment.succeeded",
"created_at": "2026-09-09T10:20:31.004Z",
"data": {
"id": "0f8c...",
"reference": "PAY-48213-A55391CEBA",
"amount": 1500.00,
"provider": "mpesa",
"provider_transaction_id": "DI80LI5BYYM",
"state": "successful"
}
}9. Validar a assinatura do webhook
A assinatura é um HMAC-SHA256 do texto "<timestamp>.<corpo em bruto>" com o segredo do webhook (visível uma vez em Developer). Valide sempre antes de actualizar a encomenda, usando o corpo em bruto (não o JSON reconstruído) e rejeitando marcas temporais com mais de 5 minutos.
import { createHmac, timingSafeEqual } from "crypto";
function verify(secret, rawBody, header) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.trim().split("=")),
);
const ts = Number(parts.t);
if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
const a = Buffer.from(parts.v1 ?? "", "utf8");
const b = Buffer.from(expected, "utf8");
return a.length === b.length && timingSafeEqual(a, b);
}10. Reembolsos
Os reembolsos existem apenas para Visa/Mastercard e são pedidos no painel, em Reembolsos, indicando o ID da transacção. Cada pedido carece de aprovação do administrador antes de ser executado. Não existe endpoint público de reembolso.
11. M-Pesa Moçambique (apenas produção)
A ligação à Vodacom M-Pesa Moçambique é exclusivamente de produção. Não existe, nem é aceite, qualquer ambiente sandbox ou de simulação: URLs como api.sandbox.vm.co.mz são recusadas e, se faltar uma credencial de produção, o pedido falha de imediato. Todas as chamadas partem do nosso servidor; a chave de API, a chave pública e o entity code nunca chegam ao navegador, aos registos ou às respostas.
- C2B — o cliente paga ao negócio e confirma com o PIN no telemóvel
- B2C — o negócio paga ao cliente; usado nos levantamentos M-Pesa
- Estados: created, pending, processing, successful, failed, cancelled, expired, reversed
- Callback C2B: https://pay.co.mz/api/public/v1/mpesa/c2b-callback
- Callback B2C: https://pay.co.mz/api/public/v1/mpesa/b2c-callback
- Nunca creditamos saldo com base numa confirmação do navegador
12. Visa/Mastercard e mKesh
Cartões e mKesh são processados em produção através do nosso provedor de cartões. A confirmação chega por callback assinado e é reconfirmada directamente no provedor antes de creditar o saldo, para que uma notificação falsa nunca gere um pagamento aprovado.
- Callback do provedor: https://pay.co.mz/api/public/v1/netshop/callback
- Checkout com a sua marca em https://pay.co.mz/checkout/<token>
- Transações abertas expiram automaticamente e são reconciliadas
- Levantamentos em mKesh e cartão exigem aprovação do administrador
13. Requisitos para entrar em produção
Antes de aceitar pagamentos reais confirme, com a Vodacom, os dados do contrato e, no painel de administração, o estado da configuração em Pagamentos.
- Entity code (service provider code) de produção atribuído pela Vodacom
- Chave de API e chave pública de produção guardadas apenas no servidor
- URLs de callback registadas no operador e acessíveis por HTTPS
- Conta verificada (KYC aprovado) e carteira activa com número válido
- Verificação de ligação bem-sucedida na consola de administração
14. Erros
Respondemos com códigos HTTP padrão e um corpo no formato {"error": "codigo"}. Guarde sempre a referência da cobrança e trate pedidos repetidos com a mesma Idempotency-Key como um só.
- 400 — invalid_request, invalid_json, invalid_amount, invalid_method, invalid_wallet, invalid_merchant_id, invalid_idempotency_key, amount_below_minimum, msisdn_required
- 401 — invalid_api_key (chave inválida, revogada ou em falta)
- 402 — insufficient_balance (levantamentos)
- 403 — merchant_not_active, blocked_by_risk
- 405 — method_not_allowed | 413 — payload_too_large | 415 — unsupported_media_type
- 429 — rate_limited; aguarde e tente de novo
HTTP/1.1 401 Unauthorized
{ "error": "invalid_api_key" }15. Apoio à integração
A nossa equipa acompanha a sua integração de ponta a ponta. Escreva para suporte@pay.co.mz ou ligue para +258 82 7369 480 com a referência da cobrança em causa e respondemos com o diagnóstico.