Skip to content

feat(ledger): add credit_then_invoice custom currency fx support - #4854

Open
mark-vass-konghq wants to merge 4 commits into
mainfrom
feat/add-cc-fx-support-to-ledger-credit_then_invoice
Open

mark-vass-konghq wants to merge 4 commits into
mainfrom
feat/add-cc-fx-support-to-ledger-credit_then_invoice

Conversation

@mark-vass-konghq

@mark-vass-konghq mark-vass-konghq commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Implement ledger-side FX handling for custom-currency credit_then_invoice charges.

The uncovered custom-currency overage is now booked using the same accounting semantics as a credit purchase:

  1. issue the uncovered custom amount as purchased credit
  2. immediately consume that credit through the originating charge
  3. convert the resulting custom receivable into the authoritative fiat invoice amount

All three operations are committed in one atomic ledger transaction group.

Why

Custom-currency overage invoice lines represent credit purchases. Previously, the ledger implementation did not fully preserve the corresponding credit-purchase source/spend attribution.

The ledger flow must therefore behave like an actual credit purchase while ensuring that the temporary custom-currency balance never becomes spendable.

Implementation

  • Added custom-currency overage accrual for usage-based and flat-fee charges.
  • Uses the charge's persisted cost basis; it is not resolved or recalculated again.
  • Converts the post-allocation overage exactly once.
  • Preserves the native custom amount, cost basis, and charge provenance on the accrued route.
  • Leaves no spendable custom-currency FBO balance or open custom receivable after accrual.
  • Books the authoritative rounded amount as a fiat receivable.
  • Authorizes and settles payments in the invoice fiat currency.
  • Uses credit-purchase-compatible identity semantics:
    • purchase issuance: SourceChargeID
    • immediate consumption: SourceChargeID and SpendChargeID
    • custom-currency invoice payment: SourceChargeID
  • Preserves the existing SpendChargeID behavior for ordinary fiat charges.

Test coverage

Added PostgreSQL-backed flow coverage for both usage-based and flat-fee charges:

  • purchase → immediate consumption → fiat conversion transaction sequence
  • source/spend charge lineage
  • native and fiat ledger balances
  • persisted cost-basis usage
  • progressive usage-based realization runs
  • payment authorization and settlement
  • non-positive fiat conversion outcomes
  • regression coverage for existing fiat payment behavior

Summary by CodeRabbit

  • New Features

    • Added custom-currency support for usage-based and flat-fee charge processing.
    • Custom-currency overages convert to fiat invoice receivables using the persisted cost basis.
    • Payment authorization and settlement follow the invoice’s fiat currency.
    • Credit and realization records retain complete custom-currency identity and lineage.
  • Bug Fixes

    • Custom currencies no longer fail lineage validation.
    • Overages are not left as spendable balances.
    • Earnings recognition excludes custom-currency credit-only charges.
    • Invalid or zero-value fiat conversions are rejected.

Greptile Summary

The PR adds ledger-side handling for custom-currency credit_then_invoice overages while preserving native credit lineage and settling the resulting invoice in fiat.

  • Books purchase, immediate consumption, and FX conversion in one transaction group.
  • Routes payment authorization and settlement using the charge’s persisted cost basis.
  • Extends credit-realization lineage identity with the managed custom-currency ID.
  • Adds PostgreSQL-backed coverage for flat-fee, usage-based, progressive accrual, and payment flows.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
openmeter/ledger/chargeadapter/usagebased.go Adds atomic custom-currency overage purchase, consumption, FX conversion, and fiat payment routing for usage-based charges.
openmeter/ledger/chargeadapter/flatfee.go Implements the equivalent custom-currency accrual and payment lifecycle for flat-fee charges.
openmeter/ledger/chargeadapter/helpers.go Centralizes persisted invoice cost-basis resolution and custom-versus-fiat payment identity.
openmeter/ledger/transactions/accrual.go Extends accrual transaction templates to preserve custom-currency charge identity and conversion routing.
openmeter/billing/charges/lineage/adapter/lineage.go Persists and queries lineage using both currency code and custom-currency ID.
openmeter/billing/charges/service/advance.go Omits custom currencies from earnings recognition while retaining recognition for fiat charges.
openmeter/ent/schema/creditrealizationlineage.go Adds nullable custom-currency identity to credit-realization lineage persistence.
tools/migrate/migrations/20260807162052_add_lineage_custom_currency_identity.up.sql Migrates the lineage table to store and index custom-currency identity.

