2025-10-03 14:26:55 +09:00
|
|
|
/**
|
|
|
|
|
* Freebit SIM Provider - Mapper
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type { SimDetails, SimUsage, SimTopUpHistory, SimType, SimStatus } from "../../contract";
|
|
|
|
|
import { simDetailsSchema, simUsageSchema, simTopUpHistorySchema } from "../../schema";
|
|
|
|
|
import {
|
|
|
|
|
type FreebitAccountDetailsRaw,
|
|
|
|
|
type FreebitTrafficInfoRaw,
|
|
|
|
|
type FreebitQuotaHistoryRaw,
|
2025-10-03 17:13:02 +09:00
|
|
|
type FreebitAuthResponseRaw,
|
2025-10-03 16:37:52 +09:00
|
|
|
type FreebitTopUpRaw,
|
|
|
|
|
type FreebitAddSpecRaw,
|
|
|
|
|
type FreebitPlanChangeRaw,
|
|
|
|
|
type FreebitCancelPlanRaw,
|
|
|
|
|
type FreebitCancelAccountRaw,
|
|
|
|
|
type FreebitEsimReissueRaw,
|
|
|
|
|
type FreebitEsimAddAccountRaw,
|
2025-10-03 14:26:55 +09:00
|
|
|
freebitAccountDetailsRawSchema,
|
|
|
|
|
freebitTrafficInfoRawSchema,
|
|
|
|
|
freebitQuotaHistoryRawSchema,
|
2025-10-03 17:13:02 +09:00
|
|
|
freebitAuthResponseRawSchema,
|
2025-10-03 16:37:52 +09:00
|
|
|
freebitTopUpRawSchema,
|
|
|
|
|
freebitAddSpecRawSchema,
|
|
|
|
|
freebitPlanChangeRawSchema,
|
|
|
|
|
freebitCancelPlanRawSchema,
|
|
|
|
|
freebitCancelAccountRawSchema,
|
|
|
|
|
freebitEsimReissueRawSchema,
|
|
|
|
|
freebitEsimAddAccountRawSchema,
|
2025-10-03 14:26:55 +09:00
|
|
|
} from "./raw.types";
|
|
|
|
|
|
|
|
|
|
function asString(value: unknown): string {
|
|
|
|
|
if (typeof value === "string") return value;
|
|
|
|
|
if (typeof value === "number") return String(value);
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function asNumber(value: unknown): number {
|
|
|
|
|
if (typeof value === "number") return value;
|
|
|
|
|
if (typeof value === "string") {
|
|
|
|
|
const parsed = parseFloat(value);
|
|
|
|
|
return isNaN(parsed) ? 0 : parsed;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseBooleanFlag(value: unknown): boolean {
|
|
|
|
|
if (typeof value === "boolean") return value;
|
|
|
|
|
if (typeof value === "number") return value === 10;
|
|
|
|
|
if (typeof value === "string") return value === "10" || value.toLowerCase() === "true";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapSimStatus(status: string | undefined): SimStatus {
|
|
|
|
|
if (!status) return "pending";
|
|
|
|
|
const normalized = status.toLowerCase();
|
|
|
|
|
if (normalized.includes("active") || normalized === "10") return "active";
|
|
|
|
|
if (normalized.includes("suspend")) return "suspended";
|
|
|
|
|
if (normalized.includes("cancel") || normalized.includes("terminate")) return "cancelled";
|
|
|
|
|
return "pending";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function deriveSimType(sizeValue: unknown, eid?: string | number | null): SimType {
|
|
|
|
|
const simSizeStr = typeof sizeValue === "number" ? String(sizeValue) : sizeValue;
|
|
|
|
|
const raw = typeof simSizeStr === "string" ? simSizeStr.toLowerCase() : undefined;
|
|
|
|
|
|
|
|
|
|
const eidStr = typeof eid === "number" ? String(eid) : eid;
|
|
|
|
|
if (eidStr && eidStr.length > 0) {
|
|
|
|
|
return "esim";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (raw) {
|
|
|
|
|
case "nano":
|
|
|
|
|
return "nano";
|
|
|
|
|
case "micro":
|
|
|
|
|
return "micro";
|
|
|
|
|
case "esim":
|
|
|
|
|
return "esim";
|
|
|
|
|
default:
|
|
|
|
|
return "standard";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function transformFreebitAccountDetails(raw: unknown): SimDetails {
|
|
|
|
|
const response = freebitAccountDetailsRawSchema.parse(raw);
|
|
|
|
|
const account = response.responseDatas.at(0);
|
|
|
|
|
if (!account) {
|
|
|
|
|
throw new Error("Freebit account details missing response data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sanitizedAccount = asString(account.account);
|
|
|
|
|
const simSizeValue = account.simSize ?? (account as any).size;
|
|
|
|
|
const eidValue = account.eid;
|
|
|
|
|
const simType = deriveSimType(
|
|
|
|
|
typeof simSizeValue === 'number' ? String(simSizeValue) : simSizeValue,
|
|
|
|
|
typeof eidValue === 'number' ? String(eidValue) : eidValue
|
|
|
|
|
);
|
|
|
|
|
const voiceMailEnabled = parseBooleanFlag(account.voicemail ?? account.voiceMail);
|
|
|
|
|
const callWaitingEnabled = parseBooleanFlag(account.callwaiting ?? account.callWaiting);
|
|
|
|
|
const internationalRoamingEnabled = parseBooleanFlag(account.worldwing ?? account.worldWing);
|
|
|
|
|
|
|
|
|
|
const simDetails: SimDetails = {
|
|
|
|
|
account: sanitizedAccount,
|
|
|
|
|
status: mapSimStatus(account.status),
|
|
|
|
|
planCode: asString(account.planCode),
|
|
|
|
|
planName: asString(account.planName),
|
|
|
|
|
simType,
|
|
|
|
|
iccid: asString(account.iccid),
|
|
|
|
|
eid: asString(eidValue),
|
|
|
|
|
msisdn: asString(account.msisdn),
|
|
|
|
|
imsi: asString(account.imsi),
|
|
|
|
|
remainingQuotaMb: asNumber(account.quota),
|
|
|
|
|
remainingQuotaKb: asNumber(account.quotaKb),
|
|
|
|
|
voiceMailEnabled,
|
|
|
|
|
callWaitingEnabled,
|
|
|
|
|
internationalRoamingEnabled,
|
|
|
|
|
networkType: asString(account.contractLine),
|
|
|
|
|
activatedAt: asString(account.startDate) || undefined,
|
|
|
|
|
expiresAt: asString(account.expireDate) || undefined,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return simDetailsSchema.parse(simDetails);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function transformFreebitTrafficInfo(raw: unknown): SimUsage {
|
|
|
|
|
const response = freebitTrafficInfoRawSchema.parse(raw);
|
|
|
|
|
|
|
|
|
|
const simUsage: SimUsage = {
|
|
|
|
|
account: asString(response.account),
|
2025-10-03 17:08:42 +09:00
|
|
|
todayUsageMb: response.traffic?.today ? asNumber(response.traffic.today) / 1024 : 0,
|
|
|
|
|
todayUsageKb: response.traffic?.today ? asNumber(response.traffic.today) : 0,
|
|
|
|
|
monthlyUsageMb: undefined,
|
|
|
|
|
monthlyUsageKb: undefined,
|
|
|
|
|
recentDaysUsage: [],
|
|
|
|
|
isBlacklisted: parseBooleanFlag(response.traffic?.blackList),
|
2025-10-03 14:26:55 +09:00
|
|
|
lastUpdated: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return simUsageSchema.parse(simUsage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function transformFreebitQuotaHistory(raw: unknown): SimTopUpHistory {
|
|
|
|
|
const response = freebitQuotaHistoryRawSchema.parse(raw);
|
|
|
|
|
|
|
|
|
|
const history: SimTopUpHistory = {
|
|
|
|
|
account: asString(response.account),
|
2025-10-03 17:08:42 +09:00
|
|
|
totalAdditions: asNumber(response.total),
|
|
|
|
|
additionCount: asNumber(response.count),
|
|
|
|
|
history: (response.quotaHistory || []).map(detail => ({
|
2025-10-03 14:26:55 +09:00
|
|
|
quotaKb: asNumber(detail.addQuotaKb),
|
|
|
|
|
quotaMb: asNumber(detail.addQuotaKb) / 1024,
|
|
|
|
|
addedDate: detail.addDate || "",
|
|
|
|
|
expiryDate: detail.expireDate || "",
|
|
|
|
|
campaignCode: detail.campaignCode || "",
|
|
|
|
|
})),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return simTopUpHistorySchema.parse(history);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 15:09:19 +09:00
|
|
|
export function normalizeAccount(account: string): string {
|
|
|
|
|
return account.replace(/\D/g, "");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitTopUpResponse(raw: unknown) {
|
|
|
|
|
return freebitTopUpRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitTopUpResponse = ReturnType<typeof transformFreebitTopUpResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitAddSpecResponse(raw: unknown) {
|
|
|
|
|
return freebitAddSpecRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitAddSpecResponse = ReturnType<typeof transformFreebitAddSpecResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitPlanChangeResponse(raw: unknown) {
|
|
|
|
|
return freebitPlanChangeRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitPlanChangeResponse = ReturnType<typeof transformFreebitPlanChangeResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitCancelPlanResponse(raw: unknown) {
|
|
|
|
|
return freebitCancelPlanRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitCancelPlanResponse = ReturnType<typeof transformFreebitCancelPlanResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitCancelAccountResponse(raw: unknown) {
|
|
|
|
|
return freebitCancelAccountRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitCancelAccountResponse = ReturnType<typeof transformFreebitCancelAccountResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitEsimReissueResponse(raw: unknown) {
|
|
|
|
|
return freebitEsimReissueRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitEsimReissueResponse = ReturnType<typeof transformFreebitEsimReissueResponse>;
|
|
|
|
|
|
2025-10-03 16:37:52 +09:00
|
|
|
export function transformFreebitEsimAddAccountResponse(raw: unknown) {
|
|
|
|
|
return freebitEsimAddAccountRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
export type FreebitEsimAddAccountResponse = ReturnType<typeof transformFreebitEsimAddAccountResponse>;
|
|
|
|
|
|
|
|
|
|
export function transformFreebitEsimActivationResponse(raw: unknown) {
|
|
|
|
|
return freebitEsimAddAccountRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:13:02 +09:00
|
|
|
export function transformFreebitAuthResponse(raw: unknown): FreebitAuthResponseRaw {
|
|
|
|
|
return freebitAuthResponseRawSchema.parse(raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 17:08:42 +09:00
|
|
|
|