Pular para o conteúdo principal

🏆 Boas Práticas de Integração

Este guia apresenta recomendações essenciais para garantir uma integração segura, performática e confiável com as APIs Credsystem.

💡 Por que seguir estas práticas?
  • 🔒 Segurança máxima para dados sensíveis
  • Performance otimizada com menos requisições
  • 🛡️ Resiliência em cenários de falha
  • Conformidade com LGPD e padrões de mercado
  • 🚀 Aprovação rápida para produção

🔐 1. Segurança

💡 Guia Completo Disponível

Para implementações técnicas detalhadas de segurança com código em 6 linguagens, consulte o Guia Completo de Segurança.

1.1 Proteção de Credenciais

🚨 NUNCA exponha credenciais em:
  • ❌ Código-fonte versionado (Git, SVN)
  • ❌ Frontend/JavaScript/localStorage
  • ❌ Aplicativos móveis (podem ser decompilados)
  • ❌ Logs, mensagens de erro ou URLs
  • ❌ Documentação pública (README, Wiki, Confluence)

✅ Armazenamento Correto:

AmbienteSolução Recomendada
DesenvolvimentoVariáveis de ambiente (.env no .gitignore)
StagingVariáveis de ambiente com acesso restrito
ProduçãoSecrets Manager (AWS, Azure Key Vault, HashiCorp Vault)
KubernetesKubernetes Secrets + External Secrets Operator

👉 Ver implementações em 6 linguagens

1.2 HTTPS, TLS e Tokens

PráticaObrigatórioDetalhes
HTTPS em todas as requisiçõesHTTP será rejeitado
TLS 1.2+Ver configuração
Validação de certificadosNunca verify=False ou rejectUnauthorized: false
Cache de tokens no backendVer implementação
Token fora do frontendNunca em localStorage/sessionStorage
Usar expires_in dinâmicoNunca assumir valores fixos
RotaçãoVer estratégia dual

⚡ 2. Performance e Cache

2.1 Rate Limiting

📊 Limites por Ambiente
Tipo de RequisiçãoHomologaçãoProdução
Autenticação (por client_id)20 req/min10 req/min
APIs de Consulta200 req/min100 req/min
APIs de Transação100 req/min50 req/min

Erro retornado ao exceder: 429 Too Many Requests com header Retry-After

Implementação de Rate Limiter local — Proteja sua aplicação de estourar os limites:

class TokenBucket {
constructor(capacity, refillRatePerSecond) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRatePerSecond;
this.lastRefill = Date.now();
}

consume() {
this._refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return true; // Requisição permitida
}
return false; // Rate limit local atingido
}

_refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}

// Uso: Permite 10 req/min para autenticação em produção
const authLimiter = new TokenBucket(10, 10 / 60);

async function safeAuthRequest() {
if (!authLimiter.consume()) {
console.warn('Rate limit local atingido. Aguardando...');
await new Promise(r => setTimeout(r, 2000));
return safeAuthRequest(); // Retry após espera
}
return await authenticate();
}

2.2 Cache Inteligente

O que cachear vs. o que NUNCA cachear:

✅ Cache recomendadoTTL sugerido❌ NUNCA cachear
Tokens de autenticaçãoexpires_in - 30sTransações financeiras
Listas de produtos5-15 minSaldos e limites em tempo real
Configurações/parâmetros30-60 minStatus de pagamento
Dados mestres (estabelecimentos)1-4 horasDados sensíveis (CPF, cartão)
Resultados de consultas pesadas1-5 minQualquer dado que mude frequentemente

Implementação de cache com TTL:

class TTLCache {
constructor() {
this.cache = new Map();
}

set(key, value, ttlMs) {
this.cache.set(key, {
value,
expiresAt: Date.now() + ttlMs
});
}

get(key) {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.cache.delete(key);
return null;
}
return entry.value;
}
}

// Exemplo: Cache de token com renovação automática
const cache = new TTLCache();

async function getToken() {
const cached = cache.get('access_token');
if (cached) return cached;

const response = await authenticate();
const ttl = (response.expires_in - 30) * 1000; // 30s antes de expirar
cache.set('access_token', response.access_token, ttl);
return response.access_token;
}

