ENGENHARIA DE SOFTWARE

Integração de APIs Logísticas em Sistemas TMS e ERP: Guia de Arquitetura, Idempotência e Resiliência em Alta Escala

Conectar motores de cálculo de frete, roteirização com restrições e auditoria de pedágios a sistemas corporativos como SAP, TOTVS e Senior exige rigor de engenharia. Descubra como projetar integrações resilientes com cabeçalhos de idempotência determinística, tratamento de erros RFC 7807 e webhooks criptografados.

Resumo Técnico para CTOs, Arquitetos e Tech Leads

Operações de frete de alta cadência emitem milhares de Conhecimentos de Transporte (CT-e) por hora. Falhas de timeout de rede, requisições duplicadas que cobram créditos em dobro e payloads sem validação de schema geram gargalos em cascata entre o ERP e a pista. Este guia técnico apresenta as melhores práticas de arquitetura distribuída, detalha o ciclo de vida de requisições idempotentes e fornece implementações de referência em cURL, TypeScript e Python.

1. O Cenário de Integração Logística Corporativa

A espinha dorsal das operações de transporte rodoviário de cargas no Brasil reside em sistemas legados e ERPs robustos, como SAP S/4HANA, TOTVS Protheus / Logix, Senior Sapiens e plataformas especialistas de TMS (Transportation Management System) como Oracle OTM, SSW e Benner.

Tradicionalmente, a comunicação com provedores externos de mapas ou cálculo de pedágio era estruturada em arquivos batch transferidos via FTP/SFTP durante as madrugadas, ou em chamadas RPC/SOAP síncronas com acoplamento rígido. No ecossistema moderno, a necessidade de emitir MDF-e e Vale-Pedágio Obrigatório (VPO) em tempo real na saída do caminhão exige uma transição para microsserviços RESTful com contratos OpenAPI 3.1 estritos, baixa latência (P95 < 250ms) e disponibilidade de missão crítica (SLA de 99.9%).

2. Padrões Arquiteturais Fundamentais para APIs de Transporte

2.1 Autenticação Segura com API Keys e Segregação de Escopos (RBAC)

O modelo de autorização corporativo da Geodagio utiliza Bearer API Keys criptografadas. As chaves públicas expostas pelo cliente possuem o formato padronizado gdg_live_xxxxxxxxxxxxxxxx ou gdg_test_xxxxxxxxxxxxxxxx.

No banco de dados do Geodagio, as chaves nunca são armazenadas em texto claro. O sistema gera um Lookup Hash determinístico utilizando HMAC-SHA-256 com um segredo criptográfico dedicado (pepper), impedindo o vazamento de credenciais mesmo em caso de dump físico de banco de dados. Adicionalmente, cada chave possui escopos mínimos concedidos (princípio do menor privilégio):

  • routes:read: Permissão para roteirização e cálculo geométrico de distâncias.
  • tolls:calculate: Permissão para cruzamento geoespacial de praças e apuração de tarifas de pedágio.
  • antt:compliance: Acesso às APIs de Piso Mínimo de Frete e consulta de situação cadastral no RNTRC.
  • billing:read: Consulta de consumo de créditos e faturas da organização.

2.2 Idempotência Determinística com o Cabeçalho Idempotency-Key

O maior pesadelo em integrações distribuídas é a incerteza de falhas na camada de rede. Considere o cenário: o ERP do embarcador envia uma requisição para emissão de cálculo de rota com débito de crédito. O servidor Geodagio processa a transação em 120ms, grava o histórico e debita os créditos. No entanto, um pico de perda de pacotes na rota de internet do cliente derruba a conexão TCP antes que os bytes da resposta HTTP cheguem ao ERP.

Se o ERP reexecutar cegamente a chamada sem controle de idempotência, a operação será executada uma segunda vez, consumindo créditos desnecessários e gerando dois registros conflitantes.

Para anular esse risco, a Geodagio implementa suporte nativo ao cabeçalho padrão Idempotency-Key: <UUIDv4>:

Máquina de Estados e Ciclo de Vida da Idempotência (Redis / Postgres)
1. ENVIO DA REQUISIÇÃO ERP envia POST /routes Idempotency-Key: uuid-v4 Payload com hash de corpo 2. MOTOR DE IDEMPOTÊNCIA Consulta chave no cache atômico Se existe e terminou: retorna cache Se em processamento: aguarda lock 3. PROCESSAMENTO CORE Executa PostGIS e Tarifas Debita créditos em transação Grava resultado final (TTL 24h) CENÁRIO DE RECONEXÃO / RETRY DO ERP Se a mesma Idempotency-Key chegar novamente, a API responde imediatamente com o payload original Header retornado: Idempotent-Replayed: true (Zero consumo adicional de créditos e latência < 15ms)

Caso o cliente reenvie a mesma chave de idempotência com um corpo (payload) modificado, a API rejeita preventivamente com o status 409 Conflict (código canônico IDEMPOTENCY_PAYLOAD_MISMATCH), protegendo contra colisão acidental de identificadores.

