Referência da API
API PagPix Hub
REST sobre HTTPS, JSON em UTF-8. Crie cobranças PIX, consulte saldo, dispare saques e receba webhooks assinados. Cada endpoint traz request e response completos, prontos pra colar.
Pra agentes de IA
Regras que valem pra TODA a API, pra não precisar inferir do resto: o único método de pagamento é PIX (method: "pix"); todo valor é inteiro em centavos, nunca decimal; datas são ISO-8601 em UTC; listagem pagina por cursor, nunca por offset; erro sempre vem como {"error": {"code", "message"}} com o código estável na tabela de erros abaixo. Toda transação tem id e public_id (o id que o lojista vê no painel), e os dois servem no GET /transactions/{id}. O ÚNICO campo obrigatório para cobrar é amount_cents. Não invente campos: o que não está documentado aqui é ignorado pelo servidor.
Resumo rápido#
| Base URL | https://pagpixhub.com/api/v1 |
| Auth | Authorization: Bearer bp_live_... |
| Content-Type | application/json |
| Encoding | UTF-8 |
| Métodos | PIX. Cartão não é aceito nesta plataforma. |
| Rate limit | Sem limite na criação de cobrança. Saque: 10 req/min por chave. |
| Chave | Gerada por você em Dashboard → Integrações (pede a senha da conta) |
| Idempotência | Header Idempotency-Key em POST /transactions e /withdrawals (TTL 24h) |
| Webhook | HMAC-SHA256 t=<ts>,v1=<sig> nos headers PagPixHub-Signature e X-PagPixHub-Signature |
| OpenAPI | /api/v1/openapi.json |
Integração em 5 passos#
- Gere a chave em Dashboard → Integrações e guarde no servidor. Ela nunca vai para o navegador do comprador.
- Cobre com
POST /transactionsmandandoamount_cents. A resposta trazpix.copy_paste, o codigo copia-e-cola (EMV) — é o que você mostra pro comprador, e é dele que você desenha o QR na sua tela. Não mandamos imagem de QR: não existe campoqr_code_base64na resposta, e opix.qr_codedoGETé um campo legado que vemnullnas cobranças criadas pela API. - Nem toda criação volta com o copia-e-cola na hora. Quando a resposta for
202compix_status: "generating", a cobrança existe e o código está sendo gerado: consulteGET /transactions/{id}atépix_statussair degenerating. Detalhe completo em Ciclo do PIX. - Cadastre uma URL de webhook e espere
transaction.paid. Não fique consultando a transação em laço: o evento chega em segundos e o polling só gasta os dois lados. Valide a assinatura antes de confiar no evento. - Confira o dinheiro em
GET /balancee retire comPOST /withdrawals.
Autenticação#
Toda chamada requer uma chave de API no header Authorization com esquema Bearer.
Gere a chave em Dashboard → API Keys. A chave começa com bp_live_ e aparece UMA VEZ, copie e guarde.
curl https://pagpixhub.com/api/v1/me \
-H "Authorization: Bearer bp_live_seu_token"Resposta 401 quando o token é inválido. Resposta 429 com header Retry-After quando excede o rate limit.
Formato de erro#
Erros sempre vêm com este formato. Use o campo error.code pra branching no código (estável); o error.message é pra humanos.
{
"error": {
"code": "validation_error",
"message": "One or more fields are invalid",
"details": { "amount_cents": ["amount_cents min R$5,00 (500 cents)"] }
}
}Códigos comuns
| unauthorized | 401 · Token ausente, inválido ou expirado |
| forbidden | 403 · Token válido mas sem permissão pro recurso |
| validation_error | 422 · Campo com formato/valor inválido (veja details) |
| rate_limited | 429 · Só nos saques: mais de 10 por minuto |
| method_not_supported | 422 · method diferente de "pix" |
| internal_error | 500 · Bug servidor; reportar ao suporte |
POST /transactions, Criar cobrança#
O endpoint principal. Cria uma cobrança e devolve o copia-e-cola do PIX. Não há nada pra cadastrar antes.
utm de cada cobrança: quem vê a URL do comprador é a sua página, não a nossa. Capture utm_source, utm_medium, utm_campaign, utm_content, utm_term (+ fbclid/ttclid/gclid) da URL da SUA página de checkout e repasse no body. Sem isso a venda aparece na UTMify SEM origem e não é atribuída ao anúncio/campanha, o tracking não funciona. Basta configurar o token em Integrações > UTMify + mandar os UTMs aqui; o envio à UTMify (aguardando pagamento, pago, reembolsado e recusado) é automático./transactionsCria uma cobrança PIX. Retorna o copia-e-cola e o QR.
Request body (JSON)
| Campo | Tipo | Descrição |
|---|---|---|
amount_cents | integer (req) | Valor em centavos. Mínimo 500 (R$ 5,00). |
customer.email | string | Email do cliente. Default gerado automaticamente. |
customer.name | string | Nome do cliente. |
customer.cpf | string | CPF/CNPJ (com ou sem máscara). |
customer.phone | string | Telefone (E.164 ou formato BR). |
description | string | Texto livre que aparece no extrato/recibo e é o nome do produto na UTMify. Max 300 chars. |
external_reference | string | Seu ID interno pra correlacionar. Max 120 chars. Volta em todos os webhooks + GET. TAMBEM separa pedidos: duas cobrancas com o MESMO valor e o MESMO cliente em 5 minutos so sao tratadas como retry se a referencia for igual. Referencias diferentes (ou header Idempotency-Key) sempre criam cobrancas diferentes. |
metadata | object | Chave→valor livre (ex: {"attendant_code":"A123"}). Máx 20 chaves. Volta INTACTA nos webhooks e no GET /transactions. |
expires_in | integer | Segundos até expirar (PIX). 60-86400. Default 3600. |
anti_desvio | boolean | ANTI-DESVIO: quando true, a resposta traz o bloco `anti_desvio` com `upload_url` (onde o comprador manda o comprovante) e `status_url`. Use quando quiser reproduzir na sua tela o "Já paguei" do checkout da PagPix Hub: se a adquirente engolir o pagamento, o comprovante do cliente é a prova que usamos pra cobrar ela e recuperar a venda. Os comprovantes aparecem no seu Financeiro › Entradas, em cada venda. |
split.recipient | string | Email OU user id de OUTRA conta PagPix Hub que recebe parte da venda. Pra split/revenue-share (ex: bot de Telegram repassando pro parceiro). |
split.amount_cents | integer | Valor FIXO do split em centavos. Use isto OU split.percent (não os dois). |
split.percent | number | Percentual (0.01–100) sobre o LÍQUIDO da venda (após taxas). Use isto OU split.amount_cents. |
utm.source | string | OBRIGATÓRIO p/ UTMify/atribuição. Capture da URL da sua página (ex: "fb"). |
utm.medium | string | OBRIGATÓRIO p/ UTMify. Capture da URL (ex: "video"). |
utm.campaign | string | OBRIGATÓRIO p/ UTMify. ID/nome da campanha, da URL. |
utm.content | string | Recomendado. ID do criativo/anúncio, da URL. |
utm.term | string | Recomendado. Placement/termo, da URL. |
utm.fbclid | string | Facebook click ID (da URL). Melhora a atribuição Meta. |
utm.gclid | string | Google click ID (da URL). |
utm.ttclid | string | TikTok click ID (da URL). |
Exemplo cURL, PIX simples
curl -X POST https://pagpixhub.com/api/v1/transactions \
-H "Authorization: Bearer bp_live_seu_token" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 1990,
"method": "pix",
"customer": {
"email": "joao@example.com",
"name": "João Silva",
"cpf": "11144477735"
},
"description": "Mensalidade Plano Pro",
"external_reference": "order_42",
"utm": {
"source": "fb",
"medium": "video",
"campaign": "camp-black-friday",
"content": "criativo-A",
"term": "feed",
"fbclid": "IwAR123...",
"ttclid": "",
"gclid": ""
}
}'Resposta sucesso (PIX) · HTTP 200
{
"id": "tx_clxabc123",
"public_id": "pag-pix-1990-4829103746",
"object": "transaction",
"status": "PENDING",
"method": "PIX",
"amount_cents": 1990,
"fee_cents": 449,
"net_cents": 1541,
"currency": "BRL",
"pix": {
"copy_paste": "00020126580014BR.GOV.BCB.PIX...",
"expires_at": "2026-06-01T16:30:00.000Z"
},
"split": null,
"created_at": "2026-06-01T15:30:00.000Z"
}Toda transação tem dois ids. id é o interno, estável, usado no cursor da listagem. public_id (formato pag-pix-<valor em centavos>-<10 dígitos>) é o id da venda que aparece no seu painel, e também serve no GET /transactions/{id} no lugar do id.
Exemplo cURL, PIX com split (revenue share / bot Telegram)
curl -X POST https://pagpixhub.com/api/v1/transactions \
-H "Authorization: Bearer bp_live_seu_token" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 5000,
"method": "pix",
"customer": { "email": "cliente@example.com" },
"description": "Acesso VIP",
"split": {
"recipient": "parceiro@example.com",
"percent": 30
}
}'Quando esse PIX for pago, 30% do líquido cai automaticamente no saldo da conta parceiro@example.com e o restante no seu. Use amount_cents em vez de percent pra um valor fixo. O recebedor precisa ser uma conta PagPix Hub (identifique por email ou user id). Funciona igual em POST /subscriptions (split aplicado em toda renovação).
Ciclo do PIX e a resposta 202#
Na maioria das cobranças o copia-e-cola sai junto com a resposta da criação (HTTP 200). Quando a adquirente demora ou recusa a primeira tentativa, a criação responde HTTP 202 com pix_status: "generating" e pix.copy_paste: null: a cobrança FOI criada, o id já é válido e nós seguimos tentando gerar o código por conta própria.
POST gera uma segunda cobrança para o mesmo pedido. Com 202, guarde o id e consulte.Resposta 202 (criação aceita, código em geração)
{
"id": "tx_clxabc123",
"public_id": "pag-pix-1990-4829103746",
"object": "transaction",
"status": "PENDING",
"pix_status": "generating",
"method": "PIX",
"amount_cents": 1990,
"pix": { "copy_paste": null, "expires_at": "2026-06-01T16:30:00.000Z" },
"message": "PIX em geracao. Faça GET /api/v1/transactions/tx_clxabc123 ate pix.copy_paste ficar disponivel (~5-30s)."
}Valores de pix_status
| generating | Código ainda não existe. Continue consultando (a cada 2s é suficiente). |
| ready | pix.copy_paste preenchido. Mostre pro comprador e pare de consultar. |
| paid | Pago. Estado final. |
| expired | Passou do expires_at sem pagamento. Estado final. |
| failed | Desistimos de gerar (status FAILED). Estado final: crie uma cobrança nova. |
status: "FAILED" / pix_status: "failed" e aparece assim no GET /transactions/{id}. Nenhuma cobrança fica em generating indefinidamente. Se o seu cliente quiser um teto menor, pare de consultar no seu próprio tempo limite e trate como falha: nada é cobrado sem o comprador pagar.Consulta até ficar pronto (Node.js)
async function esperarPix(id, token, limiteMs = 60000) {
const ate = Date.now() + limiteMs;
while (Date.now() < ate) {
const r = await fetch(`https://pagpixhub.com/api/v1/transactions/${id}`, {
headers: { Authorization: `Bearer ${token}` },
});
const tx = await r.json();
if (tx.pix_status === "ready") return tx.pix.copy_paste;
if (["failed", "expired", "paid"].includes(tx.pix_status)) {
throw new Error("cobranca terminou em " + tx.pix_status);
}
await new Promise((s) => setTimeout(s, 2000));
}
throw new Error("tempo limite esperando o PIX");
}pix_status for generating. Para saber que a venda foi PAGA, use o webhook transaction.paid — consultar em laço à espera do pagamento gasta os dois lados e ainda chega depois.GET /transactions/{id}, Consultar#
/transactions/{id}Estado atual de uma cobrança: status, pix_status, copia-e-cola, E2E, metadata.
O {id} pode ser o id ou o public_id da transação. O public_id é o id da venda que aparece no seu painel: com ele você consulta uma venda que só conhece de lá, sem precisar do id.
curl https://pagpixhub.com/api/v1/transactions/tx_clxabc123 \
-H "Authorization: Bearer bp_live_seu_token"
# mesma transação, pelo public_id
curl https://pagpixhub.com/api/v1/transactions/pag-pix-1990-4829103746 \
-H "Authorization: Bearer bp_live_seu_token"Resposta · HTTP 200
{
"id": "tx_clxabc123",
"public_id": "pag-pix-1990-4829103746",
"object": "transaction",
"status": "PAID",
"pix_status": "paid",
"method": "PIX",
"amount_cents": 1990,
"fee_cents": 449,
"net_cents": 1541,
"currency": "BRL",
"customer": { "email": "joao@example.com", "name": "João Silva", "cpf": "11144477735", "phone": null },
"pix": {
"qr_code": null,
"copy_paste": "00020126580014BR.GOV.BCB.PIX...",
"expires_at": "2026-06-01T16:30:00.000Z"
},
"external_reference": "order_42",
"metadata": { "attendant_code": "A123" },
"end_to_end_id": "E12345678202606011530abcdef12345",
"paid_at": "2026-06-01T15:32:10.000Z",
"created_at": "2026-06-01T15:30:00.000Z",
"updated_at": "2026-06-01T15:32:10.000Z"
}| id | Id interno da transação. Estável; é o que vai no cursor da listagem. |
| public_id | Id da venda que aparece no seu painel (pag-pix-<valor em centavos>-<10 dígitos>). Aceito no lugar do id nesta consulta. Pode vir null só em vendas antigas que ainda não receberam o id. |
| status | PENDING, PAID, EXPIRED, FAILED, REFUNDED ou CHARGEBACK. |
| pix_status | Estado da GERAÇÃO do código (tabela acima). status diz da venda; pix_status diz do código. |
| pix.copy_paste | O EMV. É o que o comprador copia, e é daqui que sai o QR da sua tela. |
| pix.qr_code | Campo legado. Vem null nas cobranças criadas pela API; não dependa dele. |
| end_to_end_id | E2E do PIX, preenchido depois do pagamento. Use pra conciliar com o extrato e casar um MED. |
| 404 | O id (ou public_id) não é da sua conta, ou não existe. |
GET /transactions, Listar#
/transactionsLista transações. Ordenação created_at DESC (desempate por id), cursor 100% estável.
Query params
| Campo | Tipo | Descrição |
|---|---|---|
limit | integer | Máx 100. Default 20. |
cursor | string | ID da última transação da página anterior (next_cursor). |
status | PENDING|PAID|EXPIRED|REFUNDED|CHARGEBACK | Filtra por status. |
method | PIX|CARD|APPLE_PAY|GOOGLE_PAY | Filtra por método. |
external_reference | string | Filtra pelo seu external_reference exato. |
end_to_end_id | string | Filtra pelo EndToEndId do PIX exato. Use pra achar a venda de um MED. |
created_at_from | ISO 8601 | Só txs criadas A PARTIR desta data (gte). |
created_at_to | ISO 8601 | Só txs criadas ATÉ esta data (lte). |
Cada item traz public_id, external_reference, metadata, fee_cents, net_cents, status e tracking (UTMs). Reconciliação por janela: ?created_at_from=2026-06-01T00:00:00Z&created_at_to=2026-06-02T00:00:00Z.
curl https://pagpixhub.com/api/v1/transactions?status=PAID&limit=50 \
-H "Authorization: Bearer bp_live_seu_token"{
"data": [
{
"id": "tx_clxabc123",
"public_id": "pag-pix-1990-4829103746",
"object": "transaction",
"product_id": "prd_xxx",
"checkout_id": "ck_xxx",
"customer": { "email": "...", "name": "...", "cpf": "...", "phone": "..." },
"amount_cents": 1990,
"fee_cents": 449,
"net_cents": 1541,
"currency": "BRL",
"method": "PIX",
"status": "PAID",
"pix": { "qr_code": "...", "copy_paste": "...", "expires_at": "..." },
"card": null,
"tracking": { "utm_source": "...", "utm_medium": "...", "fbclid": "..." },
"split": { "recipient": "parceiro@example.com", "amount_cents": 450 },
"paid_at": "2026-06-01T15:32:00.000Z",
"created_at": "2026-06-01T15:30:00.000Z",
"updated_at": "2026-06-01T15:32:00.000Z"
}
],
"has_more": true,
"next_cursor": "tx_clxabc123"
}GET /me, Conta atual#
/meRetorna info da conta dona da API key.
curl https://pagpixhub.com/api/v1/me -H "Authorization: Bearer bp_live_seu_token"{
"id": "usr_xxx",
"email": "voce@example.com",
"name": "Você",
"balance_cents": 12345,
"api_card_enabled": false
}GET /balance, Saldo#
/balanceSaldo da conta dona da chave: disponível para saque, em liberação e saques em aberto.
curl https://pagpixhub.com/api/v1/balance -H "Authorization: Bearer bp_live_seu_token"Resposta · HTTP 200
{
"object": "balance",
"currency": "BRL",
"available_cents": 50000, // SACÁVEL agora (já liberado)
"pending_pix_cents": 12000, // PIX pago, ainda em holding
"pending_card_cents": 0, // Cartão pago, ainda em holding (D+X)
"retained_card_cents": 0, // Retenção de cartão (libera no fim do período)
"in_withdrawal_cents": 0, // Saques abertos já debitados
"upcoming_releases": [
{ "transaction_id": "tx_xxx", "transaction_public_id": "pag-pix-12000-4829103746", "date": "2026-06-02T10:00:00.000Z", "cents": 12000, "kind": "pix_release", "method": "PIX" }
]
}O valor que você pode sacar via API é sempre available_cents. Os demais campos são informativos (calendário de liberação).
Saques (Withdrawals)#
/withdrawalsSolicita um saque PIX. Débito atômico: NUNCA saca mais que o saldo disponível.
/withdrawalsLista os saques da conta (cursor).
/withdrawals/{id}Status de um saque específico.
Segurança. O débito do saldo é feito de forma atômica e condicional no banco (WHERE saldo >= valor), é impossível sacar mais do que você tem. Só pode haver 1 saque em aberto por conta de cada vez, e toda solicitação passa por uma auditoria automática antes de ser aceita. A taxa de saque sai do valor solicitado (você recebe amount_cents − fee_cents).
POST /withdrawals · body
| Campo | Tipo | Descrição |
|---|---|---|
amount_cents | integer (req) | Valor BRUTO do saque em centavos. PIX mín R$ 100,00 (10000). |
method | "pix" | Default "pix". Só "pix" é aceito: outro valor retorna 422 withdrawal_method_not_supported. |
pix_key | string | Obrigatório. Sua chave Pix. |
pix_key_type | cpf|cnpj|evp|phone|email | Obrigatório. Tipo da chave. |
Exemplo cURL, saque PIX
curl -X POST https://pagpixhub.com/api/v1/withdrawals \
-H "Authorization: Bearer bp_live_seu_token" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 10000,
"method": "pix",
"pix_key": "voce@example.com",
"pix_key_type": "email"
}'Resposta · HTTP 201
{
"id": "wd_clxabc123",
"object": "withdrawal",
"status": "PENDING",
"method": "pix",
"amount_cents": 10000,
"fee_cents": 500,
"net_cents": 9500,
"pix_key": "voce@example.com",
"pix_key_type": "email",
"wallet_address": null,
"fail_reason": null,
"paid_at": null,
"created_at": "2026-06-01T15:30:00.000Z",
"updated_at": "2026-06-01T15:30:00.000Z"
}fail_reason só vem preenchido quando o saque termina com status FAILED ou REJECTED: é uma mensagem em português para mostrar ao seu usuário (ex. "Transferência não concluída. O valor voltou para o seu saldo."). Nos outros status é sempre null.
Erros possíveis
| Campo | Tipo | Descrição |
|---|---|---|
insufficient_balance | 422 | Saldo disponível menor que o valor pedido. |
amount_below_minimum | 422 | Abaixo do mínimo (PIX R$ 100). |
withdrawal_method_not_supported | 422 | method diferente de pix. Saque só por PIX. |
withdrawal_in_progress | 409 | Já existe um saque em aberto. Aguarde finalizar. |
audit_blocked | 422 | Auditoria automática bloqueou (drift de saldo, refund rate, etc). |
blocked | 403 | Conta bloqueada para saques. |
insufficient_scope | 403 | A API key não tem o escopo "withdrawals". |
GET /withdrawals, lista
curl "https://pagpixhub.com/api/v1/withdrawals?status=PAID&limit=50" \
-H "Authorization: Bearer bp_live_seu_token"Status possíveis: PENDING, PROCESSING, PAID, FAILED, REJECTED.
Webhooks (eventos)#
Eventos são enviados via POST pra URL configurada em Dashboard → Integrações. Cada webhook tem um segredo próprio (whsec_...), que você vê no ícone de escudo da linha do webhook, informando a senha da conta. É com ele que você valida a assinatura HMAC.
Eventos disponíveis
| transaction.created | Tx criada (PIX gerado) |
| transaction.paid | Pagamento confirmado |
| transaction.refunded | Reembolsada |
| transaction.chargeback | Chargeback |
| transaction.expired | PIX expirou |
| transaction.failed | Falha no dispatcher do PSP |
| balance.updated | Saldo da conta mudou (venda liberada, saque pedido ou pago) |
| withdrawal.paid | Saque pago |
| withdrawal.failed | Saque recusado |
| med.opened | MED aberto contra uma venda |
| med.updated | MED mudou de status ou de bloqueio |
| transaction.receipt_uploaded | Lead subiu comprovante |
| withdrawal.paid | Saque pago |
| withdrawal.failed | Saque recusado/falhou (saldo estornado) |
Envelope do evento
{
"id": "evt_<deliveryId>", // ESTÁVEL entre retentativas, dedupe por ele
"type": "transaction.paid",
"created": 1730476320, // unix timestamp (segundos)
"data": { /* objeto do evento (ver abaixo) */ }
}data, transaction.* (created / paid / refunded / chargeback / expired)
{
"id": "tx_clxabc123",
"public_id": "pag-pix-1990-4829103746",
"product_id": "prd_xxx",
"checkout_id": null,
"amount_cents": 1990,
"fee_cents": 449,
"net_cents": 1541,
"currency": "BRL",
"method": "PIX",
"status": "PAID",
"external_reference": "order_42",
"end_to_end_id": "E18236120202609111335s0000abc123",
"metadata": { "attendant_code": "A123" },
"customer": { "email": "...", "name": "...", "cpf": "...", "phone": "..." },
"tracking": { "utm_source": "fb", "utm_medium": "video", "utm_campaign": "...",
"utm_content": "...", "utm_term": "...", "fbclid": "...",
"ttclid": "...", "gclid": "..." },
"receipt_url": null,
"paid_at": "2026-06-01T15:32:00.000Z",
"created_at": "2026-06-01T15:30:00.000Z"
}external_reference, metadata e tracking (UTMs) voltam em TODOS os eventos de transação, inclusive refunded e chargeback.
public_id é o id da venda que aparece no seu painel, o mesmo que a criação e o GET devolvem. O id continua vindo como antes.
end_to_end_id é o EndToEndId do PIX, o identificador que aparece no extrato e pelo qual o MED chega. Vem preenchido no transaction.paid e nos eventos seguintes, e também no GET /transactions/{id}. É null antes do pagamento e quando a instituição que processou o PIX não informa o E2E.
data, withdrawal.paid / withdrawal.failed
{
"id": "wd_xxx",
"object": "withdrawal",
"status": "PAID",
"method": "pix",
"amount_cents": 10000,
"fee_cents": 500,
"net_cents": 9500,
"pix_key": "voce@example.com",
"wallet_address": null,
"fail_reason": null,
"paid_at": "2026-06-01T16:00:00.000Z"
}Assinatura (HMAC-SHA256), header e formato exato
A assinatura vem em dois headers com o MESMO valor: PagPixHub-Signature (canônico) e X-PagPixHub-Signature (alias). Formato:
PagPixHub-Signature: t=1730476320,v1=9f86d081884c7d659...t= unix timestamp usado na assinatura.v1= HMAC-SHA256 (hex) do payload assinado${t}.${rawBody}, ou seja, o timestamp + ponto + o corpo CRU do request (a string JSON exata, não re-serializada).- Anti-replay: rejeite se
testiver fora de uma janela (ex: ±5 min) do horário atual. - Algoritmo:
HMAC-SHA256. Secret: owhsec_do webhook.
Verificação de assinatura (Node.js), correta
import crypto from "node:crypto";
// rawBody = corpo CRU (string), NÃO o objeto re-serializado.
function verifyWebhook(rawBody, headerValue, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
headerValue.split(",").map((kv) => kv.split("="))
); // { t, v1 }
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // replay
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1)
);
}Retentativas (retry)
| Sucesso | Qualquer 2xx (usamos res.ok). Responda 200 rápido (<5s). |
| Tentativas | Até 8 no total. |
| Backoff | 30s, 2min, 10min, 30min, 1h, 3h, 6h, 24h |
| evt id | MESMO id em todas as retentativas (dedupe seguro). Só muda t/assinatura. |
| IPs de origem | Sem IPs fixos (infra serverless). Valide por HMAC, não por IP. |
evt id é estável entre retentativas, use-o pra deduplicar (idempotência no recebedor).Taxas (referência)#
As taxas variam por conta. Consulte GET /me pra ver os números atuais da sua conta, ou Dashboard → Perfil.
| PIX | Varia por conta, consulte GET /me |
| Liberação | Imediata após o pagamento |
| Saque PIX | R$ 5,00 (fixo) |
Quickstart (Node.js)#
// 1. Cria PIX (sem produto, funciona pra qualquer conta)
const res = await fetch("https://pagpixhub.com/api/v1/transactions", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.PAGPIXHUB_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount_cents: 1990,
method: "pix",
customer: {
email: "cliente@example.com",
name: "Cliente Exemplo",
cpf: "11144477735",
},
description: "Pedido #42",
external_reference: "order_42",
}),
});
const tx = await res.json();
if (!res.ok) throw new Error(tx.error.message);
// 2. Mostra QR pro cliente
console.log("Copia-cola PIX:", tx.pix.copy_paste);
console.log("Expira em:", tx.pix.expires_at);
// 3. Espera webhook 'transaction.paid' chegar no seu endpoint
// OU pollie GET /transactions/{id} periodicamente
// OU melhor: configure o webhook em Dashboard → Integrações