Parcela paga como objeto no evento INSTALLMENT_PAID
TLDR: o
paid_installmentdoaccounts.installment_paidpassa deprovider_refem string para um objeto{number, provider_ref, paid_at}, na mesma forma dooverdue_installment. A dedupe passa a reconhecer os dois formatos, para que eventos antigos não sejam disparados de novo.
Contexto
O accounts (PR #32) mudou o contrato de entrada do INSTALLMENT_PAID. Agora o paid_installment é a parcela inteira (number, amount, due_on, status, paid_at, provider_ref), no mesmo formato de uma entrada da lista installments. O formato em string não é mais aceito. Hoje todo evento de parcela paga falha no accounts com string indices must be integers, not 'str', porque o checkout ainda manda paid_installment: "pay_xxx".
Uma primeira tentativa (PR #271, branch refactor/installment-paid-in-evidence) foi fechada sem merge. O commit de docs dela (4d09824) já descreve o contrato novo, mas a dedupe descrita lá ignora os eventos antigos em string, o que faria esses eventos serem disparados de novo. Esta spec corrige esse ponto.
Objetivos
- Enviar
paid_installmentcomo objeto{number, provider_ref, paid_at}, seguindo o padrão dooverdue_installment. É o que oMarkPaiddoaccountslê:provider_ref(comnumberde fallback) para localizar a parcela epaid_atpara a data do pagamento. O endpoint não valida schema, então os outros campos do exemplo do contrato não são necessários. - Fazer a dedupe reconhecer
OutboxEventnos dois formatos: string (legado) e objeto (novo). - Aplicar a mesma dedupe de dois formatos ao
backfill_accounts_installment_paid.rb.
Fora de escopo
- Reenvio dos eventos que já falharam no
accountscom o payload antigo. Isso fica para depois, em um trabalho separado. O backfill atual não faz esse reenvio: a dedupe dele trata as parcelas com evento legado como já enviadas, e ele só olha os últimos 3 dias (SINCE). - Mudanças no
Accounts::PurchaseSerializer, noINSTALLMENT_OVERDUEou noPURCHASE_CREATED. - Barrar cobrança com status fora do
STATUS_MAP: o paid mantém o mesmo comportamento do overdue (guard na lista do gateway).
Mudanças
app/services/accounts/dispatch_installment_paid.rb
- O
envelopefaz o merge depaid_installment: {number:, provider_ref:, paid_at:}, montado a partir da cobrança encontrada emgateway_installments, do mesmo jeito que oDispatchInstallmentOverduemonta ooverdue_installment. - O guard continua na lista do gateway, sem mudança.
-
A dedupe em
already_dispatched?passa a considerar os dois formatos:sql payload -> 'payload' -> 'paid_installment' ->> 'provider_ref' = :gateway_id OR payload -> 'payload' ->> 'paid_installment' = :gateway_idEm um objeto,
->>devolve o JSON em texto, que nunca é igual a umgateway_idpuro. Em uma string,-> ... ->> 'provider_ref'devolveNULL. Assim, cada ramo casa só com o formato dele.
backfill_accounts_installment_paid.rb
- O
already_sentusa a mesma condição de dois formatos, comparando cominstallments.gateway_id. - Nada mais muda. O script continua cobrindo só as parcelas pagas que nunca tiveram evento.
- O script é local e não versionado, então fica fora do PR. O ajuste é aplicado nele à parte.
spec/services/accounts/dispatch_installment_paid_spec.rb (escrito antes do código, TDD)
expected_envelope:"paid_installment"passa a ser{"number" => 2, "provider_ref" => "pay_2", "paid_at" => "2026-09-11"}.- Dedupe com evento legado: existe um
OutboxEventcom{"payload" => {"paid_installment" => "pay_2"}}→ não cria evento. - Dedupe com evento novo: existe um
OutboxEventcom{"payload" => {"paid_installment" => {"provider_ref" => "pay_2"}}}→ não cria evento. - Os cenários atuais continuam valendo: um evento por parcela, parcela ausente do gateway e falha do gateway.
Como verificar
bundle exec rspec spec/services/accounts/dispatch_installment_paid_spec.rbpassa, junto com os specs vinculados no doc do contrato.- Backfill com
DRY_RUN = trueem staging/console: parcelas com evento legado em string não aparecem emInstallments to send. - Depois do deploy, um pagamento real gera um
OutboxEventcompaid_installmentem objeto, e oIncomingEventcorrespondente noaccountsé processado sem erro.
Documentação
.project/docs/reference/payments/accounts_installment_paid_event.md: contrato compaid_installmentem objeto{number, provider_ref, paid_at}, exemplo de payload atualizado e seção Idempotência com a dedupe nos dois formatos (a do commit4d09824corrigida: eventos legados em string continuam contando como já disparados)..project/docs/rules/payments/accounts_installment_overdue_dispatch.md: a menção ao contrato antigo do paid (“nunca um objeto”) passa a dizer que o paid também é objeto.