Pular para conteúdo

Boas Práticas de Integração

Performance

Use page_size Adequado

HTTP
1
2
3
4
5
# Para bulk sync — máximo por página
GET /empregados/?page_size=100

# Para UI — menor latência
GET /empregados/?page_size=20

Filtre na API, não no código

Python
1
2
3
4
5
6
# ❌ Ineficiente: traz tudo e filtra localmente
todos = listar_todos("/empregados/", headers)
ativos = [e for e in todos if e["is_active"]]

# ✅ Eficiente: filtra na API
ativos = listar_todos("/empregados/?is_active=true", headers)

Cache de Respostas Imutáveis

Python
from functools import lru_cache
import time

class CachedPontotelClient:
    def __init__(self, auth, cache_ttl_seconds=300):
        self.auth = auth
        self.cache = {}
        self.cache_ttl = cache_ttl_seconds

    def get(self, url: str) -> dict:
        now = time.time()
        if url in self.cache:
            cached_data, cached_at = self.cache[url]
            if now - cached_at < self.cache_ttl:
                return cached_data

        headers = self.auth.get_headers()
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        data = response.json()

        self.cache[url] = (data, now)
        return data

Segurança

Armazenamento de Credenciais

Python
# ✅ Correto: Usar variáveis de ambiente
import os
from dotenv import load_dotenv

load_dotenv()
USERNAME = os.getenv("PONTOTEL_USERNAME")
PASSWORD = os.getenv("PONTOTEL_PASSWORD")

# ❌ Nunca hardcode credenciais
USERNAME = "meu_usuario"  # NÃO FAÇA ISSO

Nunca faça commit de credenciais

  • Adicione .env ao .gitignore
  • Use secret managers (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Rotacione credenciais regularmente
  • Use credenciais separadas para Sandbox e Produção

HTTPS Obrigatório

Python
1
2
3
4
5
# ✅ Sempre HTTPS
base_url = "https://apis.pontotel.com.br"

# ❌ Nunca HTTP
base_url = "http://apis.pontotel.com.br"

Idempotência

Operações idempotentes podem ser seguramente repetidas sem efeitos duplicados.

Buscar antes de criar

Python
def criar_ou_atualizar_empregado(dados: dict, headers: dict) -> dict:
    """Garante idempotência verificando antes de criar"""

    # 1. Verificar se já existe
    existente = buscar_empregado(dados["cpf"], dados["empregador_id"], headers)

    if existente:
        # 2. Atualizar se necessário
        if dados_diferentes(existente, dados):
            return atualizar_empregado(existente["id"], dados, headers)
        return existente

    # 3. Criar apenas se não existir
    return criar_empregado(dados, headers)

Exemplos de Código por Linguagem

Python
import requests
import os
from datetime import datetime, timedelta

class PontotelClient:
    BASE_URL = "https://apis.pontotel.com.br/pontotel/api/v4"

    def __init__(self):
        self.session = requests.Session()
        self.token = None
        self.token_expires_at = None

    def _ensure_authenticated(self):
        if not self.token or datetime.now() >= self.token_expires_at:
            self._login()

    def _login(self):
        r = self.session.post(f"{self.BASE_URL}/login/", json={
            "username": os.getenv("PONTOTEL_USERNAME"),
            "password": os.getenv("PONTOTEL_PASSWORD")
        })
        r.raise_for_status()
        data = r.json()
        self.token = data["access_token"]
        self.token_expires_at = datetime.now() + timedelta(seconds=data["expires_in"] - 60)

    def get(self, path: str, params: dict = None) -> dict:
        self._ensure_authenticated()
        r = self.session.get(
            f"{self.BASE_URL}{path}",
            headers={"Authorization": f"Bearer {self.token}"},
            params=params
        )
        r.raise_for_status()
        return r.json()

    def post(self, path: str, payload: dict) -> dict:
        self._ensure_authenticated()
        r = self.session.post(
            f"{self.BASE_URL}{path}",
            headers={"Authorization": f"Bearer {self.token}"},
            json=payload
        )
        r.raise_for_status()
        return r.json()
JavaScript
class PontotelClient {
  constructor() {
    this.baseUrl = 'https://apis.pontotel.com.br/pontotel/api/v4';
    this.token = null;
    this.tokenExpiresAt = null;
  }

  async ensureAuthenticated() {
    if (!this.token || Date.now() >= this.tokenExpiresAt) {
      await this.login();
    }
  }

  async login() {
    const response = await fetch(`${this.baseUrl}/login/`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        username: process.env.PONTOTEL_USERNAME,
        password: process.env.PONTOTEL_PASSWORD
      })
    });
    const data = await response.json();
    this.token = data.access_token;
    this.tokenExpiresAt = Date.now() + (data.expires_in - 60) * 1000;
  }

  async get(path, params = {}) {
    await this.ensureAuthenticated();
    const url = new URL(`${this.baseUrl}${path}`);
    Object.entries(params).forEach(([k, v]) => url.searchParams.append(k, v));
    const response = await fetch(url, {
      headers: { 'Authorization': `Bearer ${this.token}` }
    });
    if (!response.ok) throw new Error(`API Error: ${response.status}`);
    return response.json();
  }
}

Próximos Passos