fix: create stripe customer when inviting new team member
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { HiXMark } from "react-icons/hi2";
|
import { HiXMark } from "react-icons/hi2";
|
||||||
|
|
||||||
import { type InviteMemberInput } from "@kan/api/types";
|
import type { InviteMemberInput } from "@kan/api/types";
|
||||||
|
|
||||||
|
import Button from "~/components/Button";
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
@@ -15,7 +16,7 @@ export function InviteMemberForm() {
|
|||||||
const { register, handleSubmit } = useForm<InviteMemberInput>({
|
const { register, handleSubmit } = useForm<InviteMemberInput>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
email: "",
|
email: "",
|
||||||
workspacePublicId: workspace?.publicId || "",
|
workspacePublicId: workspace.publicId || "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,12 +59,13 @@ export function InviteMemberForm() {
|
|||||||
|
|
||||||
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
<div>
|
<div>
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
isLoading={createBoard.isPending}
|
||||||
className="inline-flex w-full justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
|
className="inline-flex w-full justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
|
||||||
>
|
>
|
||||||
Invite member
|
Invite member
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -23,25 +23,6 @@ export default function MembersPage() {
|
|||||||
// { enabled: workspace?.publicId ? true : false },
|
// { enabled: workspace?.publicId ? true : false },
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpgrade = async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/stripe/create_checkout_session", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { url } = (await response.json()) as { url: string };
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
window.location.href = url;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating checkout session:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const TableRow = ({
|
const TableRow = ({
|
||||||
memberPublicId,
|
memberPublicId,
|
||||||
memberName,
|
memberName,
|
||||||
@@ -168,7 +149,7 @@ export default function MembersPage() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex items-center gap-x-1.5 rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:bg-dark-1000 dark:text-dark-50"
|
className="flex items-center gap-x-1.5 rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:bg-dark-1000 dark:text-dark-50"
|
||||||
onClick={() => handleUpgrade()}
|
onClick={() => openModal("INVITE_MEMBER")}
|
||||||
>
|
>
|
||||||
<div className="h-5 w-5 items-center">
|
<div className="h-5 w-5 items-center">
|
||||||
<HiOutlinePlusSmall
|
<HiOutlinePlusSmall
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { Stripe } from "stripe";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import * as memberRepo from "@kan/db/repository/member.repo";
|
import * as memberRepo from "@kan/db/repository/member.repo";
|
||||||
@@ -8,6 +9,16 @@ import { sendEmail } from "@kan/email";
|
|||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
|
||||||
|
const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
|
||||||
|
|
||||||
|
if (!stripeSecretKey) {
|
||||||
|
throw new Error("STRIPE_SECRET_KEY is not defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripe = new Stripe(stripeSecretKey, {
|
||||||
|
apiVersion: "2024-12-18.acacia",
|
||||||
|
});
|
||||||
|
|
||||||
export const memberRouter = createTRPCRouter({
|
export const memberRouter = createTRPCRouter({
|
||||||
invite: protectedProcedure
|
invite: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -93,10 +104,17 @@ export const memberRouter = createTRPCRouter({
|
|||||||
const invitedUserEmail = invite.data.user?.email;
|
const invitedUserEmail = invite.data.user?.email;
|
||||||
|
|
||||||
if (invitedUserAuthId && invitedUserEmail) {
|
if (invitedUserAuthId && invitedUserEmail) {
|
||||||
|
const stripeCustomer = await stripe.customers.create({
|
||||||
|
email: invitedUserEmail,
|
||||||
|
metadata: {
|
||||||
|
userId: invitedUserAuthId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const newUser = await userRepo.create(ctx.adminDb, {
|
const newUser = await userRepo.create(ctx.adminDb, {
|
||||||
email: invitedUserEmail,
|
email: invitedUserEmail,
|
||||||
id: invitedUserAuthId,
|
id: invitedUserAuthId,
|
||||||
stripeCustomerId: "",
|
stripeCustomerId: stripeCustomer.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
invitedUserId = newUser?.id;
|
invitedUserId = newUser?.id;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
"NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
"NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
||||||
"NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME",
|
"NEXT_PUBLIC_SUPABASE_AUTH_COOKIE_NAME",
|
||||||
"SUPABASE_SERVICE_API_KEY",
|
"SUPABASE_SERVICE_API_KEY",
|
||||||
|
"STRIPE_SECRET_KEY",
|
||||||
"PORT"
|
"PORT"
|
||||||
],
|
],
|
||||||
"globalPassThroughEnv": [
|
"globalPassThroughEnv": [
|
||||||
@@ -65,4 +66,4 @@
|
|||||||
"VERCEL_URL",
|
"VERCEL_URL",
|
||||||
"npm_lifecycle_event"
|
"npm_lifecycle_event"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user