mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
⭐ feat(api): Add payments with Polar.sh (#264)
## 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>
This commit is contained in:
@@ -5,9 +5,12 @@ import { describeRoute } from "hono-openapi";
|
||||
import { User } from "@nestri/core/user/index";
|
||||
import { Team } from "@nestri/core/team/index";
|
||||
import { Examples } from "@nestri/core/examples";
|
||||
import { Polar } from "@nestri/core/polar/index";
|
||||
import { Member } from "@nestri/core/member/index";
|
||||
import { assertActor, withActor } from "@nestri/core/actor";
|
||||
import { ErrorResponses, Result, validator } from "./common";
|
||||
import { Subscription } from "@nestri/core/subscription/index";
|
||||
import { PlanType } from "@nestri/core/subscription/subscription.sql";
|
||||
|
||||
export namespace TeamApi {
|
||||
export const route = new Hono()
|
||||
@@ -49,7 +52,12 @@ export namespace TeamApi {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Result(
|
||||
z.literal("ok")
|
||||
z.object({
|
||||
checkoutUrl: z.string().openapi({
|
||||
description: "The checkout url to confirm subscription for this team",
|
||||
example: "https://polar.sh/checkout/2903038439320298377"
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -63,17 +71,24 @@ export namespace TeamApi {
|
||||
}),
|
||||
validator(
|
||||
"json",
|
||||
Team.create.schema.omit({ id: true }).openapi({
|
||||
description: "Details of the team to create",
|
||||
//@ts-expect-error
|
||||
example: { ...Examples.Team, id: undefined }
|
||||
})
|
||||
Team.create.schema
|
||||
.pick({ slug: true, name: true })
|
||||
.extend({ planType: z.enum(PlanType), successUrl: z.string().url("Success url must be a valid url") })
|
||||
.openapi({
|
||||
description: "Details of the team to create",
|
||||
example: {
|
||||
slug: Examples.Team.slug,
|
||||
name: Examples.Team.name,
|
||||
planType: Examples.Subscription.planType,
|
||||
successUrl: "https://your-url.io/thanks"
|
||||
},
|
||||
})
|
||||
),
|
||||
async (c) => {
|
||||
const body = c.req.valid("json")
|
||||
const actor = assertActor("user");
|
||||
|
||||
const teamID = await Team.create(body);
|
||||
const teamID = await Team.create({ name: body.name, slug: body.slug });
|
||||
|
||||
await withActor(
|
||||
{
|
||||
@@ -82,14 +97,28 @@ export namespace TeamApi {
|
||||
teamID,
|
||||
},
|
||||
},
|
||||
() =>
|
||||
Member.create({
|
||||
async () => {
|
||||
await Member.create({
|
||||
first: true,
|
||||
email: actor.properties.email,
|
||||
}),
|
||||
});
|
||||
|
||||
await Subscription.create({
|
||||
planType: body.planType,
|
||||
userID: actor.properties.userID,
|
||||
// FIXME: Make this make sense
|
||||
tokens: body.planType === "free" ? 100 : body.planType === "pro" ? 1000 : body.planType === "family" ? 10000 : 0,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return c.json({ data: "ok" })
|
||||
const checkoutUrl = await Polar.createCheckout({ planType: body.planType, successUrl: body.successUrl, teamID })
|
||||
|
||||
return c.json({
|
||||
data: {
|
||||
checkoutUrl,
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user