71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
import {
|
|
Controller, Get, Post, Body, Headers, HttpCode, HttpStatus, NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { SubscriptionRepository } from '../../../infrastructure/repositories/subscription.repository';
|
|
import { PlanRepository } from '../../../infrastructure/repositories/plan.repository';
|
|
import { ChangePlanUseCase } from '../../../application/use-cases/change-plan.use-case';
|
|
import { CancelSubscriptionUseCase } from '../../../application/use-cases/cancel-subscription.use-case';
|
|
import { CheckTokenQuotaUseCase } from '../../../application/use-cases/check-token-quota.use-case';
|
|
import { InvoiceCurrency } from '../../../domain/entities/invoice.entity';
|
|
|
|
@Controller('api/v1/billing')
|
|
export class SubscriptionController {
|
|
constructor(
|
|
private readonly subscriptionRepo: SubscriptionRepository,
|
|
private readonly planRepo: PlanRepository,
|
|
private readonly changePlan: ChangePlanUseCase,
|
|
private readonly cancelSubscription: CancelSubscriptionUseCase,
|
|
private readonly checkQuota: CheckTokenQuotaUseCase,
|
|
) {}
|
|
|
|
@Get('subscription')
|
|
async getSubscription(@Headers('x-tenant-id') tenantId: string) {
|
|
const sub = await this.subscriptionRepo.findByTenantId(tenantId);
|
|
if (!sub) throw new NotFoundException('No active subscription');
|
|
|
|
const plan = await this.planRepo.findById(sub.planId);
|
|
const nextPlan = sub.nextPlanId ? await this.planRepo.findById(sub.nextPlanId) : null;
|
|
|
|
return {
|
|
id: sub.id,
|
|
status: sub.status,
|
|
plan: plan ? { name: plan.name, displayName: plan.displayName } : null,
|
|
nextPlan: nextPlan ? { name: nextPlan.name, displayName: nextPlan.displayName } : null,
|
|
currentPeriodStart: sub.currentPeriodStart,
|
|
currentPeriodEnd: sub.currentPeriodEnd,
|
|
trialEndsAt: sub.trialEndsAt,
|
|
cancelAtPeriodEnd: sub.cancelAtPeriodEnd,
|
|
cancelledAt: sub.cancelledAt,
|
|
};
|
|
}
|
|
|
|
@Post('subscription/upgrade')
|
|
@HttpCode(HttpStatus.OK)
|
|
async upgradePlan(
|
|
@Headers('x-tenant-id') tenantId: string,
|
|
@Body() body: { planName: string; currency?: 'USD' | 'CNY' },
|
|
) {
|
|
await this.changePlan.execute({
|
|
tenantId,
|
|
newPlanName: body.planName,
|
|
currency: body.currency === 'CNY' ? InvoiceCurrency.CNY : InvoiceCurrency.USD,
|
|
});
|
|
return { success: true };
|
|
}
|
|
|
|
@Post('subscription/cancel')
|
|
@HttpCode(HttpStatus.OK)
|
|
async cancelPlan(
|
|
@Headers('x-tenant-id') tenantId: string,
|
|
@Body() body: { immediate?: boolean },
|
|
) {
|
|
await this.cancelSubscription.execute(tenantId, body.immediate ?? false);
|
|
return { success: true };
|
|
}
|
|
|
|
@Get('usage/current')
|
|
async getCurrentUsage(@Headers('x-tenant-id') tenantId: string) {
|
|
return this.checkQuota.execute(tenantId);
|
|
}
|
|
}
|