Create & delete Stripe customers

This commit is contained in:
Joe Au-Yeung 2022-10-20 15:16:27 -04:00
parent d968b37e4c
commit 4dbcc62152
4 changed files with 83 additions and 11 deletions

View File

@ -73,7 +73,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
description={
<span className=" text-sm leading-tight text-gray-500">
Note: This will <span className="font-medium text-gray-900">cost an extra seat ($12/m)</span> on
your subscription if this invitee does not have a TEAM account.
your subscription once this member accepts your invite.
</span>
}>
<form onSubmit={inviteMember}>

View File

@ -1,5 +1,6 @@
import { useForm, Controller } from "react-hook-form";
import { classNames } from "@calcom/lib";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import slugify from "@calcom/lib/slugify";
@ -7,7 +8,7 @@ import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui";
import { Button, Avatar } from "@calcom/ui/v2";
import ImageUploader from "@calcom/ui/v2/core/ImageUploader";
import { Form, TextField } from "@calcom/ui/v2/core/form/fields";
import { Form, TextField, Label } from "@calcom/ui/v2/core/form/fields";
const CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: number) => void }) => {
const { t } = useLocale();
@ -31,6 +32,7 @@ const CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: n
name: values.name,
slug: values.slug || null,
logo: values.logo || null,
billingFrequency: values.billingFrequency,
});
}}>
<div className="mb-8">
@ -96,6 +98,42 @@ const CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: n
)}
/>
</div>
<div className="mb-8">
<Controller
control={formMethods.control}
name="billingFrequency"
defaultValue="monthly"
render={({ field: { value } }) => (
<>
<Label className="font-sm mt-8 text-gray-900">
<>{t("event_triggers")}</>
</Label>
<div className="flex rounded-md border">
<div
className={classNames(
"px-1/2 w-1/2 rounded-md py-2.5 text-center font-medium text-gray-900",
value === "monthly" && "bg-gray-200"
)}
onClick={() => {
formMethods.setValue("billingFrequency", "monthly");
}}>
<p>{t("monthly")}</p>
</div>
<div
className={classNames(
"px-1/2 w-1/2 rounded-md py-2.5 text-center font-medium text-gray-900",
value === "yearly" && "bg-gray-200"
)}
onClick={() => {
formMethods.setValue("billingFrequency", "yearly");
}}>
<p>{t("yearly")}</p>
</div>
</div>
</>
)}
/>
</div>
<div className="flex space-x-2">
<Button color="secondary" href="/settings" className="w-full justify-center">
{t("cancel")}

View File

@ -538,6 +538,9 @@ async function main() {
],
},
},
// Teams need a stripe id
stripeCustomerId: "testId",
stripeSubscriptionId: "testId",
},
[
{

View File

@ -82,6 +82,7 @@ export const viewerTeamsRouter = createProtectedRouter()
name: z.string(),
slug: z.string().optional().nullable(),
logo: z.string().optional().nullable(),
billingFrequency: z.union([z.literal("monthly"), z.literal("yearly")]),
}),
async resolve({ ctx, input }) {
const slug = input.slug || slugify(input.name);
@ -94,11 +95,32 @@ export const viewerTeamsRouter = createProtectedRouter()
if (nameCollisions) throw new TRPCError({ code: "BAD_REQUEST", message: "Team name already taken." });
// Create Stripe customer
const customer = await stripe.customers.create({
name: input.name,
});
if (!customer) throw new TRPCError({ code: "BAD_REQUEST", message: "Can not create Stripe customer" });
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [
{
price:
input.billingFrequency === "monthly"
? process.env.STRIPE_TEAM_MONTHLY_PRICE_ID
: process.env.STRIPE_TEAM_YEARLY_PRICE_ID,
},
],
});
const createTeam = await ctx.prisma.team.create({
data: {
name: input.name,
slug: slug,
logo: input.logo || null,
stripeCustomerId: customer.id,
stripeSubscriptionId: subscription.id,
},
});
@ -111,12 +133,6 @@ export const viewerTeamsRouter = createProtectedRouter()
},
});
// Create Stripe customer
const customer = await stripe.customers.create({
name: createTeam.name,
metadata: { teamId: createTeam.id },
});
// Sync Services: Close.com
closeComUpsertTeamUser(createTeam, ctx.user, MembershipRole.OWNER);
@ -175,9 +191,9 @@ export const viewerTeamsRouter = createProtectedRouter()
async resolve({ ctx, input }) {
if (!(await isTeamOwner(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });
if (process.env.STRIPE_PRIVATE_KEY) {
await downgradeTeamMembers(input.teamId);
}
// if (process.env.STRIPE_PRIVATE_KEY) {
// await downgradeTeamMembers(input.teamId);
// }
// delete all memberships
await ctx.prisma.membership.deleteMany({
@ -186,6 +202,21 @@ export const viewerTeamsRouter = createProtectedRouter()
},
});
// Delete customer from Stripe
try {
const stripeCustomerId = await ctx.prisma.team.findFirst({
where: {
id: input.teamId,
},
select: { stripeCustomerId: true },
});
console.log("🚀 ~ file: teams.tsx ~ line 213 ~ resolve ~ teamStripeCustomerId", stripeCustomerId);
const response = await stripe.customers.del(stripeCustomerId.stripeCustomerId);
console.log("🚀 ~ file: teams.tsx ~ line 214 ~ resolve ~ response", response);
} catch {
throw new TRPCError({ code: "UNAUTHORIZED", message: "Could not delete customer from Stripe" });
}
const deletedTeam = await ctx.prisma.team.delete({
where: {
id: input.teamId,