Runbook: trocar de parceiro sem downtime

Para: Dev · Adm — Conferido em 2026-10-31.

O que é

Procedimento numerado para trocar de parceiro de liquidação sem parar o negócio. Serve para três casos:

  • sair da MaximaxPay e entrar em outro PSP;
  • adicionar um segundo provedor de depósito ao lado do atual (redundância e failover);
  • reagir ao dia em que o parceiro rompe de repente (última seção).

A troca é possível porque o provedor é um processo à parte, plugado por registro, e o dinheiro em voo continua sendo tratado pelo provedor que o originou. Os estados e conceitos usados aqui estão em ciclo de vida do provedor.

Subcomandos do ops

Os comandos desta página são os do grupo dracma_ops provider: list, show, inflight, sync, register, certify, enable, drain, disable, unplug, replug e cutover (mais install). O nome do provedor é um argumento posicional e --root aponta a instância; nos exemplos, R=/dracma-hml em hml e R=/dracma em prd. Cada transição reaplica sozinha o ingress (rotas de callback) e o egress (hosts do descriptor) e, no unplug, para o adaptador. Toda operação no servidor passa pelo ops; nada é feito à mão com ssh, systemctl ou edição de arquivo.

Quando usar

Situação Use
O contrato com o parceiro termina em data conhecida. Seções 1 a 12, com folga de semanas.
Quer redundância de depósito (segundo PSP). Seções 1 a 6; pule 9 a 11 (o antigo continua).
Parceiro indisponível ou encerrado sem aviso. Parceiro que rompe de repente.
Só testar um provedor novo em hml. Seções 1 a 4.

Como funciona

A troca avança em etapas com portões de go/no-go. Em cada etapa o provedor antigo continua enabled e atendendo, de modo que sempre existe um caminho de volta. Só se desliga o antigo depois de drenado.

flowchart LR A[1 Pré-requisitos] --> B[2 Preparar e certificar em hml] B --> C[3 Instalar em paralelo] C --> D[4 Registrar] D --> E[5 Habilitar com tráfego dividido] E --> F[6 Canário por tenant e operação] F --> G[7 Observar] G --> H{go?} H -->|sim| I[8 Mover o restante] H -->|não| R[Rollback da etapa] I --> J[9 Drenar o antigo] J --> K[10 Saques em voo] K --> L[11 Desplugar] L --> M[12 Checklist final]

Etapa 1. Pré-requisitos

Antes de qualquer comando:

  1. Contrato comercial com o novo parceiro assinado, credenciais de sandbox e de produção em mãos (as de produção ficam com quem cadastra a conexão no painel; nunca vão para repositório).
  2. Documentação do parceiro: autenticação, criação de cobrança PIX, consulta, saque (se for o caso), tarifa, assinatura de callback, IPs de origem dos callbacks e ambiente de teste.
  3. Decisão registrada (ADR) sobre o parceiro novo: motivo, direções (cash_in, cash_out), data-alvo.
  4. Janela combinada com quem opera o financeiro, e quem decide go/no-go identificado.
  5. Backup do estado atual: saída de dracma_ops provider list --root $R e de dracma_ops provider inflight <antigo> --root $R guardada no registro da mudança.
  6. Alertas do provedor antigo funcionando (para ter linha de base antes de mexer).
  7. Servidor de hml com o mesmo desenho de prd (mesmo ops, mesmo executor).

Go/no-go da etapa: todos os itens acima cumpridos. Rollback: não há nada a desfazer.

Etapa 2. Preparação: adaptador e certificação em hml

2.1 Obter ou escrever o adaptador. Se já existe um adaptador SPP certificado para o parceiro (nosso ou de terceiro), use-o. Se não, escreva um dracma_psp_<nome> que fale o SPP v1: descriptor, saúde, cobrança, consulta, saque (se suportar), verificação de callback, idempotência e erros com effect correto.