2.3 Connection Pooling

Reutilize conexões HTTP para evitar overhead de handshake TLS a cada requisição:

const https = require('https');
const { Agent } = require('http');

// Reutiliza conexões automaticamente
const agent = new https.Agent({
keepAlive: true, // Reutilizar conexões
keepAliveMsecs: 30000, // Keep-alive por 30s
maxSockets: 25, // Máximo de conexões simultâneas
maxFreeSockets: 10, // Máximo de conexões ociosas
minVersion: 'TLSv1.2'
});

// Use este agent em todas as requisições Credsystem
const response = await fetch(url, { agent });

🔄 3. Resiliência e Retry

3.1 Quando fazer (e NÃO fazer) Retry

Status HTTPFazer Retry?Motivo
429✅ SimRate limit — respeite header Retry-After
500✅ SimErro interno — pode ser temporário
502 / 503 / 504✅ SimIndisponibilidade temporária
408✅ SimTimeout do servidor
Erro de rede✅ SimDNS, conexão recusada, timeout
400❌ NãoDados incorretos — corrija o payload
401❌ Não¹Token inválido — renove o token primeiro
403❌ NãoSem permissão — verifique credenciais
404❌ NãoRecurso não existe — verifique a URL
422❌ NãoValidação — corrija os dados

¹ Para 401, renove o token e tente uma vez. Se falhar novamente, não faça retry.

3.2 Backoff Exponencial com Jitter

⚠️ Nunca faça retry imediato

Retries simultâneos de múltiplos clientes podem causar thundering herd e derrubar o serviço. Use jitter aleatório.

async function callWithRetry(fn, { maxRetries = 3, baseDelayMs = 1000 } = {}) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fn();

// Retry para status específicos
if ([429, 500, 502, 503, 504].includes(response.status)) {
if (attempt === maxRetries) return response;

const retryAfter = response.headers.get('Retry-After');
const delay = retryAfter
? parseInt(retryAfter) * 1000
: baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000; // Jitter

console.warn(`Retry ${attempt + 1}/${maxRetries} em ${Math.round(delay)}ms (status: ${response.status})`);
await new Promise(r => setTimeout(r, delay));
continue;
}

return response; // Sucesso ou erro não-retryável
} catch (error) {
// Erro de rede — retry
if (attempt === maxRetries) throw error;
const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 1000;
console.warn(`Retry ${attempt + 1}/${maxRetries} em ${Math.round(delay)}ms (erro de rede)`);
await new Promise(r => setTimeout(r, delay));
}
}
}

// Uso
const response = await callWithRetry(() => fetch(apiUrl, { headers, agent }));

⏱️ 4. Timeout e Circuit Breaker

4.1 Configuração de Timeouts

🕐 Timeouts Recomendados
Tipo de OperaçãoConnectRead/TotalPor quê?
Autenticação3s5sRespostas rápidas do SSO
Consultas simples5s10sDados leves
Transações5s30sProcessamento mais demorado
Relatórios/Batch5s60sAgregações pesadas
// AbortController para timeout granular
async function fetchWithTimeout(url, options = {}, timeoutMs = 10000) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);

try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
return response;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`Timeout de ${timeoutMs}ms excedido para ${url}`);
}
throw error;
} finally {
clearTimeout(timeout);
}
}

// Uso com timeouts diferentes por operação
const token = await fetchWithTimeout(authUrl, authOptions, 5000); // 5s
const dados = await fetchWithTimeout(apiUrl, apiOptions, 10000); // 10s
const relatorio = await fetchWithTimeout(reportUrl, reportOpts, 60000); // 60s

4.2 Circuit Breaker

O padrão Circuit Breaker evita requisições inúteis quando o serviço está fora. Funciona como um disjuntor elétrico:

┌── Sucesso → Volta para CLOSED

CLOSED ──── X falhas ──── OPEN ──── após tempo ──── HALF-OPEN
(Normal) consecutivas (Bloqueado) de espera (Testando)
▲ │
└──────────────────── Sucesso ──────────────────────────┘

