feat(credits): enterprise-domain accounts get pro credits; PRO_CREDITS → 9,999#747
Conversation
…9999 - Add lib/enterprise/isEnterpriseAccount.ts: resolves whether any of an account's emails has a domain in ENTERPRISE_DOMAINS (via selectAccountEmails + extractDomain). - getAccountSubscriptionState now runs the enterprise check in parallel with the two Stripe lookups; isPro is true if any match. activeSubscription stays Stripe-only so checkAndResetCredits's early-refill (current_period_start) never fires for enterprise-only accounts — they refill on the ≥1-month path. - PRO_CREDITS raised 1000 → 9999 (matches the chat-side change). Part of recoupable/chat#1841. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds a new ChangesEnterprise pro eligibility
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
1 issue found across 6 files
Confidence score: 4/5
- In
lib/credits/getAccountSubscriptionState.ts, adding an unconditional enterprise-domain lookup on every credits read can increase latency and DB load on a hot path even when Stripe already confirms a pro account, which could degrade request performance under traffic. GateisEnterpriseAccountbehind the non-pro path (or otherwise avoid the extra query when Stripe is definitive) before merging.
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="lib/credits/getAccountSubscriptionState.ts">
<violation number="1" location="lib/credits/getAccountSubscriptionState.ts:29">
P2: Credits reads now add an unconditional enterprise-domain DB lookup, even when Stripe already proves the account is pro. This increases per-request load/latency on the hot path; consider running `isEnterpriseAccount` only when both Stripe checks are inactive.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Credits as Credits System
participant State as getAccountSubscriptionState
participant StripeAcc as getActiveSubscriptionDetails
participant StripeOrg as getOrgSubscription
participant Enterprise as isEnterpriseAccount (NEW)
participant DB as selectAccountEmails
participant EmailUtil as extractDomain
participant DomainSet as ENTERPRISE_DOMAINS
Note over Credits,DomainSet: Runtime flow for resolving pro status<br/>PRO_CREDITS is now 9999 (constant)
Credits->>State: getAccountSubscriptionState(accountId)
Note over State: Parallel lookups (Promise.all)
State->>StripeAcc: getActiveSubscriptionDetails(accountId)
State->>StripeOrg: getOrgSubscription(accountId)
State->>Enterprise: NEW: isEnterpriseAccount(accountId)
Enterprise->>DB: selectAccountEmails({ accountIds })
DB-->>Enterprise: rows[] (email + account_id)
Note over Enterprise: Loop over rows
Enterprise->>EmailUtil: extractDomain(row.email)
EmailUtil-->>Enterprise: domain or null
Enterprise->>DomainSet: check domain in ENTERPRISE_DOMAINS
DomainSet-->>Enterprise: match boolean
Enterprise-->>State: isEnterprise: true/false
StripeAcc-->>State: accountSub (Stripe.Subscription | null)
StripeOrg-->>State: orgSub (Stripe.Subscription | null)
Note over State: Resolve isPro and activeSubscription
alt Enterprise match, no Stripe subscription
State-->>Credits: { isPro: true, activeSubscription: null }
else Enterprise match + Stripe subscription (account or org)
State-->>Credits: { isPro: true, activeSubscription: Stripe sub }
else No enterprise, no Stripe subscription
State-->>Credits: { isPro: false, activeSubscription: null }
else No enterprise, Stripe subscription active
State-->>Credits: { isPro: true, activeSubscription: Stripe sub }
end
Note over Credits: activeSubscription stays Stripe-only<br/>so enterprise-only accounts refill on ≥1‑month path
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const [accountSub, orgSub, isEnterprise] = await Promise.all([ | ||
| getActiveSubscriptionDetails(accountId), | ||
| getOrgSubscription(accountId), | ||
| isEnterpriseAccount(accountId), |
There was a problem hiding this comment.
P2: Credits reads now add an unconditional enterprise-domain DB lookup, even when Stripe already proves the account is pro. This increases per-request load/latency on the hot path; consider running isEnterpriseAccount only when both Stripe checks are inactive.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/credits/getAccountSubscriptionState.ts, line 29:
<comment>Credits reads now add an unconditional enterprise-domain DB lookup, even when Stripe already proves the account is pro. This increases per-request load/latency on the hot path; consider running `isEnterpriseAccount` only when both Stripe checks are inactive.</comment>
<file context>
@@ -2,32 +2,36 @@ import type Stripe from "stripe";
+ const [accountSub, orgSub, isEnterprise] = await Promise.all([
getActiveSubscriptionDetails(accountId),
getOrgSubscription(accountId),
+ isEnterpriseAccount(accountId),
]);
const hasAccountSub = isActiveSubscription(accountSub);
</file context>
Preview verification — 2026-07-03Preview
Behavior notes (as designed, worth knowing):
Cleanup: throwaway's Seeker-org membership removed via Must land back-to-back with recoupable/chat#1842 (chat's duplicated credits stack) per recoupable/chat#1841. |
Part of recoupable/chat#1841 (item "Open — credits").
What
getAccountSubscriptionStatewas Stripe-only, so enterprise customers without a Stripe subscription (e.g. all of Seeker Music) fell to the 333 free tier. New SRP helperlib/enterprise/isEnterpriseAccount.tschecks whether any of the account's emails (lib/supabase/account_emails/selectAccountEmails) has a domain (extractDomain) inENTERPRISE_DOMAINS; the check runs in parallel with the two existing Stripe lookups.PRO_CREDITSraised from 1,000 to 9,999 inlib/credits/const.ts.Design notes
activeSubscriptionstays Stripe-only. It feedscheckAndResetCredits's early-refill viacurrent_period_start; an enterprise match returnsisPro: true, activeSubscription: null, so enterprise-pro accounts refill only on the ≥1-month path. TheAccountSubscriptionStateinterface shape is unchanged.PRO_CREDITSand pro resolution), and the two must agree or the sidebar total and the API total diverge.remaining_credits := plan total, not an increment). Any account whose balance was hand-set above/below plan will be clobbered to 9,999 (pro) or 333 (free) at its next refill.Verification
PRO_CREDITS1000 ≠ 9999 / enterprise caseisPro: false), then implementation turned them green.pnpm test).tsc --noEmit: 201 errors on baseline → 200 after (zero new; remainder are pre-existing in untouched test files).pnpm lintclean; prettier check clean on all touched files.New tests:
lib/enterprise/__tests__/isEnterpriseAccount.test.ts— domain match, case-insensitive, multi-email, non-match, no emails, null/malformed emails.lib/credits/__tests__/getAccountSubscriptionState.test.ts— enterprise-no-Stripe →isPro: true+activeSubscription: null; enterprise + Stripe keepsactiveSubscriptionStripe-only; existing Stripe-only cases unchanged.lib/credits/__tests__/const.test.ts— pinsPRO_CREDITS = 9999,DEFAULT_CREDITS = 333.🤖 Generated with Claude Code
Summary by cubic
Enterprise-domain accounts now resolve as pro in the credits system without Stripe.
PRO_CREDITSincreases to 9,999 so enterprise and pro users don’t worry about credits.New Features
lib/enterprise/isEnterpriseAccount.tsto match account email domains againstENTERPRISE_DOMAINS(viaextractDomain) and wired it intogetAccountSubscriptionStatealongside Stripe account/org checks.activeSubscriptionstays Stripe-only; enterprise-only accounts getisPro: truewithactiveSubscription: nullto avoid early refill viacurrent_period_start.Migration
Written for commit 128a5b7. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes