Skip to content

feat: auto top ups#821

Merged
johnyeocx merged 10 commits intodevfrom
charlie/eng-1060-enable-auto-top-ups
Mar 4, 2026
Merged

feat: auto top ups#821
johnyeocx merged 10 commits intodevfrom
charlie/eng-1060-enable-auto-top-ups

Conversation

@charlietlamb
Copy link
Contributor

@charlietlamb charlietlamb commented Feb 25, 2026


Summary by cubic

Adds end-to-end auto top-ups for prepaid one-off features: when a customer’s balance drops below a threshold, we create/pay a Stripe invoice, add credits, and update cache/DB, with a Balance sheet UI to configure it. Stores a flattened auto_topups list on the customer to complete ENG‑1060.

  • New Features

    • Auto top-up job and flow with shared attach lock, burst suppression, and rate limits; computes one-off price, invoices in Stripe, pays, and increments entitlement balance + option packs.
    • Triggers on Redis/Postgres deductions and the check API; new SQS job (JobName.AutoTopUp) and workflows.triggerAutoTopUp.
    • Write-through cache/DB sync: Redis Lua for atomic cusEnt balance (JSON.NUMINCRBY), cusProduct field updates, and invoice upserts; new customerEntitlementActions.adjustBalanceDbAndCache and customerProductActions.updateDbAndCache.
    • Invoices: unified actions to upsert/update from Stripe with cache write-through; void open invoices and sync cache; skip webhook-driven cache nukes for manual invoices.
    • API/CLI/UI: CustomerBillingControls schema; updateCustomer maps billing_controls → customers.auto_topups and updates cached FullCustomer; Autumn CLI supports billing_controls; Auto Top-Up section in Balance Edit sheet.
    • Cache reads normalized via schema to handle Redis cjson quirks; FullCustomer invoices deduplicated and consistently ordered.
  • Bug Fixes

    • Correct granted_balance computation in Balance Edit sheet when prepaid features have usage.
    • Hardened webhook/worker paths so invoice changes persist to DB and cache; reduced unnecessary cache nukes on customer updates.

Written for commit 8a8f881. Summary will update on new commits.

Greptile Summary

Added initial customer billing control schema to support auto top-up functionality for customer features.

Key changes:

  • API changes: New billing_controls field added to customer schemas (CustomerDataSchema, BaseApiCustomerSchema, CustomerSchema) and database table
  • API changes: New billingControlModels.ts file defines schemas for auto top-up configuration including thresholds, quantities, and purchase limits per billing interval
  • Bug fixes: Critical syntax error in billingControlModels.ts line 5 - uses z.enum(BillingInterval) instead of z.nativeEnum(BillingInterval) for TypeScript enum validation

Note: The PR title contains a typo: "cutsomer" should be "customer"

Confidence Score: 2/5

  • Not safe to merge - contains a syntax error that will cause runtime validation failures
  • Score reflects a critical syntax error in the new billing controls schema. Using z.enum() with a TypeScript enum will cause Zod validation to fail at runtime. The rest of the implementation is well-structured and consistent with the codebase patterns.
  • shared/models/cusModels/billingControlModels.ts requires immediate attention - fix the enum validation syntax

Important Files Changed

Filename Overview
shared/models/cusModels/billingControlModels.ts New billing controls schema with syntax error - uses z.enum() instead of z.nativeEnum() for TypeScript enum
shared/api/common/customerData.ts Added billing_controls field to CustomerDataSchema - correctly marked as optional
shared/api/customers/baseApiCustomer.ts Added billing_controls field to BaseApiCustomerSchema - correctly marked as nullish
shared/models/cusModels/cusModels.ts Added billing_controls field to CustomerSchema - correctly marked as nullish
shared/models/cusModels/cusTable.ts Added billing_controls column to customers table - correctly typed as jsonb with CustomerBillingControls type

Last reviewed commit: 6504a91

@vercel
Copy link

vercel bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
autumn-vite Ready Ready Preview, Comment Mar 4, 2026 0:16am

Request Review

@use-tusk
Copy link

use-tusk bot commented Feb 25, 2026

⚠️ Additional setup required (1a15b4a) View output ↗

Tip