└── Falha → Volta para OPEN
class CircuitBreaker {
constructor({ failureThreshold = 5, resetTimeoutMs = 30000 } = {}) {
this.state = 'CLOSED'; // CLOSED | OPEN | HALF_OPEN
this.failures = 0;
this.failureThreshold = failureThreshold;
this.resetTimeout = resetTimeoutMs;
this.nextAttempt = 0;
}

async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit OPEN — serviço temporariamente indisponível');
}
this.state = 'HALF_OPEN';
}

try {
const result = await fn();
this._onSuccess();
return result;
} catch (error) {
this._onFailure();
throw error;
}
}

_onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}

_onFailure() {
this.failures++;
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.resetTimeout;
console.error(`Circuit OPEN — ${this.failures} falhas consecutivas. Retry em ${this.resetTimeout / 1000}s`);
}
}
}

// Uso
const breaker = new CircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30000 });

async function callCredsystemAPI(endpoint) {
return breaker.execute(async () => {
const response = await fetch(endpoint, { headers, agent });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
});
}

📊 5. Monitoramento e Observabilidade

5.1 Métricas Essenciais

CategoriaMétricaAlerta quando...
DisponibilidadeTaxa de sucesso (%)< 99% por 5 min
LatênciaP50, P95, P99 (ms)P95 > 2x baseline
ErrosTaxa de 4xx e 5xx5xx > 1% em 5 min
Rate LimitHits em 429Qualquer ocorrência
TokensRenovações/minPicos anormais
Circuit BreakerEstado (open/closed)Mudança para OPEN
NegócioTransações aprovadas/minQueda > 20%

5.2 Logging Estruturado

🔍 Logs Seguros

✅ Registre: Request ID, Timestamp (ISO 8601), método HTTP, endpoint, status, tempo de resposta (ms), ambiente, IP de origem

❌ NUNCA registre: Tokens completos, client secrets, CPF/CNPJ completos, dados de cartão, senhas

// Log estruturado para cada requisição à API
function logAPICall(method, endpoint, statusCode, durationMs, requestId) {
const entry = {
timestamp: new Date().toISOString(),
level: statusCode >= 500 ? 'ERROR' : statusCode >= 400 ? 'WARN' : 'INFO',
service: 'credsystem-integration',
request_id: requestId,
http_method: method,
endpoint: endpoint,
status_code: statusCode,
duration_ms: durationMs,
environment: process.env.NODE_ENV
};

// Formato JSON para ingestão por ferramentas (ELK, Datadog, etc.)
console.log(JSON.stringify(entry));
}

// Mascarar dados sensíveis antes de logar
function maskSensitive(data) {
return {
...data,
cpf: data.cpf ? `***.***.***-${data.cpf.slice(-2)}` : undefined,
token: data.token ? `${data.token.slice(0, 10)}...` : undefined,
// NUNCA incluir client_secret
};
}

👉 Ver mais exemplos de logging seguro


✅ 6. Validação e Sanitização de Dados

6.1 Valide ANTES de Enviar

Reduza erros 400 validando dados antes de fazer requisições:

class CredsystemValidator {
static validateCPF(cpf) {
const cleaned = cpf.replace(/\D/g, '');
if (cleaned.length !== 11) return { valid: false, error: 'CPF deve ter 11 dígitos' };
if (/^(\d)\1+$/.test(cleaned)) return { valid: false, error: 'CPF inválido' };

// Validação dos dígitos verificadores
const calcDigit = (digits, factor) => {
const sum = digits.reduce((s, d, i) => s + d * (factor - i), 0);
const remainder = sum % 11;
return remainder < 2 ? 0 : 11 - remainder;
};

const digits = cleaned.split('').map(Number);
const d1 = calcDigit(digits.slice(0, 9), 10);
const d2 = calcDigit(digits.slice(0, 10), 11);

return {
valid: d1 === digits[9] && d2 === digits[10],
cleaned,
error: d1 !== digits[9] || d2 !== digits[10] ? 'CPF inválido' : null
};
}

static validateMonetary(value) {
if (typeof value !== 'number' || isNaN(value)) return { valid: false, error: 'Valor deve ser numérico' };
if (value <= 0) return { valid: false, error: 'Valor deve ser positivo' };
if (value > 999999.99) return { valid: false, error: 'Valor excede limite' };
return { valid: true, value: Number(value.toFixed(2)) };
}

static sanitize(data) {
return {
nome: data.nome?.trim().replace(/\s+/g, ' ').slice(0, 200),
cpf: data.cpf?.replace(/\D/g, ''),
email: data.email?.toLowerCase().trim(),
valor: Number(data.valor?.toFixed(2)),
observacao: data.observacao?.replace(/<[^>]*>/g, '').slice(0, 500) // Remove HTML
};
}
}

