Certificação de provedores¶
Para: Dev · Adm — Conferido em 2026-10-31.
Nenhum provedor entra em hml ou produção sem passar pelo kit de conformidade (dracma_spp_conformance). Esta página mostra o que ele prova, como montar o fake do parceiro, como rodar e o que precisa estar marcado no checklist antes de pedir o enable.
O que é¶
A suíte fala o protocolo SPP v1 pelo loopback, como se fosse o executor (assina HMAC v2, envia a credencial no corpo), contra o seu adaptador, que por sua vez conversa com um parceiro falso (fake-PSP) controlado pela suíte. Ela prova comportamento, não implementação: serve para qualquer linguagem.
Regras fixas:
- Sem pular teste. Não existe
skip. Se a capacidade éfalseno descriptor, o caso vira "a rota responde501 unsupported". - Sem efeito externo real. O adaptador roda em modo
sandbox, apontando só para o fake. Nada sai para o parceiro de verdade. - Mesma semântica de hoje. Cada caso preserva uma regra que o executor já consome (breaker, failover, reconciliador). Um adaptador que passa na suíte pode substituir os atuais sem mudar comportamento.
Quando usar¶
- Ao terminar um adaptador novo (antes do primeiro
registerem hml). - A cada mudança do descriptor ou de comportamento do adaptador (recertificação).
- No CI do repositório do provedor, a cada PR.
Como funciona¶
| Componente | Quem escreve | O que faz |
|---|---|---|
runner |
kit | Lê o descriptor, escolhe os casos aplicáveis pelas capacidades, executa, grava o relatório com o descriptor_sha256. |
| Cliente SPP | kit | Age como o executor: assina, gera Spp-Idempotency-Key, Spp-Timeout-Ms e traceparent; relógio e nonce injetáveis. |
| Fake-PSP | você | Servidor HTTP que fala o formato REAL do parceiro, dirigido por cenário. Registra tudo que recebe. |
Perfil (profile.yaml) |
você | Diz, por operação SPP, qual requisição o parceiro deve receber e qual resposta devolver em cada cenário. |
Cenários canônicos do fake¶
O perfil precisa mapear estes cenários para respostas reais do parceiro (os que não se aplicam ao parceiro são declarados n/a com justificativa, nunca omitidos):
| Cenário | Significado |
|---|---|
ok, ok_replay |
Sucesso; repetição com o mesmo identificador (o parceiro deduplica). |
slow, drop_after_send |
Responde depois do orçamento; fecha a conexão depois de receber o corpo. |
http_500, http_502, http_503 |
Falha de servidor do parceiro. |
http_400, http_401, http_403, http_404, http_409, http_429 |
Recusa de negócio do parceiro. |
garbage_2xx, empty_2xx, huge_body |
200 com corpo irreconhecível, vazio ou gigante. |
unknown_status |
Status de cobrança ou saque que o adaptador não conhece. |
redirect_other_host |
302 para um host fora do egress_hosts. |
Exemplo de perfil¶
provider: exemplo
operations:
create_charge:
upstream: { method: POST, path: /v1/pix/charges, must_contain: [identifier, amount] }
scenarios:
ok: { status: 201, body: { id: "ch_1", brcode: "000201...", expires_at: "2026-11-01T12:00:00Z" } }
http_400: { status: 400, body: { error: "invalid_amount" } }
http_503: { status: 503, body: "" }
garbage_2xx: { status: 200, body: "<html>ok</html>" }
get_payout:
upstream: { method: GET, path: /v1/pix/transfers, query: [clientIdentifier] }
scenarios:
ok: { status: 200, body: { status: "PAID", id: "tr_1", e2e: "E2E123", amount: "10.50" } }
http_404: { status: 404, body: { error: "not_found" } }
Rodar¶
spp-conformance run \
--url http://127.0.0.1:7101/spp/v1 \
--hmac-secret-env INTERNAL_HMAC_EXEMPLO_EXECUTOR \
--fake-profile conformance/profile.yaml \
--out relatorio.json
Código de saída diferente de zero se qualquer caso obrigatório falhar. O relatório traz descriptor_sha256, versão da suíte, casos com evidência e o resumo passed/total. Depois, o ops grava a certificação no registro:
dracma_ops provider certify exemplo --report relatorio.json
O que a suíte cobre¶
| Família | Casos | Obrigatória quando |
|---|---|---|
| Descriptor e metadados | C-DESC-01 a C-DESC-12 |
sempre |
| Autenticação e transporte | C-AUTH-01 a C-AUTH-15 (inclui a matriz de acesso: uma linha por rota servida) |
sempre |
| Cobrança | C-CHG-01 a C-CHG-17 |
cash_in |
| Consulta de cobrança | C-GET-01 a C-GET-08 |
charge_lookup |
| Resolução de referência | C-REF-01 a C-REF-04 |
charge_ref_resolution |
| Saque | C-PAY-01 a C-PAY-15 |
cash_out |
| Consulta de saque | C-PGET-01 a C-PGET-10 |
cash_out |
| Callbacks | C-CB-01 a C-CB-16 |
callbacks |
| DICT, saldo, credencial | C-KEY-*, C-BAL-*, C-CRED-* |
key_lookup, balance, credential_check |
| Saúde | C-HEALTH-01 a C-HEALTH-05 |
sempre |
| Guarda de ambiente | C-ENV-01 a C-ENV-07 |
sempre |
| Mapeamento de erros | matriz por operação (abaixo) | sempre, nas operações declaradas |
| Observabilidade | C-OBS-01 a C-OBS-04 |
sempre (C-OBS-04 é recomendado) |
| Segurança do processo | C-SEC-01 a C-SEC-07 |
sempre |
| Cripto | C-CRY-01 a C-CRY-09 |
método crypto |
| Caos | C-CHAOS-01 a C-CHAOS-13 |
sempre (C-CHAOS-13 só cripto) |
Matriz de erros (a mais importante)¶
Para toda operação de rede declarada, a suíte injeta cada cenário e confere a resposta exata. É esta tabela que garante que o breaker e o failover do executor continuam corretos:
| Cenário do fake | HTTP SPP | code |
effect |
Vira no executor | Retry interno? |
|---|---|---|---|---|---|
slow, drop_after_send |
504 | upstream_timeout |
unknown |
Timeout |
não |
http_500/502/503 |
502 ou 503 | upstream_unavailable |
unknown |
Unavailable(status) |
não |
http_400/401/403/404/409/429 |
422 | upstream_rejected |
none |
Rejected(status) |
não |
garbage_2xx, empty_2xx, huge_body |
502 | unrecognized_response |
unknown |
Unrecognized |
não |
redirect_other_host |
502 ou 422 | upstream_unavailable ou misconfigured |
unknown ou none |
— | não segue |
Em consultas, o 404 do recurso segue a regra da operação: saque desconhecido é resposta not_found (não erro), cobrança sem registro volta pending ou unrecognized, referência de outro sistema volta deposit_id: null, chave DICT inexistente é key_not_found.
Caos¶
| Injeção | O que a suíte exige |
|---|---|
| Rajada de timeouts | Todas as respostas dentro do orçamento, memória estável, health vivo. |
| 5xx intermitente | Cada resposta reflete exatamente o que o fake devolveu; replay continua válido. |
| Parceiro some durante um saque | unknown; ao voltar, a consulta pela mesma chave devolve o estado real; nenhum segundo envio. |
| Callbacks malformados (fuzz de 1000 corpos) | Nunca 5xx nem queda; sempre callback_unauthenticated ou invalid_request; p99 de 200 ms. |
| Replay de callback e de chamada SPP | Idempotente / replayed_nonce; sem crescimento de memória. |
| Corpo gigante ou lento | Corta por tempo e tamanho sem travar chamadas legítimas. |
Reinício com kill -9 em carga |
Volta em 10 s sem dupla execução. |
| Credencial rotacionada no meio da rajada | Cada chamada usa a credencial do próprio corpo. |
Suíte do executor¶
Além da suíte do autor, o executor roda X-01 a X-10 em hml com o provedor registrado: breaker por provedor, failover de depósito, saque que nunca troca de provedor, drenagem, unplug com trabalho em voo, callbacks (401 uniforme e mesma latência para conexão desconhecida), guarda de ambiente, teste diferencial contra o adaptador em-processo, mudança de descriptor e a prova de que o núcleo não tem lista de provedores.
Checklist de certificação¶
Marque tudo antes de pedir enable em hml ou prd.
Contrato
- [ ]
GET /descriptorválido;nameimutável;egress_hostscompleto e mínimo. - [ ] Toda capacidade
trueimplementada; todafalseresponde501 unsupported. - [ ] Segredos do
credential_schemacomx-secret; rótulos e ajuda em português. - [ ] Rotas servidas iguais ao OpenAPI do SPP (diff vazio).
Comportamento
- [ ] Suíte 100% nos casos obrigatórios, relatório anexado, nenhum
skip. - [ ] Caos 100%: sem queda, vazamento nem dupla execução.
- [ ] Idempotência provada para cobrança e saque.
- [ ] Matriz de erros inteira verde; nenhum erro ambíguo virou definitivo.
- [ ] Callback sobre os bytes crus, comparação em tempo constante, isca, fuzz.
Segurança e operação
- [ ] Usuário Linux próprio, unit hardened, container não-root somente leitura, egress só do descriptor, bind em loopback.
- [ ] Nenhum segredo de longa duração; logs sem credencial nem dado pessoal.
- [ ] SBOM e scan sem vulnerabilidade crítica ou alta aberta.
- [ ] Matriz de acesso no CI do provedor e pentest de entrada limpo.
- [ ] Guarda de ambiente provada; nenhum efeito externo real fora de produção.
Observabilidade e documentação
- [ ] Logs JSON com
trace_id; métricas e traces propagados; dashboard e alertas como código. - [ ] README do provedor com parceiro, credenciais, limites, particularidades e runbook de falha do parceiro.
Integração
- [ ] Suíte do executor (
X-01aX-10) verde em hml. - [ ] Certificação gravada para o
descriptor_sha256atual. - [ ] Revisão de segurança por quem não escreveu o adaptador.
Aprovação, validade e recertificação¶
- Passa com 100% dos casos obrigatórios aplicáveis, zero
skip. Casos recomendados aparecem no relatório e não bloqueiam. - Reprova com qualquer falha. A suíte nunca é editada para passar: corrige-se o adaptador. Se o contrato é que está errado, abre-se emenda ao protocolo.
- Caso intermitente reprova e abre defeito; "repetir até passar" não vale.
- A certificação vale para o
descriptor_sha256e a versão da suíte certificados. Mudou qualquer campo do descriptor, ou o comportamento do adaptador: recertifica. - Suíte nova de versão menor: provedores já habilitados têm 30 dias para recertificar e não são desabilitados.
- Se o parceiro mudou a API (sinal:
unrecognized_responseem 1% ou mais das chamadas), recertifica depois do ajuste. - A certificação não substitui o monitoramento: breaker e alertas continuam valendo em produção.
Erros comuns¶
| Sintoma | Causa provável |
|---|---|
C-CHG-02 falha |
O adaptador cria uma segunda cobrança no parceiro: repasse a chave como identificador do parceiro ou grave a chave localmente. |
C-PAY-04 falha com 2 chamadas no fake |
O adaptador reenvia o saque sozinho depois de timeout. Remova o retry: o executor decide. |
C-CB-07 falha |
O adaptador reserializa o JSON antes de conferir a assinatura. Use os bytes de body_base64. |
C-BAL-03 falha |
Saldo desconhecido virou zero. Responda unrecognized_response. |
C-SEC-01 falha |
A credencial foi parar em log, cache em disco ou resposta de erro. |
C-AUTH-11 falha |
Há rota servida que não está no OpenAPI, ou rota sem checagem de chamador. |