feat: add new subscription tier and update credit configurations
- Introduced a new "max" subscription tier with associated monthly credits and top-up limits. - Updated existing subscription tiers' monthly credits and top-up limits for "starter", "pro", and "business". - Enhanced credit display and overview components to reflect the new tier and its attributes. - Integrated Polar authentication features for improved subscription management and credit handling.
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
|
||||
import { convex } from "@convex-dev/better-auth/plugins";
|
||||
import { requireRunMutationCtx } from "@convex-dev/better-auth/utils";
|
||||
import { checkout, polar, portal, webhooks } from "@polar-sh/better-auth";
|
||||
import { Polar } from "@polar-sh/sdk";
|
||||
import { components } from "./_generated/api";
|
||||
import { internal } from "./_generated/api";
|
||||
import { DataModel } from "./_generated/dataModel";
|
||||
import { query } from "./_generated/server";
|
||||
import { betterAuth } from "better-auth/minimal";
|
||||
@@ -10,6 +14,11 @@ import authConfig from "./auth.config";
|
||||
const siteUrl = process.env.SITE_URL!;
|
||||
const appUrl = process.env.APP_URL;
|
||||
|
||||
const polarClient = new Polar({
|
||||
accessToken: process.env.POLAR_ACCESS_TOKEN!,
|
||||
server: "production",
|
||||
});
|
||||
|
||||
// Component Client — stellt Adapter, Helper und Auth-Methoden bereit
|
||||
export const authComponent = createClient<DataModel>(components.betterAuth);
|
||||
|
||||
@@ -68,6 +77,92 @@ export const createAuth = (ctx: GenericCtx<DataModel>) => {
|
||||
},
|
||||
plugins: [
|
||||
convex({ authConfig }),
|
||||
polar({
|
||||
client: polarClient,
|
||||
createCustomerOnSignUp: true,
|
||||
use: [
|
||||
checkout({
|
||||
successUrl: `${siteUrl}/dashboard?checkout=success`,
|
||||
authenticatedUsersOnly: true,
|
||||
}),
|
||||
portal(),
|
||||
webhooks({
|
||||
secret: process.env.POLAR_WEBHOOK_SECRET!,
|
||||
onCustomerStateChanged: async (payload) => {
|
||||
const runMutationCtx = requireRunMutationCtx(ctx);
|
||||
const customerState = payload.data;
|
||||
const userId = customerState.externalId;
|
||||
|
||||
if (!userId) {
|
||||
console.error("Polar customer.state_changed payload without externalId", {
|
||||
customerId: customerState.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = customerState.activeSubscriptions?.[0];
|
||||
|
||||
if (!subscription) {
|
||||
await runMutationCtx.runMutation(internal.polar.handleSubscriptionRevoked, {
|
||||
userId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const tierMetadata = subscription.metadata.tier;
|
||||
const creditsMetadata = subscription.metadata.credits;
|
||||
const tier = tierMetadata === "starter" || tierMetadata === "pro" || tierMetadata === "max"
|
||||
? tierMetadata
|
||||
: undefined;
|
||||
const monthlyCredits = Number(creditsMetadata);
|
||||
|
||||
if (!tier || !Number.isFinite(monthlyCredits) || monthlyCredits <= 0) {
|
||||
console.error("Missing or invalid Polar subscription metadata", {
|
||||
subscriptionId: subscription.id,
|
||||
tier: tierMetadata,
|
||||
credits: creditsMetadata,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await runMutationCtx.runMutation(internal.polar.handleSubscriptionActivated, {
|
||||
userId,
|
||||
tier,
|
||||
polarSubscriptionId: subscription.id,
|
||||
currentPeriodStart: subscription.currentPeriodStart.getTime(),
|
||||
currentPeriodEnd: subscription.currentPeriodEnd.getTime(),
|
||||
monthlyCredits,
|
||||
});
|
||||
},
|
||||
onOrderPaid: async (payload) => {
|
||||
const runMutationCtx = requireRunMutationCtx(ctx);
|
||||
const order = payload.data;
|
||||
const metadata = order.product?.metadata;
|
||||
const type = metadata?.type;
|
||||
const credits = Number(metadata?.credits);
|
||||
|
||||
if (type !== "topup" || !Number.isFinite(credits) || credits <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userId = order.customer.externalId;
|
||||
if (!userId) {
|
||||
console.error("Polar order.paid payload without externalId", {
|
||||
orderId: order.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await runMutationCtx.runMutation(internal.polar.handleTopUpPaid, {
|
||||
userId,
|
||||
credits,
|
||||
polarOrderId: order.id,
|
||||
amountPaidEuroCents: order.totalAmount,
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user