3. Respostas de Erro Estruturadas com RFC 7807 (Problem Details)

APIs públicas amadoras retornam strings simples de erro como {"error": "bad request"} ou mensagens de exceção SQL não tratadas. Na infraestrutura enterprise da Geodagio, todos os erros HTTP 4xx e 5xx cumprem a especificação RFC 7807 (Problem Details for HTTP APIs) sob o header Content-Type: application/problem+json:

{
  "type": "https://api.geodagio.com/errors/invalid-route-payload",
  "title": "Invalid Request Parameters",
  "status": 400,
  "code": "INVALID_COORDINATES",
  "detail": "O ponto de origem informado [lat: -95.0, lng: -46.6] possui latitude fora do intervalo geodésico válido [-90.0, 90.0].",
  "instance": "/api/v1/routes/calculate",
  "requestId": "req_01j7b9k6g7e1x8m4n2p9q0r1st",
  "invalidParams": [
    {
      "name": "origin.lat",
      "reason": "Value must be greater than or equal to -90.0",
      "received": -95.0
    }
  ]
}
Status HTTP Código Canônico Significado Operacional Ação Recomendada no TMS / ERP
400 Bad Request INVALID_PAYLOAD Payload JSON malformado ou campos obrigatórios ausentes. Corrigir mapeamento dos campos antes de nova tentativa.
401 Unauthorized INVALID_API_KEY API Key ausente, expirada ou com HMAC não localizado. Validar segredos cadastrados no cofre de senhas do ERP.
403 Forbidden INSUFFICIENT_PERMISSIONS Chave válida, mas sem o escopo exigido pelo endpoint. Habilitar o escopo no console administrativo da organização.
409 Conflict IDEMPOTENCY_CONFLICT Reenvio de chave de idempotência com corpo divergente. Gerar um novo UUIDv4 para uma operação genuinamente nova.
422 Unprocessable UNRESOLVABLE_ROUTE Coordenadas válidas no globo, mas ilhadas sem malha rodoviária viável. Validar se o ponto não recaiu sobre oceano, ilha ou floresta densa.
429 Too Many Req. RATE_LIMIT_EXCEEDED Consumo momentâneo superior ao teto contratual de requisições por segundo. Efetuar backoff exponencial respeitando o cabeçalho Retry-After.
503 Service Unav. ANTT_UNAVAILABLE Indisponibilidade temporária de base externa oficial não cacheada. Acionar contingência assíncrona ou retentativa com jitter.

4. Exemplos Prontos de Integração em Produção

4.1 Chamada de Produção com cURL e Headers Corporativos

curl -X POST "https://api.geodagio.com/api/v1/routes/calculate" \
  -H "Authorization: Bearer gdg_live_8f7b2c1e9a0d3f4b5c6e7a8b9c0d1e2f" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: c9b8a7d6-e5f4-4a3b-8c2d-1e0f9a8b7c6d" \
  -H "X-Client-System: SAP-S4HANA-v2023" \
  -d '{
    "origin": { "lat": -23.55052, "lng": -46.633308 },
    "destination": { "lat": -22.906847, "lng": -43.172896 },
    "vehicle": {
      "category": "TRUCK_5AXIS",
      "axles": 5,
      "suspendedAxles": 0
    },
    "options": {
      "avoidUnpaved": true,
      "calculateTolls": true,
      "includeAnttMinFreight": true
    }
  }'

4.2 Integração Resiliente em TypeScript / Node.js (com Retry e Idempotência)

import axios, { AxiosError } from 'axios';
import { randomUUID } from 'crypto';

interface RouteRequest {
  origin: { lat: number; lng: number };
  destination: { lat: number; lng: number };
  vehicle: { category: string; axles: number };
}

interface GeodagioRouteResponse {
  routeId: string;
  distanceKm: number;
  durationMinutes: number;
  tolls: {
    total: number;
    plazasCount: number;
    coverageStatus: string;
  };
}

const GEODAGIO_API_URL = 'https://api.geodagio.com/api/v1';
const API_KEY = process.env.GEODAGIO_API_KEY!;

export async function calculateRouteWithResilience(
  payload: RouteRequest,
  maxRetries = 3
): Promise {
  const idempotencyKey = randomUUID(); // Chave única para a transação

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await axios.post<GeodagioRouteResponse>(
        `${GEODAGIO_API_URL}/routes/calculate`,
        payload,
        {
          headers: {
            'Authorization': `Bearer ${API_KEY}`,
            'Content-Type': 'application/json',
            'Idempotency-Key': idempotencyKey,
            'User-Agent': 'TMS-Enterprise-Client/2.4'
          },
          timeout: 8000 // 8 segundos de timeout estrito
        }
      );
      return response.data;
    } catch (err) {
      const error = err as AxiosError;
      const status = error.response?.status;

      // Não repetir em caso de erros de validação do cliente (4xx)
      if (status && status >= 400 && status < 500 && status !== 429) {
        throw new Error(`Erro de validação (${status}): ${JSON.stringify(error.response?.data)}`);
      }

      // Se for a última tentativa, falhar
      if (attempt === maxRetries) {
        throw new Error(`Falha definitiva após ${maxRetries} tentativas: ${error.message}`);
      }

      // Backoff exponencial com jitter (aleatoriedade para evitar efeito manada)
      const baseDelay = Math.pow(2, attempt) * 500;
      const jitter = Math.random() * 200;
      const delayMs = baseDelay + jitter;

      console.warn(`Tentativa ${attempt} falhou. Aguardando ${Math.round(delayMs)}ms antes do retry...`);
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }

  throw new Error('Falha inesperada no fluxo de retentativas.');
}