Pontos que mais pesam na troca:

  • Idempotência. Cobrança por deposit_id, saque por chave de idempotência; repetir com a mesma chave não pode gerar segundo efeito no parceiro.
  • Consulta de saque por chave. É a fonte de verdade para resolver saque ambíguo. O adaptador precisa responder para qualquer chave aceita.
  • Erro ambíguo é ambíguo. Timeout e 5xx do parceiro viram effect: unknown. Nunca converta em falha definitiva.
  • Callback. Verificação sobre os bytes crus, em tempo constante. O callback sozinho nunca move dinheiro: o executor confirma por consulta.
  • Ambiente. Declarar environments.sandbox e a política de ambiente, para que fora de produção só se use sandbox.

2.2 Instalar o adaptador em hml e certificar.

O adaptador entra no ops por um PR no dracma_ops (bloco do serviço em services.yaml com provider: {name: novopsp}, porta, deploy e o pin do repo em release.yaml). Depois disso, o install o implanta como qualquer serviço (usuário próprio, unit hardened, par HMAC por arquivo) e o resto é com o provider:

R=/dracma-hml
dracma_ops install --env hml --root $R --only novopsp      # só o adaptador novo; --only refaz apenas esses serviços
dracma_ops provider register novopsp --root $R
dracma_ops provider certify novopsp --root $R --report relatorio.json   # ou rode o kit: --bin spp-conformance (--no-upstream, --fake-control-url …)
novopsp registrado (descriptor 7be1…44a0). Próximo: `provider certify novopsp` e `provider enable novopsp`
novopsp certificado: 58/58 casos (suite 1.0.0, descriptor 7be1…44a0)

O certify só grava a certificação se o descriptor_sha256 do relatório for o do registro. O par HMAC do kit vai por arquivo temporário 0600, nunca por argumento.

2.3 Rodar o fluxo de ponta a ponta em hml, com credenciais de sandbox: criar depósito, pagar no sandbox do parceiro, receber o callback, confirmar por consulta, fazer um saque (se houver) e consultar. Efeito externo real nunca sai de hml: e-mail e SMS do parceiro só com destinatário de teste.

Go/no-go: certificação completa (passou igual ao total) e fluxo de ponta a ponta verde em hml. Rollback: dracma_ops provider unplug novopsp --root $R em hml (sem em-voo, não precisa de --force).

Etapa 3. Instalar em paralelo em prd

A instalação em prd é a mesma de hml e roda em paralelo (número de jobs igual ao de cores, nproc por padrão):

dracma_ops install --env prd --root /dracma --only novopsp

O install cria, para o adaptador: usuário Linux próprio (dracma-novopsp), unit systemd hardened (spp-v1 §13), porta loopback do services.yaml, par INTERNAL_HMAC_NOVOPSP_EXECUTOR (gerado no seeder, entregue por arquivo ao adaptador e por env só ao executor) e o /health conferido. O egress (egress_hosts do descriptor) e as rotas de callback são aplicados pelos comandos provider register/enable, a partir do descriptor.

O provedor antigo não é tocado. Se a instalação falhar por colisão (porta, usuário, regra de firewall), pare: serviços que não instalam em paralelo não são independentes, e isso é prioridade máxima a corrigir. Não serialize para esconder o problema.

Go/no-go: unit ativa e saúde ok. Rollback: nada registrado ainda; basta não registrar (o adaptador parado fica sem egress).

Etapa 4. Registrar

R=/dracma
dracma_ops provider register novopsp --root $R
dracma_ops provider certify novopsp --root $R --report relatorio.json
dracma_ops provider list --root $R

A certificação de prd precisa ser do descriptor_sha256 do adaptador instalado em prd. Se o artefato é o mesmo de hml (mesmo commit), o hash coincide.

PROVEDOR    ESTADO      SAÚDE  MODO         EM-VOO(cob/saq)  CERT   DESCRIPTOR
maximaxpay  enabled     ok     em-processo  41/6             97/97  a1b2c3d4e5f6
novopsp     registered  ok     em-processo  0/0              58/58  7be1c0a944a0

CERT mostra passou/total só quando a certificação é do descriptor_sha256 atual (não caso contrário). MODO é remoto quando o executor fala SPP com o adaptador (veja corte para o adaptador).