// Uso antes de enviar à API
const cpfResult = CredsystemValidator.validateCPF(payload.cpf);
if (!cpfResult.valid) throw new Error(cpfResult.error);

const sanitized = CredsystemValidator.sanitize(payload);
const response = await callCredsystemAPI('/venda', sanitized);

🔔 7. Integração Assíncrona

7.1 Webhooks vs Polling

AspectoWebhooks (✅ Recomendado)Polling (⚠️ Evitar)
LatênciaTempo realDepende do intervalo
Custo Rate LimitBaixo (~1 req/evento)Alto (N req/intervalo)
ComplexidadeExige endpoint públicoMais simples
ConfiabilidadePrecisa de retryAutossuficiente
Quando usarStatus de transação, pagamentosDados analíticos, relatórios

7.2 Idempotência

⚠️ Essencial para Transações Financeiras

Sem idempotência, uma falha de rede + retry pode gerar transações duplicadas.

const crypto = require('crypto');

// Gerar chave de idempotência única por operação
function generateIdempotencyKey(operation, uniqueData) {
return crypto.createHash('sha256')
.update(`${operation}:${JSON.stringify(uniqueData)}`)
.digest('hex');
}

// Uso em transações de venda
async function realizarVenda(vendaData) {
const idempotencyKey = generateIdempotencyKey('venda', {
cpf: vendaData.cpf,
valor: vendaData.valor,
timestamp: Math.floor(Date.now() / 60000) // Janela de 1 min
});

const response = await fetch(apiUrl + '/venda', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'X-Idempotency-Key': idempotencyKey // Evita duplicação
},
body: JSON.stringify(vendaData)
});

return response.json();
}

📱 8. Integrações Mobile

8.1 Arquitetura BFF (Backend For Frontend)

🚨 NUNCA coloque credenciais Credsystem no app mobile

Aplicativos podem ser decompilados (APK/IPA). Qualquer secret no código será exposto.

Arquitetura obrigatória:

┌──────────────┐ HTTPS ┌──────────────┐ HTTPS ┌───────────────┐
│ App Mobile │ ──────────────→│ BFF/API │ ──────────────→│ APIs │
│ (iOS/Android)│ │ Gateway │ │ Credsystem │
│ │ Seu token de │ │ Token OAuth2 │ │
│ │ sessão apenas │ Credenciais │ do Credsystem │ │
└──────────────┘ │ seguras aqui │ └───────────────┘
└──────────────┘

Benefícios do BFF:

  • 🔒 Credenciais Credsystem ficam exclusivamente no servidor
  • 🔐 Certificate pinning entre app e seu BFF
  • ⚡ Agregação de múltiplas APIs em uma chamada
  • 📊 Rate limiting no gateway (protege seus limites)
  • 🔄 Alteração de credenciais sem atualizar o app

8.2 Certificate Pinning

// iOS - Certificate Pinning com Alamofire
import Alamofire

let serverTrustManager = ServerTrustManager(evaluators: [
"seu-bff.suaempresa.com.br": PublicKeysTrustEvaluator()
])

let session = Session(serverTrustManager: serverTrustManager)

session.request("https://seu-bff.suaempresa.com.br/api/vendas")
.validate()
.responseDecodable(of: VendasResponse.self) { response in
// ...
}

🛡️ 9. Compliance e LGPD

9.1 Classificação de Dados

