mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 08:45:38 +02:00
## Description <!-- Briefly describe the purpose and scope of your changes --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new subscription API endpoint for managing subscriptions and products. - Enhanced subscription management with new entities and functionalities. - Added functionality to retrieve current timestamps in both local and UTC formats. - Added Polar.sh integration with customer portal and checkout session creation APIs. - **Refactor** - Redesigned team details to now present members and subscription information instead of a plan type. - Enhanced member management by incorporating role assignments. - Streamlined user data handling and removed legacy subscription event logic. - Simplified error handling in actor functions for better clarity. - Updated plan types and UI labels to reflect new subscription tiers. - Improved database indexing for Steam user data. - **Chores** - Updated the database schema with new tables and fields to support subscription, team, and member enhancements. - Extended identifier prefixes to broaden system integration. - Added new secrets related to pricing plans in infrastructure configuration. - Configured API and auth routing with new domain and routing rules. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
96 lines
3.3 KiB
TypeScript
96 lines
3.3 KiB
TypeScript
import { z } from "zod";
|
|
import { fn } from "../utils";
|
|
import { Resource } from "sst";
|
|
import { useTeam, useUserID } from "../actor";
|
|
import { Polar as PolarSdk } from "@polar-sh/sdk";
|
|
import { validateEvent } from "@polar-sh/sdk/webhooks";
|
|
import { PlanType } from "../subscription/subscription.sql";
|
|
|
|
const polar = new PolarSdk({ accessToken: Resource.PolarSecret.value, server: Resource.App.stage !== "production" ? "sandbox" : "production" });
|
|
const planType = z.enum(PlanType)
|
|
export namespace Polar {
|
|
export const client = polar;
|
|
|
|
export const fromUserEmail = fn(z.string().min(1), async (email) => {
|
|
try {
|
|
const customers = await client.customers.list({ email })
|
|
|
|
if (customers.result.items.length === 0) {
|
|
return await client.customers.create({ email })
|
|
} else {
|
|
return customers.result.items[0]
|
|
}
|
|
|
|
} catch (err) {
|
|
//FIXME: This is the issue [Polar.sh/#5147](https://github.com/polarsource/polar/issues/5147)
|
|
// console.log("error", err)
|
|
return undefined
|
|
}
|
|
})
|
|
|
|
const getProductIDs = (plan: z.infer<typeof planType>) => {
|
|
switch (plan) {
|
|
case "free":
|
|
return [Resource.NestriFreeMonthly.value]
|
|
case "pro":
|
|
return [Resource.NestriProYearly.value, Resource.NestriProMonthly.value]
|
|
case "family":
|
|
return [Resource.NestriFamilyYearly.value, Resource.NestriFamilyMonthly.value]
|
|
default:
|
|
return [Resource.NestriFreeMonthly.value]
|
|
}
|
|
}
|
|
|
|
export const createPortal = fn(
|
|
z.string(),
|
|
async (customerId) => {
|
|
const session = await client.customerSessions.create({
|
|
customerId
|
|
})
|
|
|
|
return session.customerPortalUrl
|
|
}
|
|
)
|
|
|
|
//TODO: Implement this
|
|
export const handleWebhook = async(payload: ReturnType<typeof validateEvent>) => {
|
|
switch (payload.type) {
|
|
case "subscription.created":
|
|
const teamID = payload.data.metadata.teamID
|
|
}
|
|
}
|
|
|
|
export const createCheckout = fn(
|
|
z
|
|
.object({
|
|
planType: z.enum(PlanType),
|
|
customerEmail: z.string(),
|
|
successUrl: z.string(),
|
|
customerID: z.string(),
|
|
allowDiscountCodes: z.boolean(),
|
|
teamID: z.string()
|
|
})
|
|
.partial({
|
|
customerEmail: true,
|
|
allowDiscountCodes: true,
|
|
customerID: true,
|
|
teamID: true
|
|
}),
|
|
async (input) => {
|
|
const productIDs = getProductIDs(input.planType)
|
|
|
|
const checkoutUrl =
|
|
await client.checkouts.create({
|
|
products: productIDs,
|
|
customerEmail: input.customerEmail ?? useUserID(),
|
|
successUrl: `${input.successUrl}?checkout={CHECKOUT_ID}`,
|
|
allowDiscountCodes: input.allowDiscountCodes ?? false,
|
|
customerId: input.customerID,
|
|
customerMetadata: {
|
|
teamID: input.teamID ?? useTeam()
|
|
}
|
|
})
|
|
|
|
return checkoutUrl.url
|
|
})
|
|
} |