// SIM Management Controller Endpoints // These endpoints should be added to your subscriptions controller // Location: apps/bff/src/modules/subscriptions/subscriptions.controller.ts import { Controller, Get, Post, Body, Param, Query, Request, ParseIntPipe, BadRequestException, } from "@nestjs/common"; import { ApiTags, ApiOperation, ApiParam, ApiQuery, ApiBody, ApiResponse, ApiBearerAuth } from "@nestjs/swagger"; import { ZodValidationPipe } from "@bff/core/validation"; import type { RequestWithUser } from "@bff/modules/auth/auth.types"; import { SimManagementService } from "../sim-management.service"; import { simTopupRequestSchema, simChangePlanRequestSchema, simCancelRequestSchema, simFeaturesRequestSchema, type SimTopupRequest, type SimChangePlanRequest, type SimCancelRequest, type SimFeaturesRequest, } from "../sim-management/types/sim-requests.types"; // ==================== SIM Management Endpoints ==================== // Add these methods to your SubscriptionsController class @ApiTags("subscriptions") @Controller("subscriptions") @ApiBearerAuth() export class SimEndpointsController { constructor(private readonly simManagementService: SimManagementService) {} @Get(":id/sim/debug") @ApiOperation({ summary: "Debug SIM subscription data", description: "Retrieves subscription data to help debug SIM management issues", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiResponse({ status: 200, description: "Subscription debug data" }) async debugSimSubscription( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ): Promise> { return this.simManagementService.debugSimSubscription(req.user.id, subscriptionId); } @Get(":id/sim") @ApiOperation({ summary: "Get SIM details and usage", description: "Retrieves comprehensive SIM information including details and current usage", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiResponse({ status: 200, description: "SIM information" }) @ApiResponse({ status: 400, description: "Not a SIM subscription" }) @ApiResponse({ status: 404, description: "Subscription not found" }) async getSimInfo( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimInfo(req.user.id, subscriptionId); } @Get(":id/sim/info") @ApiOperation({ summary: "Get SIM information (alias for /sim)", description: "Retrieves comprehensive SIM information including details and current usage", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiResponse({ status: 200, description: "SIM information" }) @ApiResponse({ status: 400, description: "Not a SIM subscription" }) @ApiResponse({ status: 404, description: "Subscription not found" }) async getSimInfoAlias( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimInfo(req.user.id, subscriptionId); } @Get(":id/sim/details") @ApiOperation({ summary: "Get SIM details", description: "Retrieves detailed SIM information including ICCID, plan, status, etc.", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiResponse({ status: 200, description: "SIM details" }) async getSimDetails( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimDetails(req.user.id, subscriptionId); } @Get(":id/sim/usage") @ApiOperation({ summary: "Get SIM data usage", description: "Retrieves current data usage and recent usage history", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiResponse({ status: 200, description: "SIM usage data" }) async getSimUsage( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number ) { return this.simManagementService.getSimUsage(req.user.id, subscriptionId); } @Get(":id/sim/top-up-history") @ApiOperation({ summary: "Get SIM top-up history", description: "Retrieves data top-up history for the specified date range", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiQuery({ name: "fromDate", description: "Start date (YYYYMMDD)", example: "20240101" }) @ApiQuery({ name: "toDate", description: "End date (YYYYMMDD)", example: "20241231" }) @ApiResponse({ status: 200, description: "Top-up history" }) async getSimTopUpHistory( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Query("fromDate") fromDate: string, @Query("toDate") toDate: string ) { if (!fromDate || !toDate) { throw new BadRequestException("fromDate and toDate are required"); } return this.simManagementService.getSimTopUpHistory(req.user.id, subscriptionId, { fromDate, toDate, }); } @Post(":id/sim/top-up") @ApiOperation({ summary: "Top up SIM data quota", description: "Add data quota to the SIM service", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiBody({ description: "Top-up request", schema: { type: "object", properties: { quotaMb: { type: "number", description: "Quota in MB", example: 1000 }, amount: { type: "number", description: "Amount to charge in JPY (optional, defaults to calculated amount)", example: 500, }, currency: { type: "string", description: "ISO currency code (optional, defaults to JPY)", example: "JPY", }, }, required: ["quotaMb"], }, }) @ApiResponse({ status: 200, description: "Top-up successful" }) async topUpSim( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body(new ZodValidationPipe(simTopupRequestSchema)) body: SimTopupRequest ) { await this.simManagementService.topUpSim(req.user.id, subscriptionId, body); return { success: true, message: "SIM top-up completed successfully" }; } @Post(":id/sim/change-plan") @ApiOperation({ summary: "Change SIM plan", description: "Change the SIM service plan. The change will be automatically scheduled for the 1st of the next month. Available plans: 5GB, 10GB, 25GB, 50GB.", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiBody({ description: "Plan change request", schema: { type: "object", properties: { newPlanCode: { type: "string", description: "New plan code", enum: ["5GB", "10GB", "25GB", "50GB"], example: "25GB" }, }, required: ["newPlanCode"], }, }) @ApiResponse({ status: 200, description: "Plan change successful" }) async changeSimPlan( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body(new ZodValidationPipe(simChangePlanRequestSchema)) body: SimChangePlanRequest ) { const result = await this.simManagementService.changeSimPlan(req.user.id, subscriptionId, body); return { success: true, message: "SIM plan change completed successfully", ...result, }; } @Post(":id/sim/cancel") @ApiOperation({ summary: "Cancel SIM service", description: "Cancel the SIM service (immediate or scheduled)", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiBody({ description: "Cancellation request", schema: { type: "object", properties: { scheduledAt: { type: "string", description: "Schedule cancellation (YYYYMMDD)", example: "20241231", }, }, }, required: false, }) @ApiResponse({ status: 200, description: "Cancellation successful" }) async cancelSim( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body(new ZodValidationPipe(simCancelRequestSchema)) body: SimCancelRequest ) { await this.simManagementService.cancelSim(req.user.id, subscriptionId, body); return { success: true, message: "SIM cancellation completed successfully" }; } @Post(":id/sim/reissue-esim") @ApiOperation({ summary: "Reissue eSIM profile", description: "Reissue a downloadable eSIM profile (eSIM only). Optionally provide a new EID to transfer to.", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiBody({ description: "Optional new EID to transfer the eSIM to", schema: { type: "object", properties: { newEid: { type: "string", description: "32-digit EID", example: "89049032000001000000043598005455", }, }, required: [], }, }) @ApiResponse({ status: 200, description: "eSIM reissue successful" }) @ApiResponse({ status: 400, description: "Not an eSIM subscription" }) async reissueEsimProfile( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body() body: { newEid?: string } = {} ) { await this.simManagementService.reissueEsimProfile(req.user.id, subscriptionId, body.newEid); return { success: true, message: "eSIM profile reissue completed successfully" }; } @Post(":id/sim/features") @ApiOperation({ summary: "Update SIM features", description: "Enable/disable voicemail, call waiting, international roaming, and switch network type (4G/5G)", }) @ApiParam({ name: "id", type: Number, description: "Subscription ID" }) @ApiBody({ description: "Features update request", schema: { type: "object", properties: { voiceMailEnabled: { type: "boolean" }, callWaitingEnabled: { type: "boolean" }, internationalRoamingEnabled: { type: "boolean" }, networkType: { type: "string", enum: ["4G", "5G"] }, }, }, }) @ApiResponse({ status: 200, description: "Features update successful" }) async updateSimFeatures( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number, @Body(new ZodValidationPipe(simFeaturesRequestSchema)) body: SimFeaturesRequest ) { await this.simManagementService.updateSimFeatures(req.user.id, subscriptionId, body); return { success: true, message: "SIM features updated successfully" }; } @Get("debug/sim-details/:account") // @Public() // Uncomment if you have a Public decorator for debug endpoints @ApiOperation({ summary: "[DEBUG] Get SIM details from Freebit", description: "Query Freebit API directly to see plan code and details for any account", }) @ApiParam({ name: "account", description: "SIM account number (e.g., 02000215161147)" }) async debugSimDetails(@Param("account") account: string) { return await this.simManagementService.getSimDetailsDebug(account); } }