Skip to content

fix(billing): surface wallet credits to pentest + bg-check UIs#2755

Merged
tofikwest merged 2 commits intomainfrom
fix/billing-credit-wallet-ui-gating
May 5, 2026
Merged

fix(billing): surface wallet credits to pentest + bg-check UIs#2755
tofikwest merged 2 commits intomainfrom
fix/billing-credit-wallet-ui-gating

Conversation

@tofikwest
Copy link
Copy Markdown
Contributor

@tofikwest tofikwest commented May 5, 2026

Summary

  • /v1/billing/status now returns creditBalances (per-product wallet balances). Pentest page + BG-check wizard add wallet credits to their displayed allowance, matching the backend's actual consumption logic.
  • Fixes the case: trial used → admin grants 5 credits → user still sees "View plans" and is rerouted to billing instead of being able to start a scan.

Why this was wrong

  • BillingEntitlementsService.tryConsumeIncludedUsageForProduct already falls back to BillingCreditsService.tryConsumeForProduct when the Stripe subscription is missing or exhausted (apps/api/src/billing/billing-entitlements.service.ts:43-83). The backend would happily run the scan.
  • But BillingService.getStatus only returned subscriptions. Both the pentest SplitView and getBackgroundChecksRemaining computed allowance from subscription remainder alone, so admin grants were invisible to the customer UI.
  • Result: New Scan button → /billing/add-ons/penetration-tests, blocking a request the API would have accepted.

Fix

API

  • BillingStatus: added creditBalances: Array<{ productKey, balance }>.
  • BillingService.getStatus: injects BillingCreditsService (@Optional() so existing test ctors keep compiling), fetches balances, aggregates per product (multiple SKU-scoped rows can share a productKey).

Pentest UI

  • New pure helper getPentestAllowance(billingStatus)subscriptionRemaining + walletBalance. planRequired is true only when neither exists.
  • SplitView.tsx now delegates to the helper.

Background-check UI

  • getBackgroundChecksRemaining now adds wallet credits and returns wallet-only when no subscription exists (instead of null), so the wizard reaches the form step.
  • Local BackgroundCheckBillingStatus mirror gained the optional creditBalances field.

Non-breaking guarantees

  • creditBalances is purely additive on the API and optional on every frontend type — every existing consumer keeps compiling.
  • BillingCreditsService injection is @Optional(); the 11 existing new BillingService(...) test calls work unchanged.
  • For users without wallet credits: walletBalance = 0 → identical pre-fix behavior.

Tests

  • apps/api/src/billing/billing.service.spec.ts — 2 new jest cases:
    • getStatus aggregates wallet balances per product (multiple rows summed).
    • Returns [] when credits service is absent.
  • pentest-allowance.test.ts — 8 vitest cases (loading, no plan, trial, wallet only, sub exhausted + wallet, negative-clamp, wrong product, cancelled subscription).
  • useEmployeeBackgroundCheckData.test.ts — 7 vitest cases (mirrors above for BG check).

All 13 + 15 = 28 tests pass. No existing test was modified except adding new cases.

Test plan

  • Org with exhausted pentest trial: admin grants 5 pentest credits → reload pentest page → "+ New Scan (Plan)" button appears (not "View plans"). Click → routes to /security/penetration-tests/new. Submit → scan starts; wallet drops to 4.
  • Same flow for background checks: exhausted BG-check subscription + admin grant via POST /v1/admin/organizations/:orgId/credits {productKey:'background_check', quantity:5} → BG-check wizard shows the request form and the request goes through.
  • Org with no plan AND no credits: still sees "View plans" CTA on both surfaces.
  • Org with active subscription + no credits: behavior unchanged from main.

🤖 Generated with Claude Code


Summary by cubic

Surfaced admin-granted wallet credits in pentest and background-check UIs by adding aggregated credit balances to /v1/billing/status and updating allowance logic to match backend consumption. This fixes false paywalls when trials are exhausted but wallet credits exist.

  • Bug Fixes
    • API: Added creditBalances to BillingStatus; BillingService.getStatus aggregates wallet balances per product; injected BillingCreditsService with @Optional(); returns [] when credits service is absent.
    • Pentest UI: New getPentestAllowance helper; SplitView now uses subscription remainder + wallet credits to choose between "+ New Scan" and "View plans".
    • Background-check UI: getBackgroundChecksRemaining adds wallet credits and enables wallet-only flows when no subscription exists; local billing types gained optional creditBalances.
    • Types/defaults: Updated settings billing types and emptyBillingStatus (creditBalances: []); field is optional on frontend types and treated as 0 when absent.
    • Tests: Added coverage for API aggregation and UI allowance logic (pentest + background-check).

Written for commit 672e901. Summary will update on new commits.

The backend already falls back from Stripe subscription to the
BillingCreditBalance wallet when an active subscription is missing or
exhausted, but `/v1/billing/status` only returned subscription data.
Both the pentest page and the background-check wizard computed their
"available scans" balance from subscription remainder alone, so admin-
granted credits were invisible: the New Scan button rerouted to
billing even though the create endpoint would have happily consumed a
wallet credit. Now `getStatus` aggregates wallet balances per product
and the two UIs add them to the displayed allowance, mirroring the
backend's consumption decision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

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

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment May 5, 2026 6:32pm
comp-framework-editor Ready Ready Preview, Comment May 5, 2026 6:32pm
portal Ready Ready Preview, Comment May 5, 2026 6:32pm

Request Review

Copy link
Copy Markdown
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 11 files

Confidence score: 5/5

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

@tofikwest tofikwest merged commit 64813d9 into main May 5, 2026
11 checks passed
@tofikwest tofikwest deleted the fix/billing-credit-wallet-ui-gating branch May 5, 2026 18:39
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