Go/no-go: estado registered, saúde ok, CERT preenchido. Rollback: dracma_ops provider unplug novopsp --root $R (sem em-voo).

Etapa 5. Habilitar com tráfego dividido

dracma_ops provider enable novopsp --root $R

O enable repete a guarda de ambiente do executor: provedor sintético nunca em prd; provedor real fora de prd só com environment_policy.live_outside_prd: flag e o nome em PSP_REAL_OUTSIDE_PRD (dracma_ops secrets psp-real --env hml --enable --provider novopsp); certificação do descriptor atual. O ingress e o egress são aplicados antes da transição; se ela falha, o ops reaplica o estado real.

O novo provedor passa a enabled, mas ainda não recebe tráfego: não há conexões de tenants apontando para ele. Dois modos de dividir o tráfego, escolha conforme o caso.

Modo Como Quando
Por conexão e prioridade Criar a conexão do tenant no novo provedor com prioridade pior (número maior) que a do antigo; a menor prioridade ganha. O novo só recebe via failover. Segundo provedor de depósito como redundância.
Por tenant e operação (canário) Criar a conexão no novo provedor com prioridade melhor para um tenant piloto e uma direção. Troca de parceiro.

A prioridade vai de 1 a 1000 (menor ganha) e a direção de cada conexão é cash_in, cash_out ou both. Conexões novas são criadas no painel pelo tenant (credencial própria, validada pela checagem de credencial do provedor) ou por quem opera.

Para ter failover de depósito, o executor precisa estar com ele ligado (chave de configuração, desligada por padrão) e haver ao menos dois provedores de depósito habilitados.

Go/no-go: enabled, saúde ok, conexão piloto criada e credencial validada (valid: true). Rollback: dracma_ops provider disable novopsp --root $R (sem em-voo vai direto a disabled).

Etapa 6. Mover o roteamento em canário

Mova em degraus, sempre começando por depósito (cobrança não move dinheiro) e deixando saque por último.

Degrau Escopo Tempo mínimo de observação Operação
C0 Um tenant interno ou piloto, cash_in 24 h Depósitos reais de valor baixo.
C1 5% dos tenants, cash_in 24 h Comparar taxa de sucesso e latência.
C2 25% dos tenants, cash_in 48 h Incluir pico de fim de semana.
C3 100% dos tenants, cash_in 72 h Antigo ainda enabled.
S0 Um tenant piloto, cash_out, valor baixo 48 h Conferir saque até o estado terminal.
S1 25% e depois 100%, cash_out 48 h cada Só com S0 limpo.

Cada degrau muda prioridades de conexões, nunca o núcleo. Saques e cobranças já em voo continuam com o provedor que os originou; só trabalho novo vai para o novo.

Go/no-go de cada degrau: critérios da etapa 7 satisfeitos pelo período mínimo. Rollback de cada degrau: devolver a prioridade melhor para a conexão do provedor antigo (o trabalho novo volta a ele); o que já saiu pelo novo continua sendo acompanhado pelo novo.

Etapa 7. Observar

Olhe os dois provedores lado a lado, pelo rótulo do provedor. Nomes reais do contrato:

Sinal Métrica ou alerta O que esperar
Resultado por chamada dracma_psp_requests_total por psp, method, op, outcome success dominante; business_rejected estável.
Latência dracma_psp_request_duration_seconds por psp, method, op p95 igual ou melhor que o antigo.
Breaker dracma_psp_circuit_state Fechado; qualquer abertura sustentada é motivo de recuo.
Failover dracma_psp_failover (por motivo) Raro; pico indica instabilidade.
Saúde dracma_spp_health por provider ok o tempo todo.
Estado do registro dracma_spp_registry_state por provider e state enabled para o novo; draining só na etapa 9.
Em voo dracma_spp_inflight por provider e kind Sobe no novo, desce no antigo.
Provedor spp_upstream_requests_total, spp_upstream_duration_seconds, spp_idempotent_replays_total, spp_callbacks_verified_total Callbacks verificados com sucesso; replays idempotentes baixos.

