API Pix Gateway Pix
Crie cobranças Pix, receba avisos automáticos quando forem pagas e acompanhe tudo pelo painel. A API usa JSON, HTTPS e chaves secretas.
- Valores sempre em centavos (R$ 19,90 =
1990). - Datas em ISO 8601 (UTC), ex.:
2026-11-05T19:19:00.000Z. - Erros sempre no formato
{"error": {"code": "...", "message": "..."}}.
Autenticação
Gere sua chave em Painel da loja → Integração e envie em todas as chamadas:
Authorization: Bearer SUA_CHAVE
| Tipo | Prefixo | Uso |
|---|---|---|
| Produção | gw_live_ | Cobranças reais. Exige cadastro aprovado. |
| Teste | gw_sandbox_ | Cobranças simuladas, sem dinheiro. Funciona mesmo antes da aprovação do cadastro. |
Modo de teste recomendado para integrar
Com uma chave gw_sandbox_, tudo funciona como em produção, mas o Pix é simulado: ninguém paga de verdade e nada entra no saldo.
- Crie a cobrança normalmente (
POST /v1/charges). A resposta vem com"livemode": false. - Para "pagar", chame Simular pagamento.
- Seu sistema recebe o aviso
charge.paidno webhook cadastrado, igual em produção.
Quando estiver tudo certo, troque a chave pela gw_live_. Nenhuma outra mudança é necessária.
Primeira cobrança em 1 minuto
curl -X POST https://soulpay.co/v1/charges \
-H "Authorization: Bearer gw_sandbox_SUA_CHAVE" \
-H "Idempotency-Key: pedido-1001" \
-H "Content-Type: application/json" \
-d '{"amount_cents": 1990, "description": "Pedido 1001",
"customer_name": "Maria Silva", "customer_document": "12345678909",
"customer_phone": "11999998888"}'
Mostre ao comprador o pix_copy_paste (e um QR Code gerado a partir dele). Depois, aguarde o webhook charge.paid ou consulte o status.
Criar cobrança
Cabeçalhos
| Nome | Obrigatório | Descrição |
|---|---|---|
Authorization | sim | Bearer SUA_CHAVE |
Idempotency-Key | sim | Código único por pedido (até 100 caracteres). Ver Idempotência. |
Content-Type | sim | application/json |
Corpo
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
amount_cents | inteiro | sim | Valor em centavos. Mínimo 100 (R$ 1,00). Máximo por cobrança: R$ 3.000,00. |
customer_name | texto | sim* | Nome completo do comprador (até 200 caracteres). |
customer_document | texto | sim* | CPF ou CNPJ válido do comprador, com ou sem pontuação. |
customer_phone | texto | sim* | DDD + número (10 ou 11 dígitos). |
description | texto | não | Descrição do pedido (até 500 caracteres). |
metadata | objeto | não | Seus dados livres (até 4 KB), ex.: {"pedido": "1001"}. |
* Exigidos pela instituição que processa o Pix em produção. Recomendamos sempre enviar, também no modo de teste.
Resposta 201
{
"id": "b9718c25-4f1a-4c3e-9d2b-7a1f0e6c8d11",
"livemode": true,
"status": "pending",
"amount_cents": 1990,
"currency": "BRL",
"description": "Pedido 1001",
"pix_qr_code": null,
"pix_copy_paste": "00020126870014br.gov.bcb.pix...",
"expires_at": null,
"paid_at": null,
"fee_cents": null,
"net_cents": null,
"created_at": "2026-11-05T19:14:00.000Z"
}
fee_cents (taxa) e net_cents (valor líquido da loja) são preenchidos quando a cobrança é paga.
Consultar cobrança
Devolve a cobrança no mesmo formato acima. Só a loja dona da cobrança consegue consultá-la (outras recebem 404).
curl https://soulpay.co/v1/charges/ID_DA_COBRANCA -H "Authorization: Bearer SUA_CHAVE"
Simular pagamento só modo de teste
Muda o status de uma cobrança de teste e dispara o webhook, como se o comprador tivesse pago.
curl -X POST https://soulpay.co/v1/charges/ID_DA_COBRANCA/simular \
-H "Authorization: Bearer gw_sandbox_SUA_CHAVE" \
-H "Content-Type: application/json" \
-d '{"status": "paid"}'
Valores aceitos em status: paid, failed, canceled, expired. Em cobranças reais devolve not_test_charge.
Status da cobrança
| Status | Significado |
|---|---|
pending | Aguardando o pagamento. |
paid | Pago. Pode liberar o pedido. |
expired / canceled / failed | Não foi pago. Crie uma nova cobrança se o cliente quiser tentar de novo. |
chargeback | O comprador contestou o Pix (MED). O valor pode ficar bloqueado durante a análise. |
refunded | Valor devolvido ao comprador. Bloqueie o acesso ao produto, se for o caso. |
Webhooks (avisos automáticos)
Cadastre o endereço do seu sistema em Painel → Integração (ou pela API abaixo). Quando uma cobrança muda de status, enviamos um POST com JSON.
Cadastrar pela API
curl -X PUT https://soulpay.co/v1/webhook \
-H "Authorization: Bearer SUA_CHAVE" -H "Content-Type: application/json" \
-d '{"url": "https://sualoja.com.br/webhooks/pix"}'
# Resposta: { "url": "...", "secret": "whsec_..." } <- guarde o segredo (aparece só uma vez)
Eventos
| Evento | Quando |
|---|---|
charge.paid | Pagamento confirmado. |
charge.expired, charge.canceled, charge.failed | Cobrança encerrada sem pagamento. |
charge.chargeback | Contestação (MED) aberta. |
charge.chargeback_reverted | Contestação resolvida a favor da loja. |
charge.refunded | Valor devolvido ao comprador. |
Exemplo de aviso
POST https://sualoja.com.br/webhooks/pix
Content-Type: application/json
X-Gateway-Event: charge.paid
X-Gateway-Event-Id: evt_3f2a...
X-Gateway-Signature: t=1762370340,v1=5b1c...e9
{
"id": "evt_3f2a...",
"event": "charge.paid",
"livemode": true,
"created_at": "2026-11-05T19:19:00.000Z",
"data": { "id": "b9718c25-...", "status": "paid", "amount_cents": 1990, "net_cents": 1803, ... }
}
Regras de entrega
- Responda com status
2xxem até 5 segundos. Processe o pedido depois de responder. - Se falhar, tentamos mais 1 vez após cerca de 30 segundos. O histórico fica em Painel → Integração.
- O mesmo evento pode chegar mais de uma vez: use
X-Gateway-Event-Idpara não processar duas vezes. - Na dúvida, consulte
GET /v1/charges/{id}: ela é a fonte da verdade.
Verificar a assinatura
Confira X-Gateway-Signature antes de liberar o pedido. Ela é um HMAC-SHA256 de t + "." + corpo_bruto com o seu segredo whsec_. Use o corpo exatamente como chegou (antes de converter o JSON).
Node.js
const crypto = require('crypto');
function avisoAutentico(corpoBruto, cabecalho, segredo) {
const t = /t=(\d+)/.exec(cabecalho || '')?.[1];
const v1 = /v1=([0-9a-f]+)/.exec(cabecalho || '')?.[1];
if (!t || !v1) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // mais de 5 min: recusa
const esperado = crypto.createHmac('sha256', segredo).update(`${t}.${corpoBruto}`).digest('hex');
return v1.length === esperado.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(esperado));
}
// Express: use express.raw para ter o corpo bruto
app.post('/webhooks/pix', express.raw({ type: 'application/json' }), (req, res) => {
if (!avisoAutentico(req.body.toString(), req.get('X-Gateway-Signature'), process.env.GATEWAY_WHSEC)) {
return res.status(401).end();
}
res.status(200).end();
const evento = JSON.parse(req.body);
// processar evento.event e evento.data ...
});PHP
$corpo = file_get_contents('php://input');
$cab = $_SERVER['HTTP_X_GATEWAY_SIGNATURE'] ?? '';
preg_match('/t=(\d+)/', $cab, $t); preg_match('/v1=([0-9a-f]+)/', $cab, $v);
$esperado = hash_hmac('sha256', ($t[1] ?? '') . '.' . $corpo, getenv('GATEWAY_WHSEC'));
if (empty($v[1]) || abs(time() - (int)$t[1]) > 300 || !hash_equals($esperado, $v[1])) {
http_response_code(401); exit;
}
http_response_code(200);
$evento = json_decode($corpo, true);Python
import hmac, hashlib, re, time
def aviso_autentico(corpo_bruto: bytes, cabecalho: str, segredo: str) -> bool:
t = re.search(r"t=(\d+)", cabecalho or ""); v1 = re.search(r"v1=([0-9a-f]+)", cabecalho or "")
if not t or not v1 or abs(time.time() - int(t.group(1))) > 300:
return False
esperado = hmac.new(segredo.encode(), f"{t.group(1)}.".encode() + corpo_bruto, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, v1.group(1))Erros
{ "error": { "code": "invalid_amount", "message": "amount_cents deve ser um número inteiro..." } }
| HTTP | Código | O que fazer |
|---|---|---|
| 401 | missing_api_key, invalid_api_key | Envie uma chave válida e ativa. |
| 403 | kyc_not_approved | Cadastro ainda não aprovado. Use a chave de teste enquanto isso. |
| 403 | merchant_suspended | Conta suspensa. Fale com o suporte. |
| 400 | missing_idempotency_key | Envie o cabeçalho Idempotency-Key. |
| 400 | invalid_amount, amount_too_high | Valor em centavos entre 100 e o máximo permitido. |
| 400 | missing_customer_name, invalid_customer_document, invalid_customer_phone | Corrija os dados do comprador. |
| 400 | invalid_description, invalid_customer_name, invalid_metadata | Respeite os tamanhos máximos. |
| 400 | invalid_json | O corpo não é um JSON válido. |
| 404 | charge_not_found | Id errado ou cobrança de outra loja. |
| 409 | charge_not_pending | (Simulação) a cobrança não está mais pendente. |
| 422 | idempotency_key_reused | Mesmo código usado com outro valor. Gere outro código. |
| 422 | provider_rejected | A instituição recusou os dados. Corrija e tente com um novo código. |
| 429 | rate_limited | Muitas requisições. Aguarde o tempo de Retry-After. |
| 502 | provider_error | Falha temporária. Tente de novo com um novo Idempotency-Key. |
| 500 | internal_error | Erro nosso. Tente de novo em instantes. |
Idempotência
Se a sua chamada cair no meio (internet, timeout), repita com o mesmo Idempotency-Key: devolvemos a mesma cobrança (status 200 e cabeçalho Idempotent-Replay: true) em vez de criar outra. Use o número do seu pedido como código.
Limites
- 120 requisições por minuto por chave de API (cabeçalhos
RateLimit-LimiteRateLimit-Remaining). - Valor por cobrança: de R$ 1,00 a R$ 3.000,00.
- Saques: pelo painel da loja; valor mínimo e horário informados no painel.
Para plataformas parceiros
Plataformas de venda, áreas de membros, checkouts e marketplaces podem oferecer o Pix Gateway Pix aos seus próprios lojistas.
Como funciona hoje
- O lojista cria a conta dele no Gateway Pix e gera a chave em Painel → Integração.
- Na sua plataforma, o lojista cola a chave (guarde-a criptografada, nunca no navegador).
- Sua plataforma cria as cobranças com a chave do lojista e cadastra o próprio endereço de webhook via
PUT /v1/webhook. - Use o campo
metadatapara guardar seus identificadores (ex.:{"plataforma_pedido": "A-123"}).
gw_sandbox_ ou crie uma conta de teste sua: o modo de teste funciona antes da aprovação do cadastro.Conta de plataforma em breve
Uma chave única da plataforma para criar cobranças em nome de vários lojistas, com conexão autorizada pelo próprio lojista. Quer ser parceiro? Fale com o suporte pelo painel.
Integramos com você
Se a sua plataforma já tem uma API de pagamentos ou de pedidos, nós construímos o conector do nosso lado. Envie para o suporte pelo painel: a documentação da sua API, como funciona a autenticação, os eventos (webhooks) que vocês enviam e um ambiente de testes.
Boas práticas
- Libere o produto só com
status: "paid"confirmado (webhook com assinatura válida ou consulta à API). - Guarde o
idda cobrança junto do seu pedido. - Use HTTPS no endereço do webhook e responda rápido.
- Mostre ao comprador um nome de cobrança que ele reconheça: reduz contestações (MED).
- Não registre a chave de API nem o segredo
whsec_em logs.
Novidades
- 05/11/2026 — Cadastro pelo site, programa de indicação e escolha da adquirente no painel.
- 05/11/2026 — Modo de teste (
gw_sandbox_), simulação de pagamento e campolivemode. - 05/11/2026 — Eventos de contestação (MED) e estorno nos webhooks.