Pass Billing Checkout var in url to bypass credit card (#9283)

This commit is contained in:
Félix Malfait
2024-12-31 14:48:00 +01:00
committed by GitHub
parent 45f14c8020
commit 97f5a5b8a5
123 changed files with 524 additions and 173 deletions

View File

@ -56,7 +56,13 @@ export class BillingResolver {
async checkoutSession(
@AuthWorkspace() workspace: Workspace,
@AuthUser() user: User,
@Args() { recurringInterval, successUrlPath }: CheckoutSessionInput,
@Args()
{
recurringInterval,
successUrlPath,
plan,
requirePaymentMethod,
}: CheckoutSessionInput,
) {
const productPrice = await this.stripeService.getStripePrice(
AvailableProduct.BasePlan,
@ -75,6 +81,8 @@ export class BillingResolver {
workspace,
productPrice.stripePriceId,
successUrlPath,
plan,
requirePaymentMethod,
),
};
}

View File

@ -1,16 +1,32 @@
import { ArgsType, Field } from '@nestjs/graphql';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
import Stripe from 'stripe';
import {
IsBoolean,
IsEnum,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/billing-subscription-interval.enum';
@ArgsType()
export class CheckoutSessionInput {
@Field(() => SubscriptionInterval)
@IsString()
@IsEnum(SubscriptionInterval)
@IsNotEmpty()
recurringInterval: Stripe.Price.Recurring.Interval;
recurringInterval: SubscriptionInterval;
@Field(() => BillingPlanKey, { defaultValue: BillingPlanKey.PRO })
@IsEnum(BillingPlanKey)
@IsOptional()
plan?: BillingPlanKey;
@Field(() => Boolean, { defaultValue: true })
@IsBoolean()
@IsOptional()
requirePaymentMethod?: boolean;
@Field(() => String, { nullable: true })
@IsString()

View File

@ -1,4 +1,12 @@
import { registerEnumType } from '@nestjs/graphql';
export enum BillingPlanKey {
BASE_PLAN = 'BASE_PLAN',
PRO_PLAN = 'PRO_PLAN',
BASE = 'BASE',
PRO = 'PRO',
ENTERPRISE = 'ENTERPRISE',
}
registerEnumType(BillingPlanKey, {
name: 'BillingPlanKey',
description: 'The different billing plans available',
});

View File

@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
@ -30,6 +31,8 @@ export class BillingPortalWorkspaceService {
workspace: Workspace,
priceId: string,
successUrlPath?: string,
plan?: BillingPlanKey,
requirePaymentMethod?: boolean,
): Promise<string> {
const frontBaseUrl = this.domainManagerService.getBaseUrl();
const cancelUrl = frontBaseUrl.toString();
@ -57,6 +60,8 @@ export class BillingPortalWorkspaceService {
successUrl,
cancelUrl,
stripeCustomerId,
plan,
requirePaymentMethod,
);
assert(session.url, 'Error: missing checkout.session.url');

View File

@ -52,9 +52,9 @@ export class BillingWebhookProductService {
isValidBillingPlanKey(planKey?: string) {
switch (planKey) {
case BillingPlanKey.BASE_PLAN:
case BillingPlanKey.BASE:
return true;
case BillingPlanKey.PRO_PLAN:
case BillingPlanKey.PRO:
return true;
default:
return false;

View File

@ -5,6 +5,7 @@ import Stripe from 'stripe';
import { ProductPriceEntity } from 'src/engine/core-modules/billing/dto/product-price.entity';
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
import { AvailableProduct } from 'src/engine/core-modules/billing/enums/billing-available-product.enum';
import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { User } from 'src/engine/core-modules/user/user.entity';
@ -90,6 +91,8 @@ export class StripeService {
successUrl?: string,
cancelUrl?: string,
stripeCustomerId?: string,
plan: BillingPlanKey = BillingPlanKey.PRO,
requirePaymentMethod = true,
): Promise<Stripe.Checkout.Session> {
return await this.stripe.checkout.sessions.create({
line_items: [
@ -102,18 +105,22 @@ export class StripeService {
subscription_data: {
metadata: {
workspaceId,
plan,
},
trial_period_days: this.environmentService.get(
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
),
},
automatic_tax: { enabled: true },
tax_id_collection: { enabled: true },
automatic_tax: { enabled: !!requirePaymentMethod },
tax_id_collection: { enabled: !!requirePaymentMethod },
customer: stripeCustomerId,
customer_update: stripeCustomerId ? { name: 'auto' } : undefined,
customer_email: stripeCustomerId ? undefined : user.email,
success_url: successUrl,
cancel_url: cancelUrl,
payment_method_collection: requirePaymentMethod
? 'always'
: 'if_required',
});
}