Os valores de outcome são: success, business_rejected, circuit_open, timeout, unavailable, unsupported, misconfigured, unauthenticated, unrecognized.

Alertas que devem estar ativos e silenciosos: provedor down, breaker aberto, resposta unrecognized_response, dreno parado. Observe também no painel: conexões em provider_unavailable e cobranças com needs_reconciliation.

Critérios de go/no-go para avançar um degrau:

Critério Go No-go (recuar)
Taxa de success do novo Igual ou melhor que a do antigo na mesma janela Pior de forma sustentada
timeout e unavailable Sem aumento sobre a linha de base Aumento sustentado
unrecognized Zero Qualquer ocorrência sem explicação
Breaker do novo Sempre fechado Abriu e não se estabilizou
Callbacks Todos verificados; nenhum perdido unauthenticated inesperado ou callback sem efeito
Conciliação Cobranças pagas batem com o extrato do parceiro Divergência sem causa
Saques (S0 e S1) Chegam a estado terminal; nenhum unknown preso unknown que não resolve por consulta
needs_reconciliation Nenhum novo Qualquer um novo

Qualquer no-go: execute o rollback do degrau (etapa 6), registre a causa e só retome depois de corrigida.

Etapa 8. Mover o restante

Com C3 e S1 limpos, todo trabalho novo já está no provedor novo. Confira que as conexões de tenants novos nascem apontando para ele e que o provedor antigo deixou de receber trabalho novo.

Go/no-go: dracma_spp_inflight do antigo só decresce. Rollback: voltar prioridades para o antigo (ele continua enabled).

Etapa 9. Drenar o antigo

Só depois de uma janela estável (sugestão: uma semana inteira sem recuo):

dracma_ops provider drain maximaxpay --root $R
dracma_ops provider inflight maximaxpay --root $R
maximaxpay: drain → draining
cobranças pendentes: 4
saques não terminais: 2 (desconhecidos: 0)

Enquanto estiver em draining, o antigo não recebe trabalho novo, mas continua recebendo callbacks e sendo consultado. Acompanhe dracma_spp_inflight até zerar; o estado vira disabled sozinho e o evento provider.drained é emitido.

Go/no-go: em-voo decrescente. Rollback: dracma_ops provider enable maximaxpay --root $R (volta de draining ou disabled) e devolver as prioridades.

Etapa 10. Tratar saques em voo

Saque nunca troca de provedor. Para cada saque em voo no antigo:

Estado do saque O que fazer
processing Esperar; o reconciliador consulta por chave.
unknown Aguardar a consulta; se persistir, consultar o parceiro por canal próprio com a chave de idempotência.
Terminal (liquidado, falho ou devolvido) Nada. Já saiu da contagem.
Preso além do prazo combinado Decisão humana: confirmar com o parceiro e concluir manualmente. Nunca reenviar ao novo provedor.
dracma_ops provider inflight maximaxpay --root $R
cobranças pendentes: 0
saques não terminais: 1 (desconhecidos: 1)

Go/no-go: contador zerado, ou saques restantes com decisão humana documentada. Rollback: voltar a enabled mantém a reconciliação normal.

Etapa 11. Desplugar

Com em-voo zero e o provedor em disabled:

dracma_ops provider disable maximaxpay --root $R     # se ainda estiver em draining
dracma_ops provider unplug maximaxpay --root $R
maximaxpay: unplug → unplugged

O unplug remove as rotas de callback do ingress e o egress do usuário do adaptador (reaplicados pelo ops a partir do registro) e para a unit do adaptador (--keep-running a mantém). Registro, conexões e credenciais cifradas permanecem. Com trabalho ainda em voo, o ops recusa antes de chamar o executor, com o contador e a instrução de drenar. --force --reason "…" (motivo com 10 caracteres ou mais) só em break-glass, com motivo e decisão humana sobre cada saque (ver a última seção). Credenciais dos tenants permanecem cifradas; para encerrá-las de vez, peça a revogação ao parceiro e remova as conexões pelo painel.

Go/no-go: nenhum em-voo. Rollback: re-plugar com dracma_ops provider replug maximaxpay --root $R --enable (liga a unit, espera o /health, registra de novo e habilita se a certificação do descriptor_sha256 atual ainda vale; senão certify antes do enable), o que reativa as conexões provider_unavailable.

Etapa 12. Checklist final

  • [ ] Novo provedor enabled, saudável, certificado para o descriptor_sha256 em uso.
  • [ ] Todo trabalho novo (depósito e saque) roteado para o novo.
  • [ ] Antigo unplugged, sem em-voo; registro e credenciais preservados.
  • [ ] Nenhum alerta aberto; dracma_psp_circuit_state fechado.
  • [ ] Conciliação do período da troca bate com os extratos dos dois parceiros.
  • [ ] Nenhum saque em needs_reconciliation sem decisão.
  • [ ] Callbacks do novo parceiro configurados no painel dele (URL de callback e IPs de origem conferidos).
  • [ ] Auditoria da mudança anexada: saídas de list e inflight, certificação, decisões de go/no-go.
  • [ ] ADR atualizada e documentação interna revisada.

Rollback por etapa

Etapa Como desfazer
2 Desplugar em hml.
3 Nada registrado: não registrar.
4 dracma_ops provider unplug novopsp --root $R (sem em-voo).
5 dracma_ops provider disable novopsp --root $R.
6 e 8 Devolver prioridade melhor às conexões do antigo; o novo segue acompanhando o que já recebeu.
9 dracma_ops provider enable maximaxpay --root $R e devolver prioridades.
10 Voltar a enabled; a reconciliação normal continua.
11 Re-plugar o antigo e habilitar.

Corte para o adaptador (cutover)

Enquanto o provedor ainda roda dentro do executor (adaptador em-processo), o corte para o adaptador SPP é um interruptor por provedor, reversível, que não muda comportamento de negócio:

R=/dracma-hml
dracma_ops provider cutover maximaxpay --to remote --root $R       # EXECUTOR_PROVIDER_URLS=maximaxpay=http://127.0.0.1:<porta>/spp/v1
dracma_ops provider list --root $R                                 # MODO passa a "remoto"
dracma_ops provider cutover maximaxpay --to in-process --root $R   # volta

O cutover exige o /health do adaptador e o par HMAC no seeder; grava EXECUTOR_PROVIDER_URLS (nunca à mão), aplica o egress antes (o adaptador passa a alcançar o parceiro; o executor perde esse egress), refaz o deploy destrutivo só do executor (install --only executor) e ressincroniza. Se o redeploy falha, o ops restaura o seeder e a rede.

Parceiro que rompe de repente

O parceiro encerrou, foi suspenso, bloqueou a conta ou simplesmente está fora do ar. Nada disso exige deploy do núcleo, e o sistema já faz o seguinte sozinho: persiste, loga com trace_id, alerta, abre o breaker e (depósito, se ligado) faz failover. Saque nunca troca de provedor.

Primeiros 15 minutos

  1. Confirmar que não é falha nossa: dracma_ops provider list --root $R (saúde do adaptador), dracma_ops logs <serviço> (unit ativa), dracma_ops provider sync --root $R (reaplica ingress/egress), e se o parceiro responde por canal independente.
  2. Declarar incidente e abrir canal; registrar o horário.
  3. Parar o sangramento de depósito: se há outro provedor de depósito habilitado, o failover já desvia. Se o failover está desligado, mova prioridades das conexões para o provedor alternativo. Se não há alternativo, o depósito responde indisponível ao jogador: é o comportamento correto.
  4. Não reenviar saque. Saque em andamento fica unknown e será consultado; reenviar pode pagar duas vezes.
  5. Drenar para fechar a entrada de trabalho novo:
dracma_ops provider drain maximaxpay --root $R
dracma_ops provider inflight maximaxpay --root $R
  1. Fotografar o em-voo: guardar a saída de inflight e a lista de saques unknown com horário.

