Billing - Migrate from Stripe metering (#20298)
**Overall strategy** **1. Introduce “Billing V2” behind a workspace flag** Gate the new model with FeatureFlagKey.IS_BILLING_V2_ENABLED so existing workspaces stay on the old behavior until they’re migrated or explicitly on V2. **2. Replace workflow metered SKUs with a resource-credit product** Conceptually, billable “workflow execution” usage is not the primary subscription line item anymore. Add a RESOURCE_CREDIT product (and keep WORKFLOW_NODE_EXECUTION as deprecated for the transition). Usage and limits are expressed through credit buckets (e.g. price metadata like credit_amount), so one product can represent pooled credits instead of a narrow workflow-only meter. **3. Migrate subscriptions in two layers** Schema/catalog: persist extra price metadata (instance upgrade) so the server knows credit amounts and can match Stripe prices to the new model. Per workspace: the registered workspace command upgrade:2-2:migrate-to-billing-v2 finds subscriptions that still have WORKFLOW_NODE_EXECUTION, swaps those items to the right RESOURCE_CREDIT prices (using existing Stripe schedule + BillingSubscriptionUpdateService stack), then treats the workspace as V2 (flag). Workspaces without that legacy item or without a subscription are skipped. **4. Unify subscription lifecycle + usage on the server** **5. Refresh the product surface in Settings** Test : - [x] Subscribe v1 + Update subscribe + Migrate - [x] Subscribe v2 + Update subscribe
This commit is contained in:
@@ -15,6 +15,8 @@ import { mockedPublicWorkspaceDataBySubdomain } from '~/testing/mock-data/public
|
||||
import { mockedUserData } from '~/testing/mock-data/users';
|
||||
|
||||
import { GET_PUBLIC_WORKSPACE_DATA_BY_DOMAIN } from '@/auth/graphql/queries/getPublicWorkspaceDataByDomain';
|
||||
import { BILLING_PORTAL_SESSION } from '@/settings/billing/graphql/queries/billingPortalSession';
|
||||
import { GET_METERED_PRODUCTS_USAGE } from '@/settings/billing/graphql/queries/getMeteredProductsUsage';
|
||||
import { LIST_PLANS } from '@/settings/billing/graphql/queries/listPlans';
|
||||
import { GET_ROLES } from '@/settings/roles/graphql/queries/getRolesQuery';
|
||||
import { mockBillingPlans } from '~/testing/mock-data/billing-plans';
|
||||
@@ -528,6 +530,42 @@ export const graphqlMocks = {
|
||||
data: mockBillingPlans,
|
||||
});
|
||||
}),
|
||||
graphql.query(getOperationName(GET_METERED_PRODUCTS_USAGE) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
getMeteredProductsUsage: [
|
||||
{
|
||||
__typename: 'BillingMeteredProductUsage',
|
||||
productKey: 'WORKFLOW_NODE_EXECUTION',
|
||||
usedCredits: 1000,
|
||||
grantedCredits: 500000,
|
||||
rolloverCredits: 0,
|
||||
totalGrantedCredits: 500000,
|
||||
unitPriceCents: 1,
|
||||
},
|
||||
{
|
||||
__typename: 'BillingMeteredProductUsage',
|
||||
productKey: 'RESOURCE_CREDIT',
|
||||
usedCredits: 0,
|
||||
grantedCredits: 10000,
|
||||
rolloverCredits: 0,
|
||||
totalGrantedCredits: 10000,
|
||||
unitPriceCents: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphql.query(getOperationName(BILLING_PORTAL_SESSION) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
billingPortalSession: {
|
||||
__typename: 'BillingSession',
|
||||
url: 'https://billing.stripe.com/p/mock-portal-session',
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
http.get('https://chat-assets.frontapp.com/v1/chat.bundle.js', () => {
|
||||
return HttpResponse.text(
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user