NívelDadosObrigações
🔴 CríticoCartão de crédito, CVV, senhaCriptografia forte, PCI-DSS, nunca armazenar
🟠 SensívelCPF/CNPJ, dados bancários, endereço, telefoneLGPD, criptografia em repouso, mascarar em logs
🟡 PessoalNome, e-mail, data de nascimentoLGPD, consentimento, direito de exclusão
🟢 PúblicoProdutos, preços, endereço comercialSem restrições especiais

9.2 Obrigações Práticas

🔐 LGPD — Obrigações da sua aplicação
ObrigaçãoImplementação
Criptografia em trânsitoHTTPS/TLS 1.2+ obrigatório (ver guia)
Criptografia em repousoAES-256 para dados sensíveis no banco
Mascaramento em logs***.***.***-01 para CPF (ver exemplos)
Retenção limitadaDefinir TTL para dados pessoais — não armazene indefinidamente
Direito de exclusãoEndpoint para apagar dados pessoais sob solicitação
ConsentimentoColeta explícita autorizada antes de processar
AuditoriaRegistrar quem acessou quais dados e quando

🎯 10. Checklist de Produção

Antes de Go-Live

🔐 Segurança
  • Credenciais em gestor de secrets (AWS/Azure/Vault)
  • HTTPS/TLS 1.2+ configurado (ver implementação)
  • Certificados SSL válidos e com renovação automática
  • Validação de certificados ativa (rejectUnauthorized: true)
  • Cache de tokens implementado (ver exemplo)
  • Tokens NUNCA no frontend/localStorage
  • Logs não contêm dados sensíveis (ver checklist)
  • Rate limiter local implementado (ver exemplo)
  • Rotação de credenciais planejada (90-180 dias)
  • Separação de credenciais por ambiente (dev/stg/prod)

📚 Guia Completo: Segurança e Boas Práticas

⚡ Performance
  • Cache de tokens com TTL dinâmico (expires_in - 30s)
  • Cache de dados mestres configurado com TTL adequado
  • Connection pooling / Keep-alive habilitado (ver exemplo)
  • Compressão de payloads (Accept-Encoding: gzip)
  • Timeouts configurados por tipo de operação (ver tabela)
🔄 Resiliência
📊 Observabilidade
  • Logs estruturados em JSON (ver exemplo)
  • Métricas coletadas (Prometheus/Datadog/similar)
  • Alertas configurados (ver tabela de métricas)
  • Dashboard de monitoramento criado
  • Dados sensíveis mascarados nos logs
✅ Validação
  • Validação de CPF/CNPJ antes de enviar (ver exemplo)
  • Sanitização de inputs (trim, HTML strip)
  • Testes unitários com cobertura > 80%
  • Testes de integração em homologação
  • Testes de carga realizados
  • Documentação interna atualizada
🛡️ Compliance
  • Conformidade LGPD validada (ver obrigações)
  • Dados classificados por nível de sensibilidade
  • Auditoria de acessos implementada
  • Retenção de dados definida
  • Processo de exclusão de dados implementado (direito do titular)
  • Política de privacidade atualizada
📱 Mobile (se aplicável)
  • BFF implementado (ver arquitetura)
  • Certificate pinning configurado (ver exemplo)
  • Ofuscação de código ativa (ProGuard/R8)
  • Credenciais NUNCA no código do app
  • Tratamento de background/foreground

🚀 Pronto para Produção?

✅ Validação Final

Após completar os checklists acima:

  1. 📧 Entre em contato com suporte.tecnico@credsystem.com.br
  2. 📋 Compartilhe evidências dos testes em homologação
  3. 📊 Apresente métricas de qualidade
  4. 🔐 Solicite credenciais de produção
  5. 🚀 Agende data de go-live com suporte

📚 Recursos Adicionais

RecursoLink
🔐 SegurançaGuia Completo
🔑 Documentação de AutenticaçãoAutenticação
🌐 Ambientes e EndpointsAmbientes
📖 Documentação das APIsAPIs
⚡ TroubleshootingProblemas Comuns
💬 Suporte Técnicosuporte.tecnico@credsystem.com.br