#!/usr/bin/env python3 """Exemplo executável — Python (>=3.9). Usa as CLASSES REAIS do SDK ``foxnfe`` 1.3.0 e substitui APENAS o transporte HTTP (adapter da sessão ``requests``) por um mock: nenhuma chamada de rede, SMTP ou emissão. Pré-requisito: pip install https://docs.foxnfe.com.br/downloads/foxnfe-1.3.0-py3-none-any.whl Rode: python3 consumer.py Saída de sucesso: PYTHON_SDK_MOCK_OK ... (exit 0) """ from __future__ import annotations import hashlib import hmac import json from urllib.parse import urlparse from requests import Response from requests.adapters import BaseAdapter from foxnfe import Client # classe real do SDK # /auth/me devolve uma estrutura aninhada, exatamente como a API entrega. FIXTURES = { "/api/v1/auth/me": { "user": {"id": 1, "name": "Integração", "email": "dev@example.com", "tenant_id": 7}, "tenant": {"id": 7, "slug": "minha-empresa", "name": "Minha Empresa", "status": "active"}, "subscription": None, "abilities": [], }, "/api/v1/webhooks/config": {"configured": True, "active": True, "secret_preview": "••••••••lue-1234"}, } # ── 1. Mock só do transporte: um adapter que devolve fixtures ───────────── class FixtureTransport(BaseAdapter): external_requests = 0 def send(self, request, **kwargs): # noqa: D401 path = urlparse(request.url).path if urlparse(request.url).hostname != "sdk.invalid": FixtureTransport.external_requests += 1 if path not in FIXTURES: raise KeyError(f"rota não simulada: {path}") resp = Response() resp.status_code = 200 resp._content = json.dumps(FIXTURES[path]).encode() resp.headers["Content-Type"] = "application/json" resp.url = request.url resp.request = request return resp def close(self): pass # baseUrl inerte (sdk.invalid) garante que qualquer chamada real falharia. client = Client(tenant_slug="minha-empresa", token="token-de-teste-local", base_url="https://sdk.invalid/api/v1") client._session.mount("https://", FixtureTransport()) me = client.me() # exercita Client.me() real assert me["tenant"]["slug"] == "minha-empresa" # estrutura REAL: me["tenant"]["slug"] assert me["user"]["tenant_id"] == 7 # me["user"]["tenant_id"] cfg = client.webhook.get_config() # exercita Webhook.get_config() real assert cfg["configured"] is True assert "webhook_secret" not in cfg, "a API nunca devolve o segredo completo" # ── 2. Verificação de assinatura HMAC sobre o corpo canônico ────────────── def canonical_json(payload: dict) -> bytes: # sort_keys recursivo; sem espaços; ensure_ascii=False == JSON_UNESCAPED_UNICODE. return json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") def verify(raw_body: bytes, header_value: str, shared_secret: bytes) -> bool: expected = "sha256=" + hmac.new(shared_secret, raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header_value) # tempo constante secret = b"whsec_exemplo_apenas_para_teste_local_1234567890" envelope = { "specversion": "1.0", "id": "550e8400-e29b-41d4-a716-446655440000", "source": "https://foxnfe.com.br", "type": "br.com.centralfox.foxnfe.nfe.authorized", "datacontenttype": "application/json", "time": "2026-09-05T12:00:00-03:00", "data": {"nfe_id": 123, "status": "authorized"}, } body = canonical_json(envelope) signature = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest() tampered = canonical_json({**envelope, "data": {"nfe_id": 999, "status": "authorized"}}) assert verify(body, signature, secret) is True, "assinatura válida deveria passar" assert verify(tampered, signature, secret) is False, "corpo adulterado deveria falhar" assert verify(body, signature, b"segredo-errado") is False, "segredo errado deveria falhar" assert FixtureTransport.external_requests == 0, "nenhuma chamada pode escapar ao mock" print(f"PYTHON_SDK_MOCK_OK sdk=foxnfe@1.3.0 config={cfg['configured']} " f"tenant={me['tenant']['slug']} hmac_valid=pass hmac_tampered=reject external_requests=0")