Skip to content

feat: add wire for charges#4058

Merged
turip merged 5 commits into
mainfrom
chore/wire-charges
Apr 2, 2026
Merged

feat: add wire for charges#4058
turip merged 5 commits into
mainfrom
chore/wire-charges

Conversation

@turip
Copy link
Copy Markdown
Member

@turip turip commented Apr 2, 2026

Overview

This patch initializes charges and ledger when the credits.enabled = true everywhere where we are using billing related logic.

The new BillingRegistry type enforces that whatever uses billing must be initializing it alongside charges.

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Added a charges subsystem enabling flat-fee, usage-based, and credit-purchase billing flows.
  • Refactor

    • Reorganized billing into a registry-based architecture separating core billing from optional charges.
    • Added a ledger routing validator.
    • Simplified worker options validation by removing a required billing adapter.
  • Documentation

    • Added testing guidance to keep domain test helpers independent from application wiring.
  • Chores

    • Updated startup and test wiring to use the new billing/ledger composition.

@turip turip requested a review from a team as a code owner April 2, 2026 05:36
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c17f075-d80d-4d3c-850b-a2a2b7e7831f

📥 Commits

Reviewing files that changed from the base of the PR and between 6eaacd1 and 7c2b20e.

📒 Files selected for processing (1)
  • openmeter/ledger/testutils/deps.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • openmeter/ledger/testutils/deps.go

📝 Walkthrough

Walkthrough

Adds a BillingRegistry (billing.Service plus optional ChargesRegistry), implements a new charges factory stack and ledger routing validator provider, and updates wiring and tests to construct ledger/charges/locker components from lower-level package constructors instead of app/common wiring.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Added guidance that domain test helpers must build dependencies from underlying package constructors (repos, adapters, services, lockr) instead of importing app wiring to avoid test-only import cycles.
Billing & Charges
app/common/billing.go, app/common/charges.go
Introduced BillingRegistry and ChargesRegistry; refactored billing constructor to unexported newBillingService; added NewBillingRegistry, NewBillingCustomerOverrideService, and many NewCharges* factory functions; conditional charges stack assembly when credits enabled.
Ledger providers
app/common/ledger.go, app/common/openmeter_billingworker.go
Added NewLedgerRoutingValidator provider and included LedgerStack in billing-worker wiring; updated billing worker options to accept BillingRegistry.
Server & app wiring
cmd/server/wire.go, cmd/server/wire_gen.go, cmd/server/main.go, app/common/app.go
Replaced Application.Billing with BillingRegistry; switched wiring to construct ledger components and call NewBillingRegistry(...); updated app functions to accept BillingRegistry and use .Billing.
Billing worker / jobs wiring
cmd/billing-worker/wire_gen.go, cmd/jobs/internal/wire.go, cmd/jobs/internal/wire_gen.go
Replaced direct billing.Service construction with ledger-backed NewBillingRegistry; added ledger/resolver components and threaded BillingRegistry into downstream constructors and Application structs.
Charges construction (new file)
app/common/charges.go
New factories for meta adapter, flat-fee/usage-based/credit-purchase handlers, adapters and services; newChargesRegistry composes the full charges stack and returns *ChargesRegistry.
Tests / testutils
openmeter/ledger/testutils/deps.go, openmeter/ledger/testutils/integration.go, test/credits/sanity_test.go
Tests now build ledger deps from concrete adapter/service constructors and lockr.NewLocker; replaced manual wiring with ledgertestutils.InitDeps; updated imports and logger usage.
Billing worker options
openmeter/billing/worker/worker.go
Removed BillingAdapter from WorkerOptions and eliminated its nil-check in validation.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant App as Application (wire)
    participant Ledger as LedgerStack
    participant BillingReg as BillingRegistry
    participant BillingSvc as Billing.Service
    participant ChargesReg as ChargesRegistry
    participant DB as Database

    Client->>App: start/init (reads conf.Credits)
    App->>Ledger: construct LedgerStack (repos, validators, locker)
    App->>BillingReg: call NewBillingRegistry(ledger, conf.Credits, ...)
    BillingReg->>BillingSvc: newBillingService(...)
    alt credits enabled
        BillingReg->>ChargesReg: newChargesRegistry(...) (build adapters, handlers, services)
        ChargesReg->>DB: initialize adapters/ledgers using DB
        BillingReg->>BillingSvc: attach ChargesReg
    end
    BillingReg-->>App: return BillingRegistry (BillingSvc + optional ChargesReg)
    App->>BillingSvc: use for provisioning / router / workers
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • tothandras
  • GAlexIHU
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: introducing wire configuration for the charges subsystem, bundled with a new BillingRegistry.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/wire-charges

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/common/charges.go (1)

