Pular para o conteúdo principal

Pull diário de pagamentos

Cenário: um job diário (cron, scheduler) precisa carregar todos os pagamentos do dia anterior para o seu data warehouse, ERP ou painel financeiro.

Escopo necessário: financeiro.read.

Fluxo

  1. Calcular a janela de busca: tipicamente [ontem 00:00, hoje 00:00) em horário local do tenant.
  2. GET /v1/pagamentos?dtPagamentoInicial=...&dtPagamentoFinal=...&pageSize=200.
  3. Iterar as páginas.
  4. Para cada pagamento, fazer upsert no destino usando idPagamento (GUID) como chave única.

A chave idPagamento é um GUID estável: upsert é seguro mesmo se você reexecutar o job. Isso te dá idempotência de graça.

Implementação

from datetime import datetime, timedelta, timezone
from banqer_client import paginar

def janela_diaria(dia_referencia=None):
"""Devolve (inicio_iso, fim_iso) do dia anterior em UTC."""
if dia_referencia is None:
dia_referencia = datetime.now(timezone.utc).date()
inicio = datetime.combine(
dia_referencia - timedelta(days=1),
datetime.min.time(),
tzinfo=timezone.utc,
)
fim = inicio + timedelta(days=1)
return inicio.strftime("%Y-%m-%dT%H:%M:%SZ"), fim.strftime("%Y-%m-%dT%H:%M:%SZ")


def baixar_pagamentos_do_dia(upsert_callback):
"""Itera os pagamentos do dia anterior e chama upsert_callback por item."""
inicio, fim = janela_diaria()
print(f"Janela: {inicio} ate {fim}")

contador = 0
for pagamento in paginar("/v1/pagamentos",
params={"dtPagamentoInicial": inicio,
"dtPagamentoFinal": fim},
key="pagamentos"):
upsert_callback(pagamento)
contador += 1
return contador


# Uso com SQLite como destino exemplo
import sqlite3
conn = sqlite3.connect("pagamentos.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS pagamentos (
id_pagamento TEXT PRIMARY KEY,
id_pagamento_exibicao INTEGER,
id_ficha_cobranca TEXT,
data_pagamento TEXT,
valor_pagamento REAL,
id_produto INTEGER,
id_credor INTEGER
)
""")

def upsert(p):
conn.execute("""
INSERT INTO pagamentos VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id_pagamento) DO UPDATE SET
valor_pagamento = excluded.valor_pagamento,
data_pagamento = excluded.data_pagamento
""", (
p["idPagamento"], p["idPagamentoExibicao"], p["idFichaCobranca"],
p["dataPagamento"], p["valorPagamento"],
p["idProduto"], p["idCredor"],
))

total = baixar_pagamentos_do_dia(upsert)
conn.commit()
print(f"Total upserted: {total}")

Pontos importantes

  • Timezone: a API retorna dataPagamento em UTC (...Z no final). Se sua planilha ou painel apresenta em fuso local, faça a conversão no seu lado. Não passe filtros de data com offset de fuso: use UTC.
  • Janela com sobreposição: se quer garantia de não perder nada em caso de falha, faça a janela de N+1 dias e use o upsert por idPagamento como dedupe. Custa pouco a mais e elimina o risco de buraco.
  • Pagamentos podem ser estornados/ajustados: rerodar o pull do mesmo dia mais tarde pode retornar registros com valor diferente. Por isso o upsert é importante, não o insert puro.
  • Volume típico: 1k-10k pagamentos/dia em operações médias. Esse pull roda em poucos segundos.
  • Tarja de cobrança: existem três níveis de alocação do pagamento (parcela, contrato, título). Em V1 essa decomposição está disponível em GET /v1/pagamentos/{id} (chamada separada). Inclua na receita só se sua aplicação precisar.

Quando usar webhook em vez de pull

A API V1 é pull-only. Se você precisa de latência menor que 1 dia, o caminho é reduzir o intervalo do cron (a cada hora, a cada 15 minutos) e aceitar a janela. Webhook push está fora do escopo de V1.