Pular para o conteúdo
MOONIPdocs
IntegraçãoManual
PTEN
Ir para o painel

Pagamentos Pix

O Pix é o trilho de pagamento instantâneo brasileiro. O dinheiro liquida em segundos, 24 horas por dia, todo dia. A Moonip te dá um endpoint só que devolve o BR Code (Pix Copia e Cola) e o QR Code prontos para renderizar no seu checkout — na mesma resposta que cria a cobrança.

#O fluxo, de relance

text
1. POST /v1/charges  ────────▶  a Moonip devolve a Cobrança: ch_…, status "pending", pix.br_code + QR
2. Você renderiza o QR e o Pix Copia e Cola na sua tela
3. O cliente escaneia ou cola no app do banco dele
4. O webhook charge.paid chega no seu endpoint
5. Você libera o pedido

#1. Criar a cobrança

Endpoint POST /v1/charges (atalho Pix-first: POST /v1/pix/charges já fixa payment_method: "pix" e devolve exatamente a mesma Cobrança)

Cabeçalhos

text
Authorization: Bearer ak_live_…
Idempotency-Key: <único por cobrança que você pretende criar>
Content-Type: application/json

Authorization: Bearer <chave> é a forma canônica de apresentar a chave de API. O Idempotency-Key é obrigatório no POST /v1/charges (400 sem ele): qualquer string não vazia, com escopo por lojista ((merchant, key)); repetir a mesma chave e o mesmo corpo devolve a cobrança original.

Corpo

CampoTipoObrigatórioObservação
amountinteirosimCentavos. 1000 = R$ 10,00. Precisa ser inteiro positivo.
currencystringnãoPadrão "BRL" — a única moeda que cobrança Pix aceita.
payment_methodstringsim"pix" (único valor aceito hoje; o atalho /v1/pix/charges preenche por você).
descriptionstringnãoAté 500 caracteres. Volta dentro de metadata.description.
expires_ininteironãoSegundos até a cobrança expirar (o tempo de vida do QR Pix). 60–604800 (até 7 dias). Um valor fora da faixa é rejeitado com 400 — nunca é limitado. Omita para usar a janela padrão da conta (geralmente 3600 = 1 h). O pix.expires_at na resposta é sempre a expiração autoritativa.
customer.namestringnãoAparece no extrato do banco quando o provedor suporta.
customer.documentstringnãoCPF ou CNPJ.
customer.emailstringnãoUsado em comprovante.
customer.phonestringnão
expected_payer_tax_idstringnão⚠️ TRAVA a cobrança no CPF/CNPJ informado: com a trava ativa na sua conta, só esse documento consegue pagar e o pagamento de qualquer outro é recusado (ou devolvido, conforme o provedor). Não envie se quem paga pode ser outra pessoa. Sem a trava configurada na conta, o campo é ignorado e a cobrança é criada normalmente. Veja Trava de CPF do pagador abaixo. Exige customer.name. Precisa ser um CPF/CNPJ válido e, se você também enviar customer.document, precisa coincidir com ele.
metadataobjetonãoChave/valor livre, devolvido igual (chaves reservadas e iniciadas com _ são removidas).

Exemplo

bash
curl -X POST https://api.moonip.io/v1/charges \
  -H "Authorization: Bearer $MOONIP_API_KEY" \
  -H "Idempotency-Key: ORD-7821" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 24900,
    "currency": "BRL",
    "payment_method": "pix",
    "description": "Pedido #7821",
    "expires_in": 1800,
    "customer": { "name": "Maria Silva", "document": "12345678901" },
    "metadata": { "order_id": "ORD-7821", "sku": "premium-mensal" }
  }'

Resposta 201 Created

