Envio do organization_slug e do cliente Asaas nos eventos do accounts

TLDR: o payload de PURCHASE_CREATED, INSTALLMENT_PAID e INSTALLMENT_OVERDUE passa a levar organization_slug, ao lado de product_slug, e customer.provider_customer_id (o cus_... do Asaas), para o accounts saber de qual organização é a compra e qual é o cliente no gateway.

Contexto

Hoje o accounts recebe só o product_slug. Os três eventos montam o payload interno pelo mesmo Accounts::PurchaseSerializer, então basta um atributo novo nele para os três passarem a enviar.

O accounts também precisa do id do cliente no Asaas. Ele vem da própria cobrança: os três eventos já buscam as parcelas ao vivo (GET payments?installment=...), e cada cobrança da resposta traz customer. Esse valor foi preferido ao OrganizationCustomer#gateway_customer_id local porque é o cliente que está de fato na cobrança, e porque pode existir mais de um OrganizationCustomer para o mesmo cliente e organização (o CheckoutService busca incluindo o gateway_customer_id).

Objetivos

  • Enviar organization_slug (payment.checkout.organization.slug) no payload dos três eventos do accounts.
  • Enviar null quando a organização não tem slug (a coluna é anulável e aceita branco, ver R-003). Slug em branco ("") também sai como null.
  • Enviar customer.provider_customer_id, lido do campo customer da primeira cobrança buscada no Asaas. Vai null quando a lista vem vazia ou sem customer.

Fora de escopo

  • Não bloqueia o evento quando falta slug: o evento sai com organization_slug: null.
  • Não mexe no envelope (event_type, external_id, occurred_at).
  • Não mexe no contrato new_checkout.repayment_sync: o repayment_service monta as parcelas escolhendo as chaves, então a chave nova do hash normalizado não aparece lá.
  • Não lê o OrganizationCustomer local.
  • Não mexe em eventos já gravados no outbox.

Mudanças

  • app/serializers/accounts/purchase_serializer.rb: atributo organization_slug, logo antes de product_slug, com valor object.checkout.organization.slug.presence.
  • app/services/asaas/installments/fetch_from_gateway.rb: normalize passa a incluir gateway_customer_id (installment[:customer]).
  • app/serializers/accounts/purchase_serializer.rb: customer ganha provider_customer_id, vindo do gateway_customer_id da primeira parcela em instance_options[:installments].
  • spec/services/asaas/installments/fetch_from_gateway_spec.rb: os testes com eq no hash normalizado recebem customer no dado bruto e gateway_customer_id no esperado.
  • spec/serializers/accounts/purchase_serializer_spec.rb: inclui organization_slug: "citrg" e provider_customer_id no expected_payload, e adiciona testes para a organização sem slug (null) e para a lista de parcelas sem cliente (null).
  • spec/services/accounts/dispatch_purchase_created_spec.rb, dispatch_installment_paid_spec.rb, dispatch_installment_overdue_spec.rb: esses specs comparam o envelope inteiro com eq, então precisam receber "organization_slug" => "citrg" e "provider_customer_id" no payload esperado, e customer no dado bruto do Asaas. Muda testes existentes, e isso depende da sua aprovação deste spec.

Como verificar

  • bundle exec rspec spec/serializers/accounts spec/services/accounts passando.
  • Pelo console, Accounts::DispatchPurchaseCreated.new(payment).envelope com um pagamento real mostra organization_slug e customer.provider_customer_id no payload.

Documentação

  • .project/docs/reference/payments/accounts_purchase_created_event.md: exemplo do payload e notas sobre organization_slug e customer.provider_customer_id (null quando não há valor).
  • .project/docs/reference/payments/accounts_installment_paid_event.md e accounts_installment_overdue_event.md: exemplo do payload, se o exemplo estiver repetido nesses arquivos.
  • .project/docs/README.md: entrada deste spec no índice.