4.3 Validação de Assinatura Criptográfica de Webhooks em Python

import hmac
import hashlib
import json
from flask import Flask, request, jsonify, abort

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_9d8c7b6a5e4f3d2c1b0a9f8e7d6c5b4a"

def verify_geodagio_signature(raw_body: bytes, received_signature: str, secret: str) -> bool:
    """Valida o HMAC SHA-256 em tempo constante contra ataques de timing."""
    if not received_signature:
        return False
    
    expected_signature = hmac.new(
        key=secret.encode('utf-8'),
        msg=raw_body,
        digestmod=hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(f"sha256={expected_signature}", received_signature)

@app.route('/api/webhooks/geodagio', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Geodagio-Signature')
    raw_payload = request.get_data()

    if not verify_geodagio_signature(raw_payload, signature, WEBHOOK_SECRET):
        abort(401, description="Assinatura HMAC de webhook inválida.")

    event_data = json.loads(raw_payload)
    event_type = event_data.get('event')

    if event_type == 'batch.routes.completed':
        batch_id = event_data['data']['batchId']
        download_url = event_data['data']['downloadUrl']
        print(f"Lote {batch_id} concluído com sucesso. Processando download: {download_url}")
        # Dispara job de conciliação fiscal no ERP

    return jsonify({"status": "received"}), 200

if __name__ == '__main__':
    app.run(port=8080)

5. Checklist de Homologação e Go-Live para Equipes de TI

Antes de liberar a integração para o ambiente produtivo de faturamento de frete, o time de engenharia deve auditar os seguintes pontos de controle:

Item de Controle Critério de Aceite Status de Validação
Cofre de Segredos API Keys armazenadas em variáveis de ambiente ou Secrets Manager (AWS Secrets Manager / Azure Key Vault / HashiCorp Vault), nunca em repositório Git. Mandatório
Timeout de Socket Configuração de timeout estrito de conexão (máx 3s) e de leitura (máx 10s) para evitar congelamento de threads no ERP. Mandatório
Circuit Breaker Implementação de disjuntor de circuito (Circuit Breaker) para chavear para modo offline / estimativa auxiliar em caso de falha sistêmica externa prolongada. Recomendado
Sanitização de Logs Filtros que mascaram tokens de autorização e documentos fiscais (CPF/CNPJ) nos logs de aplicação do ERP (conformidade LGPD). Mandatório
Teste de Idempotência Envio de 5 requisições concorrentes com a mesma chave para validar que apenas um débito de crédito ocorre na conta. Mandatório

Perguntas Frequentes sobre Integração de APIs Logísticas (FAQ)

A Geodagio oferece ambiente de Sandbox para testes de homologação?

Sim. O console disponibiliza geração de chaves no modo de teste com o prefixo gdg_test_. As requisições executam exatamente o mesmo motor geométrico e regras fiscais sem debitar créditos reais da conta, permitindo que as equipes de desenvolvimento validem toda a esteira de integração do ERP antes do corte para produção.

Qual a política de SLA e monitoramento da API Geodagio?

A plataforma Geodagio opera com redundância geográfica e alta disponibilidade com SLA contratual garantido de 99,9% para planos Enterprise. Métricas de latência P50, P95 e P99 e status em tempo real de todos os microsserviços estão permanentemente disponíveis em nossa página pública de status.

Como a API lida com frotas com eixos suspensos no cálculo de pedágio?

A API aceita no payload do veículo a especificação exata de eixos totais e suspendedAxles (eixos suspensos). O motor de precificação aplica as regras da Lei nº 13.103/15 e decretos estaduais pertinentes: quando o caminhão trafega descarregado comprovado por MDF-e, a cobrança das praças isenta os eixos que não tocam o solo.

É possível processar arquivos de lote com milhares de rotas de uma só vez?

Sim. Além dos endpoints síncronos de cálculo unitário, a Geodagio oferece a API de Processamento de Lotes (Batch Processing). O sistema aceita uploads de arquivos XLSX ou payloads JSON com até 50.000 rotas simultâneas, processando os cálculos em paralelo em workers distribuídos e notificando o ERP via Webhook na conclusão.

Pronto para Integrar APIs Logísticas de Missão Crítica?

Crie sua conta corporativa no Geodagio, gere sua chave de API instantaneamente e experimente o playground interativo com documentação OpenAPI completa.