json
{
  "id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
  "object": "charge",
  "amount": 24900,
  "currency": "BRL",
  "status": "pending",
  "payment_method": "pix",
  "customer": { "name": "Maria Silva", "document": "12345678901" },
  "pix": {
    "br_code": "000201BRCODEPIX",
    "qr_code_url": "https://qr.example/img.png",
    "expires_at": "2026-07-23T15:00:00.000Z"
  },
  "checkout_url": "https://checkout.moonip.io/a1b2c3d4-0000-0000-0000-000000000009",
  "settlement": {},
  "metadata": { "order_id": "ORD-7821", "sku": "premium-mensal", "description": "Pedido #7821" },
  "created_at": "2026-07-23T14:30:00.000Z"
}
CampoO que fazer com ele
pix.br_codeO payload EMV do "Pix Copia e Cola". Renderize num botão de copiar.
pix.qr_code_url / pix.qr_code_base64A imagem do QR, quando o provedor devolve uma: qr_code_url para uma URL https:// (jogue num <img src="...">), ou qr_code_base64 (um PNG para decodificar). Um provedor que devolve só o br_code traz nenhum dos dois — sempre renderize o br_code como alternativa.
checkout_urlO checkout hospedado da Moonip para esta cobrança — uma página de pagamento pronta (QR, copia-e-cola, status ao vivo). Redirecione o pagador para cá em vez de montar a sua própria tela. Também vem no GET /v1/charges/{id}, então dá para buscar depois a partir de um id guardado. Você não consegue montar essa URL sozinho: o checkout espera o id sem o prefixo ch_.
pix.expires_atQuando a cobrança expira. Mostre uma contagem regressiva; depois disso o status vira expired.
pix.txidTXID do BACEN — opcional, só aparece quando o provedor vinculou um. Nunca trate como id da cobrança.
settlementSempre presente. O settlement.end_to_end_id costuma vir vazio até a liquidação: só aparece depois que a cobrança chega em paid e foi liquidada/conciliada.

O objeto pix é { br_code?, qr_code_url?, qr_code_base64?, expires_at?, txid? } e fica ausente quando não há br_code, imagem de QR nem expiração a mostrar.

#Status

StatusSignificado
pendingCriada; esperando o pagador.
processingPagamento visto; ainda não confirmado (ex.: retido para revisão).
paidPaga e creditada.
failedPagamento recusado.
expiredO expires_at passou sem pagamento.
cancelledCancelada antes do pagamento.
refundedDevolvida ao pagador.
disputedChargeback ou disputa.

#TXID e end_to_end_id

ch_… é sempre o id Moonip da cobrança — o que você guarda, consulta e usa para conciliar webhook. pix.txid e settlement.end_to_end_id são dado contextual do Pix definido pelo BACEN, expostos só quando se aplicam:

json
{
  "id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
  "pix": { "txid": "BACEN-TXID-77" },
  "settlement": {}
}

charge.id !== charge.pix.txid, sempre — mesmo quando os dois existem. Nunca chaveie seu banco pelo TXID nem pelo end_to_end_id; use o ch_….

#Trava de CPF do pagador

Use expected_payer_tax_id para exigir que apenas um CPF/CNPJ específico pague a cobrança — por exemplo, garantir que o comprador pague da própria conta, e não de um terceiro.

⚠️ Mandar este campo TRAVA a cobrança. Com a trava ativa na sua conta, só o CPF/CNPJ informado consegue pagar: quem pagar de outra conta tem o pagamento recusado pelo banco (ou devolvido, conforme o provedor). Envie o campo só quando você quer essa restrição — por exemplo, aposta ou depósito que precisa sair da conta do próprio cliente. Se o pagador pode ser outra pessoa (pai pagando pelo filho, empresa pagando pelo sócio), não envie: use só customer.document, que é informativo e não trava nada.

Disponibilidade. A trava é desabilitada por padrão: ela depende do provedor que atende a sua conta e só vale onde estiver configurada pelo suporte. Numa conta sem a trava configurada, a cobrança com expected_payer_tax_id é criada normalmente, sem trava — qualquer pagador consegue pagar e o pagamento é creditado. Você percebe pela resposta: a cobrança não traz expected_payer_tax_id. Quando a trava vale, o campo volta na cobrança e as regras abaixo se aplicam. Quer a trava? Fale com o suporte.

bash
curl -X POST https://api.moonip.io/v1/charges \
  -H "Authorization: Bearer $MOONIP_API_KEY" \
  -H "Idempotency-Key: ORD-7821" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 24900,
    "payment_method": "pix",
    "customer": { "name": "Maria Silva", "document": "12345678901" },
    "expected_payer_tax_id": "12345678901"
  }'

