Gateway Pix · Documentação da API
Painel da lojaOpenAPI (JSON)Termos

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.

URL base: https://soulpay.co
  • 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
TipoPrefixoUso
Produçãogw_live_Cobranças reais. Exige cadastro aprovado.
Testegw_sandbox_Cobranças simuladas, sem dinheiro. Funciona mesmo antes da aprovação do cadastro.
A chave é secreta: use só no seu servidor. Nunca coloque em aplicativos, no navegador ou em repositórios públicos. Se vazar, gere outra no painel (a antiga para na hora).

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.

  1. Crie a cobrança normalmente (POST /v1/charges). A resposta vem com "livemode": false.
  2. Para "pagar", chame Simular pagamento.
  3. Seu sistema recebe o aviso charge.paid no 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

POST/v1/charges

Cabeçalhos

NomeObrigatórioDescrição
AuthorizationsimBearer SUA_CHAVE
Idempotency-KeysimCódigo único por pedido (até 100 caracteres). Ver Idempotência.
Content-Typesimapplication/json

Corpo

CampoTipoObrigatórioDescrição
amount_centsinteirosimValor em centavos. Mínimo 100 (R$ 1,00). Máximo por cobrança: R$ 3.000,00.
customer_nametextosim*Nome completo do comprador (até 200 caracteres).
customer_documenttextosim*CPF ou CNPJ válido do comprador, com ou sem pontuação.
customer_phonetextosim*DDD + número (10 ou 11 dígitos).
descriptiontextonãoDescrição do pedido (até 500 caracteres).
metadataobjetonãoSeus 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

GET/v1/charges/{id}

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

POST/v1/charges/{id}/simular

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

StatusSignificado
pendingAguardando o pagamento.
paidPago. Pode liberar o pedido.
expired / canceled / failedNão foi pago. Crie uma nova cobrança se o cliente quiser tentar de novo.
chargebackO comprador contestou o Pix (MED). O valor pode ficar bloqueado durante a análise.
refundedValor 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

PUT/v1/webhook   GET/v1/webhook   DELETE/v1/webhook
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

EventoQuando
charge.paidPagamento confirmado.
charge.expired, charge.canceled, charge.failedCobrança encerrada sem pagamento.
charge.chargebackContestação (MED) aberta.
charge.chargeback_revertedContestação resolvida a favor da loja.
charge.refundedValor 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 2xx em 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-Id para 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..." } }
HTTPCódigoO que fazer
401missing_api_key, invalid_api_keyEnvie uma chave válida e ativa.
403kyc_not_approvedCadastro ainda não aprovado. Use a chave de teste enquanto isso.
403merchant_suspendedConta suspensa. Fale com o suporte.
400missing_idempotency_keyEnvie o cabeçalho Idempotency-Key.
400invalid_amount, amount_too_highValor em centavos entre 100 e o máximo permitido.
400missing_customer_name, invalid_customer_document, invalid_customer_phoneCorrija os dados do comprador.
400invalid_description, invalid_customer_name, invalid_metadataRespeite os tamanhos máximos.
400invalid_jsonO corpo não é um JSON válido.
404charge_not_foundId errado ou cobrança de outra loja.
409charge_not_pending(Simulação) a cobrança não está mais pendente.
422idempotency_key_reusedMesmo código usado com outro valor. Gere outro código.
422provider_rejectedA instituição recusou os dados. Corrija e tente com um novo código.
429rate_limitedMuitas requisições. Aguarde o tempo de Retry-After.
502provider_errorFalha temporária. Tente de novo com um novo Idempotency-Key.
500internal_errorErro 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-Limit e RateLimit-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

  1. O lojista cria a conta dele no Gateway Pix e gera a chave em Painel → Integração.
  2. Na sua plataforma, o lojista cola a chave (guarde-a criptografada, nunca no navegador).
  3. Sua plataforma cria as cobranças com a chave do lojista e cadastra o próprio endereço de webhook via PUT /v1/webhook.
  4. Use o campo metadata para guardar seus identificadores (ex.: {"plataforma_pedido": "A-123"}).
Para testar, peça ao lojista uma chave 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 id da 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 campo livemode.
  • 05/11/2026 — Eventos de contestação (MED) e estorno nos webhooks.