Send email on paid subscription
This commit is contained in:
parent
2a52e7693c
commit
20f1306be2
|
@ -76,7 +76,11 @@ const CreateNewTeamPage = () => {
|
||||||
|
|
||||||
const currentStepIndex = steps.indexOf(currentStep);
|
const currentStepIndex = steps.indexOf(currentStep);
|
||||||
|
|
||||||
const purchaseTeamMutation = trpc.useMutation(["viewer.teams.purchaseTeamSubscription"]);
|
const purchaseTeamMutation = trpc.useMutation(["viewer.teams.purchaseTeamSubscription"], {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
router.push(data.url);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Stripe from "stripe";
|
||||||
import stripe from "@calcom/app-store/stripepayment/lib/server";
|
import stripe from "@calcom/app-store/stripepayment/lib/server";
|
||||||
import EventManager from "@calcom/core/EventManager";
|
import EventManager from "@calcom/core/EventManager";
|
||||||
import { sendScheduledEmails } from "@calcom/emails";
|
import { sendScheduledEmails } from "@calcom/emails";
|
||||||
|
import { sendTeamInvite } from "@calcom/features/ee/teams/lib/inviteMember";
|
||||||
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
|
||||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||||
|
@ -169,18 +170,56 @@ async function handleTeamSubscriptionSuccess(event: Stripe.Event) {
|
||||||
|
|
||||||
// Update team record with Stripe ids
|
// Update team record with Stripe ids
|
||||||
if (checkoutSession.payment_status === "paid") {
|
if (checkoutSession.payment_status === "paid") {
|
||||||
await prisma.team.update({
|
const team = await prisma.team.update({
|
||||||
where: {
|
where: {
|
||||||
id: parseInt(checkoutSession.metadata.teamId),
|
id: parseInt(checkoutSession.metadata.teamId),
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
|
subscriptionStatus: "ACTIVE",
|
||||||
metadata: {
|
metadata: {
|
||||||
stripeCustomerId: checkoutSession.customer as string,
|
stripeCustomerId: checkoutSession.customer as string,
|
||||||
stripeSubscriptionId: checkoutSession.subscription as string,
|
stripeSubscriptionId: checkoutSession.subscription as string,
|
||||||
subscriptionStatus: "active",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const members = await prisma.membership.findMany({
|
||||||
|
where: {
|
||||||
|
teamId: parseInt(checkoutSession.metadata.teamId),
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
email: true,
|
||||||
|
name: true,
|
||||||
|
username: true,
|
||||||
|
locale: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log("🚀 ~ file: webhook.ts ~ line 199 ~ handleTeamSubscriptionSuccess ~ members", members);
|
||||||
|
|
||||||
|
const teamOwner = members.find((member) => member.role === "OWNER");
|
||||||
|
console.log("🚀 ~ file: webhook.ts ~ line 204 ~ handleTeamSubscriptionSuccess ~ teamOwner", teamOwner);
|
||||||
|
|
||||||
|
console.log("🚀 ~ file: webhook.ts ~ line 184 ~ handleTeamSubscriptionSuccess ~ team", team);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
members.map(
|
||||||
|
(member) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
sendTeamInvite({
|
||||||
|
member: member.user,
|
||||||
|
inviter: teamOwner.user.name,
|
||||||
|
teamOwnerLocale: teamOwner.user.locale,
|
||||||
|
teamId: team.id,
|
||||||
|
teamName: team.name,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new HttpCode({
|
throw new HttpCode({
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { prisma } from "@calcom/prisma";
|
||||||
|
|
||||||
import { PendingMember } from "./types";
|
import { PendingMember } from "./types";
|
||||||
|
|
||||||
const inviteMember = async ({
|
export const createMember = async ({
|
||||||
teamId,
|
teamId,
|
||||||
teamName,
|
teamName,
|
||||||
inviter,
|
inviter,
|
||||||
|
@ -22,7 +22,6 @@ const inviteMember = async ({
|
||||||
language: string;
|
language: string;
|
||||||
teamSubscriptionActive?: boolean;
|
teamSubscriptionActive?: boolean;
|
||||||
} => {
|
} => {
|
||||||
const translation = await getTranslation(language ?? "en", "common");
|
|
||||||
if (pendingMember.username) {
|
if (pendingMember.username) {
|
||||||
await prisma.membership.create({
|
await prisma.membership.create({
|
||||||
data: {
|
data: {
|
||||||
|
@ -43,7 +42,6 @@ const inviteMember = async ({
|
||||||
id: true,
|
id: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log("🚀 ~ file: inviteMember.ts ~ line 31 ~ user", user);
|
|
||||||
if (user) {
|
if (user) {
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
|
@ -73,26 +71,42 @@ const inviteMember = async ({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamSubscriptionActive) {
|
|
||||||
const token: string = randomBytes(32).toString("hex");
|
|
||||||
|
|
||||||
await prisma.verificationToken.create({
|
|
||||||
data: {
|
|
||||||
identifier: pendingMember.email,
|
|
||||||
token,
|
|
||||||
expires: new Date(new Date().setHours(168)), // +1 week
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await sendTeamInviteEmail({
|
|
||||||
language: translation,
|
|
||||||
from: inviter,
|
|
||||||
to: pendingMember.email,
|
|
||||||
teamName: teamName,
|
|
||||||
joinLink: `${WEBAPP_URL}/signup?token=${token}&callbackUrl=/settings/teams`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default inviteMember;
|
|
||||||
|
export const sendTeamInvite = async ({ member, inviter, teamOwnerLocale, teamId, teamName }) => {
|
||||||
|
if (member.role === "OWNER") return;
|
||||||
|
|
||||||
|
const translation = await getTranslation(member.locale || teamOwnerLocale || "en", "common");
|
||||||
|
|
||||||
|
if (member.username) {
|
||||||
|
await sendTeamInviteEmail({
|
||||||
|
language: translation,
|
||||||
|
from: inviter,
|
||||||
|
to: member.email,
|
||||||
|
teamName,
|
||||||
|
joinLink: WEBAPP_URL + `/settings/teams/${teamId}/members`,
|
||||||
|
});
|
||||||
|
// Send an invite with a signup link if not a user
|
||||||
|
} else {
|
||||||
|
const token: string = randomBytes(32).toString("hex");
|
||||||
|
|
||||||
|
await prisma.verificationToken.create({
|
||||||
|
data: {
|
||||||
|
identifier: pendingMember.email,
|
||||||
|
token,
|
||||||
|
expires: new Date(new Date().setHours(168)), // +1 week
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendTeamInviteEmail({
|
||||||
|
language: translation,
|
||||||
|
from: inviter,
|
||||||
|
to: member.email,
|
||||||
|
teamName: teamName,
|
||||||
|
joinLink: `${WEBAPP_URL}/signup?token=${token}&callbackUrl=/settings/teams`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🚀 ~ file: inviteMember.ts ~ line 100 ~ sendTeamInvite ~ member", member);
|
||||||
|
};
|
||||||
|
|
|
@ -56,6 +56,11 @@ const ProfileView = () => {
|
||||||
router.push("/settings");
|
router.push("/settings");
|
||||||
},
|
},
|
||||||
onSuccess: (team) => {
|
onSuccess: (team) => {
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: team-profile-view.tsx ~ line 59 ~ const{data:team,isLoading}=trpc.useQuery ~ team",
|
||||||
|
team
|
||||||
|
);
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
form.setValue("name", team.name || "");
|
form.setValue("name", team.name || "");
|
||||||
form.setValue("url", team.slug || "");
|
form.setValue("url", team.slug || "");
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from "@calcom/app-store/stripepayment/lib/team-billing";
|
} from "@calcom/app-store/stripepayment/lib/team-billing";
|
||||||
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
import { getUserAvailability } from "@calcom/core/getUserAvailability";
|
||||||
import { sendTeamInviteEmail } from "@calcom/emails";
|
import { sendTeamInviteEmail } from "@calcom/emails";
|
||||||
import inviteMember from "@calcom/features/ee/teams/lib/inviteMember";
|
import { createMember } from "@calcom/features/ee/teams/lib/inviteMember";
|
||||||
import { deleteTeamFromStripe, purchaseTeamSubscription } from "@calcom/features/ee/teams/lib/payments";
|
import { deleteTeamFromStripe, purchaseTeamSubscription } from "@calcom/features/ee/teams/lib/payments";
|
||||||
import { HOSTED_CAL_FEATURES, IS_STRIPE_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
import { HOSTED_CAL_FEATURES, IS_STRIPE_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||||
|
@ -501,7 +501,6 @@ export const viewerTeamsRouter = createProtectedRouter()
|
||||||
async resolve({ ctx, input }) {
|
async resolve({ ctx, input }) {
|
||||||
const { slug, name, logo, members } = input;
|
const { slug, name, logo, members } = input;
|
||||||
const { prisma } = ctx;
|
const { prisma } = ctx;
|
||||||
console.log("🚀 ~ file: teams.tsx ~ line 499 ~ resolve ~ input", input);
|
|
||||||
|
|
||||||
// Tentatively create the team in the DB
|
// Tentatively create the team in the DB
|
||||||
const createTeam = await prisma.team.create({
|
const createTeam = await prisma.team.create({
|
||||||
|
@ -509,29 +508,35 @@ export const viewerTeamsRouter = createProtectedRouter()
|
||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
logo,
|
logo,
|
||||||
// members: {
|
members: {
|
||||||
// create: {
|
create: {
|
||||||
// userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
// role: MembershipRole.OWNER,
|
accepted: true,
|
||||||
// accepted: true,
|
role: "OWNER",
|
||||||
// },
|
},
|
||||||
// },
|
},
|
||||||
|
subscriptionStatus: "PENDING",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tentatively create members on team
|
// Tentatively create members on team
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
inviteMember({
|
if (member.userId !== ctx.user.id)
|
||||||
teamId: createTeam.id,
|
createMember({
|
||||||
teamName: name,
|
teamId: createTeam.id,
|
||||||
inviter: ctx.user.name,
|
teamName: name,
|
||||||
pendingMember: member,
|
inviter: ctx.user.name,
|
||||||
});
|
pendingMember: member,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_STRIPE_ENABLED)
|
if (!IS_STRIPE_ENABLED)
|
||||||
throw new TRPCError({ code: "FORBIDDEN", message: "Team billing is not enabled" });
|
throw new TRPCError({ code: "FORBIDDEN", message: "Team billing is not enabled" });
|
||||||
return await purchaseTeamSubscription({ ...input, email: ctx.user.email });
|
return await purchaseTeamSubscription({
|
||||||
|
teamId: createTeam.id,
|
||||||
|
seats: members.length,
|
||||||
|
email: ctx.user.email,
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -169,8 +169,9 @@ const SettingsSidebarContainer = ({ className = "" }) => {
|
||||||
teamMenuState &&
|
teamMenuState &&
|
||||||
teams.map((team, index: number) => {
|
teams.map((team, index: number) => {
|
||||||
if (
|
if (
|
||||||
teamMenuState.some((teamState) => teamState.teamId === team.id) &&
|
teamMenuState.some((teamState) => teamState.teamId === team.id)
|
||||||
team.subscriptionStatus !== "PENDING"
|
// &&
|
||||||
|
// team.subscriptionStatus !== "PENDING"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
|
|
Loading…
Reference in New Issue
Block a user