Base URL: https://offzinnn-api.onrender.com · Content-Type: application/json
| Conceito | Papel |
|---|---|
OFF Card OFFC-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX | Credencial secreta. Faz login e paga. Armazenado apenas como hash. |
ID B0000000000000000001 | Chave pública. Só recebe. Não permite login. |
| sessionId | Token Bearer (validade de 30 dias), obtido em login/registro. |
| txId | Código do comprovante (e chave de idempotência opcional enviada por você). |
Valores são inteiros em OFF. Respostas de sucesso trazem "success": true; erros trazem "success": false e "error" com um código estável.
Authorization: Bearer <sessionId> Content-Type: application/json
Sessão inválida ou expirada: 403 INVALID_SESSION. As sessões são as mesmas do OFF Bank, então uma conta antiga também pode usar a API.
/api/v1/card/registerPúblicoCria uma conta e já emite o OFF Card e uma sessão. O cardCode só aparece nesta resposta.
{
"username": "alice",
"password": "uma-senha-forte"
}Resposta{
"success": true,
"userId": "B0000000000000000001",
"username": "alice",
"cardCode": "OFFC-7K2MX-…",
"sessionId": "<token>"
}Erros: 400 INVALID_USERNAME / INVALID_PASSWORD · 409 USERNAME_TAKEN.
/api/v1/card/loginPúblicoCria uma sessão Bearer. Use ou o card ou usuário + senha. O ID público nunca faz login.
Corpo{ "cardCode": "OFFC-7K2MX-…" }
// ou
{ "username": "alice", "password": "…" }Resposta{
"success": true,
"userId": "B0000000000000000001",
"username": "alice",
"sessionId": "<token>"
}Credenciais inválidas: 401 INVALID_CREDENTIALS. Após 10 falhas em 15 min o IP é bloqueado (429 IP_BLOCKED).
/api/v1/card/logoutBearerRevoga a sessão informada no cabeçalho.
Resposta{ "success": true }/api/v1/card/meBearerDados da conta autenticada e saldo.
Resposta{
"success": true,
"userId": "B0000000000000000001",
"username": "alice",
"offKey": "OFF-…",
"balance": "1250",
"card": { "exists": true, "last4": "7K2M" }
}/api/v1/card/generateBearerGera um OFF Card (ou rotaciona, invalidando o anterior). Útil se o card vazou ou se a conta é antiga e ainda não tem card.
Resposta{
"success": true,
"rotated": true,
"cardCode": "OFFC-NEW…",
"warning": "…"
}/api/v1/card/transferBearerTransfere OFF da conta autenticada para um ID público.
Corpo{
"toId": "B0000000000000000002",
"amount": 50,
"message": "pedido #1042",
"txId": "pedido-1042"
}Resposta{
"success": true,
"txId": "OFFPAY-9F3A…",
"clientTxId": "pedido-1042",
"from": "B…01", "to": "B…02",
"amount": "50",
"balance": "1200",
"receiptUrl": "/tx/OFFPAY-9F3A…"
}toId aceita o ID público B… ou a Chave OFF OFF-…. amount é inteiro (sem casas decimais) e respeita os limites do OFF Pay (diário e noturno). txId é opcional e idempotente: repetir a mesma chamada devolve o resultado original com "duplicate": true, sem transferir de novo. Erros: 402 INSUFFICIENT_FUNDS · 404 RECIPIENT_NOT_FOUND · 409 DAILY_LIMIT (traz available) / NIGHT_LIMIT / RECIPIENT_BLOCKED · 403 ACCOUNT_BLOCKED · 400 INVALID_TO_ID / INVALID_AMOUNT / AMOUNT_TOO_SMALL / SAME_ACCOUNT.
/api/v1/card/transfer/cardPúblico (card)Transfere usando o card do pagador, sem sessão. Pensado para servidores e lojas parceiras: o jogador informa o card e a loja cobra.
Corpo{
"cardCode": "OFFC-7K2MX-…",
"toId": "B0000000000000000002",
"amount": 50,
"txId": "pedido-1042"
}RespostaMesma resposta de /transfer.Card inexistente: 404 CARD_NOT_FOUND. Nunca exponha o card do jogador no front-end público de terceiros; chame esta rota do seu servidor.
/api/v1/card/infoPúblico (card)Saldo e dados da conta a partir do card, sem sessão.
Corpo{ "cardCode": "OFFC-7K2MX-…" }Resposta{
"success": true,
"userId": "B…01",
"username": "alice",
"balance": "1250",
"totalTransactions": 14
}/api/v1/card/transactions?page=1&limit=20BearerExtrato paginado (limit máx. 50).
Resposta{
"success": true,
"page": 1,
"transactions": [
{ "id": "OFF…", "type": "transfer", "from": "B…01", "to": "B…02",
"amount": "50", "message": null, "date": "…", "direction": "out" }
]
}/api/v1/card/lookup/:idPúblicoConfirma que um ID público existe e devolve o nome (para mostrar "Pagar para alice?" antes de enviar).
Resposta{ "success": true, "id": "B…02", "username": "bob" }Inexistente: 404 ID_NOT_FOUND.
/api/v1/card/tx/:txidPúblicoDados públicos de um comprovante. A página /tx/<txid> usa este endpoint.
{
"success": true,
"tx": { "id": "OFF…", "type": "transfer", "date": "…",
"from": "B…01", "to": "B…02", "amount": "50", "message": null }
}/api/v1/card/statsPúblicoRetrato público do sistema (cache de 30 s).
Resposta{
"success": true,
"totalAccounts": 120,
"totalCards": 98,
"totalOff": "5230100",
"totalTransactions": 4821,
"transactions24h": 312,
"volume24h": "154320"
}/api/v1/card/healthPúblicoVerificação de saúde.
Resposta{ "ok": true, "uptime": 12345.67 }| Status | Código |
|---|---|
| 400 | INVALID_TO_ID · INVALID_AMOUNT · AMOUNT_TOO_SMALL · INVALID_TX_ID · SAME_ACCOUNT · INVALID_USERNAME · INVALID_PASSWORD · MISSING_CREDENTIALS |
| 401 | INVALID_CREDENTIALS |
| 402 | INSUFFICIENT_FUNDS (traz o balance) |
| 403 | INVALID_SESSION · ACCOUNT_BLOCKED · ORIGIN_NOT_ALLOWED |
| 404 | CARD_NOT_FOUND · RECIPIENT_NOT_FOUND · ID_NOT_FOUND · INVALID_TRANSACTION |
| 409 | USERNAME_TAKEN · DAILY_LIMIT · NIGHT_LIMIT · RECIPIENT_BLOCKED |
| 429 | RATE_LIMIT_EXCEEDED (média de 5 req/s por IP, rajada de 20) · IP_BLOCKED (10 falhas de login/card em 15 min) |
| 500 | INTERNAL_ERROR |
As transferências usam o mesmo motor do OFF Pay (site e bot): limite diário em janela móvel de 24 h e limite noturno por envio (horário de Fortaleza). Os valores são configurados em off_pay_settings; o padrão é 2.000 OFF por dia e 500 OFF por envio à noite.
const API = "https://offzinnn-api.onrender.com/api/v1/card";
// cobrança na sua loja: chame do SERVIDOR, nunca do navegador
const r = await fetch(`${API}/transfer/card`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
cardCode: jogador.card, // informado pelo jogador
toId: "B0000000000000000002", // ID público da sua loja
amount: 50,
txId: `pedido-${pedido.id}` // idempotente
})
});
const data = await r.json();
if (data.success) console.log("Pago! Comprovante:", data.txId);