it0/packages/services/billing-service/src/interfaces/rest/controllers/subscription.controller.ts

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);
}
}