-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
47 add stripe checkout endpoint (#4147)
* Add self billing feature flag * Add two core tables for billing * Remove useless imports * Remove graphql decorators * Rename subscriptionProduct table * WIP: Add stripe config * Add controller to get product prices * Add billing service * Remove unecessary package * Simplify stripe service * Code review returns * Use nestjs param * Rename subscription to basePlan * Rename env variable * Add checkout endpoint * Remove resolver * Merge controllers * Fix security issue * Handle missing url error * Add workspaceId in checkout metadata
- Loading branch information
Showing
4 changed files
with
135 additions
and
50 deletions.
There are no files selected for viewing
116 changes: 116 additions & 0 deletions
116
packages/twenty-server/src/core/billing/billing.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { | ||
Body, | ||
Controller, | ||
Get, | ||
Param, | ||
Post, | ||
Res, | ||
UseGuards, | ||
} from '@nestjs/common'; | ||
|
||
import { Response } from 'express'; | ||
|
||
import { | ||
AvailableProduct, | ||
BillingService, | ||
PriceData, | ||
RecurringInterval, | ||
} from 'src/core/billing/billing.service'; | ||
import { StripeService } from 'src/core/billing/stripe/stripe.service'; | ||
import { EnvironmentService } from 'src/integrations/environment/environment.service'; | ||
import { AuthUser } from 'src/decorators/auth/auth-user.decorator'; | ||
import { User } from 'src/core/user/user.entity'; | ||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; | ||
|
||
@Controller('billing') | ||
export class BillingController { | ||
constructor( | ||
private readonly stripeService: StripeService, | ||
private readonly billingService: BillingService, | ||
private readonly environmentService: EnvironmentService, | ||
) {} | ||
|
||
@Get('/product-prices/:product') | ||
async get( | ||
@Param() params: { product: AvailableProduct }, | ||
@Res() res: Response<PriceData | { error: string }>, | ||
) { | ||
const stripeProductId = this.billingService.getProductStripeId( | ||
params.product, | ||
); | ||
|
||
if (!stripeProductId) { | ||
res.status(404).send({ | ||
error: `Product '${ | ||
params.product | ||
}' not found, available products are ['${Object.values( | ||
AvailableProduct, | ||
).join("','")}']`, | ||
}); | ||
|
||
return; | ||
} | ||
|
||
res.json(await this.billingService.getProductPrices(stripeProductId)); | ||
} | ||
|
||
@UseGuards(JwtAuthGuard) | ||
@Post('/checkout') | ||
async post( | ||
@AuthUser() user: User, | ||
@Body() body: { recurringInterval: RecurringInterval }, | ||
@Res() res: Response, | ||
) { | ||
const productId = this.billingService.getProductStripeId( | ||
AvailableProduct.BasePlan, | ||
); | ||
|
||
if (!productId) { | ||
res | ||
.status(404) | ||
.send( | ||
'BasePlan productId not found, please check your BILLING_STRIPE_BASE_PLAN_PRODUCT_ID env variable', | ||
); | ||
|
||
return; | ||
} | ||
|
||
const productPrices = await this.billingService.getProductPrices(productId); | ||
const recurringInterval = body.recurringInterval; | ||
const priceId = productPrices[recurringInterval]?.id; | ||
|
||
if (!priceId) { | ||
res | ||
.status(404) | ||
.send( | ||
`BasePlan priceId not found, please check body.recurringInterval and product '${AvailableProduct.BasePlan}' prices`, | ||
); | ||
|
||
return; | ||
} | ||
const frontBaseUrl = this.environmentService.getFrontBaseUrl(); | ||
const session = await this.stripeService.stripe.checkout.sessions.create({ | ||
line_items: [ | ||
{ | ||
price: priceId, | ||
quantity: 1, | ||
}, | ||
], | ||
mode: 'subscription', | ||
metadata: { | ||
workspaceId: user.defaultWorkspace.id, | ||
}, | ||
customer_email: user.email, | ||
success_url: frontBaseUrl, | ||
cancel_url: frontBaseUrl, | ||
}); | ||
|
||
if (!session.url) { | ||
res.status(400).send('Error: missing checkout.session.url'); | ||
|
||
return; | ||
} | ||
|
||
res.redirect(303, session.url); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
import { Module } from '@nestjs/common'; | ||
|
||
import { ProductPriceController } from 'src/core/billing/controllers/product-price.controller'; | ||
import { BillingController } from 'src/core/billing/billing.controller'; | ||
import { EnvironmentModule } from 'src/integrations/environment/environment.module'; | ||
import { BillingService } from 'src/core/billing/billing.service'; | ||
import { StripeModule } from 'src/core/billing/stripe/stripe.module'; | ||
|
||
@Module({ | ||
imports: [StripeModule], | ||
controllers: [ProductPriceController], | ||
controllers: [BillingController], | ||
providers: [EnvironmentModule, BillingService], | ||
}) | ||
export class BillingModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 0 additions & 46 deletions
46
packages/twenty-server/src/core/billing/controllers/product-price.controller.ts
This file was deleted.
Oops, something went wrong.