UranoPag
Texto completo

API UranoPag

Cobranças PIX, saldo, saques e webhooks assinados. Endereço base https://api.uranopag.app/v1

Visão geral

A API UranoPag permite gerar cobranças PIX, consultar vendas, acompanhar o saldo e solicitar saques PIX a partir do seu sistema. Todas as requisições e respostas usam JSON (UTF-8) sobre HTTPS.

ItemValor
Endereço basehttps://api.uranopag.app/v1
ProtocoloREST + JSON
AmbienteSomente produção. Não existe sandbox nem chave de teste: toda cobrança é real.
ValoresSempre inteiros em centavos (1000 = R$ 10,00)
DatasISO 8601 em UTC

Autenticação

Crie sua chave no painel em Desenvolvedores → API e Webhooks → API Keys. A chave completa aparece uma única vez; guarde-a em um cofre de segredos. Ela começa com up_live_.

Authorization: Bearer up_live_SUA_CHAVE
Content-Type: application/json
  • Use a chave somente no seu servidor. Nunca no navegador ou aplicativo.
  • Você pode restringir a chave a IPs ou faixas (CIDR). Requisições de outros IPs recebem 403 ip_not_allowed.
  • Revogue a chave no painel se suspeitar de vazamento; o efeito é imediato.

Idempotência

Criação de cobrança e de saque exigem o cabeçalho Idempotency-Key, de 16 a 128 caracteres (letras, números e hífen; use um UUID). Repetir a mesma requisição com a mesma chave devolve o mesmo recurso, sem criar outro. Repetir a chave com dados diferentes devolve 409 idempotency_mismatch.

Idempotency-Key: 2f1c9a7e-5b8d-4c3a-9e21-7d6f0b4a1c55

Isso garante que cliques repetidos, novas tentativas de rede ou reenvios nunca gerem uma cobrança ou um saque duplicado.

Criar cobrança PIX

POST /v1/charges
CampoTipoObrigatórioRegra
amount_centsinteirosimMínimo 100 (R$ 1,00). Teto: ticket máximo da sua conta.
external_idtextonãoSeu identificador do pedido (até 255; letras, números, . _ : -). Único por conta.
descriptiontextonãoAté 200 caracteres.
payer.nametextosim3 a 120 caracteres.
payer.documenttextosimCPF (11) ou CNPJ (14), com ou sem máscara.
payer.emailtextonãoE-mail válido.
payer.phonetextonãoDDD + número, só dígitos.
curl -X POST "https://api.uranopag.app/v1/charges" \
  -H "Authorization: Bearer up_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f1c9a7e-5b8d-4c3a-9e21-7d6f0b4a1c55" \
  -d '{
    "amount_cents": 15000,
    "external_id": "pedido-8842",
    "description": "Pedido 8842",
    "payer": { "name": "Maria da Silva", "document": "529.982.247-25", "email": "maria@email.com" }
  }'

Resposta 201:

{
  "id": "3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70",
  "object": "charge",
  "external_id": "pedido-8842",
  "amount_cents": 15000,
  "fee_cents": 449,
  "net_cents": 14551,
  "status": "pending",
  "pix_copy_paste": "00020101021226...6304ABCD",
  "end_to_end_id": null,
  "expires_at": "2026-10-19T03:30:00.000Z",
  "paid_at": null,
  "created_at": "2026-10-18T03:30:00.000Z"
}

Mostre pix_copy_paste como texto copiável (Pix Copia e Cola) e gere o QR Code a partir dele com qualquer biblioteca de QR. O pagamento é confirmado pelo webhook payment.paid.

Consultar e listar cobranças

GET /v1/charges/{id}
GET /v1/charges?page=1&limit=20&status=paid&external_id=pedido-8842

limit vai de 1 a 100 (padrão 20). Filtros opcionais: status e external_id. A lista vem da mais recente para a mais antiga, com has_more indicando se há próxima página.

{ "data": [ { "id": "3b0d7c1e-...", "object": "charge", "status": "paid", "end_to_end_id": "E18236120202610180331s0123456789", "...": "..." } ], "page": 1, "limit": 20, "has_more": false }
statusSignificado
pendingAguardando pagamento.
paidPago e confirmado.
expiredNão pago dentro do prazo.
failedFalha na emissão ou recusa do banco.
in_reviewPagamento em análise por contestação (MED).
defendedContestação defendida: a venda segue paga.
refundedValor devolvido ao pagador.

