Skip to content

fix(credits): list usage corrections as refunded credit transactions - #5197

Draft
GAlexIHU wants to merge 1 commit into
mainfrom
claude/credit-transaction-corrections-2c2553
Draft

GAlexIHU wants to merge 1 commit into
mainfrom
claude/credit-transaction-corrections-2c2553

Conversation

@GAlexIHU

@GAlexIHU GAlexIHU commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Editing a subscription recreates subscription items, which shrinks or deletes the old flat-fee charges and corrects their credit allocations. The credit transaction listing showed the original consumed rows but not the corrections, so the history had prorated usage that seemingly didn't affect the balance.

  • Corrections increase FBO, so the consumed loader (negative FBO movement) never saw them. The funded loader looks at positive movement too, but only emits credit purchases, so it dropped them.
  • Balances are read from the ledger per row, so available_balance jumped between rows.
  • Correcting advance-backed usage also writes an advance cancellation (FBO -x, advance +x). It has zero balance impact but negative FBO, so it showed up as a fake consumed -x row with an invented before.

Change

  • New credit transaction type refunded (TypeSpec + generated SDKs + v3 handler mapping).
  • New refunded loader: ledger transactions with ledger.transaction.direction=correction on FBO/receivable. Each row nets the customer balance impact (FBO + advance) per ledger group, booked time and currency, and hides zero sums. This is needed because one backfilled-advance correction writes offsetting postings (attribution unwind −8, purchased credit re-issue +8, collection reversal +8, advance cancellation 0).
  • Rows keep the corrected charge's labels (charge_id, subscription/feature references), so clients can relate refunds to the consumption they undo. Collapsing them in the UI is left for later.
  • Refunds are booked at the original collection time, so they sort right after the consumption they correct. created_at is the correction time.
  • consumed now excludes direction=correction transactions, which removes the fake rows.
  • Credit voids are also ledger corrections; they are skipped and stay voided.
  • The ledger candidate paging loop moved from the funded loader into a shared helper (candidate_loader.go) that both loaders use. Funded behavior is unchanged.
  • customerbalance README documents refund semantics.

Tests

test/credits/sanity_credit_transactions_test.go checks exact rows (type, amount, before/after) and a gap-free balance chain for:

  • flat fee delete: funded / partially funded / advance only, plus the type=refunded filter and page size 1
  • flat fee prorated shrink (subscription edit): funded / partially funded
  • usage correction after a grant backfilled the advance

Before the fix, the same scenarios listed no refund row and a fake consumed row wherever an advance was involved.

Notes

  • Adding an enum value is an API change; clients that switch strictly on type should handle refunded.
  • make gen-api fails locally in the legacy Python client step (missing venv, unrelated). I ran the AIP generation steps from api/spec/Makefile plus pnpm run build/format. The legacy spec is untouched.

🤖 Generated with Claude Code

RetriggerConfidence Score: 4/5

The PR appears safe to merge, with a non-blocking gap in public API test coverage.

Fix All in Claude CodeFindings

  1. P2 Public filter lacks coverage ▶
Fix with agent prompt
### Issue 1
api/v3/handlers/customers/credits/convert.go:535-537
The new `refunded` filter is tested through `CustomerBalanceSvc.ListCreditTransactions`, which bypasses the HTTP request and this API-to-domain conversion. An API-level test of `filter[type]=refunded` and the returned transaction type would catch a public API regression that the current service tests cannot.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR adds refunded credit transactions for corrected usage and fees, nets related ledger postings into balance-impact rows, and excludes corrections from consumed rows. It updates the API types, generated clients, documentation, and service-level scenarios for deletion, proration, backfill, filtering, and paging.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Usage or fee consumed] --> B[Charge corrected]
  B --> C[Correction ledger group]
  C --> D[Net FBO and advance by booked time and currency]
  D --> E{Nonzero impact?}
  E -- Yes --> F[refunded transaction]
  E -- No --> G[No visible row]
  C --> H[Exclude correction postings from consumed]
Loading

Reviews (1) · Last reviewed commit: "fix(credits): list usage corrections as ..."

Usage corrections (charge delete/shrink, e.g. after subscription edits)
were invisible in the credit transaction listing: they increase FBO, so
the consumed loader skipped them, and the funded loader dropped them
because they are not credit purchases. Balances jumped between rows.

Advance cancellations written by corrections (FBO -x, advance +x) also
leaked into the consumed loader as phantom consumption.

- add `refunded` credit transaction type
- list corrections as refunded rows, netting balance impact per ledger
  group, booked time and currency; zero sums are hidden
- exclude correction-direction transactions from consumed
- extract shared ledger candidate paging from the funded loader

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@GAlexIHU GAlexIHU added the release-note/bug-fix Release note: Bug Fixes label Sep 24, 2026
@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment on lines 535 to +537
txType = customerbalance.CreditTransactionTypeVoided
case api.BillingCreditTransactionTypeRefunded:
txType = customerbalance.CreditTransactionTypeRefunded

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Public filter lacks coverage The new refunded filter is tested through CustomerBalanceSvc.ListCreditTransactions, which bypasses the HTTP request and this API-to-domain conversion. An API-level test of filter[type]=refunded and the returned transaction type would catch a public API regression that the current service tests cannot.

Prompt To Fix With AI
This is a comment left during a code review.
Path: api/v3/handlers/customers/credits/convert.go
Line: 535-537

Comment:
**Public filter lacks coverage** The new `refunded` filter is tested through `CustomerBalanceSvc.ListCreditTransactions`, which bypasses the HTTP request and this API-to-domain conversion. An API-level test of `filter[type]=refunded` and the returned transaction type would catch a public API regression that the current service tests cannot.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/bug-fix Release note: Bug Fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant