clean code and normalize billing eventNames (#9928)
Solves : https://github.com/twentyhq/private-issues/issues/251 **TLDR:** Clean Billing Code using feedback of the previous PR (#9865). Normalized the metadata and names of the products, prices, and meters in Stripe so that they can be accessed in stripe's test mode and live mode. **In order to test:** 1. Have the environment variable IS_BILLING_ENABLED set to true and add the other required environment variables for Billing to work 2. Do a database reset (to ensure that the new feature flag is properly added and that the billing tables are created) 3. Run the command: npx nx run twenty-server:command billing:sync-plans-data (if you don't do that the products and prices will not be present in the database) 4. Run the server , the frontend, the worker, and the stripe listen command (stripe listen --forward-to http://localhost:3000/billing/webhooks) 5. Buy a subscription for the Acme workspace 6. Create a workflow and run it 7. After the run has been finished check in sprite the quantity of events in the CreditMeter, you should see that there is a new occurence with value one.
This commit is contained in:
committed by
GitHub
parent
625466f38d
commit
d777f62651
@ -13,7 +13,7 @@ import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing
|
||||
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
|
||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import { BillingRestApiExceptionFilter } from 'src/engine/core-modules/billing/filters/billing-api-exception.filter';
|
||||
import { BillingExecuteBilledFunctionListener } from 'src/engine/core-modules/billing/listeners/billing-execute-billed-function.listener';
|
||||
import { BillingFeatureUsedListener } from 'src/engine/core-modules/billing/listeners/billing-feature-used.listener';
|
||||
import { BillingWorkspaceMemberListener } from 'src/engine/core-modules/billing/listeners/billing-workspace-member.listener';
|
||||
import { BillingPlanService } from 'src/engine/core-modules/billing/services/billing-plan.service';
|
||||
import { BillingPortalWorkspaceService } from 'src/engine/core-modules/billing/services/billing-portal.workspace-service';
|
||||
@ -65,7 +65,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
BillingResolver,
|
||||
BillingPlanService,
|
||||
BillingWorkspaceMemberListener,
|
||||
BillingExecuteBilledFunctionListener,
|
||||
BillingFeatureUsedListener,
|
||||
BillingService,
|
||||
BillingWebhookProductService,
|
||||
BillingWebhookPriceService,
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
export const BILLING_EXECUTE_BILLED_FUNCTION =
|
||||
'billing_execute_billed_function';
|
||||
@ -0,0 +1 @@
|
||||
export const BILLING_FEATURE_USED = 'BILLING_FEATURE_USED';
|
||||
@ -1,5 +1,3 @@
|
||||
export enum BillingMeterEventName {
|
||||
WORKFLOW_NODE_RUN = 'creditexecutiontest1',
|
||||
WORKFLOW_NODE_RUN = 'WORKFLOW_NODE_RUN',
|
||||
}
|
||||
//this is a test event name (no conventions) would you want camel case?, snake case, or all caps?
|
||||
//Something like workflowNodeRunBillingMeterEvent ?
|
||||
|
||||
@ -1,33 +1,32 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { OnCustomBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-custom-batch-event.decorator';
|
||||
import { BILLING_EXECUTE_BILLED_FUNCTION } from 'src/engine/core-modules/billing/constants/billing-execute-billed-function.constant';
|
||||
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
||||
import { BillingUsageService } from 'src/engine/core-modules/billing/services/billing-usage.service';
|
||||
import { BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
|
||||
|
||||
@Injectable()
|
||||
export class BillingExecuteBilledFunctionListener {
|
||||
export class BillingFeatureUsedListener {
|
||||
constructor(
|
||||
private readonly billingUsageService: BillingUsageService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
) {}
|
||||
|
||||
@OnCustomBatchEvent(BILLING_EXECUTE_BILLED_FUNCTION)
|
||||
async handleExecuteBilledFunctionEvent(
|
||||
@OnCustomBatchEvent(BILLING_FEATURE_USED)
|
||||
async handleBillingFeatureUsedEvent(
|
||||
payload: WorkspaceEventBatch<BillingUsageEvent>,
|
||||
) {
|
||||
if (!this.environmentService.get('IS_BILLING_ENABLED')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canExecuteBilledFunction =
|
||||
await this.billingUsageService.canExecuteBilledFunction(
|
||||
payload.workspaceId,
|
||||
);
|
||||
const canFeatureBeUsed = await this.billingUsageService.canFeatureBeUsed(
|
||||
payload.workspaceId,
|
||||
);
|
||||
|
||||
if (!canExecuteBilledFunction) {
|
||||
if (!canFeatureBeUsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export class BillingUsageService {
|
||||
private readonly stripeBillingMeterEventService: StripeBillingMeterEventService,
|
||||
) {}
|
||||
|
||||
async canExecuteBilledFunction(workspaceId: string): Promise<boolean> {
|
||||
async canFeatureBeUsed(workspaceId: string): Promise<boolean> {
|
||||
const isBillingEnabled = this.environmentService.get('IS_BILLING_ENABLED');
|
||||
const isBillingPlansEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
@ -82,7 +82,7 @@ export class BillingUsageService {
|
||||
});
|
||||
} catch (error) {
|
||||
throw new BillingException(
|
||||
'Failed to send billing meter events to Cache Service',
|
||||
`Failed to send billing meter events to Stripe: ${error}`,
|
||||
BillingExceptionCode.BILLING_METER_EVENT_FAILED,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { BILLING_EXECUTE_BILLED_FUNCTION } from 'src/engine/core-modules/billing/constants/billing-execute-billed-function.constant';
|
||||
import { BILLING_FEATURE_USED } from 'src/engine/core-modules/billing/constants/billing-feature-used.constant';
|
||||
import { BillingMeterEventName } from 'src/engine/core-modules/billing/enums/billing-meter-event-names';
|
||||
import { BillingUsageEvent } from 'src/engine/core-modules/billing/types/billing-usage-event.type';
|
||||
import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory';
|
||||
@ -74,7 +74,7 @@ export class WorkflowExecutorWorkspaceService {
|
||||
(result.result ? undefined : 'Execution result error, no data or error');
|
||||
|
||||
if (!error) {
|
||||
this.sendUsageEvent();
|
||||
this.sendWorkflowNodeRunEvent();
|
||||
}
|
||||
|
||||
const updatedStepOutput = {
|
||||
@ -136,12 +136,12 @@ export class WorkflowExecutorWorkspaceService {
|
||||
return { ...updatedOutput, status: WorkflowRunStatus.FAILED };
|
||||
}
|
||||
|
||||
async sendUsageEvent() {
|
||||
private sendWorkflowNodeRunEvent() {
|
||||
const workspaceId =
|
||||
this.scopedWorkspaceContextFactory.create().workspaceId ?? '';
|
||||
|
||||
this.workspaceEventEmitter.emitCustomBatchEvent<BillingUsageEvent>(
|
||||
BILLING_EXECUTE_BILLED_FUNCTION,
|
||||
BILLING_FEATURE_USED,
|
||||
[
|
||||
{
|
||||
eventName: BillingMeterEventName.WORKFLOW_NODE_RUN,
|
||||
|
||||
@ -27,10 +27,10 @@ export class WorkflowRunnerWorkspaceService {
|
||||
payload: object,
|
||||
source: ActorMetadata,
|
||||
) {
|
||||
const canExecuteBilledFunction =
|
||||
await this.billingUsageService.canExecuteBilledFunction(workspaceId);
|
||||
const canFeatureBeUsed =
|
||||
await this.billingUsageService.canFeatureBeUsed(workspaceId);
|
||||
|
||||
if (!canExecuteBilledFunction) {
|
||||
if (!canFeatureBeUsed) {
|
||||
this.logger.log(
|
||||
'Cannot execute billed function, there is no subscription for this workspace',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user