Sequence Diagram

sequenceDiagram
  participant Charge as Custom-currency charge
  participant Ledger
  participant Native as Native custom accounts
  participant Fiat as Fiat receivable
  participant Payment
  Charge->>Ledger: Accrue uncovered custom amount
  Ledger->>Native: Issue purchased credit
  Ledger->>Native: Immediately consume credit
  Ledger->>Fiat: Convert native receivable to rounded fiat amount
  Charge->>Payment: Authorize invoice amount in fiat
  Payment->>Ledger: Settle fiat receivable
Loading

Reviews (11): Last reviewed commit: "fix: remove guards" | Re-trigger Greptile

Context used (3)

@mark-vass-konghq mark-vass-konghq self-assigned this Aug 4, 2026
@mark-vass-konghq mark-vass-konghq added release-note/feature Release note: Exciting New Features area/billing labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Custom-currency overages now create fiat receivables through atomic ledger transactions. Payment flows use invoice currency and persisted cost basis. Lineage stores managed currency identity, and earnings recognition skips custom currencies.

Changes

Custom currency identity and lineage

Layer / File(s) Summary
Currency identity and lineage
openmeter/ent/schema/..., openmeter/billing/charges/lineage/..., openmeter/ledger/recognizer/..., tools/migrate/migrations/*
Lineage stores currency codes and optional custom-currency IDs. Queries match the complete currency identity. Custom currencies no longer fail lineage validation.
Accrual transaction provenance
openmeter/ledger/transactions/accrual.go, openmeter/ledger/chargeadapter/helpers.go, openmeter/ledger/testutils/integration.go
Accrual transactions validate cost-basis currency and preserve source and spend charge identities.

Charge adapter settlement

Layer / File(s) Summary
Charge adapter accrual and payment flow
openmeter/ledger/chargeadapter/flatfee.go, openmeter/ledger/chargeadapter/usagebased.go, openmeter/ledger/chargeadapter/*_customcurrency_test.go
Adapters convert custom overages to fiat, purchase and consume custom currency immediately, and settle invoices with persisted cost basis.
Ledger assertions and documentation
openmeter/ledger/chargeadapter/*_test.go, openmeter/billing/charges/README.md, openmeter/billing/charges/*/handler.go
Tests verify balances, transaction provenance, payment lifecycle, and zero-fiat outcomes. Documentation describes the supported flow and the remaining zero-fiat limitation.

Earnings recognition

