Primeiro depósito e primeiro saque no sandbox da Nero

Para: Dev — Conferido em 2026-10-18.

Roteiro para a Nero ver o ciclo inteiro funcionando na operação nero-br-hml (operação Teste) antes de escrever o conector. Nenhum dinheiro real se move. Os comandos genéricos estão em Primeiro depósito e primeiro saque; esta página traz as constantes e os resultados esperados da Nero.

O que é

Os dez passos usam o cliente de shell de Exemplos executáveis (bash, openssl, curl) e python3 ou jq só para ler JSON. O shell serve para depurar: em produção use Go ou Node, com comparação em tempo constante e segredo fora da linha de comando.

Quando usar

No primeiro dia de integração, antes de abrir qualquer rota do portal. Cada passo mostra o que o conector da Nero deve fazer com o resultado.

Como funciona

Passo 0: pré-requisitos

  • Credenciais da operação Teste: chave key_test_ com os escopos deposits:write, withdrawals:write e read, o segredo num arquivo 0400 e o segredo de webhook.
  • URL de webhook cadastrada na operação. Fora de produção, o Dracma só entrega a domínios reservados ou liberados no onboarding: peça a liberação do domínio de hml da Nero. Sem a URL, os passos de webhook mostram só o log de entregas.
  • Relógio sincronizado (NTP): a janela é de ±300 segundos.
  • As variáveis do cliente (DRACMA_BASE_URL, DRACMA_KEY_ID, DRACMA_KEY_SECRET_FILE) e as funções dr, js e novo_id do roteiro genérico.

Passo 1: ping

dr GET /ping

Esperado: 200 com operation_id, environment: "sandbox" e server_time. Compare server_time com date -u: desvio acima de 5 segundos pede NTP. 401 unknown_key é chave de outro tipo ou revogada; 401 invalid_signature é segredo ou path errado; 401 timestamp_out_of_window é relógio.

Passo 2: moedas, métodos e exigências

dr GET /currencies

Esperado: BRL com methods: ["pix"]. Se deposit_requirements for ["player.name", "player.email"], o PSP de depósito da operação é o Mercado Pago: os dois campos são obrigatórios e o e-mail tem de ser .test, .invalid ou .example. Com ["player.name"], é a MaximaxPay; com [], o simulador.

Passo 3: primeiro depósito

DEP_EXT="dep_$(novo_id)"
cat > nero-dep.json <<EOF
{"external_id":"$DEP_EXT","player":{"external_ref":"nero-hml-jogador-0001","document":{"type":"CPF","number":"52998224725","country":"BR"},"name":"Jogador Teste","email":"jogador1@nero.test","ip":"203.0.113.10"},"amount":{"amount":"5000","currency":"BRL"},"method":"pix"}
EOF
dr POST /deposits nero-dep.json "$DEP_EXT" | tee nero-dep-resp.json
DEP_ID=$(js '["id"]' < nero-dep-resp.json)

52998224725 é um CPF de teste (Dados de teste). Esperado: 201 com pending e instructions.pix.copy_paste contendo SANDBOX, ou 202 com created (espere uns segundos e faça o passo 5). Repetindo exatamente o mesmo comando (mesmo arquivo, mesma chave) a resposta é a mesma, com Idempotent-Replayed: true.

Passo 4: pagar no sandbox

dr POST "/sandbox/deposits/$DEP_ID/pay" "" "sbx_$(novo_id)"

Corpo vazio é "pago pelo próprio jogador, valor exato". Para os caminhos de revisão, mande outro payer_document e veja under_review com PAYER_MISMATCH (Receitas por caminho).

Passo 5: fetch-back

dr GET "/deposits/$DEP_ID" | js '["status"], v["version"], v["amount"]'

Esperado: paid, versão 3 e 5000 BRL. É aqui, e só aqui, que o conector credita, depois de conferir external_id, amount, currency e player_ref com o que gravou.

Passo 6: eventos e webhook

dr GET "/events?object_id=$DEP_ID"        # deposit.pending e deposit.paid, na ordem
dr GET /sandbox/webhooks                   # log de entregas: evento, tentativa e o status HTTP da Nero
EVT=$(dr GET "/events?object_id=$DEP_ID" | js '["data"][-1]["id"]')
echo "{\"event_id\":\"$EVT\"}" > redeliver.json
dr POST /sandbox/webhooks/redeliver redeliver.json "sbx_$(novo_id)"

