diff --git a/apps/rowboat/app/actions/billing.actions.ts b/apps/rowboat/app/actions/billing.actions.ts index db2445b3..636287eb 100644 --- a/apps/rowboat/app/actions/billing.actions.ts +++ b/apps/rowboat/app/actions/billing.actions.ts @@ -21,9 +21,8 @@ import { ModelsResponse } from "../lib/types/billing_types"; import { z } from "zod"; -import { WithStringId } from "../lib/types/types"; -export async function getCustomer(): Promise>> { +export async function getCustomer(): Promise> { const user = await authCheck(); if (!user.billingCustomerId) { throw new Error("Customer not found"); @@ -41,7 +40,7 @@ export async function authorizeUserAction(request: z.infer) { } const customer = await getCustomer(); - await libLogUsage(customer._id, request); + await libLogUsage(customer.id, request); return; } @@ -61,7 +60,7 @@ export async function getCustomerPortalUrl(returnUrl: string): Promise { } const customer = await getCustomer(); - return await createCustomerPortalSession(customer._id, returnUrl); + return await createCustomerPortalSession(customer.id, returnUrl); } export async function getPrices(): Promise> { @@ -80,7 +79,7 @@ export async function updateSubscriptionPlan(plan: z.infer = { plan, returnUrl }; - const url = await libUpdateSubscriptionPlan(customer._id, request); + const url = await libUpdateSubscriptionPlan(customer.id, request); return url; } @@ -90,6 +89,6 @@ export async function getEligibleModels(): Promise>; + customer: z.infer; usage: z.infer; } diff --git a/apps/rowboat/app/billing/callback/page.tsx b/apps/rowboat/app/billing/callback/page.tsx index eae1fa02..c74bd14f 100644 --- a/apps/rowboat/app/billing/callback/page.tsx +++ b/apps/rowboat/app/billing/callback/page.tsx @@ -13,7 +13,7 @@ export default async function Page( ) { const searchParams = await props.searchParams; const customer = await requireBillingCustomer(); - await syncWithStripe(customer._id); + await syncWithStripe(customer.id); const redirectUrl = searchParams.redirect as string; redirect(redirectUrl || '/projects'); } \ No newline at end of file diff --git a/apps/rowboat/app/billing/page.tsx b/apps/rowboat/app/billing/page.tsx index 974c73f9..2ff9141c 100644 --- a/apps/rowboat/app/billing/page.tsx +++ b/apps/rowboat/app/billing/page.tsx @@ -12,6 +12,6 @@ export default async function Page() { } const customer = await requireBillingCustomer(); - const usage = await getUsage(customer._id); + const usage = await getUsage(customer.id); return ; } \ No newline at end of file diff --git a/apps/rowboat/app/lib/billing.ts b/apps/rowboat/app/lib/billing.ts index 7a0c00ee..ff586a47 100644 --- a/apps/rowboat/app/lib/billing.ts +++ b/apps/rowboat/app/lib/billing.ts @@ -1,4 +1,3 @@ -import { WithStringId } from './types/types'; import { z } from 'zod'; import { Customer, AuthorizeRequest, AuthorizeResponse, LogUsageRequest, UsageResponse, CustomerPortalSessionResponse, PricesResponse, UpdateSubscriptionPlanRequest, UpdateSubscriptionPlanResponse, ModelsResponse, UsageItem } from './types/billing_types'; import { redirect } from 'next/navigation'; @@ -14,7 +13,7 @@ const BILLING_API_KEY = process.env.BILLING_API_KEY || 'test'; let logCounter = 1; const GUEST_BILLING_CUSTOMER = { - _id: "guest-user", + id: "guest-user", userId: "guest-user", name: "Guest", email: "guest@rowboatlabs.com", @@ -23,7 +22,6 @@ const GUEST_BILLING_CUSTOMER = { subscriptionPlan: "free" as const, subscriptionStatus: "active" as const, createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), }; @@ -41,7 +39,7 @@ export class UsageTracker{ } } -export async function getCustomerForUserId(userId: string): Promise> | null> { +export async function getCustomerForUserId(userId: string): Promise | null> { const usersRepository = container.resolve("usersRepository"); const user = await usersRepository.fetch(userId); @@ -64,10 +62,10 @@ export async function getCustomerIdForProject(projectId: string): Promise> | null> { +export async function getBillingCustomer(id: string): Promise | null> { const response = await fetch(`${BILLING_API_URL}/api/customers/${id}`, { method: 'GET', headers: { @@ -86,7 +84,7 @@ export async function getBillingCustomer(id: string): Promise>> { +async function createBillingCustomer(userId: string, email: string): Promise> { const response = await fetch(`${BILLING_API_URL}/api/customers`, { method: 'POST', headers: { @@ -266,7 +264,7 @@ export async function getEligibleModels(customerId: string): Promise>> { +export async function requireBillingCustomer(): Promise> { const user = await requireAuth(); const usersRepository = container.resolve("usersRepository"); @@ -283,7 +281,7 @@ export async function requireBillingCustomer(): Promise> | null; + let customer: z.infer | null; if (user.billingCustomerId) { customer = await getBillingCustomer(user.billingCustomerId); } else { @@ -291,7 +289,7 @@ export async function requireBillingCustomer(): Promise>> { +export async function requireActiveBillingSubscription(): Promise> { const billingCustomer = await requireBillingCustomer(); if (USE_BILLING && billingCustomer.subscriptionStatus !== "active" && billingCustomer.subscriptionStatus !== "past_due") { diff --git a/apps/rowboat/app/lib/types/billing_types.ts b/apps/rowboat/app/lib/types/billing_types.ts index adb13a8b..0ac385d6 100644 --- a/apps/rowboat/app/lib/types/billing_types.ts +++ b/apps/rowboat/app/lib/types/billing_types.ts @@ -1,3 +1,17 @@ +/** + * 🚨 ATTENTION: DO NOT MODIFY THIS FILE! 🚨 + * + * This file contains billing types that are manually copied + * from the billing service repository. Any manual changes will be + * overwritten during the next sync. + * + * If you need to modify billing types: + * 1. Make changes in the billing service repo + * 2. Copy the updated file from there + * 3. Never edit this file directly + * + * This file is a manual copy - keep it in sync with the source! + */ import { z } from "zod"; export const SubscriptionPlan = z.enum(["free", "starter", "pro"]); @@ -57,7 +71,7 @@ export const LogUsageRequest = z.object({ export const CustomerUsageData = z.record(z.string(), z.number()); export const Customer = z.object({ - _id: z.string(), + id: z.string(), userId: z.string(), email: z.string(), stripeCustomerId: z.string(), @@ -65,7 +79,7 @@ export const Customer = z.object({ subscriptionPlan: SubscriptionPlan.optional(), subscriptionStatus: z.enum([ 'active', 'past_due' ]).optional(), createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), + updatedAt: z.string().datetime().optional(), subscriptionPlanUpdatedAt: z.string().datetime().optional(), usage: CustomerUsageData.optional(), usageUpdatedAt: z.string().datetime().optional(), diff --git a/apps/rowboat/app/projects/[projectId]/workflow/page.tsx b/apps/rowboat/app/projects/[projectId]/workflow/page.tsx index 008f4317..864d31c4 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/page.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/page.tsx @@ -47,7 +47,7 @@ export default async function Page( let eligibleModels: z.infer | "*" = '*'; if (USE_BILLING) { - eligibleModels = await getEligibleModels(customer._id); + eligibleModels = await getEligibleModels(customer.id); } console.log('/workflow page.tsx serve'); diff --git a/apps/rowboat/src/application/use-cases/conversations/run-conversation-turn.use-case.ts b/apps/rowboat/src/application/use-cases/conversations/run-conversation-turn.use-case.ts index 635a960d..eb3c70e8 100644 --- a/apps/rowboat/src/application/use-cases/conversations/run-conversation-turn.use-case.ts +++ b/apps/rowboat/src/application/use-cases/conversations/run-conversation-turn.use-case.ts @@ -165,6 +165,7 @@ export class RunConversationTurnUseCase implements IRunConversationTurnUseCase { } } finally { // Log billing usage + console.log('finally logging billing usage'); if (USE_BILLING && billingCustomerId) { await logUsage(billingCustomerId, { items: usageTracker.flush(), diff --git a/apps/rowboat/src/application/use-cases/projects/create-project.use-case.ts b/apps/rowboat/src/application/use-cases/projects/create-project.use-case.ts index c439c788..8a5d4e10 100644 --- a/apps/rowboat/src/application/use-cases/projects/create-project.use-case.ts +++ b/apps/rowboat/src/application/use-cases/projects/create-project.use-case.ts @@ -65,7 +65,7 @@ export class CreateProjectUseCase implements ICreateProjectUseCase { } // validate enough credits - const result = await authorize(customer._id, { + const result = await authorize(customer.id, { type: "create_project", data: { existingProjectCount: count,