# Autenticação (https://docs.weetpay.com/docs/authentication)
Client ID e client secret nos cabeçalhos de cada requisição.
Toda chamada à API leva as credenciais geradas em **Desenvolvedores** no painel. Não existe token de sessão nem etapa de login: cada requisição é autenticada sozinha, sempre por HTTPS.
## Formatos aceitos [#formatos-aceitos]
Use **um** dos três formatos (são equivalentes):
```http
vspi: weetpay-100123
vsps: 3f1c9a52-7b0e-4d61-9a8c-2e5f4b7d1a90
```
Mesmos nomes da API de gateway compatível: quem migra não precisa mudar código, só a URL e as chaves.
```http
x-client-id: weetpay-100123
x-client-secret: 3f1c9a52-7b0e-4d61-9a8c-2e5f4b7d1a90
```
```http
Authorization: Basic d2VldHBheS0xMDAxMjM6M2YxYzlhNTItN2IwZS00ZDYxLTlhOGMtMmU1ZjRiN2QxYTkw
```
O valor é `base64(":")`.
## Exemplos [#exemplos]
```bash
curl https://api.weetpay.com/api/v1/gateway/walletBalance \
-H "vspi: $WEETPAY_CLIENT_ID" \
-H "vsps: $WEETPAY_CLIENT_SECRET"
```
```js
const res = await fetch('https://api.weetpay.com/api/v1/gateway/walletBalance', {
headers: { vspi: process.env.WEETPAY_CLIENT_ID, vsps: process.env.WEETPAY_CLIENT_SECRET },
});
console.log(await res.json()); // { currentBalance: "397.70" }
```
```php
true,
CURLOPT_HTTPHEADER => ['vspi: ' . getenv('WEETPAY_CLIENT_ID'), 'vsps: ' . getenv('WEETPAY_CLIENT_SECRET')],
]);
var_dump(json_decode(curl_exec($ch), true));
```
```python
import os, requests
r = requests.get(
"https://api.weetpay.com/api/v1/gateway/walletBalance",
headers={"vspi": os.environ["WEETPAY_CLIENT_ID"], "vsps": os.environ["WEETPAY_CLIENT_SECRET"]},
timeout=15,
)
print(r.json())
```
## Respostas de autenticação [#respostas-de-autenticação]
| HTTP | `message` | `code` | O que fazer |
| ----- | -------------------------------------------------------------- | ----------------- | --------------------------------------------------------------- |
| `401` | Credenciais ausentes | — | envie o client ID e o client secret |
| `401` | Credenciais inválidas | — | confira as chaves; o secret pode ter sido atualizado no painel |
| `403` | Verificação de identidade pendente | `KYC_REQUIRED` | conclua a verificação no painel |
| `403` | Conta bloqueada. Fale com o suporte. | `ACCOUNT_BLOCKED` | fale com o suporte |
| `429` | Limite de requisições por minuto atingido | `RATE_LIMITED` | espere e tente de novo ([limites](/docs/limits-and-rules)) |
| `429` | Muitas tentativas com credenciais inválidas. Tente mais tarde. | `RATE_LIMITED` | o IP errou credenciais demais; corrija e aguarde alguns minutos |
## Boas práticas [#boas-práticas]
O client secret dá acesso ao seu saldo. **Nunca** o coloque em aplicativo, site (JavaScript no navegador) ou repositório. Chame a API só do seu servidor.
* Guarde as chaves em variáveis de ambiente ou num cofre de segredos.
* Se suspeitar de vazamento, use **Atualizar credenciais** em Desenvolvedores: o secret antigo para de funcionar na hora (o client ID continua o mesmo).
* Deixe o **saque pela API** desligado se você só recebe pagamentos — assim, mesmo com as chaves vazadas, ninguém tira dinheiro da conta.
* O client secret também assina os callbacks das suas transações: atualizá-lo muda a chave de validação ([Callbacks](/docs/webhooks)).
# Erros (https://docs.weetpay.com/docs/errors)
Formato único de erro, códigos HTTP e mensagens mais comuns.
Todo erro da API tem o mesmo formato:
```json
{ "response": "ERROR", "message": "amount inválido (em reais, ex.: 10.50)" }
```
Alguns erros de autenticação trazem também `code` (`RATE_LIMITED`, `ACCOUNT_BLOCKED`, `KYC_REQUIRED`). `message` é em português e pronta para log; para decidir o que fazer no código, use o **status HTTP** (e o `code`, quando houver).
## Status HTTP [#status-http]
| HTTP | Significado | Repetir? |
| --------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `200` | sucesso | — |
| `400` | dados inválidos ou regra de negócio (saldo, limite, `requestNumber` repetido) | não, corrija o pedido |
| `401` | credenciais ausentes ou inválidas | não |
| `403` | conta bloqueada, KYC pendente ou operação não liberada | não |
| `404` | transação ou endpoint não encontrado | não |
| `409` | chave de idempotência reutilizada com outro pedido | não |
| `429` | limite de requisições | sim, com espera |
| `500` / `502` / `503` | falha temporária | sim, com espera crescente — em saques, **sempre com a mesma `Idempotency-Key`** |
## Mensagens comuns [#mensagens-comuns]
### Cobranças [#cobranças]
| `message` | Causa |
| ---------------------------------------------------------------- | -------------------------------------------------------- |
| `requestNumber é obrigatório` | campo ausente ou vazio |
| `requestNumber já utilizado` | já existe uma cobrança com esse `requestNumber` na conta |
| `amount inválido (em reais, ex.: 10.50)` | zero, negativo, não numérico ou com mais de 2 casas |
| `client.name é obrigatório` | nome com menos de 3 caracteres |
| `client.document inválido (CPF ou CNPJ)` | dígitos verificadores não conferem |
| `client.email inválido` | e-mail mal formatado |
| `callbackUrl inválida` | não é http(s) ou passa de 500 caracteres |
| `Split não está liberado nesta conta. Fale com o suporte.` (403) | `split` sem liberação do Compliance |
| `split.account inválida para split` | conta de destino inexistente, inativa ou a própria conta |
| `split.percentageSplit deve ser um inteiro entre 1 e 100` | percentual fora da faixa |
Valores abaixo do mínimo ou acima do máximo de cobrança da plataforma também voltam como `400`, com o limite na mensagem.
### Saques [#saques]
| `message` | Causa |
| ------------------------------------------------------------------------------- | ----------------------------------------------- |
| `Saque pela API não está liberado nesta conta. Ative em Desenvolvedores.` (403) | ligue o saque pela API no painel |
| `typeKey inválido (use CPF, CNPJ, EMAIL, PHONE ou EVP)` | tipo de chave desconhecido |
| `key inválida para o typeKey informado` | chave não corresponde ao tipo |
| `document deve ser o mesmo CPF/CNPJ da chave` | chave CPF/CNPJ com `document` diferente |
| `Saque só para a chave CPF/CNPJ do titular da conta…` (403) | conta sem liberação para pagar terceiros |
| `insufficient balance` | saldo menor que valor + tarifa de saque |
| `O valor mínimo para saque é …` / `O valor máximo por saque é …` | fora dos limites por saque |
| `Limite diário de saque atingido…` | soma das últimas 24 h passaria do limite diário |
| `Esta chave de idempotência já foi usada em outro saque` (409) | mesma `Idempotency-Key` com outro valor/chave |
| `Pagamento de PIX copia e cola (QRCODE) ainda não está disponível` | `typeKey: "QRCODE"` não é suportado |
### Consulta [#consulta]
| `message` | Causa |
| -------------------------------------------------- | -------------------------------------------- |
| `Informe requestNumber, idTransaction ou endToEnd` | corpo sem nenhum identificador |
| `Transação não encontrada` (404) | não existe na sua conta |
| `Endpoint não encontrado` (404) | caminho errado (confira `/api/v1/gateway/…`) |
# Início rápido (https://docs.weetpay.com/docs/getting-started)
Da criação da conta à primeira cobrança paga, em cinco passos.
### Crie e verifique sua conta [#crie-e-verifique-sua-conta]
Cadastre-se em [app.weetpay.com](https://app.weetpay.com) e envie os documentos da verificação de identidade. **A API só funciona depois da aprovação** — antes disso toda chamada responde `403` com `code: "KYC_REQUIRED"`.
### Gere as credenciais [#gere-as-credenciais]
No painel, abra **Desenvolvedores** e revele as chaves com o seu PIN de transação. Você recebe:
* **Client ID** — `weetpay-`, fixo para sempre;
* **Client secret** — um UUID. Guarde num cofre de segredos; **Atualizar credenciais** gera um novo e invalida o anterior na hora.
### Gere uma cobrança [#gere-uma-cobrança]
```bash
curl -X POST https://api.weetpay.com/api/v1/gateway/request-qrcode \
-H "Content-Type: application/json" \
-H "vspi: $WEETPAY_CLIENT_ID" \
-H "vsps: $WEETPAY_CLIENT_SECRET" \
-d '{
"requestNumber": "pedido-12345",
"amount": 49.90,
"callbackUrl": "https://sualoja.com.br/webhooks/weetpay",
"client": { "name": "Maria da Silva", "document": "12345678909", "email": "maria@email.com" }
}'
```
A resposta traz o PIX copia e cola (`paymentCode`) e a imagem do QR Code (`paymentCodeBase64`):
```json
{
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"paymentCode": "00020101021226850014br.gov.bcb.pix…",
"paymentCodeBase64": "iVBORw0KGgoAAAANSUhEUgAA…",
"response": "OK",
"status": "WAITING_FOR_APPROVAL"
}
```
Mostre ao cliente a imagem `data:image/png;base64,{paymentCodeBase64}` e um botão "copiar" com o `paymentCode`.
### Receba o aviso de pagamento [#receba-o-aviso-de-pagamento]
Quando o PIX for pago, a WeetPay faz um `POST` no `callbackUrl`:
```json
{
"requestNumber": "pedido-12345",
"statusTransaction": "PAID_OUT",
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"typeTransaction": "PIX",
"value": 49.9,
"debtorName": "MARIA DA SILVA",
"debtorDocument": "12345678909",
"date": "2026-10-30T13:03:40.000Z",
"endToEnd": "E18236120202610301303s0123456789"
}
```
Confira a assinatura `X-WeetPay-Signature`, libere o pedido e responda `200`. Detalhes em [Callbacks e webhooks](/docs/webhooks).
### Confira quando quiser [#confira-quando-quiser]
Use [Consultar transação](/docs/transactions/get-transaction) para checar o status a qualquer momento (por exemplo, quando o cliente volta para a página do pedido) e [Consultar saldo](/docs/account/balance) para ver quanto está disponível.
## Clientes de referência [#clientes-de-referência]
Prontos para copiar, sem dependências:
* [weetpay-client.mjs](/examples/weetpay-client.mjs) — Node.js 18+ (`fetch` nativo), com validação de callback;
* [weetpay-client.php](/examples/weetpay-client.php) — PHP 7.4+ com cURL, com validação de callback.
```js
import { weetpay } from './weetpay-client.mjs';
const { status, data } = await weetpay('POST', '/api/v1/gateway/request-qrcode', {
requestNumber: 'pedido-12345',
amount: 49.9,
client: { name: 'Maria da Silva', document: '12345678909' },
});
```
# Idempotência (https://docs.weetpay.com/docs/idempotency)
Repita requisições com segurança — uma cobrança ou um saque nunca são criados duas vezes.
Falhas de rede acontecem: o pedido chega à WeetPay, mas a resposta se perde. A API foi feita para que você possa **repetir a mesma requisição sem medo**.
## Saques (`pix-payment`) [#saques-pix-payment]
Envie uma chave única por saque — de preferência o ID do pagamento no seu sistema:
```http
POST /api/v1/gateway/pix-payment
Idempotency-Key: saque-2026-10-30-0001
```
Também é aceito o cabeçalho `x-idempotency-key` ou, sem cabeçalho, o campo `requestNumber` do corpo.
| Situação | Resultado |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| primeira vez | saque criado, `200 { id, status: "processing" }` |
| mesma chave, mesmo valor e mesma chave PIX | **o mesmo saque** — `200` com o mesmo `id` e o cabeçalho `Idempotent-Replayed: true`. Nada é debitado de novo |
| mesma chave, outro valor ou outra chave PIX | `409` — "Esta chave de idempotência já foi usada em outro saque" |
| sem chave nenhuma | o mesmo valor para a mesma chave PIX em menos de **1 minuto** devolve o saque anterior |
A chave vale para sempre na sua conta (não expira) e tem no máximo 100 caracteres.
Gere a chave **uma vez** por pagamento e guarde-a antes de chamar a API. Gerar uma chave nova a cada tentativa anula a proteção.
## Cobranças (`request-qrcode`) [#cobranças-request-qrcode]
O `requestNumber` é único por conta: uma segunda cobrança com o mesmo `requestNumber` é recusada com `400 requestNumber já utilizado`. Se a resposta da criação se perder, recupere a cobrança com [Consultar transação](/docs/transactions/get-transaction) informando o `requestNumber` — o PIX copia e cola volta em `pixCopiaECola` enquanto ela estiver pendente.
## Callbacks [#callbacks]
Do lado do seu sistema, os avisos também podem chegar repetidos (reenvio após timeout, por exemplo). Processe cada `idTransaction` / `id` uma única vez. Veja [Callbacks e webhooks](/docs/webhooks#entrega-e-reenvio).
# Introdução (https://docs.weetpay.com/docs)
Bem-vindo à documentação da API WeetPay
Baixar OpenAPI (Postman/Insomnia)
Guia para IAs (llms.txt)
Início rápido
A **WeetPay** é um gateway de pagamentos PIX. Pela API sua aplicação gera cobranças com QR Code dinâmico, recebe o aviso de pagamento em tempo real, envia PIX para qualquer chave e consulta transações e saldo — com poucas chamadas HTTP e valores em reais.
## O que dá para fazer [#o-que-dá-para-fazer]
Recebimentos (**cash-in**) viram cobranças PIX copia e cola com QR Code pronto para exibir. Quando o cliente paga, o valor entra no seu saldo e a WeetPay avisa o seu sistema no `callbackUrl`. Pagamentos (**cash-out**) saem do seu saldo para uma chave PIX, com proteção contra envio duplicado e resultado também por callback.
## Por que integrar? [#por-que-integrar]
quatro endpoints, JSON puro e valores em reais (
`49.90`
)
mesmos caminhos, campos e respostas da API de gateway mais usada no mercado — migre trocando a URL e as chaves
aviso de pagamento e de saque com HMAC-SHA256 e reenvio automático
a Idempotency-Key garante que um PIX nunca saia duas vezes
PIX copia e cola e imagem PNG em base64 na mesma resposta
divida recebimentos com outra conta WeetPay (mediante liberação)
## Módulos [#módulos]
## URLs base [#urls-base]
| Ambiente | URL |
| -------------- | -------------------------------------------------------------- |
| API (produção) | `https://api.weetpay.com` |
| Painel | `https://app.weetpay.com` — credenciais em **Desenvolvedores** |
| Documentação | `https://docs.weetpay.com` |
Toda requisição leva o **client ID** e o **client secret** nos cabeçalhos `vspi` e `vsps`. Erros sempre vêm como `{ "response": "ERROR", "message": "…" }`. Veja [Autenticação](/docs/authentication) e [Erros](/docs/errors).
## Próximos passos [#próximos-passos]
Siga o [Início rápido](/docs/getting-started) para gerar sua primeira cobrança em minutos. Para assistentes e agentes de código, use o [llms.txt](/llms.txt) (índice) ou o [llms-full.txt](/llms-full.txt) (conteúdo completo).
# Limites e regras (https://docs.weetpay.com/docs/limits-and-rules)
Limites de requisição, de valores e as regras de segurança para saques e split.
## Requisições [#requisições]
| Limite | Valor | Resposta |
| ---------------------------- | ------------------------------------------------- | ----------------------------- |
| Por conta (client ID) | **600 requisições por minuto** | `429`, `code: "RATE_LIMITED"` |
| Credenciais inválidas por IP | 30 erros em 5 minutos bloqueiam o IP por um tempo | `429`, `code: "RATE_LIMITED"` |
Ao receber `429`, espere alguns segundos e repita com espera crescente. Para acompanhar pagamentos, use [callbacks](/docs/webhooks) em vez de consultas em loop.
## Valores [#valores]
* Valores sempre em **reais**, com até 2 casas decimais (`49.9`, `"49.90"` ou `"49,90"`).
* Cobranças e saques têm **mínimo e máximo por operação** definidos pela WeetPay; saques têm também **limite diário** (janela móvel de 24 h). Quando um limite é ultrapassado, a mensagem de erro informa o valor permitido.
* A **validade do QR Code** é definida pela plataforma (padrão: 1 hora). Depois disso a cobrança fica `EXPIRED`.
* Tarifas de recebimento e de saque são as do seu contrato e aparecem no painel. Recebimentos entram no saldo já líquidos; a tarifa de saque é debitada junto com o valor.
## Saques pela API [#saques-pela-api]
**Liberação pelo cliente.** O saque pela API vem **desligado**. Ligue em **Desenvolvedores** no painel (exige o PIN de transação). Desligado, `pix-payment` responde `403`.
**Destino.** Sem liberação da WeetPay, o saque só vai para a **chave CPF/CNPJ do titular da conta**. Para pagar terceiros (fornecedores, afiliados, clientes), peça a liberação ao suporte.
**Aprovação.** Saques acima do valor de aprovação automática da plataforma passam por análise da operação antes do envio. Enquanto isso o saque continua `PROCESSING` e o valor fica reservado.
**Resultado.** Pago ou não, o resultado chega no `callbackUrl`. Não pago (recusado na análise, chave inexistente, falha no banco) tem valor e tarifa **estornados** automaticamente.
## Split [#split]
O campo `split` de [Gerar cobrança PIX](/docs/cash-in/create-charge) divide o valor recebido com outra conta WeetPay. Ele move dinheiro entre contas, por isso só funciona em contas **liberadas pelo Compliance**. A conta de destino precisa estar ativa e com a verificação aprovada; o percentual é um inteiro de 1 a 100.
## Verificação de identidade [#verificação-de-identidade]
A API só responde para contas com a verificação de identidade (KYC) **aprovada** e não bloqueadas. Até lá, todas as chamadas retornam `403` com `code: "KYC_REQUIRED"`.
# Callbacks e webhooks (https://docs.weetpay.com/docs/webhooks)
Avisos assinados de pagamento e de saque — o jeito certo de conciliar sem consultar a API em loop.
A WeetPay avisa o seu sistema de duas formas, que podem ser usadas juntas:
| | **Callback da transação** | **Webhook do painel** |
| -------------- | ---------------------------------------------------- | ------------------------------------------------------------------- |
| Onde configura | campo `callbackUrl` de cada cobrança ou saque na API | **Webhooks** no painel, um endereço por evento |
| Formato | o payload da API compatível (valores em **reais**) | envelope `{ id, event, createdAt, data }` (valores em **centavos**) |
| Assinado com | o **client secret** da API | o segredo do webhook (`whsec_…`), visível no painel |
| Eventos | pagamento da cobrança; resultado do saque | cobrança criada, paga, devolvida; saque concluído, falhou |
Em ambos: `POST` com `Content-Type: application/json`, assinatura no cabeçalho `X-WeetPay-Signature` e reenvio automático em caso de falha.
## Callback de cobrança paga [#callback-de-cobrança-paga]
Enviado ao `callbackUrl` de [Gerar cobrança PIX](/docs/cash-in/create-charge) quando o PIX é pago:
```json
{
"requestNumber": "pedido-12345",
"statusTransaction": "PAID_OUT",
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"typeTransaction": "PIX",
"value": 49.9,
"debtorName": "MARIA DA SILVA",
"debtorDocument": "12345678909",
"date": "2026-10-30T13:03:40.000Z",
"endToEnd": "E18236120202610301303s0123456789"
}
```
| Campo | Descrição |
| ------------------------------ | ------------------------------------------------ |
| `requestNumber` | o identificador que você enviou na criação |
| `statusTransaction` | sempre `PAID_OUT` (só pagamentos geram callback) |
| `idTransaction` | ID da cobrança na WeetPay |
| `value` | valor pago, em reais (número) |
| `debtorName`, `debtorDocument` | quem pagou, como informado pelo banco |
| `date` | data/hora do pagamento (ISO 8601, UTC) |
| `endToEnd` | End-to-End ID do PIX |
## Callback de saque [#callback-de-saque]
Enviado ao `callbackUrl` de [Enviar PIX](/docs/cash-out/pix-payment) quando o saque termina:
```json
{
"id": "cmh1x9k2m0004ab12xy98zt76",
"status": "sucess",
"date": "2026-10-30T14:10:05.000Z",
"message": "",
"value": 150,
"endToEnd": "E00416968202610301410abcdEFGH1234"
}
```
```json
{
"id": "cmh1x9k2m0004ab12xy98zt76",
"status": "not paid",
"date": "2026-10-30T14:10:05.000Z",
"message": "Chave PIX não encontrada",
"value": 150,
"endToEnd": ""
}
```
`status` é `"sucess"` (com essa grafia, mantida por compatibilidade com a API de gateway de origem) ou `"not paid"`. Saque não pago tem o **valor e a tarifa estornados** automaticamente ao seu saldo; o motivo vem em `message`.
## Webhooks do painel [#webhooks-do-painel]
Cadastre em **Webhooks** no painel uma URL para cada evento que quiser receber. O corpo vem num envelope:
```json
{
"id": "cmh1xa0b10007ab12qq11ww22",
"event": "TRANSACTION_PAID",
"createdAt": "2026-10-30T13:03:41.000Z",
"data": {
"id": "cmh1x2y3z0001ab12cd34ef56",
"externalId": "pedido-12345",
"status": "PAID",
"amount": 4990,
"fee": 99,
"netAmount": 4891,
"description": "Camiseta básica",
"pixCopiaECola": "00020101021226850014br.gov.bcb.pix…",
"endToEndId": "E18236120202610301303s0123456789",
"payer": { "name": "MARIA DA SILVA", "document": "12345678909" },
"expiresAt": "2026-10-30T14:02:11.000Z",
"paidAt": "2026-10-30T13:03:40.000Z",
"createdAt": "2026-10-30T13:02:11.000Z"
}
}
```
| `event` | Quando | `data` |
| ---------------------- | ------------------------------------ | ------------------------------------------------ |
| `TRANSACTION_CREATED` | cobrança criada | cobrança |
| `TRANSACTION_PAID` | cobrança paga | cobrança |
| `TRANSACTION_REFUNDED` | cobrança devolvida / contestada | cobrança + `refund { id, kind, amount, reason }` |
| `WITHDRAWAL_COMPLETED` | saque pago | saque |
| `WITHDRAWAL_FAILED` | saque recusado ou falhou (estornado) | saque |
Na cobrança, `externalId` é o seu `requestNumber`; `amount`, `fee` e `netAmount` estão em **centavos**. O saque traz `id`, `transactionId`, `status` (`PAID`, `FAILED`, `REJECTED`), `amount`, `fee`, `pixKeyType`, `pixKey`, `endToEndId`, `reason`, `paidAt` e `createdAt`.
## Cabeçalhos [#cabeçalhos]
| Cabeçalho | Valor |
| --------------------- | ---------------------------------------------------------------------------- |
| `X-WeetPay-Signature` | `t=,v1=` — veja abaixo |
| `X-WeetPay-Event` | evento (`TRANSACTION_PAID`, `WITHDRAWAL_COMPLETED`…) |
| `X-WeetPay-Delivery` | ID da entrega — o mesmo em todas as tentativas; use para descartar repetidos |
| `User-Agent` | `WeetPay-Webhooks/1.0` |
## Validando a assinatura [#validando-a-assinatura]
`v1` é o HMAC-SHA256, em hexadecimal, de `"."`, usando como chave o **client secret** (callbacks da transação) ou o **segredo do webhook** (webhooks do painel). Calcule sobre o corpo **exatamente como chegou**, antes de qualquer parse, compare em tempo constante e recuse avisos com `t` muito antigo (ex.: mais de 5 minutos).
```js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function validWeetPaySignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries((header ?? '').split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
// Express: app.post('/webhooks/weetpay', express.raw({ type: 'application/json' }), (req, res) => {
// if (!validWeetPaySignature(req.body.toString('utf8'), req.get('X-WeetPay-Signature'), process.env.WEETPAY_CLIENT_SECRET)) return res.sendStatus(401);
// const event = JSON.parse(req.body); … ; res.sendStatus(200);
// });
```
```php
$tolerance) return false;
return hash_equals(hash_hmac('sha256', $t . '.' . $rawBody, $secret), $parts['v1']);
}
$raw = file_get_contents('php://input');
if (!weetpay_valid_signature($raw, $_SERVER['HTTP_X_WEETPAY_SIGNATURE'] ?? null, getenv('WEETPAY_CLIENT_SECRET'))) {
http_response_code(401); exit;
}
$event = json_decode($raw, true);
http_response_code(200);
```
```python
import hmac, hashlib, time
def valid_weetpay_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in (header or "").split(",") if "=" in p)
t = int(parts.get("t", 0))
if not t or "v1" not in parts or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
```
## Entrega e reenvio [#entrega-e-reenvio]
* Responda com qualquer **2xx em até 10 segundos**. Outro status, timeout ou erro de conexão conta como falha.
* Falhas são reenviadas após **1, 5, 15, 60, 180, 360, 720 e 1440 minutos** (até 9 tentativas, cerca de 2 dias). Depois disso a entrega fica como falha no histórico; você pode reenviar manualmente pelo painel.
* O mesmo aviso pode chegar mais de uma vez: trate pelo `idTransaction` / `id` (ou `X-WeetPay-Delivery`) e torne o processamento **idempotente**.
* A URL precisa ser **http(s) pública**. Redirecionamentos não são seguidos e endereços de rede interna (localhost, 10.x, 192.168.x…) são recusados.
* Responda rápido e processe depois (fila): o aviso não espera o seu sistema terminar o pedido.
Não confie só no corpo do aviso para liberar um pedido de valor alto: valide a assinatura ou confirme com [Consultar transação](/docs/transactions/get-transaction).
# Consultar saldo (https://docs.weetpay.com/docs/account/balance)
{/* */}
# Conta (https://docs.weetpay.com/docs/account)
Saldo disponível da carteira WeetPay.
`GET /api/v1/gateway/walletBalance` devolve o saldo disponível em reais, como string com duas casas: `{ "currentBalance": "397.70" }`.
* Cobranças pagas entram **líquidas** da tarifa de recebimento.
* Saques reservam o valor **e** a tarifa no momento do pedido; se o saque não for pago, os dois voltam ao saldo.
* Antes de um saque, confira se `currentBalance` cobre o valor mais a tarifa de saque — senão a API responde `insufficient balance`.
Extratos, relatórios e repasses ficam no [painel](https://app.weetpay.com).
# Gerar cobrança PIX (https://docs.weetpay.com/docs/cash-in/create-charge)
{/* */}
# Cash-in (https://docs.weetpay.com/docs/cash-in)
Receba pagamentos com PIX copia e cola e QR Code dinâmico.
`POST /api/v1/gateway/request-qrcode` gera uma cobrança PIX. A resposta traz o PIX copia e cola (`paymentCode`) e a imagem PNG do QR Code em base64 (`paymentCodeBase64`) para você mostrar ao cliente.
## Fluxo [#fluxo]
1. Seu servidor chama [Gerar cobrança PIX](/docs/cash-in/create-charge) com o `requestNumber` do pedido.
2. Você exibe o QR Code e o botão "copiar código" ao cliente.
3. O cliente paga no app do banco.
4. A WeetPay credita o valor (líquido da tarifa) no seu saldo e faz `POST` no `callbackUrl` com `statusTransaction: "PAID_OUT"`.
5. Seu sistema valida a assinatura, libera o pedido e responde `200`.
## Exibindo o QR Code [#exibindo-o-qr-code]
```html
```
## Status da cobrança [#status-da-cobrança]
| `processingStatus` | Significado |
| ---------------------- | ---------------------------------------- |
| `WAITING_FOR_APPROVAL` | aguardando pagamento |
| `PAID_OUT` | paga — valor creditado no saldo |
| `EXPIRED` | QR venceu sem pagamento (padrão: 1 hora) |
| `CANCELED` | cancelada pela operação |
| `REFUNDED` | devolvida / contestada (MED) |
## Dicas [#dicas]
* Use um `requestNumber` por tentativa de pagamento. Se o QR expirar, gere outra cobrança com um novo `requestNumber` (ex.: `pedido-12345-2`).
* Informe o CPF/CNPJ real do pagador em `client.document`: ele é validado e repassado ao banco.
* Envie `utm_source`, `utm_campaign`… na query string para guardar a origem da venda junto da cobrança.
# Cash-out (https://docs.weetpay.com/docs/cash-out)
Envie PIX do seu saldo para qualquer chave, com proteção contra envio duplicado.
`POST /api/v1/gateway/pix-payment` debita o valor e a tarifa do seu saldo e envia um PIX para a chave informada. A resposta é imediata (`status: "processing"`); o resultado chega no `callbackUrl`.
Antes do primeiro saque, ligue **Saque pela API** em **Desenvolvedores** no painel. Sem liberação da WeetPay, o destino precisa ser a chave CPF/CNPJ do próprio titular. Veja [Limites e regras](/docs/limits-and-rules#saques-pela-api).
## Tipos de chave [#tipos-de-chave]
| `typeKey` | `key` | `document` |
| --------- | -------------------------------------------------------- | ---------------------------- |
| `CPF` | 11 dígitos | o mesmo CPF da chave |
| `CNPJ` | 14 dígitos | o mesmo CNPJ da chave |
| `EMAIL` | e-mail | CPF/CNPJ do titular da chave |
| `PHONE` | celular com DDD (ex.: `11999998888` ou `+5511999998888`) | CPF/CNPJ do titular da chave |
| `EVP` | chave aleatória (UUID) | CPF/CNPJ do titular da chave |
## Fluxo [#fluxo]
1. Gere e guarde uma `Idempotency-Key` para o pagamento ([Idempotência](/docs/idempotency)).
2. Chame [Enviar PIX](/docs/cash-out/pix-payment). Em erro de rede ou `5xx`, repita **com a mesma chave**.
3. O saque fica `PROCESSING` (análise da operação, quando aplicável, e envio ao banco).
4. O `callbackUrl` recebe `status: "sucess"` com o `endToEnd`, ou `status: "not paid"` com o motivo em `message` — nesse caso valor e tarifa voltam ao saldo.
## Status do saque [#status-do-saque]
| `processingStatus` (consulta) | Callback `status` | Significado |
| ----------------------------- | ----------------- | ------------------------------ |
| `PROCESSING` | — | em análise ou enviado ao banco |
| `PAID_OUT` | `sucess` | pago |
| `NOT_PAID` | `not paid` | recusado ou falhou; estornado |
# Enviar PIX (saque) (https://docs.weetpay.com/docs/cash-out/pix-payment)
{/* */}
# Consultar transação (https://docs.weetpay.com/docs/transactions/get-transaction)
{/* */}
# Transações (https://docs.weetpay.com/docs/transactions)
Consulte cobranças e saques por qualquer identificador.
`POST /api/v1/gateway/walletTransaction` devolve uma cobrança (`transactionType: "PIX_IN"`) ou um saque (`"PIX_OUT"`) da sua conta, no mesmo formato.
## Identificadores [#identificadores]
| Campo | Encontra |
| --------------- | -------------------------------------------- |
| `requestNumber` | cobrança, pelo identificador que você enviou |
| `idTransaction` | cobrança ou saque, pelo ID da WeetPay |
| `endToEnd` | cobrança ou saque, pelo End-to-End ID do PIX |
## Status [#status]
| `processingStatus` | Tipo | Significado |
| ---------------------- | --------- | ----------------------------------------------- |
| `WAITING_FOR_APPROVAL` | `PIX_IN` | aguardando pagamento |
| `PAID_OUT` | ambos | pago |
| `EXPIRED` | `PIX_IN` | expirou sem pagamento |
| `CANCELED` | `PIX_IN` | cancelada |
| `REFUNDED` | `PIX_IN` | devolvida |
| `PROCESSING` | `PIX_OUT` | em análise ou em envio |
| `NOT_PAID` | `PIX_OUT` | não pago (motivo em `message`), valor estornado |
## Quando consultar [#quando-consultar]
* O cliente voltou à página do pedido e você ainda não recebeu o callback.
* Conciliação ou contingência (o seu servidor esteve fora do ar).
* Para recuperar o `pixCopiaECola` de uma cobrança pendente.
Cobranças pendentes são conferidas no banco a cada consulta (no máximo uma vez a cada 10 s). Não consulte em loop: use [callbacks](/docs/webhooks).