Primeiras 24 horas

  1. Se o parceiro voltou: acompanhar o breaker fechar, a saúde reabilitar e o reconciliador resolver os unknown; reabrir com dracma_ops provider enable maximaxpay --root $R se foi drenado à toa.
  2. Se o parceiro não volta: iniciar a troca de parceiro (etapas 1 a 6) com o plano de contingência mais rápido (canário comprimido, mas com os mesmos portões). Priorize depósito.
  3. Falar com o parceiro por todos os canais comerciais para obter: extrato dos saques do período, status de cada chave de idempotência e prazo de devolução de saldo retido.
  4. Comunicar tenants afetados (conexões ficam provider_unavailable quando o provedor está disabled ou unplugged) com a estimativa de retomada ou a alternativa.
  5. Revisar alertas: dreno parado, saques unknown antigos, needs_reconciliation.

Saques desconhecidos

Saque unknown é aquele cujo resultado no parceiro não se sabe. Tratamento:

  1. O reconciliador consulta por chave de idempotência assim que o provedor responde. Se o parceiro só conhece o saque pelo painel, consulte lá.
  2. O resultado da consulta decide: liquidado, falho definitivo, devolvido ou ainda em processamento. not_found não é definitivo por padrão (silêncio do parceiro não prova que não houve saque); só vale como definitivo se o descriptor do provedor declarar que o parceiro responde assim de forma conclusiva.
  3. Se o parceiro encerrou e não responde: o saque fica needs_reconciliation. Decisão humana, com evidência (extrato do banco, comprovante, confirmação do parceiro), e nunca reenvio automático.
  4. Registre cada decisão com quem, quando e a evidência.

Conciliação

Depois da estabilização:

  1. Baixe os extratos do parceiro e do banco para o período da falha.
  2. Compare com os depósitos e saques do executor: pagos, expirados, ambiguous e needs_reconciliation.
  3. Pagamentos que chegaram por tentativa antiga após failover (ambiguous) são aceitos e marcados needs_reconciliation: decida cada um (creditar, estornar, aguardar).
  4. Ajustes de ledger seguem o fluxo normal da área financeira; o provedor nunca altera saldo.
  5. Registre o resultado e anexe ao postmortem.

Se o parceiro encerrou de vez

  1. Drenar (já feito), tratar saques (acima) e desplugar com --force somente se restar trabalho sem solução, com motivo auditado:
dracma_ops provider unplug maximaxpay --root $R --force --reason "parceiro encerrou operação; saques restantes em decisão humana; incidente 4471"
  1. Cobranças pendentes viram expired; saques em voo ficam needs_reconciliation com o motivo provider_unplugged.
  2. Credenciais e conexões permanecem cifradas e preservadas; revogue o que puder no parceiro.
  3. Conclua a entrada do novo parceiro pelas etapas 1 a 12.

Erros comuns

Erro Efeito Como evitar
Habilitar e achar que já há tráfego Nada muda; o roteamento é por conexão. Criar conexões e ajustar prioridades.
Mover saque junto com depósito Risco de saque ambíguo em dois provedores. Depósito primeiro, saque depois e em degraus.
Reenviar saque unknown ao novo provedor Pagamento em duplicidade. Nunca. Consultar por chave e decidir humanamente.
Desplugar o antigo cedo demais Cobranças expiram e saques ficam em decisão manual. Drenar e esperar o contador zerar.
Certificar em hml e habilitar em prd com adaptador diferente O descriptor_sha256 não coincide e o enable é recusado. Promover o mesmo artefato do git.
Instalar em série para contornar colisão Mascara falta de independência. Corrigir a colisão.
Falha de failover por estar desligado Depósitos falham em vez de trocar. Ligar o failover antes da troca, com dois provedores habilitados.
Usar credencial de produção em hml Efeito real fora de produção. Sandbox em hml; a exceção exige a chave de política do ops e auditoria.

Próximos passos

  • Estados, drenagem e API de registro: ciclo de vida do provedor.
  • Depois da troca, arquive o resultado (checklist da etapa 12) e atualize a documentação do parceiro novo.