Como funciona:

  • A cobrança é criada e paga como qualquer outra cobrança Pix.
  • Na liquidação, a Moonip lê o documento real do pagador no banco e compara com expected_payer_tax_id.
  • Coincide → a cobrança vira paid e seu saldo é creditado, normalmente.
  • Pagou um documento diferente → depende de como a trava é aplicada no provedor que atende a sua conta:
    • Detectiva (fail-closed): a cobrança NÃO é creditada. A Moonip devolve o dinheiro automaticamente a quem pagou (uma devolução Pix padrão, endereçada pelo end_to_end_id original); a cobrança fica em processing até essa devolução liquidar, e só então vira refunded. Você nunca recebe dinheiro de terceiro.
    • Preventiva: a regra do banco deveria recusar o outro pagador antes de o pagamento entrar. Se mesmo assim um terceiro conseguir pagar (a regra não valeu), os fundos são creditados do mesmo jeito e a cobrança continua paid, marcada como divergência de pagador — o dinheiro é seu.
  • O banco não informou o documento do pagador → a cobrança fica em processing (nunca creditada) para revisão manual, em vez de creditar um pagador não verificável.

Observações:

  • A garantia de captura e devolução vale só para o caminho detectivo (fail-closed): a cobrança fica em processing até a devolução automática liquidar e só então vira refunded, e seu saldo de lojista nunca é creditado para o pagador que não coincide. Nesse caminho a checagem é no momento da liquidação, não um bloqueio no banco. No caminho preventivo, um pagamento que passa pela regra do banco é creditado do mesmo jeito e a cobrança continua paid (marcada como divergência de pagador). Nos dois modos o BACEN deixa qualquer pagador ler o QR, então o app do pagador ainda pode exibir a cobrança.
  • expected_payer_tax_id exige customer.name (necessário na cobrança e para a devolução), precisa ser um CPF (11 dígitos) ou CNPJ (14 dígitos) com dígito verificador válido e — se você também enviar customer.document — precisa coincidir com ele. Caso contrário, a requisição é rejeitada com 400.
  • O valor é devolvido na cobrança (expected_payer_tax_id); o documento real do pagador nunca é exposto.

#2. Consultar a cobrança

Endpoint GET /v1/charges/{id}

bash
curl https://api.moonip.io/v1/charges/ch_a1b2c3d4-0000-0000-0000-000000000009 \
  -H "Authorization: Bearer $MOONIP_API_KEY"

Aceita tanto o id com prefixo ch_ quanto o id cru. Depois que o Pix é pago e liquidado, o settlement.end_to_end_id aparece e o status vira paid:

json
{
  "id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
  "object": "charge",
  "status": "paid",
  "amount": 24900,
  "currency": "BRL",
  "payment_method": "pix",
  "customer": { "name": "Maria Silva", "document": "12345678901" },
  "settlement": { "end_to_end_id": "E-END-TO-END-99" },
  "metadata": { "order_id": "ORD-7821" },
  "created_at": "2026-07-23T14:30:00.000Z"
}

#Consultar em laço ou usar webhook?

Use exclusivamente webhook para liberar pedido e gravar no banco — o webhook é a fonte da verdade. O GET /v1/charges/{id} serve para consulta pontual (ferramenta de suporte, um botão "verificar status" que alguém aperta), não para ficar consultando em laço.

#3. Listar cobranças

Endpoint GET /v1/charges — paginação por cursor (nunca por offset: não existe page/offset).

QueryTipoPadrãoObservação
limitinteiro251–100.
starting_afterstring—Cursor: o id ch_… (ou o id cru) da última cobrança que você viu.
statusstring—Um dos status acima.
created_after / created_beforedata ISO-8601—UTC estrito (…Z) — offsets são rejeitados.
customer_idstring—Filtra pelo documento do pagador.
bash
curl "https://api.moonip.io/v1/charges?limit=25&status=paid" \
  -H "Authorization: Bearer $MOONIP_API_KEY"

Resposta 200 OK — o envelope de cursor:

json
{
  "object": "list",
  "data": [ { "id": "ch_a1b2c3d4-0000-0000-0000-000000000009", "object": "charge", "status": "paid" } ],
  "has_more": true,
  "next_cursor": "ch_a1b2c3d4-0000-0000-0000-000000000009"
}

Percorra as páginas passando o next_cursor como starting_after até has_more ser false. A ordenação é da mais recente para a mais antiga.

#4. Receber o webhook

Quando o pagador paga, você recebe um POST assinado no seu endpoint registrado, com o envelope canônico de evento:

