This commit is contained in:
2026-04-30 11:07:38 -04:00
parent 9fb5379b7a
commit 19c5a951fe
4 changed files with 47 additions and 10 deletions

View File

@@ -74,7 +74,7 @@ export const loadBillingConfig = (): BillingConfig => ({
darkWebScans: 12, darkWebScans: 12,
}, },
plus: { plus: {
priceId: process.env.STRIPE_PLUS_TIER_PRICE_ID!, priceId: process.env.STRIPE_PLUS_TIER_PRICE_ID || 'price_plus',
monthlyPriceCents: 1999, monthlyPriceCents: 1999,
callMinutesLimit: 2000, callMinutesLimit: 2000,
smsCountLimit: 10000, smsCountLimit: 10000,
@@ -82,7 +82,7 @@ export const loadBillingConfig = (): BillingConfig => ({
voiceCloning: true, voiceCloning: true,
}, },
premium: { premium: {
priceId: process.env.STRIPE_PREMIUM_TIER_PRICE_ID!, priceId: process.env.STRIPE_PREMIUM_TIER_PRICE_ID || 'price_premium',
monthlyPriceCents: 4999, monthlyPriceCents: 4999,
callMinutesLimit: 10000, callMinutesLimit: 10000,
smsCountLimit: 50000, smsCountLimit: 50000,

View File

@@ -4,6 +4,7 @@ export {
requireTier, requireTier,
checkUsageLimit, checkUsageLimit,
withUsageTracking, withUsageTracking,
withSubscription,
requireSubscription, requireSubscription,
} from './middleware/billing.middleware'; } from './middleware/billing.middleware';

View File

@@ -7,7 +7,8 @@ const billingService = BillingService.getInstance();
export interface AuthenticatedRequest extends Request { export interface AuthenticatedRequest extends Request {
userId?: string; userId?: string;
tier?: SubscriptionTier; tier?: SubscriptionTier;
usage?: { current: number; limit: number; remaining: number }; usage?: { current: number; limit: number; remaining: number; withinLimit: boolean };
subscriptionId?: string;
} }
export function requireTier( export function requireTier(
@@ -108,24 +109,52 @@ export function withUsageTracking(
}; };
} }
export function withSubscription() {
return async (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): Promise<void> => {
const { userId, tier } = req;
if (!userId || !tier) {
next();
return;
}
try {
// Fetch subscription from database
// TODO: Replace with actual database query
const subscriptionId = (req as any).subscriptionId;
if (subscriptionId) {
req.subscriptionId = subscriptionId;
}
next();
} catch (error) {
res.status(500).json({
error: 'Failed to fetch subscription',
message: error instanceof Error ? error.message : 'Unknown error',
});
}
};
}
export function requireSubscription() { export function requireSubscription() {
return async ( return async (
req: AuthenticatedRequest, req: AuthenticatedRequest,
res: Response, res: Response,
next: NextFunction next: NextFunction
): Promise<void> => { ): Promise<void> => {
const { userId } = req; const { userId, subscriptionId } = req;
if (!userId) { if (!userId) {
res.status(401).json({ error: 'Authentication required' }); res.status(401).json({ error: 'Authentication required' });
return; return;
} }
// Check if user has active subscription if (!subscriptionId) {
// This would typically query the database
const hasSubscription = (req as any).subscriptionId != null;
if (!hasSubscription) {
res.status(402).json({ res.status(402).json({
error: 'Active subscription required', error: 'Active subscription required',
}); });

View File

@@ -132,7 +132,14 @@ export class BillingService {
): Promise<Stripe.Invoice> { ): Promise<Stripe.Invoice> {
return await stripe.invoices.create({ return await stripe.invoices.create({
customer: customerId, customer: customerId,
metadata: { ...metadata, description }, line_items: [
{
amount_data: { currency: 'usd', unit_amount: amount },
description: description,
quantity: 1,
},
],
metadata: metadata,
}); });
} }