Compare commits

..

1 Commits

Author SHA1 Message Date
Henry
b3ca6c725c feat(cloud): add notification client 2025-11-24 22:16:59 +00:00
5 changed files with 20 additions and 83 deletions

View File

@@ -35,10 +35,7 @@ services:
- SMTP_PASSWORD=${SMTP_PASSWORD}
- SMTP_SECURE=${SMTP_SECURE}
- EMAIL_FROM=${EMAIL_FROM}
# Notifications
- NOVU_API_KEY=${NOVU_API_KEY}
- DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL}
# S3 storage
- S3_ACCESS_KEY_ID=${S3_ACCESS_KEY_ID}

View File

@@ -1,8 +1,5 @@
import type { Subscription } from "@better-auth/stripe";
import type Stripe from "stripe";
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { stripe } from "@better-auth/stripe";
import { ChatOrPushProviderEnum } from "@novu/api/models/components";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { createAuthEndpoint, createAuthMiddleware } from "better-auth/api";
@@ -181,17 +178,19 @@ export const initAuth = (db: dbClient) => {
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
await triggerWorkflow(db, "trial-start", subscription);
// Called when a trial starts
// @todo: send trial start email
// await sendTrialStartEmail(subscription.referenceId);
},
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
onTrialEnd: async ({ subscription }, request) => {
// Called when a trial ends
// @todo: send trial end email
// await sendTrialEndEmail(user.email);
},
onTrialExpired: async (subscription) => {
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
// Called when a trial expires without conversion
// @todo: send trial expired email
// await sendTrialExpiredEmail(subscription.referenceId);
},
},
},
@@ -203,17 +202,19 @@ export const initAuth = (db: dbClient) => {
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
await triggerWorkflow(db, "trial-start", subscription);
// Called when a trial starts
// @todo: send trial start email
// await sendTrialStartEmail(subscription.referenceId);
},
onTrialEnd: async ({ subscription }) => {
await triggerWorkflow(db, "trial-end", subscription);
onTrialEnd: async ({ subscription }, request) => {
// Called when a trial ends
// @todo: send trial end email
// await sendTrialEndEmail(user.email);
},
onTrialExpired: async (subscription) => {
await triggerWorkflow(
db,
"trial-expired",
subscription,
);
// Called when a trial expires without conversion
// @todo: send trial expired email
// await sendTrialExpiredEmail(subscription.referenceId);
},
},
},
@@ -417,17 +418,6 @@ export const initAuth = (db: dbClient) => {
},
workflowId: "user-signup",
});
await notificationClient.subscribers.credentials.update(
{
providerId: ChatOrPushProviderEnum.Discord,
credentials: {
webhookUrl: process.env.DISCORD_WEBHOOK_URL!,
},
integrationIdentifier: "discord",
},
user.id,
);
} catch (error) {
console.error(
"Error adding user to notification client",
@@ -472,34 +462,3 @@ export const initAuth = (db: dbClient) => {
},
});
};
async function triggerWorkflow(
db: dbClient,
workflowId: string,
subscription: Subscription,
cancellationDetails?: Stripe.Subscription.CancellationDetails | null,
) {
try {
if (!subscription.stripeCustomerId || !notificationClient) return;
const user = await userRepo.getByStripeCustomerId(
db,
subscription.stripeCustomerId,
);
if (!user || !notificationClient) return;
await notificationClient.trigger({
to: {
subscriberId: user.id,
},
payload: {
...subscription,
cancellationDetails,
},
workflowId,
});
} catch (error) {
console.error("Error triggering workflow", error);
}
}

View File

@@ -28,15 +28,6 @@ export const getById = async (db: dbClient, userId: string) => {
});
};
export const getByStripeCustomerId = async (
db: dbClient,
stripeCustomerId: string,
) => {
return await db.query.users.findFirst({
where: eq(users.stripeCustomerId, stripeCustomerId),
});
};
export const getByEmail = (db: dbClient, email: string) => {
return db.query.users.findFirst({
columns: {

View File

@@ -71,13 +71,4 @@ export const updateSubscriptionSeats = async (
return updatedSubscription;
};
export const getCancellationDetails = async (
stripeSubscriptionId: string,
): Promise<Stripe.Subscription.CancellationDetails | null> => {
const stripe = createStripeClient();
const stripeSubscription =
await stripe.subscriptions.retrieve(stripeSubscriptionId);
return stripeSubscription.cancellation_details ?? null;
};
export { createStripeClient };

View File

@@ -99,7 +99,6 @@
"NEXT_PUBLIC_KAN_ENV",
"NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY",
"STRIPE_SECRET_KEY",
"DISCORD_WEBHOOK_URL",
"STRIPE_WEBHOOK_SECRET",
"STRIPE_WEBHOOK_SECRET_LEGACY",
"STRIPE_PRO_PLAN_MONTHLY_PRICE_ID",