diff --git a/apps/bff/src/integrations/freebit/services/freebit-auth.service.ts b/apps/bff/src/integrations/freebit/services/freebit-auth.service.ts index 90a0a97a..6950ae1a 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-auth.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-auth.service.ts @@ -3,12 +3,21 @@ import { ConfigService } from "@nestjs/config"; import { Logger } from "nestjs-pino"; import { getErrorMessage } from "@bff/core/utils/error.util"; import type { - FreebitConfig, - FreebitAuthRequest, - FreebitAuthResponse, + AuthRequest as FreebitAuthRequest, + AuthResponse as FreebitAuthResponse, } from "@customer-portal/domain/sim/providers/freebit"; +import { Providers } from "@customer-portal/domain/sim"; import { FreebitError } from "./freebit-error.service"; +interface FreebitConfig { + baseUrl: string; + oemId: string; + oemKey: string; + timeout: number; + retryAttempts: number; + detailsEndpoint?: string; +} + @Injectable() export class FreebitAuthService { private readonly config: FreebitConfig; @@ -60,10 +69,10 @@ export class FreebitAuthService { throw new Error("Freebit API not configured: FREEBIT_OEM_KEY is missing"); } - const request: FreebitAuthRequest = { + const request = Providers.Freebit.schemas.auth.parse({ oemId: this.config.oemId, oemKey: this.config.oemKey, - }; + }); const response = await fetch(`${this.config.baseUrl}/authOem/`, { method: "POST", @@ -75,13 +84,15 @@ export class FreebitAuthService { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } - const data = (await response.json()) as FreebitAuthResponse; - if (data.resultCode !== "100") { + const json = (await response.json()) as unknown; + const data = Providers.Freebit.mapper.transformFreebitAuthResponse(json); + + if (data.resultCode !== "100" || !data.authKey) { throw new FreebitError( - `Authentication failed: ${data.status.message}`, + `Authentication failed: ${data.status?.message ?? "Unknown error"}`, data.resultCode, - data.status.statusCode, - data.status.message + data.status?.statusCode, + data.status?.message ); } diff --git a/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts b/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts index 3d3b118e..47c8fd88 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-mapper.service.ts @@ -1,9 +1,4 @@ import { Injectable } from "@nestjs/common"; -import type { - FreebitAccountDetailsResponse, - FreebitTrafficInfoResponse, - FreebitQuotaHistoryResponse, -} from "@customer-portal/domain/sim/providers/freebit"; import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; import { Providers } from "@customer-portal/domain/sim"; @@ -12,21 +7,21 @@ export class FreebitMapperService { /** * Map Freebit account details response to SimDetails */ - mapToSimDetails(response: FreebitAccountDetailsResponse): SimDetails { + mapToSimDetails(response: unknown): SimDetails { return Providers.Freebit.transformFreebitAccountDetails(response); } /** * Map Freebit traffic info response to SimUsage */ - mapToSimUsage(response: FreebitTrafficInfoResponse): SimUsage { + mapToSimUsage(response: unknown): SimUsage { return Providers.Freebit.transformFreebitTrafficInfo(response); } /** * Map Freebit quota history response to SimTopUpHistory */ - mapToSimTopUpHistory(response: FreebitQuotaHistoryResponse, account: string): SimTopUpHistory { + mapToSimTopUpHistory(response: unknown, account: string): SimTopUpHistory { return Providers.Freebit.transformFreebitQuotaHistory(response, account); } diff --git a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts index f3a4760d..c873b177 100644 --- a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts +++ b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts @@ -5,16 +5,15 @@ import { FreebitClientService } from "./freebit-client.service"; import { FreebitMapperService } from "./freebit-mapper.service"; import { FreebitAuthService } from "./freebit-auth.service"; import type { - FreebitAccountDetailsResponse, - FreebitTrafficInfoResponse, - FreebitTopUpResponse, - FreebitQuotaHistoryResponse, - FreebitPlanChangeResponse, - FreebitAddSpecResponse, - FreebitCancelPlanResponse, - FreebitEsimReissueResponse, - FreebitEsimAddAccountResponse, - FreebitEsimAccountActivationResponse, + TopUpResponse, + PlanChangeResponse, + AddSpecResponse, + CancelPlanResponse, + CancelAccountResponse, + EsimReissueResponse, + EsimAddAccountResponse, + EsimActivationResponse, + QuotaHistoryRequest, } from "@customer-portal/domain/sim/providers/freebit"; import type { FreebitTopUpRequest, @@ -73,7 +72,7 @@ export class FreebitOperationsService { ]) ); - let response: FreebitAccountDetailsResponse | undefined; + let response: Providers.Freebit.mapper.FreebitAccountDetailsResponse | undefined; let lastError: unknown; for (const ep of candidates) { @@ -82,7 +81,7 @@ export class FreebitOperationsService { this.logger.warn(`Retrying Freebit account details with alternative endpoint: ${ep}`); } response = await this.client.makeAuthenticatedRequest< - FreebitAccountDetailsResponse, + Providers.Freebit.mapper.FreebitAccountDetailsResponse, typeof request >(ep, request); break; @@ -120,7 +119,7 @@ export class FreebitOperationsService { const request: FreebitTrafficInfoRequest = Providers.Freebit.schemas.trafficInfo.parse({ account }); const response = await this.client.makeAuthenticatedRequest< - FreebitTrafficInfoResponse, + Providers.Freebit.mapper.FreebitTrafficInfoResponse, typeof request >("/mvno/getTrafficInfo/", request); @@ -159,7 +158,7 @@ export class FreebitOperationsService { ? { ...baseRequest, runTime: payload.options?.scheduledAt } : baseRequest; - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( endpoint, request ); @@ -198,8 +197,8 @@ export class FreebitOperationsService { }); const response = await this.client.makeAuthenticatedRequest< - FreebitQuotaHistoryResponse, - FreebitQuotaHistoryRequest + Providers.Freebit.mapper.FreebitQuotaHistoryResponse, + QuotaHistoryRequest >("/mvno/getQuotaHistory/", request); return this.mapper.mapToSimTopUpHistory(response, account); @@ -239,10 +238,10 @@ export class FreebitOperationsService { runTime: parsed.scheduledAt, }; - const response = await this.client.makeAuthenticatedRequest< - FreebitPlanChangeResponse, - typeof request - >("/mvno/changePlan/", request); + const response = await this.client.makeAuthenticatedRequest( + "/mvno/changePlan/", + request + ); this.logger.log(`Successfully changed plan for account ${parsed.account} to ${parsed.newPlanCode}`, { account: parsed.account, @@ -314,7 +313,7 @@ export class FreebitOperationsService { payload.contractLine = features.networkType; } - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( "/master/addSpec/", payload ); @@ -352,7 +351,7 @@ export class FreebitOperationsService { runTime: parsed.runDate, }; - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( "/mvno/releasePlan/", request ); @@ -381,7 +380,7 @@ export class FreebitOperationsService { requestDatas: [{ kind: "MVNO", account }], }; - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( "/mvno/reissueEsim/", request ); @@ -422,14 +421,14 @@ export class FreebitOperationsService { planCode: parsed.planCode, }); - const request: FreebitEsimAddAccountRequest = { + const payload: FreebitEsimAddAccountRequest = { ...requestPayload, authKey: await this.auth.getAuthKey(), }; - await this.client.makeAuthenticatedRequest( + await this.client.makeAuthenticatedRequest( "/mvno/esim/addAcnt/", - request + payload ); this.logger.log(`Successfully reissued eSIM profile via addAcnt for account ${parsed.account}`, { @@ -515,10 +514,10 @@ export class FreebitOperationsService { Providers.Freebit.schemas.esimActivationRequest.parse(payload); // Use JSON request for PA05-41 - await this.client.makeAuthenticatedJsonRequest< - FreebitEsimAccountActivationResponse, - typeof payload - >("/mvno/esim/addAcct/", payload); + await this.client.makeAuthenticatedJsonRequest( + "/mvno/esim/addAcct/", + payload + ); this.logger.log("Successfully activated new eSIM account via PA05-41", { account, diff --git a/packages/domain/sim/providers/freebit/index.ts b/packages/domain/sim/providers/freebit/index.ts index 0c17e26b..0d622d65 100644 --- a/packages/domain/sim/providers/freebit/index.ts +++ b/packages/domain/sim/providers/freebit/index.ts @@ -47,6 +47,7 @@ export type CancelAccountResponse = ReturnType; export type EsimAddAccountResponse = ReturnType; export type EsimActivationResponse = ReturnType; +export type AuthResponse = ReturnType; export * from "./mapper"; export * from "./raw.types"; diff --git a/packages/domain/sim/providers/freebit/mapper.ts b/packages/domain/sim/providers/freebit/mapper.ts index 5e14083c..0bbb75a3 100644 --- a/packages/domain/sim/providers/freebit/mapper.ts +++ b/packages/domain/sim/providers/freebit/mapper.ts @@ -8,6 +8,7 @@ import { type FreebitAccountDetailsRaw, type FreebitTrafficInfoRaw, type FreebitQuotaHistoryRaw, + type FreebitAuthResponseRaw, type FreebitTopUpRaw, type FreebitAddSpecRaw, type FreebitPlanChangeRaw, @@ -18,6 +19,7 @@ import { freebitAccountDetailsRawSchema, freebitTrafficInfoRawSchema, freebitQuotaHistoryRawSchema, + freebitAuthResponseRawSchema, freebitTopUpRawSchema, freebitAddSpecRawSchema, freebitPlanChangeRawSchema, @@ -206,4 +208,10 @@ export function transformFreebitEsimActivationResponse(raw: unknown) { return freebitEsimAddAccountRawSchema.parse(raw); } +export type FreebitEsimActivationResponse = ReturnType; + +export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw { + return freebitAuthResponseRawSchema.parse(raw); +} + diff --git a/packages/domain/sim/providers/freebit/raw.types.ts b/packages/domain/sim/providers/freebit/raw.types.ts index af28d5a0..e72a2ef8 100644 --- a/packages/domain/sim/providers/freebit/raw.types.ts +++ b/packages/domain/sim/providers/freebit/raw.types.ts @@ -166,3 +166,16 @@ export const freebitQuotaHistoryRawSchema = z.object({ export type FreebitQuotaHistoryRaw = z.infer; +export const freebitAuthResponseRawSchema = z.object({ + resultCode: z.string().optional(), + status: z + .object({ + message: z.string().optional(), + statusCode: z.union([z.string(), z.number()]).optional(), + }) + .optional(), + authKey: z.string().optional(), +}); + +export type FreebitAuthResponseRaw = z.infer; +