end_to_end_id é o código E2E do PIX (o mesmo do comprovante bancário). Ele é preenchido quando o pagamento é confirmado e a instituição o informa.

Consultar saldo

GET /v1/balance
{ "object": "balance", "available_cents": 125000, "blocked_cents": 0, "updated_at": "2026-10-18T03:31:00.000Z" }

available_cents é o que pode ser sacado. blocked_cents está retido (análise ou contestação) e não pode ser sacado.

Solicitar e consultar saques PIX

POST /v1/withdrawals
CampoTipoRegra
amount_centsinteiroValor debitado do saldo. A taxa de saque da conta é descontada desse valor.
pix_key_typetextocpf, cnpj, email, phone ou random
pix_keytextoChave PIX de destino.
curl -X POST "https://api.uranopag.app/v1/withdrawals" \
  -H "Authorization: Bearer up_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d6f0b4a-1c55-4c3a-9e21-2f1c9a7e5b8d" \
  -d '{ "amount_cents": 50000, "pix_key_type": "email", "pix_key": "financeiro@empresa.com" }'
{
  "id": "9c1e2a4b-...",
  "object": "withdrawal",
  "amount_cents": 50000,
  "fee_cents": 200,
  "net_cents": 49800,
  "status": "pending",
  "pix_key": "financeiro@empresa.com",
  "pix_key_type": "email",
  "end_to_end_id": null,
  "failure_reason": null,
  "created_at": "2026-10-18T03:40:00.000Z",
  "processed_at": null
}
GET /v1/withdrawals/{id}
GET /v1/withdrawals?page=1&limit=20
statusSignificado
pendingSolicitado, aguardando processamento.
processingEnviado à instituição, aguardando liquidação.
completedPIX liquidado no destino.
rejectedRecusado. O valor volta ao saldo (uma única vez).
failedNão liquidado (ex.: chave inválida). O valor volta ao saldo (uma única vez).

Regras: o saldo é debitado no pedido; existe um intervalo mínimo entre pedidos e um limite diário por conta. Pedidos repetidos com a mesma Idempotency-Key nunca geram saque duplicado.

Webhooks

Cadastre a URL (HTTPS pública) em Desenvolvedores → API e Webhooks → Webhooks e escolha os eventos. Ao criar, o painel mostra o segredo de assinatura (whsec_...) uma única vez.

EventoQuando
payment.paidCobrança paga e confirmada.
payment.holdValor retido para análise.
payment.releasedRetenção encerrada; valor liberado.
payment.medContestação (MED) aberta: status in_review.
payment.med_defendedContestação defendida: venda segue paga.
payment.refundedValor devolvido ao pagador.
withdrawal.completedSaque liquidado.
withdrawal.rejectedSaque recusado ou falho; valor devolvido ao saldo.

Cada entrega é um POST JSON com estes cabeçalhos:

CabeçalhoConteúdo
X-UranoPag-Event-IdIdentificador estável do evento (igual em reenvios). Use para não processar duas vezes.
X-UranoPag-TimestampUnix timestamp (segundos) da assinatura.
X-UranoPag-Signaturesha256= + HMAC-SHA256 hex de timestamp + "." + corpo bruto, com o seu segredo.
X-UranoPag-Resendtrue quando é um reenvio.
{
  "id": "payment.paid:3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70",
  "event": "payment.paid",
  "created_at": "2026-10-18T03:31:02.000Z",
  "data": {
    "object": "charge",
    "id": "3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70",
    "external_id": "pedido-8842",
    "amount_cents": 15000,
    "fee_cents": 449,
    "net_cents": 14551,
    "status": "paid",
    "end_to_end_id": "E18236120202610180331s0123456789",
    "paid_at": "2026-10-18T03:31:00.000Z",
    "created_at": "2026-10-18T03:30:00.000Z"
  }
}

Validação da assinatura (Node.js):

import crypto from "node:crypto";

app.post("/webhooks/uranopag", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.get("X-UranoPag-Timestamp") ?? "";
  const got = req.get("X-UranoPag-Signature") ?? "";
  const raw = req.body.toString("utf8");
  const expected = "sha256=" + crypto.createHmac("sha256", process.env.URANOPAG_WEBHOOK_SECRET).update(`${ts}.${raw}`).digest("hex");
  const ok = got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
  if (!ok || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
  const event = JSON.parse(raw);
  // processe de forma idempotente usando event.id
  res.sendStatus(200);
});

PHP:

<?php
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_URANOPAG_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_URANOPAG_SIGNATURE'] ?? '';
$exp = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, getenv('URANOPAG_WEBHOOK_SECRET'));
if (!hash_equals($exp, $got) || abs(time() - (int)$ts) > 300) { http_response_code(401); exit; }
$event = json_decode($raw, true);
http_response_code(200);

Python:

import hmac, hashlib, os, time
raw = request.get_data()
ts = request.headers.get("X-UranoPag-Timestamp", "")
exp = "sha256=" + hmac.new(os.environ["URANOPAG_WEBHOOK_SECRET"].encode(), f"{ts}.".encode() + raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(exp, request.headers.get("X-UranoPag-Signature", "")) or abs(time.time() - int(ts or 0)) > 300:
    abort(401)
  • Responda 2xx em até 10 segundos. Outros códigos, timeout ou erro de rede contam como falha.
  • Entregas com falha podem ser reenviadas pelo painel (uma a uma ou "Reenviar falhados"). Um reenvio só repete o estado atual: nunca altera saldo, venda ou saque.
  • O mesmo evento pode chegar mais de uma vez. Guarde o id e ignore repetições.
  • Para liberar pedidos de valor alto, confirme também com GET /v1/charges/{id}.

Erros

Erros sempre têm o formato abaixo. Mensagens são genéricas por segurança; use o code no seu tratamento.

{ "error": { "code": "validation_error", "message": "Dados inválidos.", "fields": ["payer.document"] } }
HTTPcodeQuando
400invalid_json / idempotency_requiredCorpo inválido ou Idempotency-Key ausente.
401unauthorizedChave ausente, inválida ou revogada.
403ip_not_allowed / account_restricted / onboarding_pendingIP fora da lista, conta com restrição ou cadastro não aprovado.
404not_foundRecurso inexistente ou de outra conta.
409idempotency_mismatch / external_id_takenChave de idempotência ou external_id já usados com outros dados.
422validation_error / above_max_ticket / amount_below_fee / daily_limit / insufficient_fundsRegra de valor ou de saldo.
429rate_limited / too_frequentMuitas requisições (aguarde o Retry-After) ou saques próximos demais.
503provider_unavailableInstituição indisponível no momento. Tente de novo com a mesma Idempotency-Key.
500internal_errorErro interno. Tente novamente.

Limites de uso

O limite é por chave de API, por segundo, e é definido para a sua conta (padrão: 50 requisições por segundo). Ao exceder, a API responde 429 com Retry-After: 1. Pagamentos recebidos e webhooks nunca são afetados por esse limite.

Exemplos completos

Node.js (fetch):

const res = await fetch("https://api.uranopag.app/v1/charges", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.URANOPAG_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({ amount_cents: 15000, external_id: "pedido-8842", payer: { name: "Maria da Silva", document: "52998224725" } }),
});
const charge = await res.json();
if (!res.ok) throw new Error(charge.error.code);
console.log(charge.pix_copy_paste);

PHP (cURL):

<?php
$ch = curl_init('https://api.uranopag.app/v1/charges');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . getenv('URANOPAG_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => json_encode(['amount_cents' => 15000, 'payer' => ['name' => 'Maria da Silva', 'document' => '52998224725']]),
]);
$charge = json_decode(curl_exec($ch), true);

Python (requests):

import os, uuid, requests
r = requests.post("https://api.uranopag.app/v1/charges",
    headers={"Authorization": f"Bearer {os.environ['URANOPAG_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={"amount_cents": 15000, "payer": {"name": "Maria da Silva", "document": "52998224725"}}, timeout=20)
r.raise_for_status()
print(r.json()["pix_copy_paste"])