Compare commits

..

8 Commits

Author SHA1 Message Date
Henry
9df9b9146c Merge branch 'main' into feat/seats 2025-09-02 22:35:56 +01:00
Henry
7966ec01f5 chore: update proration comment 2025-09-02 22:34:27 +01:00
Henry
2779c06bcc chore: add translations 2025-09-02 22:24:45 +01:00
Henry
4f9841ceaf chore: remove pricing notice 2025-09-02 22:24:33 +01:00
Henry
a596b1c40b feat: rework upgrade 2025-09-02 22:19:42 +01:00
Henry
bc98fc848f feat: update subscription when increasing/decreasing workspace members 2025-09-02 14:05:35 +01:00
Henry
5f4678ab4e feat: prompt user to create subscription if inactive 2025-09-02 13:12:43 +01:00
Henry
a7c68db90e feat: setup subscriptions 2025-09-01 22:17:09 +01:00
8 changed files with 41 additions and 37 deletions

View File

@@ -70,7 +70,7 @@ export default async function handler(
mode: "subscription",
line_items: [
{
price: process.env.STRIPE_PRO_PLAN_MONTHLY_PRICE_ID,
price: process.env.STRIPE_PRO_PLAN_PRICE_ID,
quantity: 1,
},
],

View File

@@ -36,7 +36,7 @@ export default async function handler(
const event = stripe.webhooks.constructEvent(
rawBody,
sig,
process.env.STRIPE_WEBHOOK_SECRET_LEGACY!,
process.env.STRIPE_WEBHOOK_SECRET!,
);
const { db } = await createNextApiContext(req);

View File

@@ -1,5 +1,4 @@
import { t } from "@lingui/core/macro";
import { env } from "next-runtime-env";
import { HiEllipsisHorizontal, HiOutlinePlusSmall } from "react-icons/hi2";
import { twMerge } from "tailwind-merge";
@@ -12,6 +11,7 @@ import FeedbackModal from "~/components/FeedbackModal";
import Modal from "~/components/modal";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
import { PageHead } from "~/components/PageHead";
import { env } from "~/env";
import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import { api } from "~/utils/api";
@@ -171,7 +171,7 @@ export default function MembersPage() {
{t`Members`}
</h1>
<div className="flex items-center gap-3">
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
{env.NEXT_PUBLIC_KAN_ENV === "cloud" && (
<div
className={twMerge(
"flex items-center rounded-full border px-3 py-1 text-center text-xs",

View File

@@ -22,11 +22,6 @@ services:
# Stripe
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- STRIPE_WEBHOOK_SECRET_LEGACY=${STRIPE_WEBHOOK_SECRET_LEGACY}
- STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID=${STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID}
- STRIPE_TEAM_PLAN_YEARLY_PRICE_ID=${STRIPE_TEAM_PLAN_YEARLY_PRICE_ID}
- STRIPE_PRO_PLAN_MONTHLY_PRICE_ID=${STRIPE_PRO_PLAN_MONTHLY_PRICE_ID}
- STRIPE_PRO_PLAN_YEARLY_PRICE_ID=${STRIPE_PRO_PLAN_YEARLY_PRICE_ID}
# Email
- SMTP_HOST=${SMTP_HOST}

View File

@@ -114,13 +114,13 @@ async function downloadImage(url: string): Promise<Buffer> {
export const initAuth = (db: dbClient) => {
return betterAuth({
secret: process.env.BETTER_AUTH_SECRET!,
baseURL: env("NEXT_PUBLIC_BASE_URL"),
baseURL: process.env.NEXT_PUBLIC_BASE_URL!,
trustedOrigins: process.env.BETTER_AUTH_TRUSTED_ORIGINS
? [
env("NEXT_PUBLIC_BASE_URL") ?? "",
process.env.NEXT_PUBLIC_BASE_URL!,
...process.env.BETTER_AUTH_TRUSTED_ORIGINS.split(","),
]
: [env("NEXT_PUBLIC_BASE_URL") ?? ""],
: [process.env.NEXT_PUBLIC_BASE_URL!],
database: drizzleAdapter(db, {
provider: "pg",
schema: {
@@ -196,13 +196,6 @@ export const initAuth = (db: dbClient) => {
return isUserInWorkspace;
},
getCheckoutSessionParams: () => {
return {
params: {
allow_promotion_codes: true,
},
};
},
},
}),
]
@@ -324,17 +317,37 @@ export const initAuth = (db: dbClient) => {
},
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (
if (ctx.path.startsWith("/get-session")) {
const user = ctx.context.session?.user;
if (
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
user &&
!user.stripeCustomerId
) {
const stripe = createStripeClient();
const stripeCustomer = await stripe.customers.create({
email: user.email,
metadata: {
userId: user.id,
},
});
await userRepo.update(db, user.id, {
stripeCustomerId: stripeCustomer.id,
});
}
} else if (
ctx.path === "/magic-link/verify" &&
(ctx.query?.callbackURL as string | undefined)?.includes(
"type=invite",
)
) &&
ctx.query?.memberPublicId
) {
const userId = ctx.context.newSession?.session.userId;
const callbackURL = ctx.query?.callbackURL as string | undefined;
const memberPublicId = callbackURL?.split("memberPublicId=")[1];
const memberPublicId = ctx.query.memberPublicId as string;
if (userId && memberPublicId) {
if (userId) {
const member = await memberRepo.getByPublicId(db, memberPublicId);
if (member?.id) {

View File

@@ -45,14 +45,14 @@ export const sendEmail = async (
html,
};
// if (cloudMailerClient) {
// const response = await cloudMailerClient.emails.send({
// ...options,
// body: html,
// });
if (cloudMailerClient) {
const response = await cloudMailerClient.emails.send({
...options,
body: html,
});
// return response;
// }
return response;
}
const response = await transporter.sendMail(options);

View File

@@ -64,7 +64,7 @@ export const updateSubscriptionSeats = async (
quantity: newQuantity,
},
],
proration_behavior: "create_prorations",
proration_behavior: "always_invoice", // Invoice immediately
},
);

View File

@@ -99,11 +99,7 @@
"NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY",
"STRIPE_SECRET_KEY",
"STRIPE_WEBHOOK_SECRET",
"STRIPE_WEBHOOK_SECRET_LEGACY",
"STRIPE_PRO_PLAN_MONTHLY_PRICE_ID",
"STRIPE_PRO_PLAN_YEARLY_PRICE_ID",
"STRIPE_TEAM_PLAN_MONTHLY_PRICE_ID",
"STRIPE_TEAM_PLAN_YEARLY_PRICE_ID",
"STRIPE_PRO_PLAN_PRICE_ID",
"NEXT_PUBLIC_STORAGE_DOMAIN",
"NEXT_PUBLIC_STORAGE_URL",
"NEXT_PUBLIC_AVATAR_BUCKET_NAME",