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

stateDiagram-v2 [*] --> registered : register registered --> enabled : enable (saudável + certificado) enabled --> draining : drain enabled --> draining : disable (com em-voo maior que 0) draining --> disabled : em-voo chega a 0 enabled --> disabled : disable (em-voo igual a 0) disabled --> enabled : enable disabled --> unplugged : unplug draining --> unplugged : unplug --force (break-glass) unplugged --> registered : register (re-plugar)

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):

  1. Descriptor válido, spp_version compatível e name sem colisão.
  2. health igual a ok.
  3. Egress e ingress aplicados pelo ops e conferidos.
  4. Certificação válida para o descriptor_sha256 atual em hml e prd. Em dev basta o harness local.
  5. 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:

  1. Persiste. Nada é perdido: cobranças e saques em curso ficam gravados com o estado conhecido.
  2. Loga com trace_id. Cada falha tem rastro correlacionável do executor ao provedor.
  3. Alerta. Provedor down, breaker aberto, resposta unrecognized_response e dreno parado são alertas versionados no Grafana.
  4. Protege. O breaker abre; depósitos usam failover (se ligado e houver outro provedor); saques não trocam de provedor.
  5. 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):

  1. Obter ou escrever o adaptador do novo parceiro e certificá-lo em hml.
  2. Instalar em paralelo, registrar e habilitar o novo provedor ao lado do antigo.
  3. Criar conexões dos tenants no novo provedor e mover o roteamento por tenant e operação, em canário.
  4. Observar métricas e alertas; recuar se algo sair do esperado.
  5. 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