Ciclo de vida do provedor: plugar, drenar, desplugar¶
Para: Dev · Adm — Conferido em 2026-10-31.
O que é¶
Um provedor de liquidação (PSP, gateway, provedor cripto) é um processo à parte, falando o SPP v1 em loopback com o executor. O registro de provedores do executor guarda, para cada provedor, um estado. O estado decide se ele recebe trabalho novo, se aceita callbacks e se ainda é consultado sobre o que ficou em voo.
Plugar e desplugar um provedor não exigem deploy do núcleo: são operações do dracma_ops sobre o registro. Esta página descreve os estados, o passo a passo de plugar, o que acontece ao desplugar com dinheiro em andamento, failover, breaker, saúde e o que fazer quando o parceiro cai sem ninguém desplugar nada.
Subcomandos do ops
Os comandos dracma_ops provider ... desta página são os subcomandos do ops para o registro de provedores: register, enable, drain, disable, unplug, certify, list e inflight (mais install, que agrupa os passos de instalação). Eles são a interface única: ninguém edita unit, firewall nem rota à mão.
Quando usar¶
- Você vai entrar com um provedor novo (nosso ou de terceiro) e quer saber exatamente o que o ops faz.
- Você vai sair de um parceiro (ou apenas pausá-lo) sem perder dinheiro em voo.
- Um provedor caiu e você precisa saber o que o sistema faz sozinho e o que cabe ao operador.
- Para a troca completa de um parceiro por outro, com canário e rollback, siga o runbook de troca de parceiro.
Como funciona¶
Estados e transições¶
Em palavras: registered → enabled, enabled ↔ draining → disabled ↔ enabled, disabled → unplugged e unplugged → registered. O unplug --force com trabalho em voo é o único atalho, e é break-glass auditado (veja Desplugar). O unregister, que apagaria o registro, é recusado enquanto existir conexão de tenant; dado financeiro nunca é apagado.
Tabela de estados¶
| Estado | Recebe trabalho novo? | Callbacks | Consulta/reconciliação em voo | Como entra |
|---|---|---|---|---|
registered |
não | não | não | register: o executor lê o descriptor, valida e grava o descriptor_sha256. |
enabled |
sim, se estiver saudável | sim | sim | enable: exige health ok e certificação válida. |
draining |
não | sim | sim | drain, ou disable quando há trabalho em voo. |
disabled |
não | só se houver em-voo | só se houver em-voo | disable com em-voo zero, ou drain concluído. Reversível com enable. |
unplugged |
não | não | não | unplug: adaptador parado e removido. Registro, conexões e credenciais cifradas permanecem. |
Pré-condições para enabled (todas verificadas pelo registro):
- Descriptor válido,
spp_versioncompatível enamesem colisão. healthigual aok.- Egress e ingress aplicados pelo ops e conferidos.
- Certificação válida para o
descriptor_sha256atual em hml e prd. Em dev basta o harness local. - Se o provedor não é sintético: política de ambiente satisfeita (guarda de ambiente do SPP).
Plugar¶
Plugar é uma sequência de cinco fases, todas pelo ops. Cada fase é idempotente: repetir não duplica nada.
1. Instalar (usuário, unit, porta, par HMAC, egress, ingress). O comando lê o descriptor do pacote do adaptador e faz tudo de uma vez:
dracma_ops provider install --name exemplo --env hml --jobs "$(nproc)"
Saída esperada (exemplo):
[install] provedor exemplo (dracma_psp_exemplo) versão 0.3.1, spp 1.0
[install] usuário linux: dracma-psp-exemplo (uid 9211, sem shell, sem home)
[install] unit systemd: dracma_psp_exemplo.service (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, ProtectHome)
[install] porta loopback: 127.0.0.1:9411
[install] par HMAC: semeado no seeder da aplicação (chamador executor, chamado exemplo)
[install] egress: 2 hosts liberados para o uid 9211 (api.exemplo-psp.example:443, sandbox.exemplo-psp.example:443)
[install] ingress: rota de callbacks genérica já cobre o provedor (nenhuma edição de rota)
[install] subindo unit ... ok
[install] GET /spp/v1/health -> ok
O que cada item significa:
| Passo | O que o ops faz | Origem da informação |
|---|---|---|
| Usuário Linux | Cria usuário próprio, sem shell e sem acesso ao diretório de outros serviços. | Padrão da casa (um serviço, um usuário). |
| Unit systemd | Gera a unit hardened (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, ProtectHome, sistema de arquivos somente leitura). |
Padrão da casa e §13 do SPP. |
| Porta | Atribui uma porta única e faz o bind só em 127.0.0.1, nunca em todas as interfaces. |
Ops. |
| Par HMAC | Semeia o segredo INTERNAL_HMAC_<PROVEDOR>_EXECUTOR (nome do provedor em maiúsculas, - vira _). É o único segredo de longa duração do provedor. |
Ops; ver o contrato SPP. |
| Egress | Gera a regra de saída por UID apenas para os egress_hosts do descriptor, mais DNS e telemetria local. Todo o resto é descartado e alertado. |
Descriptor. |
| Ingress | Garante que a rota genérica de callbacks encaminhe o novo name ao executor. Nenhum provedor novo exige editar rota. |
Registro. |
A instalação é paralela, com o número de jobs igual ao de cores (nproc). Se uma instalação em paralelo falhar por colisão (porta, usuário, regra), os serviços não estão independentes: o ops expõe a colisão e não serializa para mascarar; corrija a independência antes de seguir.
2. Registrar. O executor lê o descriptor pela URL de loopback, valida e grava a cópia versionada. O estado inicial é registered.
dracma_ops provider register --name exemplo --url http://127.0.0.1:9411/spp/v1
[register] GET http://127.0.0.1:9411/spp/v1/descriptor -> 200
[register] descriptor válido; spp_version 1.0 (comum com o executor: 1.0)
[register] descriptor_sha256 = 3f9a...c1d2
[register] estado: registered
3. Certificar. A suíte de conformidade do SPP roda contra o adaptador e o ops grava o resultado, amarrado ao descriptor_sha256:
dracma_ops provider certify --name exemplo
[certify] suite spp-conformance 1.0 contra http://127.0.0.1:9411/spp/v1
[certify] passou 58 de 58
[certify] resultado gravado para descriptor_sha256 3f9a...c1d2
4. Habilitar. O registro confere as pré-condições e muda o estado:
dracma_ops provider enable --name exemplo
[enable] health: ok
[enable] certificação válida para 3f9a...c1d2
[enable] egress e ingress conferidos
[enable] estado: enabled
Se faltar algo, a saída diz exatamente o quê:
[enable] recusado: not_certified (descriptor_sha256 mudou desde a certificação)
5. Conferir.
dracma_ops provider list
NOME ESTADO SAÚDE EM-VOO(cobr/saq/unk) DESCRIPTOR CERTIFICADO
maximaxpay enabled ok 12 / 3 / 0 a1b2...09ff sim
exemplo enabled ok 0 / 0 / 0 3f9a...c1d2 sim
Habilitar um provedor não manda tráfego a ele: o roteamento é por conexão de tenant, com prioridade e direção. O provedor recém-habilitado só recebe trabalho quando existirem conexões apontando para ele (veja o runbook).
Drenar¶
drain fecha a porta de entrada e mantém a de saída: o provedor não recebe trabalho novo, mas continua recebendo callbacks e sendo consultado sobre o que ficou em voo.
dracma_ops provider drain --name maximaxpay
dracma_ops provider inflight --name maximaxpay
[drain] estado: draining
cobranças pendentes: 7
saques não terminais: 3 (desconhecidos: 0)
O que acontece com cada tipo de trabalho:
| Trabalho | Durante draining |
Quando termina |
|---|---|---|
| Cobranças pendentes | Continuam sendo consultadas e aceitas no provedor atual até serem pagas, expirarem ou serem canceladas. Novas cobranças não vão para ele; o failover de depósito escolhe outro provedor habilitado. | Ao ficarem terminais ou expiradas. |
Saques em voo (aceitos, unknown, processing) |
Continuam sendo reconciliados por este provedor (consulta de saque por chave). Nunca há failover automático de saque nem reenvio a outro provedor. | Ao ficarem terminais. |
| Callbacks | Continuam aceitos e verificados. | Com o contador em zero. |
| Conexões dos tenants | Continuam existindo e visíveis no painel. Novas operações são roteadas para outras conexões. | Reativam sozinhas se o provedor voltar a enabled. |
Quando o contador em-voo chega a zero, o estado passa sozinho para disabled e o executor emite o evento provider.drained. Se o dreno parar (por exemplo, um saque preso em unknown), o alerta de drain parado dispara: a causa quase sempre é um saque que precisa de consulta ao parceiro ou decisão humana.
Para desabilitar de vez:
dracma_ops provider disable --name maximaxpay
Com em-voo maior que zero, disable vira drain e o provedor passa por draining até zerar. Com em-voo zero vai direto para disabled. É reversível com enable.
Desplugar¶
unplug para e remove o adaptador. O registro, as conexões dos tenants e as credenciais cifradas permanecem, de modo que re-plugar é barato e nada se perde.
dracma_ops provider unplug --name maximaxpay
Sem --force, com trabalho em voo, a operação é recusada:
[unplug] recusado: in_flight_work (cobranças 7, saques 3). Drene antes ou use --force com motivo.
Com --force (break-glass auditado, exige motivo de 10 a 1000 caracteres):
dracma_ops provider unplug --name maximaxpay --force --reason "parceiro encerrou o contrato em 2026-11-30; ver incidente 4471"
O efeito sobre o que estava em voo:
| Trabalho | Efeito do unplug --force |
|---|---|
| Cobranças pendentes | Passam a expired pela regra normal de expiração. |
| Saques em voo | Ficam em needs_reconciliation com o motivo provider_unplugged, sinalizados para decisão humana. Nada é perdido e nada é reenviado. |
| Conexões dos tenants | Ficam provider_unavailable (visíveis no painel). |
| Credenciais | Continuam cifradas no cofre do executor. O adaptador nunca guardou segredo, então não sobra segredo para trás no host. |
| Auditoria | Registra quem, quando, motivo, descriptor_sha256 e trace_id. |
Um saque em needs_reconciliation é resolvido por um humano: consulte o parceiro pelo canal dele (painel, extrato, suporte) usando a chave de idempotência do saque, e conclua como liquidado ou falho. Veja o plano completo no runbook.
Re-plugar¶
unplugged → registered: suba o adaptador de novo (install) e rode register. O registro valida o descriptor outra vez e compara o descriptor_sha256. As conexões dos tenants que estavam provider_unavailable reativam sozinhas quando o provedor chega a enabled.
dracma_ops provider install --name maximaxpay --env hml --jobs "$(nproc)"
dracma_ops provider register --name maximaxpay --url http://127.0.0.1:9412/spp/v1
dracma_ops provider certify --name maximaxpay
dracma_ops provider enable --name maximaxpay
Se o adaptador voltou com um descriptor diferente, vale a regra da seção seguinte.
Mudança de descriptor¶
O registro compara o descriptor_sha256 a cada novo register.
| Tipo de mudança | Exemplo | Efeito |
|---|---|---|
| Compatível | Capacidade nova, deprecations, links. |
Só é registrada e auditada. O provedor continua no estado em que estava. |
| Incompatível | Capacidade removida, schema de credencial mais restrito, egress_hosts ou environments alterados. |
O provedor volta a registered automaticamente e precisa de nova certificação e novo enable. |
| Capacidade removida que alguma conexão usa | Remover cash_out com conexão de saque ativa. |
Recusada. |
Troca de name |
Qualquer uma. | Impossível: o name é imutável. Outro nome é outro provedor. |
Como a certificação vale para um descriptor_sha256 específico, qualquer mudança de descriptor exige certificar de novo antes de habilitar em hml e prd.
Failover de depósito¶
Criar uma cobrança não move dinheiro, então trocar de provedor quando a criação falha por indisponibilidade é seguro. O failover de depósito obedece a regras fixas, que não dependem de qual provedor é.
| Regra | Detalhe |
|---|---|
| Só infraestrutura troca de provedor | Timeout, Unavailable e CircuitOpen. Recusa de negócio (Rejected), configuração (Misconfigured) e o resto nunca trocam. |
| Saque nunca | Não existe failover automático de saque, nem reenvio a outro provedor. |
| Cada provedor uma vez por cobrança | As tentativas ficam gravadas e excluem o provedor da escolha seguinte. Repetir no mesmo provedor usa o mesmo deposit_id como chave de idempotência. |
| Uma cobrança ativa por depósito | A linha continua única e passa a apontar para o provedor corrente; cada tentativa fica registrada para auditoria. |
| Timeout e 5xx são ambíguos | O provedor pode ter criado o QR. A tentativa fica ambiguous; se um pagamento chegar por ela depois da troca, é aceito mas marcado needs_reconciliation para decisão humana. |
| Provedor sem nada enviado | Provedor disabled, unplugged ou unhealthy (nenhum byte saiu) vira CircuitOpen em create_charge, o que é failover seguro. |
Failover vem desligado
No executor atual o failover de depósito é uma chave de configuração, desligada por padrão. Sem um segundo provedor real habilitado não há para onde trocar. Ligue só depois de ter dois provedores de depósito habilitados e certificados.
Breaker¶
O breaker é do núcleo e protege o parceiro e o jogador sem consultar o provedor.
| Parâmetro | Valor |
|---|---|
| Janela | 30 s |
| Amostras mínimas | 20 |
| Taxa de falha para abrir | 50% ou mais |
| Tempo aberto | 30 s, depois uma sonda única (meio-aberto) |
| Escopo | Por provedor, operação e conexão |
| O que conta como falha | Timeout, Unavailable e CircuitOpen de infraestrutura |
| O que não conta | Rejected, Unsupported, Misconfigured, Unrecognized |
Breaker aberto responde CircuitOpen sem chamar o provedor. O estado aparece em dracma_psp_circuit_state.
Saúde¶
O executor consulta a rota de saúde de cada provedor a cada 10 s.
| Regra | Valor |
|---|---|
| Frequência | A cada 10 s |
Para ficar unhealthy |
3 falhas seguidas |
Efeito de unhealthy |
Não recebe trabalho novo; o failover de depósito escolhe outro provedor. |
| Para voltar | 2 sucessos seguidos (histerese). O provedor reabilita sozinho. |
| Relação com o breaker | A saúde não substitui o breaker: o breaker reage ao tráfego real, a saúde ao sinal de vida. |
As verificações de saúde nunca criam efeito no parceiro nem usam credencial de tenant. Os estados possíveis são ok, degraded (opera com restrição) e down.
Falha do provedor sem desplugar¶
O piso de independência (S12) vale nos dois sentidos: o executor sobe e funciona sem o provedor, e o provedor sobe sozinho, sem o executor. Quando um provedor cai ou o parceiro fica fora, sem ninguém desplugar, o sistema faz sozinho:
- Persiste. Nada é perdido: cobranças e saques em curso ficam gravados com o estado conhecido.
- Loga com
trace_id. Cada falha tem rastro correlacionável do executor ao provedor. - Alerta. Provedor
down, breaker aberto, respostaunrecognized_responsee dreno parado são alertas versionados no Grafana. - Protege. O breaker abre; depósitos usam failover (se ligado e houver outro provedor); saques não trocam de provedor.
- Retoma. Quando o provedor volta, a saúde reabilita, o breaker fecha na sonda e o reconciliador reconsulta o que ficou
unknown.
O que o operador faz: acompanhar o alerta, confirmar a causa (processo, rede, parceiro) e, se o parceiro está fora por tempo prolongado, decidir entre esperar, drenar ou trocar de parceiro.
Trocar de parceiro sem downtime¶
Visão geral (o passo a passo completo, com comandos, métricas e rollback, está no runbook de troca de parceiro):
- Obter ou escrever o adaptador do novo parceiro e certificá-lo em hml.
- Instalar em paralelo, registrar e habilitar o novo provedor ao lado do antigo.
- Criar conexões dos tenants no novo provedor e mover o roteamento por tenant e operação, em canário.
- Observar métricas e alertas; recuar se algo sair do esperado.
- Drenar o provedor antigo, tratar os saques em voo e só então desplugar.
Como o antigo continua enabled até a drenagem e o novo entra por roteamento, em nenhum momento há janela sem provedor.
API de registro do executor¶
O ops fala com o registro por rotas do executor, só para o chamador ops (HMAC interno v2). Todas as mutações vão para a auditoria. Estas são as rotas, que o dracma_ops provider encapsula:
GET /internal/v1/providers
GET /internal/v1/providers/{name}
POST /internal/v1/providers
GET /internal/v1/providers/{name}/inflight
POST /internal/v1/providers/{name}/enable
POST /internal/v1/providers/{name}/drain
POST /internal/v1/providers/{name}/disable
POST /internal/v1/providers/{name}/unplug
POST /internal/v1/providers/{name}/certify
| Rota | Corpo | Resposta | Subcomando do ops |
|---|---|---|---|
| Listar | nenhum | 200 com providers |
list |
| Ler um | nenhum | 200 com a entrada e o descriptor guardado; 404 |
list --name |
| Registrar | url loopback |
201 com a entrada; 409, 422 |
register |
| Em voo | nenhum | 200 com contadores |
inflight |
| Habilitar, drenar, desabilitar | opcional | 200 com a nova entrada; 409 |
enable, drain, disable |
| Desplugar | opcional: force, reason |
200; 409 se em voo sem force |
unplug |
| Certificar | certification |
200 |
certify |
Registrar (o url precisa ser http://127.0.0.1:<porta>/spp/v1, só loopback):
{ "url": "http://127.0.0.1:9411/spp/v1" }
Resposta 201:
{
"name": "exemplo",
"state": "registered",
"health": "ok",
"url": "http://127.0.0.1:9411/spp/v1",
"descriptor_sha256": "3f9a5d0e7b1c4a28e6f0d93b52c7a1840be9d6f3a27c5d81e04b9a6c7f30c1d2",
"certification": null,
"inflight": { "charges_pending": 0, "payouts_non_terminal": 0, "payouts_unknown": 0 },
"registered_at": "2026-10-31T12:00:00Z",
"changed_at": "2026-10-31T12:00:00Z"
}
Certificar (corpo certification; o descriptor_sha256 precisa ser o do registro):
{
"certification": {
"descriptor_sha256": "3f9a5d0e7b1c4a28e6f0d93b52c7a1840be9d6f3a27c5d81e04b9a6c7f30c1d2",
"suite_version": "1.0",
"passed": 58,
"total": 58,
"at": "2026-10-31T12:05:00Z",
"report_sha256": "9c1e0f2a47d38b65a1f0c2e9d4b7a3685f10e2c4d7a9b3f6180e5c2a4d7b9f01"
}
}
Em voo (GET .../inflight):
{ "charges_pending": 7, "payouts_non_terminal": 3, "payouts_unknown": 0 }
Desplugar com força:
{ "force": true, "reason": "parceiro encerrou o contrato; ver incidente 4471" }
Recusas comuns, todas 409 com corpo application/problem+json: transição inválida, in_flight_work (desplugar sem force), not_certified e not_healthy (habilitar). Um register com descriptor inválido, versão incompatível (incompatible_spp_version) ou host de egress inconsistente responde 422.
Situação e comportamento¶
| Situação | Comportamento do sistema | Ação do operador |
|---|---|---|
Provedor registered |
Não recebe trabalho nem callbacks. | Certificar e habilitar. |
Provedor enabled e saudável |
Recebe trabalho conforme o roteamento. | Nenhuma. |
| 3 falhas de saúde seguidas | unhealthy: sem trabalho novo; depósito faz failover (se ligado). |
Investigar processo e rede. |
| 2 sucessos de saúde | Reabilita sozinho. | Nenhuma. |
| Breaker aberto | CircuitOpen sem chamar o provedor; sonda após 30 s. |
Observar; investigar o parceiro. |
| Criação de cobrança com timeout | Tentativa ambiguous; failover para outro provedor, se ligado. |
Conciliar se um pagamento chegar pela tentativa antiga. |
| Criação de saque com timeout | Saque unknown; consulta por chave; nunca reenvia. |
Nenhuma, salvo se ficar preso. |
| Recusa de negócio na criação de cobrança | Rejected; não troca de provedor; não conta para o breaker. |
Corrigir dado ou credencial. |
drain com saque unknown |
Fica em draining até a consulta resolver. |
Tratar o saque preso. |
unplug com em-voo e sem force |
Recusado com in_flight_work. |
Drenar antes. |
unplug --force |
Cobranças expiram; saques vão a needs_reconciliation. |
Decidir cada saque. |
| Descriptor mudou de forma incompatível | Volta a registered. |
Certificar e habilitar de novo. |
| Provedor fora, não desplugado | Persiste, loga com trace_id, alerta e retoma ao voltar. |
Acompanhar o alerta. |
Conexão em provedor disabled ou unplugged |
provider_unavailable; reativa sozinha com enabled. |
Mover o roteamento, se for definitivo. |
| Callback de provedor desconhecido ou desabilitado sem em-voo | Resposta uniforme de não autenticado ao PSP. | Nenhuma. |
Erros comuns¶
| Sintoma | Causa provável | Correção |
|---|---|---|
enable recusado com not_certified |
O descriptor_sha256 mudou desde a certificação. |
Rodar certify de novo. |
enable recusado com not_healthy |
A rota de saúde não responde ok. |
Ver a unit, a porta e o log do adaptador. |
register com incompatible_spp_version |
O descriptor não declara versão comum com o executor. | Ajustar spp_versions_supported no adaptador. |
unplug recusado com in_flight_work |
Há cobrança ou saque em voo. | drain, esperar o contador chegar a zero, ou --force com motivo. |
| Drain não termina | Saque em unknown ou processing sem resposta do parceiro. |
Consultar o parceiro por canal próprio; resolver manualmente. |
| Nenhum tráfego chega ao provedor novo | Habilitar não roteia: faltam conexões dos tenants apontando para ele. | Criar as conexões e ajustar prioridades. |
| Failover não acontece | O failover de depósito está desligado ou só há um provedor habilitado. | Habilitar um segundo provedor e ligar o failover. |
| Instalação paralela falha | Colisão de porta, usuário ou regra: serviços não independentes. | Corrigir a colisão; não serializar. |
Próximos passos¶
- Execute a troca completa com canário e rollback: runbook de troca de parceiro.
- Se o parceiro sumiu: parceiro que rompe de repente.