Skip to content

Fix billing adapters and add a reproducible GKE/Stripe example - #3

Merged
saiyam1814 merged 8 commits into
mainfrom
fix/adapter-issues
Oct 16, 2026
Merged

saiyam1814 merged 8 commits into
mainfrom
fix/adapter-issues

Conversation

@saiyam1814

@saiyam1814 saiyam1814 commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

This change fixes billing adapter failures that could stall usage delivery or produce incorrect reconciliation results, and adds a reproducible GKE GPU example with Stripe test billing.

This PR builds on the adapter guides merged into main in PR #2.

Billing behavior

  • Stripe: A plan with more than 20 prices no longer blocks usage delivery for every tenant. Automatic subscription setup is rejected permanently for that plan, while customer creation and usage delivery continue.
  • Metronome: Changing a tenant's rate card hands over its managed contracts at UTC midnight. A future contract is archived when needed for a same-day change; existing managed contracts can be adopted after local state loss. Contracts vBilling did not create are left alone. Existing overlapping contracts require an invoice review for the overlap.
  • Metronome with Stripe: Customers found by alias or configured by ID receive the Stripe billing-provider configuration when they do not already have one.
  • Reconciliation: SKU, region, project and cluster filters select tenant/metric pairs, then compare their complete totals with the billing provider's customer totals.
  • Billing states: The billing-state API is available without a Stripe or Metronome webhook secret. Webhook updates still require their own secrets.
  • Permanent rejections: Rejected webhook catalog events no longer pause usage indefinitely. Offboarding records each destination's result and stops retrying permanent rejections. Lago subscription termination treats a 404 as already complete.

Reproducible GPU example

examples/gke-stripe documents shared L4 GPU allocation, an optional private A100 worker, the native Go collector, customer/meter mapping, Stripe Products/Prices/subscriptions, usage verification, recovery and scoped cleanup. Helpers keep credentials and runtime state outside Git, restrict Stripe operations to test mode, and validate resource ownership before deletion. CI runs the offline helper tests and a focused credential-pattern check.

CI reliability

Dispatcher and API tests now wait for their background dispatcher to exit after cancellation, including failure paths. This prevents final cursor writes from racing with spool and temporary-directory cleanup. Production Go behavior is unchanged by this test-lifecycle fix.

Validation

  • go vet ./...
  • go test -race ./...
  • go test -race ./internal/pipeline ./internal/api -count=100
  • 28 offline demo-helper tests, shell syntax checks, Helm lint/render and credential scans passed during example validation.
  • The original hardware demo ran with real GPUs and Stripe test usage. The generalized example has not been rerun as a fresh end-to-end cloud deployment.

Adapter tests cover the billing fixes, including Metronome request schemas derived from its published OpenAPI specification. The Metronome changes have not been tested against a live Metronome account. Enforcement for tenant clusters with their own nodes remains a documented limitation.

Automatic subscriptions refused such a plan with a retryable error, so
the pipeline retried that tenant forever and held up Stripe delivery for
every tenant. The error is now permanent: the tenant is logged as
rejected, its customer and usage keep flowing, and everyone else is
unaffected.
A rate card change created a contract on the new rate card and left the
old one open, so usage was rated twice. vBilling now keeps one open
contract per tenant: the new contract starts at UTC midnight and every
other open vBilling contract of the tenant ends there, or is archived if
it would not have started by then. Contracts vBilling did not create are
never touched.

Uniqueness keys carry the start date (vbilling:<rate card>@<date>:<tenant
hash>), so a tenant can return to an earlier rate card; v0.2.0 keys are
recognized and those contracts adopted. A tenant that loses its state
adopts its open contract instead of creating another.

With the Stripe link on, customers that vBilling did not create (found by
alias or pinned by ID) now get the Stripe billing provider configuration
when they have none.

The new requests are validated against Metronome's published OpenAPI
schemas, added to testdata.
Billing platforms report totals per tenant and metric, but a sku, region,
project or cluster filter summed only the matching ledger events, so a
tenant with two SKUs always mismatched. Filters now pick the tenant and
metric pairs to compare, and each row covers all of that tenant's usage
of the metric.
PUT /api/v1/billing-states/{tenant} returned 404 unless a Stripe or
Metronome webhook secret was set, so Lago and custom billing platforms
could not drive enforcement. The enforcer now always exists; webhooks
still need their own secrets.
- A webhook receiver that rejects the catalog event with 400, 413 or 422
  no longer pauses usage delivery: the catalog is informational.
- Offboarding records each destination's result: a permanent rejection
  is final, and destinations that succeeded are not called again while
  another one retries.
- Lago treats 404 on terminating a subscription as already done, and its
  DELETE errors are classified like its other requests.
Stripe plans over 20 prices, Metronome contract handover and Stripe
links, filtered reconciliation, and billing states without webhook
secrets. Also corrects the gpu_profile example in a code comment.
@saiyam1814 saiyam1814 changed the title Fix adapter issues found while documenting the billing adapters Fix billing adapters and add a reproducible GKE/Stripe example Oct 15, 2026
@saiyam1814
saiyam1814 changed the base branch from docs/billing-adapters to main October 16, 2026 05:27
@saiyam1814
saiyam1814 merged commit c7d9c8d into main Oct 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant