Skip to content

fix: billing gate broke for scale/enterprise and downgraded users#794

Merged
graphite-app[bot] merged 1 commit intomainfrom
fix-billing-gate
Mar 22, 2026
Merged

fix: billing gate broke for scale/enterprise and downgraded users#794
graphite-app[bot] merged 1 commit intomainfrom
fix-billing-gate

Conversation

@Prasanna721
Copy link
Member

@Prasanna721 Prasanna721 commented Mar 22, 2026

The subscription check on plugins, connections, and usage pages was hardcoded to only look at api_pro. Users on scale or enterprise plans saw "Upgrade to Pro" and couldn't create plugin keys or manage connections.

Also, the check only looked at product status but not the allowed flag from autumn — so users who downgraded but were still in their billing period got locked out early.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 22, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
supermemory-app 55ef8ab Mar 22 2026, 04:08 PM

@claude
Copy link

claude bot commented Mar 22, 2026

Claude finished @Prasanna721's task in 1m 47s —— View job


Review Complete

  • Fetch PR diff and existing review comments
  • Analyze billing gate logic changes
  • Check for bugs, security issues, and edge cases
  • Submit review

Overview: Fixes billing gate logic to support scale/enterprise tiers and handle downgraded users during their billing grace period.

Issues found: None — this looks good to ship.

The updated isAllowedFrom function at packages/lib/queries.ts:25-34 correctly:

  • Checks for status === "active" only, which aligns with console-v2 behavior
  • Validates from the minimum required tier upward (pro → scale → enterprise)
  • Users keep access until billing period ends (status remains "active")
  • Free users correctly fail since PLAN_TIERS only includes paid products

The plan detection in use-token-usage.ts:17-24 correctly checks from highest tier down to determine the user's actual subscription level.

Note on Sentry comment: The previous inline comment about s.allowed || s.status !== null is no longer applicable — that logic was replaced with s?.status === "active" in the latest commit.

Note on deployment failure: The Cloudflare deployment failure appears unrelated to these code changes. The logic is sound; this may be a CI configuration issue worth investigating separately.

Score: 10/10

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Overview: Fixes billing gate logic to support scale/enterprise tiers and users in their remaining billing period after downgrading.

Issues found: None — this looks good to ship.

The isAllowedFrom function correctly:

  • Checks from the minimum required tier upward through the hierarchy (pro → scale → enterprise)
  • Uses allowed || status !== null to grant access if the user has an active subscription OR is still within their billing period after cancellation
  • The plan detection in use-token-usage.ts correctly checks from highest tier down to determine the user's current plan

The 403 handling in plugins-detail.tsx provides a clear error message instead of a generic failure.

Score: 10/10

const minIndex = PLAN_TIERS.indexOf(minimumTier)
return PLAN_TIERS.slice(minIndex).some((tier) => {
const s = status[tier]
return s != null && (s.allowed || s.status !== null)

This comment was marked as outdated.

@Prasanna721
Copy link
Member Author

Updated the isAllowedFrom check to use s?.status === "active" instead of s?.allowed === true.

This aligns with how console-v2 handles billing gates — it reads autumn.customer.products, filters by status === "active", and checks against paid product IDs. When a user downgrades, their current plan keeps status: "active" until the billing period ends, so they retain access during the grace period.

Since PLAN_TIERS only includes api_pro, api_scale, api_enterprise (not api_free), free users correctly fail the check — we're only looking for active status on paid product IDs.

Comment on lines +29 to 36
const minIndex = PLAN_TIERS.indexOf(minimumTier)
return PLAN_TIERS.slice(minIndex).some((tier) => {
const s = status[tier]
return s?.status === "active"
})
}

export const fetchSubscriptionStatus = (
Copy link

Choose a reason for hiding this comment

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

Bug: The isAllowedFrom function only checks for status === "active" and ignores the allowed flag, incorrectly locking out users who are in a grace period after downgrading.
Severity: HIGH

Suggested Fix

Modify the isAllowedFrom function to check both the allowed flag and the subscription status. The condition inside the some callback should be updated to check if s?.status === "active" or if s?.allowed is true.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/lib/queries.ts#L29-L36

Potential issue: The `isAllowedFrom` function determines feature access based on
subscription status. It correctly fetches both `status` and an `allowed` flag for each
subscription tier. However, the logic only checks if `s?.status === "active"` and
completely ignores the `s.allowed` boolean. This contradicts the PR's goal, which is to
grant access to users who have downgraded but are still in their billing period
(indicated by `allowed: true`). As a result, these users will be incorrectly locked out
of paid features.

@graphite-app
Copy link

graphite-app bot commented Mar 22, 2026

Merge activity

The subscription check on plugins, connections, and usage pages was hardcoded to only look at `api_pro`. Users on scale or enterprise plans saw "Upgrade to Pro" and couldn't create plugin keys or manage connections.

Also, the check only looked at product `status` but not the `allowed` flag from autumn — so users who downgraded but were still in their billing period got locked out early.
@graphite-app graphite-app bot force-pushed the fix-billing-gate branch from 9d8f994 to 55ef8ab Compare March 22, 2026 16:05
@graphite-app graphite-app bot merged commit 55ef8ab into main Mar 22, 2026
6 of 7 checks passed
Comment on lines +29 to +33
const minIndex = PLAN_TIERS.indexOf(minimumTier)
return PLAN_TIERS.slice(minIndex).some((tier) => {
const s = status[tier]
return s?.status === "active"
})
Copy link

Choose a reason for hiding this comment

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

Bug: The isAllowedFrom function only checks if a subscription status is "active", ignoring the allowed flag. This will lock out users in a grace period after downgrading.
Severity: HIGH

Suggested Fix

Modify the isAllowedFrom function to correctly handle grace periods by checking the allowed flag in addition to the status. The logic should return true if s?.allowed is true or if s?.status === "active". This ensures users in a grace period retain access as intended.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/lib/queries.ts#L29-L33

Potential issue: The `isAllowedFrom` function determines feature access based on
subscription tier. While the `fetchSubscriptionStatus` function correctly fetches both a
subscription `status` and an `allowed` flag for real-time entitlement, the gating logic
in `isAllowedFrom` only checks if `s?.status === "active"`. It completely ignores the
`allowed` flag. Consequently, users who have downgraded but are still within their paid
billing period (a "grace period" where `allowed` would be `true` but `status` is not
"active") will be incorrectly locked out of features like plugins and connections. This
contradicts the intended fix of the pull request.

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