223-234: Consider a small deps struct for newChargesRegistry.

Ten positional arguments is starting to get pretty brittle here, especially with several related billing / ledger services in the mix. Bundling them into a config struct would make the wiring easier to scan and safer to extend.

As per coding guidelines, **/*.go: In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/common/charges.go` around lines 223 - 234, The newChargesRegistry
function signature is brittle with ten positional parameters; create a small
deps/config struct (e.g., ChargesDeps or NewChargesConfig) that contains fields
for *entdb.Client, *slog.Logger, *lockr.Locker, billing.Service, rating.Service,
feature.FeatureConnector, streaming.Connector, ledger.Ledger,
ledger.AccountResolver, and ledgeraccount.Service, update newChargesRegistry to
accept that struct (or pointer) and construct the ChargesRegistry from its
fields, then update all callers to build and pass the struct; keep the
ChargesRegistry type and internal field names unchanged to minimize churn and
add a short constructor helper if needed to preserve call-site clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/common/charges.go`:
- Around line 223-234: The newChargesRegistry function signature is brittle with
ten positional parameters; create a small deps/config struct (e.g., ChargesDeps
or NewChargesConfig) that contains fields for *entdb.Client, *slog.Logger,
*lockr.Locker, billing.Service, rating.Service, feature.FeatureConnector,
streaming.Connector, ledger.Ledger, ledger.AccountResolver, and
ledgeraccount.Service, update newChargesRegistry to accept that struct (or
pointer) and construct the ChargesRegistry from its fields, then update all
callers to build and pass the struct; keep the ChargesRegistry type and internal
field names unchanged to minimize churn and add a short constructor helper if
needed to preserve call-site clarity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77269598-1fdf-49ba-b437-fd9dfee9331e

📥 Commits

Reviewing files that changed from the base of the PR and between 233aa34 and 867ecc6.

📒 Files selected for processing (1)
  • app/common/charges.go

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
app/common/charges.go (1)

32-65: Optional: Consider adding brief doc comments to exported functions.

Functions like NewChargesMetaAdapter, NewChargesFlatFeeHandler, etc. are exported and could benefit from a one-liner doc comment explaining what they create. This helps with IDE autocomplete and godoc.

📝 Example doc comments
+// NewChargesMetaAdapter creates the shared metadata adapter for charges.
 func NewChargesMetaAdapter(
 	db *entdb.Client,
 	logger *slog.Logger,
 ) (meta.Adapter, error) {
+// NewChargesFlatFeeHandler creates the ledger-backed handler for flat-fee charges.
 func NewChargesFlatFeeHandler(
 	ledgerService ledger.Ledger,
 	accountResolver ledger.AccountResolver,
 	accountService ledgeraccount.Service,
 ) flatfee.Handler {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/common/charges.go` around lines 32 - 65, Add single-line godoc comments
above each exported constructor function to describe what they create and
return: e.g., add comments for NewChargesMetaAdapter, NewChargesFlatFeeHandler,
NewChargesCreditPurchaseHandler, and NewChargesUsageBasedHandler using the
standard Go comment form "// NewChargesMetaAdapter ...", briefly stating the
purpose (e.g., creates a charges meta.Adapter connected to entdb.Client and
logger) and the return type; keep each comment one sentence and placed
immediately above the corresponding function.
app/common/app.go (1)

93-107: Minor: Parameter naming inconsistency.

NewAppSandboxFactory uses billing BillingRegistry while the other functions use billingRegistry BillingRegistry. This is a small thing, but consistent naming helps readability.

🔧 Optional rename for consistency
 func NewAppSandboxFactory(
 	appsConfig config.AppsConfiguration,
 	appService app.Service,
-	billing BillingRegistry,
+	billingRegistry BillingRegistry,
 ) (*appsandbox.Factory, error) {
 	factory, err := appsandbox.NewFactory(appsandbox.Config{
 		AppService:     appService,
-		BillingService: billing.Billing,
+		BillingService: billingRegistry.Billing,
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/common/app.go` around lines 93 - 107, Rename the parameter in
NewAppSandboxFactory from billing to billingRegistry to match the naming used
across other functions; update the function signature
(NewAppSandboxFactory(appsConfig config.AppsConfiguration, appService
app.Service, billingRegistry BillingRegistry)) and all internal references
(e.g., BillingService: billingRegistry.Billing) so the BillingRegistry type name
and usage are consistent with other functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/common/app.go`:
- Around line 93-107: Rename the parameter in NewAppSandboxFactory from billing
to billingRegistry to match the naming used across other functions; update the
function signature (NewAppSandboxFactory(appsConfig config.AppsConfiguration,
appService app.Service, billingRegistry BillingRegistry)) and all internal
references (e.g., BillingService: billingRegistry.Billing) so the
BillingRegistry type name and usage are consistent with other functions.

In `@app/common/charges.go`:
- Around line 32-65: Add single-line godoc comments above each exported
constructor function to describe what they create and return: e.g., add comments
for NewChargesMetaAdapter, NewChargesFlatFeeHandler,
NewChargesCreditPurchaseHandler, and NewChargesUsageBasedHandler using the
standard Go comment form "// NewChargesMetaAdapter ...", briefly stating the
purpose (e.g., creates a charges meta.Adapter connected to entdb.Client and
logger) and the return type; keep each comment one sentence and placed
immediately above the corresponding function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d32c8f7a-6f87-45d2-8850-a142111a4f27

📥 Commits

Reviewing files that changed from the base of the PR and between 867ecc6 and 4b44259.

📒 Files selected for processing (16)
  • AGENTS.md
  • app/common/app.go
  • app/common/billing.go
  • app/common/charges.go
  • app/common/ledger.go
  • app/common/openmeter_billingworker.go
  • cmd/billing-worker/wire_gen.go
  • cmd/jobs/internal/wire.go
  • cmd/jobs/internal/wire_gen.go
  • cmd/server/main.go
  • cmd/server/wire.go
  • cmd/server/wire_gen.go
  • openmeter/billing/worker/worker.go
  • openmeter/ledger/testutils/deps.go
  • openmeter/ledger/testutils/integration.go
  • test/credits/sanity_test.go
💤 Files with no reviewable changes (1)
  • openmeter/billing/worker/worker.go
✅ Files skipped from review due to trivial changes (2)
  • AGENTS.md
  • openmeter/ledger/testutils/deps.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • cmd/server/wire.go
  • cmd/server/main.go
  • cmd/jobs/internal/wire.go
  • app/common/openmeter_billingworker.go
  • app/common/ledger.go
  • cmd/billing-worker/wire_gen.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/common/billing.go (1)

124-145: Consider collapsing the credits-only deps into one input.

locker, ledgerService, accountResolver, and accountService are only used inside the creditsConfig.Enabled branch, but keeping them on the top-level constructor means every Wire graph has to grow with that optional path. You can already see that in cmd/server/wire_gen.go Lines 309-316, where the ledger stack is built before the branch is evaluated. A small ChargesDeps bundle or factory would keep this constructor easier to read and evolve.

As per coding guidelines: "In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/common/billing.go` around lines 124 - 145, The constructor
NewBillingRegistry currently takes locker, ledgerService, accountResolver, and
accountService even though they are only used when creditsConfig.Enabled; create
a small composite type (e.g., ChargesDeps) or a provider factory (e.g.,
ChargesDepsFactory func() (*ChargesDeps, error)) that groups locker,
ledgerService, accountResolver, and accountService, change NewBillingRegistry to
accept that bundle or factory instead of the individual symbols, and update the
constructor body to only call or dereference the bundle when
creditsConfig.Enabled (keep existing logic that uses those symbols but reference
them via ChargesDeps.*); also update your dependency injection/wire wiring to
construct the ChargesDeps only for the credits-enabled branch so the top-level
graph no longer includes ledger/locker/account providers unconditionally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/common/billing.go`:
- Around line 124-145: The constructor NewBillingRegistry currently takes
locker, ledgerService, accountResolver, and accountService even though they are
only used when creditsConfig.Enabled; create a small composite type (e.g.,
ChargesDeps) or a provider factory (e.g., ChargesDepsFactory func()
(*ChargesDeps, error)) that groups locker, ledgerService, accountResolver, and
accountService, change NewBillingRegistry to accept that bundle or factory
instead of the individual symbols, and update the constructor body to only call
or dereference the bundle when creditsConfig.Enabled (keep existing logic that
uses those symbols but reference them via ChargesDeps.*); also update your
dependency injection/wire wiring to construct the ChargesDeps only for the
credits-enabled branch so the top-level graph no longer includes
ledger/locker/account providers unconditionally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1755d0e8-dbaa-4e84-9828-cfff9495ca55

📥 Commits

Reviewing files that changed from the base of the PR and between 4b44259 and 6eaacd1.

📒 Files selected for processing (2)
  • app/common/billing.go
  • cmd/server/wire_gen.go

@turip turip merged commit e443895 into main Apr 2, 2026
24 checks passed
@turip turip deleted the chore/wire-charges branch April 2, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants