// Certa Fiscal — exemplo de integração em Java 17+ (java.net.http, sem dependências). // // Compilar/rodar: javac CertaFiscalExemplo.java // java CertaFiscalExemplo cf_test_SUACHAVE ULID_DA_EMPRESA // // Demonstra: Bearer, Idempotency-Key (retry nunca duplica), X-Correlation-Id, // retry com backoff p/ 429/5xx e leitura dos achados. // // Observação: para manter o exemplo sem dependências, a leitura do JSON usa // expressões regulares suficientes para o formato da resposta. Em produção, // use Jackson/Gson e mapeie o schema completo (docs/openapi.yaml). import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class CertaFiscalExemplo { private static final String BASE_URL = "http://127.0.0.1:8000/api/v1"; // produção: https://.../api/v1 public static void main(String[] args) throws Exception { if (args.length < 2) { System.err.println("Uso: java CertaFiscalExemplo "); System.exit(2); } final String apiKey = args[0]; final String empresaId = args[1]; HttpClient http = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) .build(); // 1) Health-check Resposta saude = chamar(http, "GET", "/health", null, apiKey, ""); if (saude.status != 200) { System.err.println("API indisponível (HTTP " + saude.status + ")"); System.exit(3); } System.out.println("Health-check: OK"); // 2) Analisar operação — no ERP, use o ID do teu registro como chave. String idempotencyKey = "pedido-" + UUID.randomUUID(); String corpo = """ {"company_id":"%s","origin_uf":"RS","destination_uf":"SC", "ncm":"22021000","cest":"0301100","cfop":"6102","csosn":"102", "value":1500.00,"product_description":"Refrigerante Cola 2 L (exemplo Java)"} """.formatted(empresaId); Resposta r = chamar(http, "POST", "/analyses/operation", corpo, apiKey, idempotencyKey); if (r.status != 200 && r.status != 201) { System.err.println("Erro HTTP " + r.status + ": " + r.corpo); System.exit(1); } System.out.printf("Análise %s | risco=%s | pontuação=%s | motor=%s%n", extrair(r.corpo, "reference"), extrair(r.corpo, "risk_level"), extrair(r.corpo, "score"), extrair(r.corpo, "engine_version")); boolean bloquear = false; Matcher achados = Pattern.compile( "\\{[^{}]*\"rule_code\"\\s*:\\s*\"([^\"]+)\"[^{}]*\"severity\"\\s*:\\s*\"([^\"]+)\"" + "[^{}]*\"title\"\\s*:\\s*\"([^\"]+)\"[^{}]*\"confidence\"\\s*:\\s*(\\d+)[^{}]*\\}") .matcher(r.corpo); while (achados.find()) { System.out.printf(" [%s %s] %s (confiança %s%%)%n", achados.group(2), achados.group(1), achados.group(3), achados.group(4)); if (achados.group(2).equals("high") || achados.group(2).equals("critical")) { bloquear = true; } } System.out.println(bloquear ? "VEREDITO: NÃO emitir — corrigir os achados primeiro." : "VEREDITO: liberado para emitir."); } private record Resposta(int status, String corpo) {} private static Resposta chamar(HttpClient http, String metodo, String caminho, String corpoJson, String apiKey, String idempotencyKey) throws InterruptedException { for (int tentativa = 1; ; tentativa++) { HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(BASE_URL + caminho)) .timeout(Duration.ofSeconds(30)) .header("Accept", "application/json") .header("Authorization", "Bearer " + apiKey) .header("X-Correlation-Id", UUID.randomUUID().toString()); if (!idempotencyKey.isEmpty()) builder.header("Idempotency-Key", idempotencyKey); if (corpoJson != null) { builder.header("Content-Type", "application/json") .method(metodo, HttpRequest.BodyPublishers.ofString(corpoJson)); } else { builder.method(metodo, HttpRequest.BodyPublishers.noBody()); } int status; String corpo = ""; try { HttpResponse resp = http.send(builder.build(), HttpResponse.BodyHandlers.ofString()); status = resp.statusCode(); corpo = resp.body(); } catch (Exception e) { // falha de rede/timeout status = 0; } // 401/403/422 = configuração/dados: não repetir. boolean repetir = (status == 0 || status == 429 || status >= 500) && tentativa < 4; if (!repetir) return new Resposta(status, corpo); long esperaMs = 1000L << (tentativa - 1); // 1s, 2s, 4s — MESMA chave System.err.println("HTTP " + status + " — repetindo em " + (esperaMs / 1000) + "s..."); Thread.sleep(esperaMs); } } private static String extrair(String json, String campo) { Matcher m = Pattern.compile("\"" + campo + "\"\\s*:\\s*\"?([^,\"}]+)").matcher(json); return m.find() ? m.group(1) : "?"; } }