Add stripe integration

This commit is contained in:
Corey Blais
2026-04-16 20:25:21 -04:00
parent 3a0e30085c
commit c757132cbd
13 changed files with 462 additions and 12 deletions
@@ -1,5 +1,5 @@
import { db } from '../db/client.js';
import type { BillingPlan, RescueVerificationStatus, UserRow, WorkspaceMemberRow, WorkspaceRow, WorkspaceType } from '../types.js';
import type { BillingPlan, RescueVerificationStatus, SubscriptionStatus, UserRow, WorkspaceMemberRow, WorkspaceRow, WorkspaceType } from '../types.js';
export const getNextWorkspaceId = async () => {
const result = await db.query<{ next_id: number }>('SELECT COALESCE(MAX(id), 0) + 1 AS next_id FROM workspaces');
@@ -8,7 +8,7 @@ export const getNextWorkspaceId = async () => {
export const getWorkspaceById = async (workspaceId: number) => {
const result = await db.query<WorkspaceRow>(
`SELECT id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at
`SELECT id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at
FROM workspaces
WHERE id = $1`,
[workspaceId],
@@ -37,6 +37,8 @@ export const listMembershipsForUser = async (userId: string) => {
billing_email: string | null;
billing_plan: BillingPlan;
subscription_status: WorkspaceRow['subscription_status'];
stripe_customer_id: string | null;
stripe_subscription_id: string | null;
rescue_verification_status: RescueVerificationStatus;
workspace_created_at: string;
workspace_updated_at: string;
@@ -56,6 +58,8 @@ export const listMembershipsForUser = async (userId: string) => {
workspaces.billing_email,
workspaces.billing_plan,
workspaces.subscription_status,
workspaces.stripe_customer_id,
workspaces.stripe_subscription_id,
workspaces.rescue_verification_status,
workspaces.created_at AS workspace_created_at,
workspaces.updated_at AS workspace_updated_at
@@ -217,7 +221,7 @@ export const updateWorkspace = async ({
updated_at = CURRENT_TIMESTAMP
FROM input
WHERE workspaces.id = input.workspace_id
RETURNING workspaces.id, workspaces.name, workspaces.workspace_type, workspaces.billing_email, workspaces.billing_plan, workspaces.subscription_status, workspaces.rescue_verification_status, workspaces.created_at, workspaces.updated_at`,
RETURNING workspaces.id, workspaces.name, workspaces.workspace_type, workspaces.billing_email, workspaces.billing_plan, workspaces.subscription_status, workspaces.stripe_customer_id, workspaces.stripe_subscription_id, workspaces.rescue_verification_status, workspaces.created_at, workspaces.updated_at`,
[workspaceId, name, workspaceType, billingEmail, billingPlan],
);
@@ -252,7 +256,7 @@ export const findAlternateWorkspaceForUser = async (userId: string, excludeWorks
export const listOwnedWorkspacesByOwnerEmail = async (ownerEmail: string, excludeWorkspaceId: number) => {
const result = await db.query<WorkspaceRow>(
`SELECT workspaces.id, workspaces.name, workspaces.workspace_type, workspaces.billing_email, workspaces.billing_plan, workspaces.subscription_status, workspaces.rescue_verification_status, workspaces.created_at, workspaces.updated_at
`SELECT workspaces.id, workspaces.name, workspaces.workspace_type, workspaces.billing_email, workspaces.billing_plan, workspaces.subscription_status, workspaces.stripe_customer_id, workspaces.stripe_subscription_id, workspaces.rescue_verification_status, workspaces.created_at, workspaces.updated_at
FROM workspace_members
INNER JOIN workspaces ON workspaces.id = workspace_members.workspace_id
WHERE LOWER(COALESCE(workspace_members.invite_email, workspace_members.email)) = LOWER($1)
@@ -353,6 +357,8 @@ export const listRescueWorkspacesForAdmin = async () => {
workspaces.billing_email,
workspaces.billing_plan,
workspaces.subscription_status,
workspaces.stripe_customer_id,
workspaces.stripe_subscription_id,
workspaces.rescue_verification_status,
workspaces.created_at,
workspaces.updated_at,
@@ -398,7 +404,7 @@ export const updateRescueVerificationStatus = async (workspaceId: number, status
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
AND workspace_type = 'rescue'
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at`,
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at`,
[workspaceId, status],
);
@@ -415,13 +421,67 @@ export const cancelRescueVerificationRequest = async (workspaceId: number) => {
WHERE id = $1
AND workspace_type = 'rescue'
AND rescue_verification_status = 'pending'
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, rescue_verification_status, created_at, updated_at`,
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at`,
[workspaceId],
);
return result.rows[0] ?? null;
};
export const setWorkspaceStripeCustomerId = async (workspaceId: number, stripeCustomerId: string) => {
const result = await db.query<WorkspaceRow>(
`UPDATE workspaces
SET stripe_customer_id = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at`,
[workspaceId, stripeCustomerId],
);
return result.rows[0] ?? null;
};
export const setWorkspaceStripeSubscription = async ({
workspaceId,
stripeCustomerId,
stripeSubscriptionId,
subscriptionStatus,
}: {
workspaceId: number;
stripeCustomerId: string | null;
stripeSubscriptionId: string;
subscriptionStatus: SubscriptionStatus;
}) => {
const result = await db.query<WorkspaceRow>(
`UPDATE workspaces
SET stripe_customer_id = COALESCE($2, stripe_customer_id),
stripe_subscription_id = $3,
subscription_status = $4,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at`,
[workspaceId, stripeCustomerId, stripeSubscriptionId, subscriptionStatus],
);
return result.rows[0] ?? null;
};
export const setWorkspaceSubscriptionStatusByStripeSubscriptionId = async (
stripeSubscriptionId: string,
subscriptionStatus: SubscriptionStatus,
) => {
const result = await db.query<WorkspaceRow>(
`UPDATE workspaces
SET subscription_status = $2,
updated_at = CURRENT_TIMESTAMP
WHERE stripe_subscription_id = $1
RETURNING id, name, workspace_type, billing_email, billing_plan, subscription_status, stripe_customer_id, stripe_subscription_id, rescue_verification_status, created_at, updated_at`,
[stripeSubscriptionId, subscriptionStatus],
);
return result.rows[0] ?? null;
};
export const getPlatformAdminSummary = async () => {
const result = await db.query<{
total_birds: number;