Receitas por caminho¶
Para: Dev — Conferido em 2026-10-18.
Doze receitas para provar, na operação Teste, cada caminho que o seu conector precisa tratar. Cada uma diz o que chamar, o que esperar e o que o seu livro deve fazer.
O que é¶
Uma receita é um roteiro curto: crie o objeto, dispare a ação de sandbox, espere o webhook, faça o fetch-back e confira o efeito no seu livro. Todas usam as rotas descritas em Sandbox.
Regras que valem para todas:
POSTsignifica requisição assinada comIdempotency-Key. Em ações de sandbox use uma chave nova a cada ação.- Entre um passo e o seguinte, espere o webhook ou leia o objeto (fetch-back). Nunca assuma o estado pelo que você acabou de chamar.
- Os corpos completos de criação estão em Depósitos e Saques.
- Cada jogador pode pedir poucos saques por hora, também no sandbox (Limites e quotas). Para rodar várias receitas seguidas, use um
external_refde jogador diferente em cada uma. - Para sacar, a operação precisa de caixa, e o caixa só cresce com depósitos pagos. Antes de qualquer receita de saque, pague um depósito (receita 1). Sem caixa livre, a criação do saque responde
422 insufficient_operation_balance.
Quando usar¶
Rode todas antes do go-live e depois de cada mudança no seu receptor ou no seu livro. Em cada uma, confira três coisas: 1 crédito por depósito pago, 0 crédito quando o webhook é forjado ou reentregue, e reserva devolvida em rejected, failed e cancelled.
Como funciona¶
Ação, evento e versão¶
Cada transição de estado grava um evento e sobe object_version em 1. Use esta tabela para saber o que o seu receptor vai ver.
| O que você faz | Estado resultante | Evento | object_version |
|---|---|---|---|
POST /v1/deposits |
pending (o objeto nasce created, que não gera evento) |
deposit.pending |
2 |
.../pay exato, pagador igual ao jogador |
paid |
deposit.paid |
3 |
.../pay com outro pagador ou outro valor |
under_review |
deposit.under_review |
3 |
.../expire |
expired |
deposit.expired |
3 |
POST /v1/deposits/{id}/cancel |
cancelled |
deposit.cancelled |
3 |
.../refund (de paid) |
refunded |
deposit.refunded |
4 |
.../chargeback (de paid) |
chargeback |
deposit.chargeback |
4 |
POST /v1/withdrawals automático |
approved |
withdrawal.approved |
1 |
POST /v1/withdrawals com revisão |
under_review |
withdrawal.under_review |
1 |
.../approve |
approved |
withdrawal.approved |
+1 |
.../reject |
rejected |
withdrawal.rejected |
+1 |
Simulador envia o PIX (.../pay ou automático) |
processing |
withdrawal.processing |
+1 |
| Simulador liquida | paid |
withdrawal.paid |
+1 |
.../fail |
processing, depois failed |
withdrawal.processing, withdrawal.failed |
+1, +1 |
.../return (de paid) |
returned |
withdrawal.returned |
+1 |
- No saque, a versão do primeiro evento é 1, exceto quando a chave PIX precisa de consulta ao DICT: o saque nasce
requested(estado que não aparece emGET /v1/eventsnem em webhook), e a primeira versão visível é 2. - Compare sempre a versão do evento com a que você já aplicou, não com um número fixo. Eventos podem chegar fora de ordem: aplique só a versão maior.
- O catálogo completo, com payloads, está em Catálogo de eventos.
Valor terminado em 77: saque que espera por você¶
Por padrão, o simulador paga o saque sozinho assim que ele é despachado: approved, processing e paid chegam em poucos segundos. Isso é útil para o caminho feliz, mas não deixa tempo para você agir.
Para controlar o desfecho, crie o saque com um valor cujos dois últimos dígitos na menor unidade são 77 (em BRL, centavos ,77; por exemplo "2077", R$ 20,77). O saque fica em processing e só muda quando você chamar pay, fail ou return. Sem esse gatilho, uma chamada a .../pay pode correr contra o pagamento automático e receber 409 invalid_state_transition.
| Quero testar | Crie o saque com | Depois chame |
|---|---|---|
| Pagamento (receita 5) | qualquer valor | nada; o simulador paga |
| Pagamento explícito | valor xx77 |
pay |
| Falha do PSP e devolução da reserva | valor xx77 |
fail |
| Devolução depois de pago | qualquer valor | espere paid e chame return |
O gatilho 77 só existe no simulador. Ele não afeta nenhuma operação Real.
Exemplos¶
Os exemplos de shell supõem dr como no Primeiro depósito e primeiro saque e as variáveis DEP_ID, DEP_EXT, WDR_ID.
Receita 1: depósito pago pelo próprio jogador, paid¶
POST /v1/depositscomplayer.documentigual ao CPF do jogador. A resposta é201compendingeinstructions.pix, ou202comcreated, que passa apendingem seguida.POST /v1/sandbox/deposits/{id}/paycom corpo vazio. Resposta200com o objeto.- Chega o webhook
deposit.paid. FaçaGET /v1/deposits/{id}e confirastatus: paid. Credite uma vez, porexternal_id. - Confira que o evento aparece em
GET /v1/events?object_id={id}.
dr POST "/sandbox/deposits/$DEP_ID/pay" "" "sbx_$(novo_id)"
dr GET "/deposits/$DEP_ID"
dr GET "/events?object_id=$DEP_ID"
Receita 2: pago por terceiro, under_review com PAYER_MISMATCH¶
- Crie o depósito (receita 1, passo 1).
- Chame
paycom umpayer_documentdiferente do documento do jogador, por exemplo outro CPF de teste. - Resultado:
under_review, comreview_reasonscontendoPAYER_MISMATCH. Nada é creditado e o webhook édeposit.under_review. - A decisão (aprovar ou devolver) é de um revisor no painel (Revisão de depósitos e saques). Aprovado, o depósito vai a
paide aí você credita; devolvido, vai areturnede você não credita.
echo '{"payer_document":{"type":"CPF","number":"11144477735","country":"BR"}}' > pagador.json
dr POST "/sandbox/deposits/$DEP_ID/pay" pagador.json "sbx_$(novo_id)"
Receita 3: valor divergente, under_review com AMOUNT_MISMATCH¶
- Crie um depósito de
5000. - Chame
paycom{"paid_amount": {"amount": "4999", "currency": "BRL"}}. Moeda diferente da do depósito:400 validation_failedcomreason: currency_mismatch. - Resultado:
under_reviewcomAMOUNT_MISMATCH. Nada é creditado e o webhookdeposit.paidnão é emitido para esse depósito.
Receita 4: expiração, expired¶
- Crie o depósito e espere
pending. POST /v1/sandbox/deposits/{id}/expire:expired, com o webhookdeposit.expired.expiresó vale a partir depending; fora disso,409 invalid_state_transition.- Chame
paydepois: não leva apaid. POST /v1/deposits/{id}/cancel:409 invalid_state_transition, porqueexpiredé terminal.
Receita 5: saque automático, paid¶
- Tenha um jogador com um depósito
paid(receita 1). A regra X× só conta o que o Dracma liquidou. - Reserve o valor no seu livro e chame
POST /v1/withdrawalscom valor dentro do limite ereview_requested: false. Mandeplayer.ip: sem ele o saque nunca é automático (OWNERSHIP_UNVERIFIED). - A resposta é
201comapprovededecision.mode: automatic. O simulador paga sozinho: chegamwithdrawal.approved,withdrawal.processingewithdrawal.paid. Faça o fetch-back e finalize a reserva ao lerpaid.
O primeiro saque pode ir para revisão
Dependendo das regras da operação (veja GET /v1/rules), o primeiro saque de um jogador vai para revisão com FIRST_WITHDRAWAL. Leia decision.reasons. Para chegar ao caminho automático, faça antes um primeiro saque e aprove-o (receita 8).
Receita 6: saque acima do limite, under_review com AUTO_MULTIPLIER_EXCEEDED¶
Peça um saque maior que o total depositado e liquidado vezes o multiplicador da operação. Resultado: under_review, decision.mode: manual e decision.reasons com AUTO_MULTIPLIER_EXCEEDED (ou NO_SETTLED_DEPOSIT, se o jogador nunca teve depósito liquidado). A reserva continua no seu livro.
Receita 7: review_requested: true nunca é automático¶
Repita a receita 5 com "review_requested": true. Resultado: under_review com OPERATOR_REVIEW_REQUESTED em decision.reasons, mesmo que as regras liberassem o automático. O operador só consegue tornar a decisão mais restritiva; nunca pular uma revisão.
Receita 8: decisões e resultado do PSP no saque¶
POST /v1/sandbox/withdrawals/{id}/{action}:
action |
De, para | O que o seu livro faz |
|---|---|---|
approve |
under_review para approved (o Dracma já despacha) |
Nada. A reserva continua. |
reject |
requested ou under_review para rejected |
Devolve a reserva. |
pay |
approved ou processing para paid (assíncrono) |
Finaliza a saída ao ler paid. |
fail |
approved ou processing para failed (assíncrono) |
Devolve a reserva ao ler failed. O objeto traz failure_code. |
return |
paid para returned (síncrono) |
A política é sua. Recomendação: nada automático; abra uma decisão manual e dispare um alerta. |
Para pay e fail com tempo de agir, use o valor terminado em 77 (seção acima). approve só vale em under_review; ação fora da máquina de estados responde 409 invalid_state_transition. O cancelamento pelo operador é POST /v1/withdrawals/{id}/cancel: funciona em requested, under_review e em approved antes de o envio sair; depois disso, 409.
Chave PIX que não é CPF ou CNPJ
No simulador, o titular de uma chave email, phone ou random é um CPF sintético estável, derivado da chave, e não o do jogador. Com uma chave dessas, o saque tende a voltar 422 document_mismatch ou rejected. Para o caminho feliz use key_type: cpf com o CPF do próprio jogador. Para testar a recusa por titularidade, use uma chave cpf de outra pessoa.
Receita 9: queda e retomada sem duplicar¶
POST /v1/sandbox/outagecom{"down": true}. Daí em diante, toda rota da operação fora de/sandbox/*responde503(service_unavailable,retryable: true,Retry-After: 5).GET /v1/pingcontinua respondendo.POST /v1/depositscomIdempotency-Key: K:503.POST /v1/sandbox/outagecom{"down": false}e uma chave de controle nova.- Repita o passo 2 com a mesma
Ke o mesmo corpo:201ou202. Repita mais uma vez: a mesma resposta, agora comIdempotent-Replayed: true. GET /v1/deposits?external_id=...: exatamente 1 depósito.
É esse o comportamento que o seu cliente precisa ter em produção: diante de 5xx, 429 ou erro de rede, repita com a mesma chave. Há um helper pronto em Exemplos executáveis.
Receita 10: reentrega de webhook, teste da sua deduplicação¶
- Rode uma receita que gere webhook (a 1) e anote o
Dracma-Event-Id. POST /v1/sandbox/webhooks/redelivercom{"event_id": "evt_..."}:200comscheduled.- O seu receptor recebe o mesmo
Dracma-Event-Ide responde200sem efeito. O crédito continua sendo 1. GET /v1/sandbox/webhooksmostra a entrega e o status HTTP que você devolveu.
Receita 11: webhook forjado não cria dinheiro¶
Assine você mesmo um deposit.paid com o seu segredo de webhook, apontando para um depósito que não está pago, e envie ao seu receptor. A assinatura é válida, mas o fetch-back devolve pending. Resultado esperado: 200 e nenhum crédito. Veja Receptor passo a passo.
Receita 12: estorno e chargeback depois de paid¶
- Tenha um depósito
paidjá creditado no seu livro (receita 1). POST /v1/sandbox/deposits/{id}/refundou.../chargeback, com corpo vazio ou{}. A resposta é200com o objeto emrefundedouchargebackeversionmaior. Fora depaid:409 invalid_state_transition. Campo no corpo:400.- Chega
deposit.refundedoudeposit.chargeback. Faça o fetch-back e aplique a sua política de débito. Recomendação: abra uma decisão manual e dispare um alerta; nunca debite sozinho (Política de exceções).
Só existe no sandbox
Estorno e chargeback são gatilhos de simulação. Em produção não há um gatilho equivalente hoje: o seu conector precisa tratar os dois estados, mas não os verá acontecer. Veja Devoluções, estornos e disputas.
Erros e casos de borda¶
| Sintoma | Causa provável | O que fazer |
|---|---|---|
pay em saque devolve 409 invalid_state_transition |
O simulador já pagou sozinho | Crie o saque com valor xx77 |
422 insufficient_operation_balance ao criar saque |
Caixa da operação sem saldo livre | Pague antes um depósito (receita 1) |
429 rate_limited ao criar saque |
Muitos saques do mesmo jogador na hora | Use outro external_ref ou espere o Retry-After |
Saque sempre under_review |
Falta player.ip, primeiro saque ou chave nova |
Leia decision.reasons |
422 document_mismatch no saque |
Chave PIX de outro titular, ou chave não-CPF no simulador | Use chave cpf do próprio jogador |
Depósito created por muito tempo |
A cobrança ainda está sendo criada | Espere o webhook deposit.pending ou leia o objeto |
Veja também¶
- Sandbox: rotas de controle, escopos e erros.
- Suíte de conformidade: as mesmas receitas, automatizadas.
- Ciclo do depósito e Ciclo do saque.
- Retentativas, skipped e DLQ.