New to Tusk? Learn more here.
Follow the setup instructions so Tusk can start generating tests.


View check history

Commit Status Output Created (UTC)
6504a91 ⚠️ Additional setup required Output Feb 25, 2026 11:39AM
1a15b4a ⚠️ Additional setup required Output Feb 25, 2026 12:30PM

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 5 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 9 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="shared/models/cusModels/cusModels.ts">

<violation number="1" location="shared/models/cusModels/cusModels.ts:21">
P1: Schema mismatch: `CustomerSchema` now uses a flat `auto_topup` array field, but `CustomerDataSchema` and `BaseApiCustomerSchema` still use `billing_controls: CustomerBillingControlsSchema` (a wrapper object containing `auto_topup`). This discrepancy between the data model and API schemas will cause data mapping failures — the field name and nesting structure differ across layers. Either the API schemas should be updated to match, or this model should stay consistent with them.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 23 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/src/_luaScriptsV2/incrementCusEntBalance.lua">

<violation number="1" location="server/src/_luaScriptsV2/incrementCusEntBalance.lua:48">
P1: `JSON.NUMINCRBY` with a JSONPath (`$...`) returns a JSON array string (e.g. `"[42]"`), not a plain number. `tonumber("[42]")` returns `nil` in Lua, so `new_balance` will always be `null` in the response. You need to decode the array first.</violation>
</file>

<file name="server/src/internal/balances/autoTopUp/handleAutoTopUpJob.ts">

<violation number="1" location="server/src/internal/balances/autoTopUp/handleAutoTopUpJob.ts:136">
P2: Lock cleanup is fragile: the code between lock acquisition (step 6) and the `try/finally` (step 8) is not protected. If `clearLock` in step 7 throws, or if future code is added between these steps, the lock will leak until TTL expiry. Wrap all post-lock logic in a single `try/finally` immediately after acquiring the lock.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@charlietlamb charlietlamb changed the title feat: add intial cutsomer billing control schema feat: auto top ups Feb 26, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 15 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/src/internal/balances/autoTopUp/executeAutoTopUp.ts">

<violation number="1" location="server/src/internal/balances/autoTopUp/executeAutoTopUp.ts:87">
P1: Division-by-zero risk: `??` does not guard against `billing_units === 0`. If the price config has `billing_units: 0`, `topUpPacks` becomes `Infinity`, which will produce invalid Stripe invoice amounts. Use `|| 1` instead of `?? 1`, or add an explicit zero-guard.</violation>
</file>

<file name="server/src/internal/billing/v2/execute/executeAutumnActions/updateCustomerEntitlements.ts">

<violation number="1" location="server/src/internal/billing/v2/execute/executeAutumnActions/updateCustomerEntitlements.ts:59">
P2: The `||` fallback between `customer_id` and `internal_customer_id` is fragile and may produce the wrong cache key. These are semantically different identifiers (external vs internal), and the cache key must match whichever ID was used when the `FullCustomer` cache entry was originally written. If the wrong ID is selected here, the Redis increment will target a non-existent key and the real cached balance will remain stale. Consider determining which field the cache is keyed on and using that consistently, or using `??` if the intent is to only fall back on `null`/`undefined` (not empty string).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 25 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/src/internal/balances/autoTopUp/autoTopup.ts">

<violation number="1" location="server/src/internal/balances/autoTopUp/autoTopup.ts:116">
P2: Handle lock-conflict (429) errors from acquireLock so duplicate auto‑topup jobs skip instead of erroring and generating Sentry/log noise.</violation>
</file>

<file name="vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx">

<violation number="1" location="vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx:505">
P1: Bug: `prepaidAllowance` is computed as `defaultGPB - defaultBalance`, which equals **usage**, not the actual prepaid allowance. This causes an incorrect `granted_balance` to be sent to the balance-update API whenever usage > 0.

The old code used `cusEntsToPrepaidQuantity(...)` directly. You should do the same here — either import `cusEntsToPrepaidQuantity` + `nullish` and compute it from `selectedCusEnt`/`entityId` (both available as props), or expose the value from `useBalanceEditForm`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@johnyeocx johnyeocx merged commit 9844524 into dev Mar 4, 2026
6 of 9 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.

2 participants