Create & delete Stripe customers
This commit is contained in:
parent
d968b37e4c
commit
4dbcc62152
|
@ -73,7 +73,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
|
||||||
description={
|
description={
|
||||||
<span className=" text-sm leading-tight text-gray-500">
|
<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
|
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>
|
</span>
|
||||||
}>
|
}>
|
||||||
<form onSubmit={inviteMember}>
|
<form onSubmit={inviteMember}>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
|
||||||
|
import { classNames } from "@calcom/lib";
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
|
@ -7,7 +8,7 @@ import { trpc } from "@calcom/trpc/react";
|
||||||
import { Icon } from "@calcom/ui";
|
import { Icon } from "@calcom/ui";
|
||||||
import { Button, Avatar } from "@calcom/ui/v2";
|
import { Button, Avatar } from "@calcom/ui/v2";
|
||||||
import ImageUploader from "@calcom/ui/v2/core/ImageUploader";
|
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 CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: number) => void }) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
@ -31,6 +32,7 @@ const CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: n
|
||||||
name: values.name,
|
name: values.name,
|
||||||
slug: values.slug || null,
|
slug: values.slug || null,
|
||||||
logo: values.logo || null,
|
logo: values.logo || null,
|
||||||
|
billingFrequency: values.billingFrequency,
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
|
@ -96,6 +98,42 @@ const CreateANewTeamForm = (props: { nextStep: () => void; setTeamId: (teamId: n
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div className="flex space-x-2">
|
||||||
<Button color="secondary" href="/settings" className="w-full justify-center">
|
<Button color="secondary" href="/settings" className="w-full justify-center">
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
|
|
|
@ -538,6 +538,9 @@ async function main() {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Teams need a stripe id
|
||||||
|
stripeCustomerId: "testId",
|
||||||
|
stripeSubscriptionId: "testId",
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,6 +82,7 @@ export const viewerTeamsRouter = createProtectedRouter()
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
slug: z.string().optional().nullable(),
|
slug: z.string().optional().nullable(),
|
||||||
logo: z.string().optional().nullable(),
|
logo: z.string().optional().nullable(),
|
||||||
|
billingFrequency: z.union([z.literal("monthly"), z.literal("yearly")]),
|
||||||
}),
|
}),
|
||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
const slug = input.slug || slugify(input.name);
|
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." });
|
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({
|
const createTeam = await ctx.prisma.team.create({
|
||||||
data: {
|
data: {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
slug: slug,
|
slug: slug,
|
||||||
logo: input.logo || null,
|
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
|
// Sync Services: Close.com
|
||||||
closeComUpsertTeamUser(createTeam, ctx.user, MembershipRole.OWNER);
|
closeComUpsertTeamUser(createTeam, ctx.user, MembershipRole.OWNER);
|
||||||
|
|
||||||
|
@ -175,9 +191,9 @@ export const viewerTeamsRouter = createProtectedRouter()
|
||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
if (!(await isTeamOwner(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });
|
if (!(await isTeamOwner(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
|
||||||
if (process.env.STRIPE_PRIVATE_KEY) {
|
// if (process.env.STRIPE_PRIVATE_KEY) {
|
||||||
await downgradeTeamMembers(input.teamId);
|
// await downgradeTeamMembers(input.teamId);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// delete all memberships
|
// delete all memberships
|
||||||
await ctx.prisma.membership.deleteMany({
|
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({
|
const deletedTeam = await ctx.prisma.team.delete({
|
||||||
where: {
|
where: {
|
||||||
id: input.teamId,
|
id: input.teamId,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user