Layer / File(s) Summary
Fiat earnings recognition and test wiring
openmeter/billing/charges/service/advance.go, openmeter/billing/charges/service/*_test.go
Advance processing returns only fiat currencies for recognition. Tests cover credit-then-invoice and credit_only behavior with the ledger-backed recognizer.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding ledger-side FX support for custom-currency credit_then_invoice charges.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-cc-fx-support-to-ledger-credit_then_invoice

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.

@mark-vass-konghq
mark-vass-konghq force-pushed the feat/add-cc-fx-support-to-ledger-credit_then_invoice branch from 827444d to 2d176f3 Compare August 4, 2026 16:15
@mark-vass-konghq
mark-vass-konghq marked this pull request as ready for review August 4, 2026 16:39
@mark-vass-konghq
mark-vass-konghq requested a review from a team as a code owner August 4, 2026 16:39

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go (1)

59-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider InexactFloat64() with require.Equal for the balance assertions.

These amounts (0, 10, 40, -10) are well within float precision. require.True(t, x.Equal(y)) prints only false on failure, so a wrong balance gives no numbers to work from. Some calls here add a "got %s" message, but most balance assertions do not. The same pattern repeats at lines 223-224, 240-241, and 246-248.

♻️ Example for the FBO and accrued assertions
-	require.True(t, env.sumBalance(t, fboSubAccount).Equal(alpacadecimal.Zero))
-	require.True(t, env.sumBalance(t, env.customOpenReceivableSubAccountForFlatFee(t, customCurrencyIdentity, &settlementCurrency, costBasis)).Equal(alpacadecimal.Zero))
+	require.Equal(t, 0.0, env.sumBalance(t, fboSubAccount).InexactFloat64())
+	require.Equal(t, 0.0, env.sumBalance(t, env.customOpenReceivableSubAccountForFlatFee(t, customCurrencyIdentity, &settlementCurrency, costBasis)).InexactFloat64())
 
 	// The consumed amount is accrued natively, preserving cost basis and fiat provenance.
 	accruedSubAccount := env.customAccruedSubAccountForFlatFee(t, customCurrencyIdentity, &settlementCurrency, &costBasis)
-	require.True(t, env.sumBalance(t, accruedSubAccount).Equal(alpacadecimal.NewFromInt(40)))
+	require.Equal(t, 40.0, env.sumBalance(t, accruedSubAccount).InexactFloat64())

As per coding guidelines: "When precision permits, compare alpacadecimal.Decimal through InexactFloat64() with require.Equal; inline one-off expected balances."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go` around lines
59 - 71, Update the balance assertions in the affected test, including the
repeated assertions around the FBO, accrued, receivable, and brokerage checks,
to compare each balance’s InexactFloat64() result with an inline expected float
using require.Equal. Preserve the existing expected amounts and apply the same
diagnostic-friendly pattern to the referenced later assertions.

Source: Coding guidelines

openmeter/ledger/chargeadapter/flatfee.go (1)

430-448: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

One invoice cost-basis rule, copied into two adapters. Both helpers encode the same domain rule: fiat charges settle at par, and custom-currency charges settle at the persisted ResolvedCostBasis. The accrual leg and the payment leg must land on the same ledger route, so the two copies have to stay identical forever. Extracting the rule once removes that coupling risk.

  • openmeter/ledger/chargeadapter/flatfee.go#L430-L448: replace flatFeeInvoiceCostBasis with a call to a shared helper that takes the charge currency and the resolved cost-basis state.
  • openmeter/ledger/chargeadapter/usagebased.go#L419-L437: replace usageBasedInvoiceCostBasis with a call to the same shared helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/flatfee.go` around lines 430 - 448, Extract
the duplicated invoice cost-basis rule from flatFeeInvoiceCostBasis in
openmeter/ledger/chargeadapter/flatfee.go:430-448 and usageBasedInvoiceCostBasis
in openmeter/ledger/chargeadapter/usagebased.go:419-437 into one shared helper
accepting the charge currency and resolved cost-basis state. Update both
adapters to call it, preserving par settlement for fiat, persisted
ResolvedCostBasis for custom currencies, and the existing missing-cost-basis
error.
openmeter/billing/charges/service/base_test.go (1)

460-467: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inline this trivial test wrapper.

newFiatCurrency only invokes currencyx.NewFiatCurrency and asserts that it did not fail. Inline those operations at the test site and remove this helper.

As per coding guidelines, “Do not extract trivial or single-use helpers …; inline pass-through wrappers,” and do not add local must-style wrappers.

Proposed change
--- a/openmeter/billing/charges/service/advance_test.go
+++ b/openmeter/billing/charges/service/advance_test.go
@@
-	costBasisIntent := costbasis.NewIntent(costbasis.ManualIntent{
-		FiatCurrency: s.newFiatCurrency("USD"),
+	fiatCurrency, err := currencyx.NewFiatCurrency("USD")
+	s.Require().NoError(err)
+
+	costBasisIntent := costbasis.NewIntent(costbasis.ManualIntent{
+		FiatCurrency: fiatCurrency,
@@
-	_, err := s.Charges.usageBasedService.Create(ctx, usagebased.CreateInput{
+	_, err = s.Charges.usageBasedService.Create(ctx, usagebased.CreateInput{

--- a/openmeter/billing/charges/service/base_test.go
+++ b/openmeter/billing/charges/service/base_test.go
@@
-func (s *BaseSuite) newFiatCurrency(code currencyx.Code) *currencyx.FiatCurrency {
-	s.T().Helper()
-
-	fiatCurrency, err := currencyx.NewFiatCurrency(code)
-	s.Require().NoError(err)
-
-	return fiatCurrency
-}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/service/base_test.go` around lines 460 - 467,
Remove the newFiatCurrency helper method from BaseSuite and inline its
implementation at all call sites within the test file. At each location where
newFiatCurrency is invoked with a code parameter, replace it with a direct call
to currencyx.NewFiatCurrency followed by s.Require().NoError to assert the error
handling inline, then assign the result to the variable as before.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/billing/charges/service/advance_test.go`:
- Around line 353-378: Update the costBasisIntent fixture in the test around
newUsageBasedCharge to use EUR for costBasisIntent.FiatCurrency instead of USD,
while keeping the expected result currency as USD. Leave the charge setup and
assertions otherwise unchanged.

In `@openmeter/ledger/chargeadapter/flatfee_test.go`:
- Around line 1205-1239: Ensure both transactionGroupEntries helpers assert that
the queried ledger entries are non-empty before returning: add
require.NotEmpty(t, entries) in flatfee_test.go at lines 1205-1239 and
usagebased_test.go at lines 906-940, after the query error assertion and before
the return.

---

Nitpick comments:
In `@openmeter/billing/charges/service/base_test.go`:
- Around line 460-467: Remove the newFiatCurrency helper method from BaseSuite
and inline its implementation at all call sites within the test file. At each
location where newFiatCurrency is invoked with a code parameter, replace it with
a direct call to currencyx.NewFiatCurrency followed by s.Require().NoError to
assert the error handling inline, then assign the result to the variable as
before.

In `@openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go`:
- Around line 59-71: Update the balance assertions in the affected test,
including the repeated assertions around the FBO, accrued, receivable, and
brokerage checks, to compare each balance’s InexactFloat64() result with an
inline expected float using require.Equal. Preserve the existing expected
amounts and apply the same diagnostic-friendly pattern to the referenced later
assertions.

In `@openmeter/ledger/chargeadapter/flatfee.go`:
- Around line 430-448: Extract the duplicated invoice cost-basis rule from
flatFeeInvoiceCostBasis in openmeter/ledger/chargeadapter/flatfee.go:430-448 and
usageBasedInvoiceCostBasis in
openmeter/ledger/chargeadapter/usagebased.go:419-437 into one shared helper
accepting the charge currency and resolved cost-basis state. Update both
adapters to call it, preserving par settlement for fiat, persisted
ResolvedCostBasis for custom currencies, and the existing missing-cost-basis
error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b427db5-7588-4c71-8bcd-0b6e16383741

📥 Commits

Reviewing files that changed from the base of the PR and between 887e0ca and 2d176f3.

⛔ Files ignored due to path filters (9)
  • openmeter/ent/db/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (25)
  • openmeter/billing/charges/README.md
  • openmeter/billing/charges/flatfee/handler.go
  • openmeter/billing/charges/lineage/adapter/lineage.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/billing/charges/lineage/service.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/billing/charges/service/advance_test.go
  • openmeter/billing/charges/service/base_test.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/billing/charges/service/usagebased_costbasis_test.go
  • openmeter/billing/charges/usagebased/handler.go
  • openmeter/ent/schema/creditrealizationlineage.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go
  • openmeter/ledger/chargeadapter/flatfee_test.go
  • openmeter/ledger/chargeadapter/helpers.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/ledger/transactions/accrual.go
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.down.sql
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql
💤 Files with no reviewable changes (1)
  • openmeter/billing/charges/service/usagebased_costbasis_test.go

Comment thread openmeter/billing/charges/service/advance_test.go
Comment thread openmeter/ledger/chargeadapter/flatfee_test.go Outdated
@mark-vass-konghq
mark-vass-konghq force-pushed the feat/add-cc-fx-support-to-ledger-credit_then_invoice branch from 16f9347 to 180ae17 Compare August 5, 2026 09:12
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@mark-vass-konghq
mark-vass-konghq force-pushed the feat/add-cc-fx-support-to-ledger-credit_then_invoice branch from 180ae17 to 6a2bacf Compare August 5, 2026 09:12
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (5)
tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql (1)

2-2: 🚀 Performance & Scalability | 🔵 Trivial

Verify the migration lock window before merge.

Changing currency with ALTER COLUMN TYPE requires an ACCESS EXCLUSIVE lock. On a large or busy credit_realization_lineages table, concurrent reads and writes can wait during the migration. Check the table size and expected duration, then use a maintenance window or an additive rollout if the lock is not acceptable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql`
at line 2, Assess the lock impact of the ALTER TABLE operation in migration
20260804095904_add_lineage_custom_currency_identity, including the size and
traffic of credit_realization_lineages and the expected ALTER COLUMN duration.
Schedule execution during an approved maintenance window, or replace the direct
currency type change with an additive rollout if the ACCESS EXCLUSIVE lock is
not acceptable.

Source: Linters/SAST tools

openmeter/ledger/chargeadapter/flatfee_test.go (1)

1227-1248: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Worth hoisting transactionAnnotations into IntegrationEnv too.

This PR already moved the entries query into the shared harness as TransactionGroupEntries. The annotations query is the same shape and appears to exist per test file (the usage-based custom-currency test calls env.transactionAnnotations as well), so moving it next to TransactionGroupEntries would finish the cleanup nicely.

While you are there: transactionBookedAtTimes at line 1217 asserts require.NotEmpty(t, transactions) but this helper does not. Callers are currently protected by require.ElementsMatch on three template codes, so nothing silently passes today, but matching the sibling helper keeps it safe for future callers.

As per coding guidelines: "put suite-wide behavior in the shared harness instead of exposing per-test knobs."

♻️ Proposed assertion tweak (before the wider move)
 		All(t.Context())
 	require.NoError(t, err)
+	require.NotEmpty(t, transactions, "expected at least one ledger transaction for group")
 
 	out := make([]models.Annotations, 0, len(transactions))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/flatfee_test.go` around lines 1227 - 1248,
Move transactionAnnotations from flatFeeHandlerTestEnv into the shared
IntegrationEnv alongside TransactionGroupEntries, updating callers—including
usage-based custom-currency tests—to use the shared helper. Preserve its
namespace/group filtering, ordering, and annotation collection behavior, and add
require.NotEmpty on the queried transactions to match transactionBookedAtTimes.

Source: Coding guidelines

openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go (2)

58-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Decimal assertion style drifts between the two new custom-currency test files. Both files assert the same kinds of balances, but one uses require.True(...Equal(...)) and the other mostly uses InexactFloat64() with require.Equal. All the expected values here (10, 40, 60, -10) are safely representable, so the InexactFloat64 form works everywhere and gives much clearer failure output.

  • openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go#L58-L75: convert the balance and result assertions to require.Equal(t, <float>, ....InexactFloat64()).
  • openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go#L108-L114: convert the accrued-entry amount assertion on line 110 to the same form so the file is uniform.

As per coding guidelines: "When precision permits, compare alpacadecimal.Decimal through InexactFloat64() with require.Equal; inline one-off expected balances."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go` around
lines 58 - 75, The decimal assertions in
openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go lines 58-75
should use require.Equal with InexactFloat64() and inline expected values for
the result and balance checks, replacing Equal-based require.True assertions.
Apply the same assertion style to the accrued-entry amount in
openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go lines 108-114; no
other assertions require changes.

Source: Coding guidelines


728-731: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The currency parameter is now unused here.

After the switch to AccruedSubAccountForCurrency, currency currencyx.Code no longer affects the result. Dropping it (and updating the two call sites) keeps the helper honest about what it needs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go` around
lines 728 - 731, Remove the unused currencyx.Code parameter from
usageBasedHandlerTestEnv.customUnknownAccruedSubAccountForUsageBased, then
update both call sites to stop passing currency while preserving the existing
AccruedSubAccountForCurrency arguments and behavior.
openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go (1)

186-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider given/when/then intent comments for the lifecycle test.

This one walks accrual → authorization → settlement, so a couple of short intent markers would make the phases easy to scan. The existing inline comments already carry most of the meaning, so this is only a polish item.

As per coding guidelines: "Begin non-trivial service or lifecycle subtests with concise given, when, and then intent comments."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go` around lines
186 - 205, Update TestOnFlatFeeCustomCurrencyPaymentLifecycle with concise
given, when, and then intent comments marking the accrual, authorization, and
settlement phases respectively. Keep the existing test logic and inline comments
unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/billing/charges/README.md`:
- Line 162: Update the wording in the charges README so the phrase uses the
hyphenated form, changing the existing “end to end” text to “end-to-end” while
leaving the rest of the sentence unchanged.

---

Nitpick comments:
In `@openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go`:
- Around line 186-205: Update TestOnFlatFeeCustomCurrencyPaymentLifecycle with
concise given, when, and then intent comments marking the accrual,
authorization, and settlement phases respectively. Keep the existing test logic
and inline comments unchanged.

In `@openmeter/ledger/chargeadapter/flatfee_test.go`:
- Around line 1227-1248: Move transactionAnnotations from flatFeeHandlerTestEnv
into the shared IntegrationEnv alongside TransactionGroupEntries, updating
callers—including usage-based custom-currency tests—to use the shared helper.
Preserve its namespace/group filtering, ordering, and annotation collection
behavior, and add require.NotEmpty on the queried transactions to match
transactionBookedAtTimes.

In `@openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go`:
- Around line 58-75: The decimal assertions in
openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go lines 58-75
should use require.Equal with InexactFloat64() and inline expected values for
the result and balance checks, replacing Equal-based require.True assertions.
Apply the same assertion style to the accrued-entry amount in
openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go lines 108-114; no
other assertions require changes.
- Around line 728-731: Remove the unused currencyx.Code parameter from
usageBasedHandlerTestEnv.customUnknownAccruedSubAccountForUsageBased, then
update both call sites to stop passing currency while preserving the existing
AccruedSubAccountForCurrency arguments and behavior.

In
`@tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql`:
- Line 2: Assess the lock impact of the ALTER TABLE operation in migration
20260804095904_add_lineage_custom_currency_identity, including the size and
traffic of credit_realization_lineages and the expected ALTER COLUMN duration.
Schedule execution during an approved maintenance window, or replace the direct
currency type change with an additive rollout if the ACCESS EXCLUSIVE lock is
not acceptable.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 81f48bcf-785e-4211-ab6d-12054d337de4

📥 Commits

Reviewing files that changed from the base of the PR and between 769279e and 6a2bacf.

⛔ Files ignored due to path filters (9)
  • openmeter/ent/db/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (29)
  • openmeter/billing/charges/README.md
  • openmeter/billing/charges/flatfee/charge.go
  • openmeter/billing/charges/flatfee/handler.go
  • openmeter/billing/charges/lineage/adapter/lineage.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/billing/charges/lineage/service.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/billing/charges/service/advance_test.go
  • openmeter/billing/charges/service/base_test.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/billing/charges/service/usagebased_costbasis_test.go
  • openmeter/billing/charges/usagebased/charge.go
  • openmeter/billing/charges/usagebased/handler.go
  • openmeter/ent/schema/creditrealizationlineage.go
  • openmeter/ledger/chargeadapter/creditpurchase_test.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go
  • openmeter/ledger/chargeadapter/flatfee_test.go
  • openmeter/ledger/chargeadapter/helpers.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/ledger/testutils/integration.go
  • openmeter/ledger/transactions/accrual.go
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.down.sql
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql
💤 Files with no reviewable changes (1)
  • openmeter/billing/charges/service/usagebased_costbasis_test.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • openmeter/ent/schema/creditrealizationlineage.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/billing/charges/flatfee/handler.go
  • openmeter/billing/charges/usagebased/handler.go
  • openmeter/ledger/transactions/accrual.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/billing/charges/service/advance_test.go
  • openmeter/billing/charges/lineage/adapter/lineage.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/billing/charges/lineage/service.go

balance queries, and historical migration.
Ledger-backed charge adapters implement this boundary directly:

- credit allocation and correction stay in the custom currency end to end

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the hyphenated form.

Change end to end to end-to-end on Line 162.

🧰 Tools
🪛 LanguageTool

[grammar] ~162-~162: Use a hyphen to join words.
Context: ...rrection stay in the custom currency end to end ([credit_only](#settlement-seman...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/README.md` at line 162, Update the wording in the
charges README so the phrase uses the hyphenated form, changing the existing
“end to end” text to “end-to-end” while leaving the rest of the sentence
unchanged.

Source: Linters/SAST tools

@mark-vass-konghq
mark-vass-konghq force-pushed the feat/add-cc-fx-support-to-ledger-credit_then_invoice branch from 6a2bacf to 4f9580a Compare August 5, 2026 11:03
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql (1)

2-2: 🩺 Stability & Availability | 🔵 Trivial

Verify the migration lock impact before deployment.

ALTER TABLE ... ALTER COLUMN ... TYPE ... takes an ACCESS EXCLUSIVE lock. On a busy credit_realization_lineages table, lock acquisition can block reads and writes. Confirm that this widening is metadata-only for the supported PostgreSQL version and fits the migration window or lock-timeout policy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql`
at line 2, Review the migration statement for credit_realization_lineages before
deployment: verify that widening currency to character varying(24) is
metadata-only on the supported PostgreSQL version, assess its ACCESS EXCLUSIVE
lock impact during the migration window, and apply the project’s established
lock-timeout policy if required.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql`:
- Line 2: Review the migration statement for credit_realization_lineages before
deployment: verify that widening currency to character varying(24) is
metadata-only on the supported PostgreSQL version, assess its ACCESS EXCLUSIVE
lock impact during the migration window, and apply the project’s established
lock-timeout policy if required.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 25672f2f-5af8-442d-8d47-9f79a2ab12a8

📥 Commits

Reviewing files that changed from the base of the PR and between 0f8b7cc and 4f9580a.

⛔ Files ignored due to path filters (9)
  • openmeter/ent/db/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/creditrealizationlineage.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/creditrealizationlineage_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (29)
  • openmeter/billing/charges/README.md
  • openmeter/billing/charges/flatfee/charge.go
  • openmeter/billing/charges/flatfee/handler.go
  • openmeter/billing/charges/lineage/adapter/lineage.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/billing/charges/lineage/service.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/billing/charges/service/advance_test.go
  • openmeter/billing/charges/service/base_test.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/billing/charges/service/usagebased_costbasis_test.go
  • openmeter/billing/charges/usagebased/charge.go
  • openmeter/billing/charges/usagebased/handler.go
  • openmeter/ent/schema/creditrealizationlineage.go
  • openmeter/ledger/chargeadapter/creditpurchase_test.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go
  • openmeter/ledger/chargeadapter/flatfee_test.go
  • openmeter/ledger/chargeadapter/helpers.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/ledger/testutils/integration.go
  • openmeter/ledger/transactions/accrual.go
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.down.sql
  • tools/migrate/migrations/20260804095904_add_lineage_custom_currency_identity.up.sql
💤 Files with no reviewable changes (1)
  • openmeter/billing/charges/service/usagebased_costbasis_test.go
🚧 Files skipped from review as they are similar to previous changes (25)
  • openmeter/ledger/recognizer/service_test.go
  • openmeter/billing/charges/usagebased/handler.go
  • openmeter/billing/charges/lineage/service/service.go
  • openmeter/billing/charges/usagebased/charge.go
  • openmeter/ledger/chargeadapter/flatfee_test.go
  • openmeter/billing/charges/flatfee/charge.go
  • openmeter/ledger/chargeadapter/usagebased_test.go
  • openmeter/ledger/chargeadapter/creditpurchase_test.go
  • openmeter/billing/charges/service/advance.go
  • openmeter/ent/schema/creditrealizationlineage.go
  • openmeter/billing/charges/service/base_test.go
  • openmeter/billing/charges/lineage/lineage_test.go
  • openmeter/ledger/recognizer/recognize.go
  • openmeter/ledger/chargeadapter/helpers.go
  • openmeter/ledger/transactions/accrual.go
  • openmeter/ledger/chargeadapter/flatfee.go
  • openmeter/ledger/testutils/integration.go
  • openmeter/billing/charges/flatfee/handler.go
  • openmeter/ledger/chargeadapter/usagebased.go
  • openmeter/billing/charges/lineage/adapter/lineage.go
  • openmeter/billing/charges/service/lineage_test.go
  • openmeter/billing/charges/lineage/service.go
  • openmeter/ledger/chargeadapter/flatfee_customcurrency_test.go
  • openmeter/billing/charges/service/advance_test.go
  • openmeter/ledger/chargeadapter/usagebased_customcurrency_test.go

@mark-vass-konghq
mark-vass-konghq force-pushed the feat/add-cc-fx-support-to-ledger-credit_then_invoice branch from 7f8590d to 273d3e3 Compare August 7, 2026 16:41
@github-actions

Copy link
Copy Markdown

This pull request has been inactive for 45 days. Push a commit or leave a comment describing the next step to remove the stale label. Maintainers can apply keep-open with a concrete reason when longer-lived work is intentional. This pull request will close automatically after another 15 days without activity.

@github-actions github-actions Bot added the stale Inactive pull request awaiting author or maintainer follow-up label Sep 22, 2026

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

area/billing release-note/feature Release note: Exciting New Features stale Inactive pull request awaiting author or maintainer follow-up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant