Skip to content

Commit

Permalink
🚸 (billing) Use Stripe checkout page for new subscription with existi…
Browse files Browse the repository at this point in the history
…ng customer
  • Loading branch information
baptisteArno committed May 5, 2023
1 parent e15e27f commit b9f94cd
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 44 deletions.
94 changes: 67 additions & 27 deletions apps/builder/src/features/billing/api/createCheckoutSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,42 +94,82 @@ export const createCheckoutSession = authenticatedProcedure
: undefined,
})

const session = await stripe.checkout.sessions.create({
success_url: `${returnUrl}?stripe=${plan}&success=true`,
cancel_url: `${returnUrl}?stripe=cancel`,
allow_promotion_codes: true,
customer: customer.id,
customer_update: {
address: 'auto',
name: 'never',
},
mode: 'subscription',
metadata: {
workspaceId,
plan,
additionalChats,
additionalStorage,
userId: user.id,
},
const checkoutUrl = await createCheckoutSessionUrl(stripe)({
customerId: customer.id,
userId: user.id,
workspaceId,
currency,
billing_address_collection: 'required',
automatic_tax: { enabled: true },
line_items: parseSubscriptionItems(
plan,
additionalChats,
additionalStorage,
isYearly
),
plan,
returnUrl,
additionalChats,
additionalStorage,
isYearly,
})

if (!session.url)
if (!checkoutUrl)
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Stripe checkout session creation failed',
})

return {
checkoutUrl: session.url,
checkoutUrl,
}
}
)

type Props = {
customerId: string
workspaceId: string
currency: 'usd' | 'eur'
plan: 'STARTER' | 'PRO'
returnUrl: string
additionalChats: number
additionalStorage: number
isYearly: boolean
userId: string
}

export const createCheckoutSessionUrl =
(stripe: Stripe) =>
async ({
customerId,
userId,
workspaceId,
currency,
plan,
returnUrl,
additionalChats,
additionalStorage,
isYearly,
}: Props) => {
const session = await stripe.checkout.sessions.create({
success_url: `${returnUrl}?stripe=${plan}&success=true`,
cancel_url: `${returnUrl}?stripe=cancel`,
allow_promotion_codes: true,
customer: customerId,
customer_update: {
address: 'auto',
name: 'never',
},
mode: 'subscription',
metadata: {
workspaceId,
plan,
additionalChats,
additionalStorage,
userId,
},
currency,
billing_address_collection: 'required',
automatic_tax: { enabled: true },
line_items: parseSubscriptionItems(
plan,
additionalChats,
additionalStorage,
isYearly
),
})

return session.url
}
30 changes: 16 additions & 14 deletions apps/builder/src/features/billing/api/updateSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
priceIds,
} from '@typebot.io/lib/pricing'
import { chatPriceIds, storagePriceIds } from './getSubscription'
import { createCheckoutSessionUrl } from './createCheckoutSession'

export const updateSubscription = authenticatedProcedure
.meta({
Expand All @@ -26,6 +27,7 @@ export const updateSubscription = authenticatedProcedure
})
.input(
z.object({
returnUrl: z.string(),
workspaceId: z.string(),
plan: z.enum([Plan.STARTER, Plan.PRO]),
additionalChats: z.number(),
Expand All @@ -36,7 +38,8 @@ export const updateSubscription = authenticatedProcedure
)
.output(
z.object({
workspace: workspaceSchema,
workspace: workspaceSchema.nullish(),
checkoutUrl: z.string().nullish(),
})
)
.mutation(
Expand All @@ -48,6 +51,7 @@ export const updateSubscription = authenticatedProcedure
additionalStorage,
currency,
isYearly,
returnUrl,
},
ctx: { user },
}) => {
Expand Down Expand Up @@ -127,21 +131,19 @@ export const updateSubscription = authenticatedProcedure
items,
})
} else {
const { data: paymentMethods } = await stripe.paymentMethods.list({
customer: workspace.stripeId,
})
if (paymentMethods.length === 0) {
throw Error('No payment method found')
}
await stripe.subscriptions.create({
customer: workspace.stripeId,
items,
const checkoutUrl = await createCheckoutSessionUrl(stripe)({
customerId: workspace.stripeId,
userId: user.id,
workspaceId,
currency,
default_payment_method: paymentMethods[0].id,
automatic_tax: {
enabled: true,
},
plan,
returnUrl,
additionalChats,
additionalStorage,
isYearly,
})

return { checkoutUrl }
}

const updatedWorkspace = await prisma.workspace.update({
Expand Down
15 changes: 12 additions & 3 deletions apps/builder/src/features/billing/components/ChangePlanForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,17 @@ export const ChangePlanForm = ({ workspace, onUpgradeSuccess }: Props) => {
description: error.message,
})
},
onSuccess: ({ workspace: { plan } }) => {
onSuccess: ({ workspace, checkoutUrl }) => {
if (checkoutUrl) {
window.location.href = checkoutUrl
return
}
onUpgradeSuccess()
showToast({
status: 'success',
description: scopedT('updateSuccessToast.description', { plan }),
description: scopedT('updateSuccessToast.description', {
plan: workspace?.plan,
}),
})
},
})
Expand Down Expand Up @@ -83,7 +89,10 @@ export const ChangePlanForm = ({ workspace, onUpgradeSuccess }: Props) => {
isYearly,
} as const
if (workspace.stripeId) {
updateSubscription(newSubscription)
updateSubscription({
...newSubscription,
returnUrl: window.location.href,
})
} else {
setPreCheckoutPlan(newSubscription)
}
Expand Down

4 comments on commit b9f94cd

@vercel
Copy link

@vercel vercel bot commented on b9f94cd May 5, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on b9f94cd May 5, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on b9f94cd May 5, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

dicanatural.online
goalsettingbot.com
91181264.your-access.one
abg-assistent.m-vogel.de
ai.chromebookstoreph.com
chat.hiabhaykulkarni.com
contextone.wpwakanda.com
form.sergiolimajr.com.br
hunterbot.saleshunter.ai
invite.bridesquadapp.com
link.cascadigital.com.br
login.algorithmpress.com
onboarding.growthside.io
reward.onlinebotdemo.xyz
stap.venturemarketing.in
type.opaulovieira.com.br
aibot.angrybranding.co.uk
app.photosbackupin1.click
bot.aidigitalmarketing.kr
bot.amicidisanfaustino.it
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
bot.fusionstarreviews.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thehomebuyersusa.com
chat.thisiscrushhouse.com
healthandsafetycourses.uk
sellmyharleylouisiana.com
testbot.sharemyreview.net
typebot-viewer.vercel.app
verfica.botmachine.com.br
ap-help.algorithmpress.com
ap-main.algorithmpress.com
bot.adventureconsulting.hu
bot2.fusionstarreviews.com
casestudyemb.wpwakanda.com
chat.atlasoutfittersk9.com
configurator.bouclidom.com
demo.virtuesocialmedia.com
help.atlasoutfittersk9.com
herbalife.barrettamario.it
homepageonly.wpwakanda.com
liveconvert.kandalearn.com
mainmenu1one.wpwakanda.com
newsletter.itshcormeos.com
rsvp.virtuesocialmedia.com
tarian.theiofoundation.org
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
bot.pinpointinteractive.com
bot.polychromes-project.com
bot.seidinembroseanchetu.it
chat.semanalimpanome.com.br
designguide.techyscouts.com
liveconvert2.kandalearn.com
poll.mosaicohairboutique.it
presente.empresarias.com.mx
register.algorithmpress.com
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
open.campus.aalen.university
piazzatorre.barrettamario.it
poll.mosaicohairboutique.com
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
bot.cabin-rentals-of-georgia.net
open.campus.bot.aalen.university
sondaggio.mosaicohairboutique.it
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
sondaggio.mosaicohairboutique.com
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

@vercel
Copy link

@vercel vercel bot commented on b9f94cd May 5, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs.typebot.io
docs-typebot-io.vercel.app

Please sign in to comment.