json
{
  "id": "evt_5f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c",
  "object": "event",
  "api_version": "2026-07-23",
  "type": "charge.paid",
  "created_at": "2026-07-23T14:31:00.000Z",
  "data": {
    "object": {
      "id": "ch_a1b2c3d4-0000-0000-0000-000000000009",
      "object": "charge",
      "amount": 24900,
      "currency": "BRL",
      "status": "paid",
      "payment_method": "pix",
      "settlement": { "end_to_end_id": "E-END-TO-END-99" }
    }
  }
}

Veja Webhooks para verificação de assinatura, retentativa e registro do endpoint.

#Exemplos de código

Se preferir, há SDKs oficiais para Node.js, Python, PHP e .NET — o mesmo pacote neutro para qualquer marca, configurado com a URL base da API desta página, https://api.moonip.io — que já cuidam de idempotência, retry e verificação de webhook:

nodepythonphpdotnet
client.charges.create(input, { idempotencyKey })client.charges.create(input, idempotency_key)$client->charges->create($input, $idempotencyKey)client.Charges.CreateAsync(input, idempotencyKey)
client.charges.retrieve(id)client.charges.retrieve(id)$client->charges->retrieve($id)client.Charges.RetrieveAsync(id)
client.charges.list(query)client.charges.list(**query)$client->charges->list($query)client.Charges.ListAsync(query)

charges.list devolve o envelope de cursor { object: "list", data, has_more, next_cursor }. Os equivalentes em HTTP direto seguem abaixo.

#Node.js (fetch)

js
const res = await fetch('https://api.moonip.io/v1/charges', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.MOONIP_API_KEY}`,
    'Idempotency-Key': pedido.id,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 24900,
    currency: 'BRL',
    payment_method: 'pix',
    customer: { name: pedido.cliente.nome, document: pedido.cliente.documento },
    metadata: { order_id: pedido.id },
  }),
});

if (!res.ok) throw new Error(`Moonip ${res.status}: ${await res.text()}`);
const cobranca = await res.json();
// cobranca.id (ch_…) → guarde no pedido
// cobranca.pix.br_code / qr_code_url → renderize na hora

#PHP

php
$ch = curl_init('https://api.moonip.io/v1/charges');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'Authorization: Bearer ' . getenv('MOONIP_API_KEY'),
    'Idempotency-Key: ' . $pedido->id,
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'amount'         => 24900,
    'currency'       => 'BRL',
    'payment_method' => 'pix',
    'customer'       => ['name' => $pedido->nomeCliente],
    'metadata'       => ['order_id' => $pedido->id],
  ]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 201) throw new Exception("Moonip $status: $body");
$cobranca = json_decode($body, true);

#Python (requests)

python
import os, requests

r = requests.post(
    "https://api.moonip.io/v1/charges",
    headers={
        "Authorization": "Bearer " + os.environ['MOONIP_API_KEY'],
        "Idempotency-Key": pedido.id,
    },
    json={
        "amount": 24900,
        "currency": "BRL",
        "payment_method": "pix",
        "customer": {"name": pedido.cliente.nome},
        "metadata": {"order_id": pedido.id},
    },
    timeout=10,
)
r.raise_for_status()
cobranca = r.json()

#Casos de borda e dúvidas

P: Os campos pix.br_code / QR não vieram na resposta. R: A criação Pix-first devolve os dois de forma síncrona. Se faltarem, o status normalmente já vem failed — confira primeiro se a requisição não tem erro de validação (GET /v1/charges/{id} relê a cobrança guardada).

P: Por quanto tempo o QR vale? R: Veja o pix.expires_at da cobrança — é definido por cobrança. Depois de expirar, o status vira expired e é preciso criar uma cobrança nova.

P: Dá para estornar uma cobrança Pix? R: Reembolso hoje é solicitado pelo painel administrativo — a API não expõe um endpoint de reembolso utilizável pelo lojista (recusa chamadas com chave de API). O mesmo vale para /v1/charges/{id}/refund, que não existe.

P: Pago taxa se o cliente nunca pagar? R: Não. A taxa incide só em cobrança paid, e nunca é discriminada na resposta pública de charge.

P: Meu cliente pagou o valor errado. R: O Pix é de valor exato. Cobrança paga a menor continua pending e o banco devolve o pagador. Pagar a mais é raro, e é tratado do mesmo jeito.

#Próximo

→ Configure webhooks para produção