Skip to content

Commit a200cdd

Browse files
authored
webhook on web for new pricing (#5466)
1 parent 8a97582 commit a200cdd

File tree

1 file changed

+168
-12
lines changed
  • web/pages/api/stripe/event_webhook

1 file changed

+168
-12
lines changed

web/pages/api/stripe/event_webhook/index.ts

Lines changed: 168 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -784,11 +784,11 @@ const PricingVersion20240913 = {
784784
});
785785

786786
const { error: updateError } = await dbExecute(
787-
`UPDATE organization
788-
SET subscription_status = 'active',
789-
stripe_subscription_id = $1,
790-
stripe_subscription_item_id = $2,
791-
tier = 'pro-20250202',
787+
`UPDATE organization
788+
SET subscription_status = 'active',
789+
stripe_subscription_id = $1,
790+
stripe_subscription_item_id = $2,
791+
tier = 'pro-20250202',
792792
stripe_metadata = $3
793793
WHERE id = $4`,
794794
[subscriptionId, subscriptionItemId, { addons: addons }, orgId || ""],
@@ -820,6 +820,155 @@ const PricingVersion20240913 = {
820820
handleDelete: PricingVersionOld.handleDelete,
821821
};
822822

823+
// New pricing version (2025-12-10): $79/mo flat + $6/GB byte-based billing
824+
// Prompts now included, no per-seat, no per-request
825+
const ProVersion20251210 = {
826+
async handleCreate(event: Stripe.Event) {
827+
const subscription = event.data.object as Stripe.Subscription;
828+
const subscriptionId = subscription.id;
829+
const subscriptionItemId = subscription?.items.data[0].id;
830+
const orgId = subscription.metadata?.orgId;
831+
832+
// New pricing: all features included in base plan
833+
const addons: Addons = {
834+
alerts: true,
835+
prompts: true,
836+
experiments: true,
837+
evals: true,
838+
};
839+
840+
const { error: updateError } = await dbExecute(
841+
`UPDATE organization
842+
SET subscription_status = 'active',
843+
stripe_subscription_id = $1,
844+
stripe_subscription_item_id = $2,
845+
tier = 'pro-20251210',
846+
stripe_metadata = $3
847+
WHERE id = $4`,
848+
[subscriptionId, subscriptionItemId, { addons: addons }, orgId || ""],
849+
);
850+
851+
if (updateError) {
852+
logger.error({ error: updateError }, "Failed to update organization");
853+
}
854+
855+
// Invite members after org is updated
856+
await inviteOnboardingMembers(orgId);
857+
858+
// Send PostHog event
859+
await sendSubscriptionEvent("subscription_created", subscription, {
860+
includeOrgData: true,
861+
addons: JSON.stringify(addons),
862+
});
863+
},
864+
865+
handleUpdate: async (_event: Stripe.Event) => {
866+
const subscription = _event.data.object as Stripe.Subscription;
867+
await sendSubscriptionCanceledEvent(subscription);
868+
},
869+
handleCheckoutSessionCompleted: async (_event: Stripe.Event) => {
870+
return;
871+
},
872+
handleDelete: PricingVersionOld.handleDelete,
873+
};
874+
875+
// New Team pricing version (2025-12-10): $799/mo flat + $6/GB byte-based billing
876+
const TeamVersion20251210 = {
877+
async handleCreate(event: Stripe.Event) {
878+
const subscription = event.data.object as Stripe.Subscription;
879+
const subscriptionId = subscription.id;
880+
const subscriptionItemId = subscription?.items.data[0].id;
881+
const orgId = subscription.metadata?.orgId;
882+
883+
// Get the existing subscription from the organization
884+
type OrgSubscriptionData = {
885+
stripe_subscription_id: string | null;
886+
name: string | null;
887+
owner: string | null;
888+
};
889+
890+
const { data: orgDataArray, error: orgDataError } =
891+
await dbExecute<OrgSubscriptionData>(
892+
`SELECT stripe_subscription_id, name, owner FROM organization WHERE id = $1 LIMIT 1`,
893+
[orgId || ""],
894+
);
895+
896+
if (orgDataError) {
897+
logger.error(
898+
{ error: orgDataError },
899+
"Failed to fetch organization data",
900+
);
901+
}
902+
903+
const orgData =
904+
orgDataArray && orgDataArray.length > 0 ? orgDataArray[0] : null;
905+
906+
// Cancel old subscription if it exists AND it's different from the new one
907+
if (
908+
orgData &&
909+
orgData.stripe_subscription_id &&
910+
typeof orgData.stripe_subscription_id === "string" &&
911+
orgData.stripe_subscription_id !== subscriptionId // Don't cancel the subscription we just created!
912+
) {
913+
try {
914+
logger.info("Cancelling old subscription");
915+
await stripe.subscriptions.cancel(orgData.stripe_subscription_id, {
916+
invoice_now: true,
917+
prorate: true,
918+
});
919+
} catch (_e) {
920+
logger.error({ error: _e }, "Error canceling old subscription");
921+
}
922+
}
923+
924+
// New pricing: all features included in base plan
925+
const addons: Addons = {
926+
alerts: true,
927+
prompts: true,
928+
experiments: true,
929+
evals: true,
930+
};
931+
932+
// Update to new subscription
933+
const { error: updateError } = await dbExecute(
934+
`UPDATE organization
935+
SET subscription_status = 'active',
936+
stripe_subscription_id = $1,
937+
stripe_subscription_item_id = $2,
938+
tier = 'team-20251210',
939+
stripe_metadata = $3
940+
WHERE id = $4`,
941+
[subscriptionId, subscriptionItemId, { addons }, orgId || ""],
942+
);
943+
944+
if (updateError) {
945+
logger.error({ error: updateError }, "Failed to update organization");
946+
}
947+
948+
// Invite members after org is updated
949+
await inviteOnboardingMembers(orgId);
950+
951+
// Create Slack channel and invite team members
952+
if (orgData?.name) {
953+
await createSlackChannelAndInviteMembers(orgId, orgData.name);
954+
}
955+
956+
// Send PostHog event
957+
await sendSubscriptionEvent("subscription_created", subscription, {
958+
includeOrgData: true,
959+
});
960+
},
961+
962+
handleUpdate: async (_event: Stripe.Event) => {
963+
const subscription = _event.data.object as Stripe.Subscription;
964+
await sendSubscriptionCanceledEvent(subscription);
965+
},
966+
handleCheckoutSessionCompleted: async (_event: Stripe.Event) => {
967+
return;
968+
},
969+
handleDelete: PricingVersionOld.handleDelete,
970+
};
971+
823972
const InvoiceHandlers = {
824973
async handleInvoiceCreated(event: Stripe.Event) {
825974
const invoice = event.data.object as Stripe.Invoice;
@@ -999,13 +1148,20 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
9991148
| Stripe.Subscription
10001149
| Stripe.Checkout.Session;
10011150

1002-
const pricingFunctions =
1003-
stripeObject.metadata?.["tier"] === "pro-20240913" ||
1004-
stripeObject.metadata?.["tier"] === "pro-20250202"
1005-
? PricingVersion20240913
1006-
: stripeObject.metadata?.["tier"] === "team-20250130"
1007-
? TeamVersion20250130
1008-
: PricingVersionOld;
1151+
const tier = stripeObject.metadata?.["tier"];
1152+
let pricingFunctions;
1153+
1154+
if (tier === "pro-20251210") {
1155+
pricingFunctions = ProVersion20251210;
1156+
} else if (tier === "team-20251210") {
1157+
pricingFunctions = TeamVersion20251210;
1158+
} else if (tier === "pro-20240913" || tier === "pro-20250202") {
1159+
pricingFunctions = PricingVersion20240913;
1160+
} else if (tier === "team-20250130") {
1161+
pricingFunctions = TeamVersion20250130;
1162+
} else {
1163+
pricingFunctions = PricingVersionOld;
1164+
}
10091165

10101166
if (event.type === "test_helpers.test_clock.advancing") {
10111167
return res.status(200).end();

0 commit comments

Comments
 (0)