Esperado no receptor da Nero: o mesmo Dracma-Event-Id, resposta 200 e nenhum crédito novo.

Passo 7: primeiro saque

A reserva no nero_ledger acontece antes deste POST. O primeiro saque de um jogador costuma ir para revisão.

WDR_EXT="wdr_$(novo_id)"
cat > nero-wdr.json <<EOF
{"external_id":"$WDR_EXT","player":{"external_ref":"nero-hml-jogador-0001","document":{"type":"CPF","number":"52998224725","country":"BR"},"name":"Jogador Teste","ip":"203.0.113.10"},"amount":{"amount":"2000","currency":"BRL"},"method":"pix","destination":{"pix":{"key_type":"cpf","key":"52998224725"}},"review_requested":false}
EOF
dr POST /withdrawals nero-wdr.json "$WDR_EXT" | tee nero-wdr-resp.json
WDR_ID=$(js '["id"]' < nero-wdr-resp.json)
dr POST "/sandbox/withdrawals/$WDR_ID/approve" "" "sbx_$(novo_id)"    # simula o revisor no painel

Esperado: 201 com under_review, decision.mode: manual e FIRST_WITHDRAWAL em decision.reasons; depois do approve, approved. O simulador paga sozinho em seguida; confira com dr GET "/withdrawals/$WDR_ID" até paid e psp_reference. No conector: finalizar a reserva só ao ler paid no fetch-back.

Passo 8: segundo saque, o caminho automático

Com o primeiro saque pago e X = 1 (D2), um saque que caiba em "depositado menos sacado" (aqui, até R$ 30,00) tende a sair no automático:

WDR2_EXT="wdr_$(novo_id)"
sed "s/$WDR_EXT/$WDR2_EXT/" nero-wdr.json > nero-wdr2.json
dr POST /withdrawals nero-wdr2.json "$WDR2_EXT" | js '["status"], v["decision"]["mode"], v["decision"]["reasons"]'

Esperado: approved, automatic e lista vazia. Se vier under_review, leia decision.reasons: as regras da operação (GET /v1/rules) podem pedir revisão por outros motivos. Sem player.ip o resultado é sempre under_review com OWNERSHIP_UNVERIFIED. Para ver a devolução da reserva, crie outro saque de valor terminado em 77 e chame .../fail ou .../reject.

Passo 9: queda simulada e retomada sem duplicar

echo '{"down":true}' > down.json; echo '{"down":false}' > up.json
dr POST /sandbox/outage down.json "sbx_$(novo_id)"
DEP2_EXT="dep_$(novo_id)"; sed "s/$DEP_EXT/$DEP2_EXT/" nero-dep.json > nero-dep2.json
dr POST /deposits nero-dep2.json "$DEP2_EXT"          # 503 service_unavailable, Retry-After 5
dr POST /sandbox/outage up.json "sbx_$(novo_id)"
dr POST /deposits nero-dep2.json "$DEP2_EXT"          # MESMA chave e corpo: 201 ou 202
dr POST /deposits nero-dep2.json "$DEP2_EXT"          # mesma resposta, Idempotent-Replayed: true
dr GET "/deposits?external_id=$DEP2_EXT" | js '["data"].__len__()'   # 1

É exatamente o comportamento que o outbox do nero_payments precisa ter em produção.

Passo 10: no dia seguinte, a reconciliação

ONTEM=$(date -u -d yesterday +%F)
dr GET "/reconciliation?date=$ONTEM&currency=BRL&limit=200"

Esperado depois das 02:00 UTC: itens deposit e withdrawal com status: paid, totals, digest e o header Dracma-Signature na última página. Antes disso: 409 day_open com Retry-After. Confira o digest com Digest e VerifyDigest dos exemplos.

Exemplos

Todo o roteiro é o exemplo. Os resultados esperados de cada passo estão logo abaixo do comando.

Erros e casos de borda

Se vier Causa provável
400 com required_by_operation ou reserved_domain_required no depósito Falta player.name ou player.email, ou o e-mail não é de domínio reservado (passo 2)
422 insufficient_operation_balance no saque A operação ainda não tem caixa livre: pague o depósito do passo 4 antes
409 invalid_state_transition no pay do saque O simulador já pagou sozinho; use valor terminado em 77 para controlar
429 rate_limited Limite de depósitos ou saques por jogador por hora; use outro external_ref

Veja também