Assist_Design/apps/bff/src/integrations/whmcs/services/whmcs-currency.service.ts
barsa e5ce4e166c Refactor mappers and services for improved type safety and code clarity
- Updated export statements in user and mapping mappers for consistency.
- Enhanced FreebitAuthService to explicitly define response types for better type inference.
- Refactored various services to improve error handling and response structure.
- Cleaned up unused code and comments across multiple files to enhance readability.
- Improved type annotations in invoice and subscription services for better validation and consistency.
2025-10-22 10:58:16 +09:00

209 lines
6.9 KiB
TypeScript

import { Injectable, Inject, OnModuleInit } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { WhmcsConnectionOrchestratorService } from "../connection/services/whmcs-connection-orchestrator.service";
import type { WhmcsCurrenciesResponse, WhmcsCurrency } from "@customer-portal/domain/billing";
@Injectable()
export class WhmcsCurrencyService implements OnModuleInit {
private defaultCurrency: WhmcsCurrency | null = null;
private currencies: WhmcsCurrency[] = [];
constructor(
@Inject(Logger) private readonly logger: Logger,
private readonly connectionService: WhmcsConnectionOrchestratorService
) {}
async onModuleInit() {
try {
// Check if WHMCS is available before trying to load currencies
this.logger.debug("Checking WHMCS availability before loading currencies");
const isAvailable = await this.connectionService.isAvailable();
if (!isAvailable) {
this.logger.warn("WHMCS service is not available, using fallback currency configuration");
this.setFallbackCurrency();
return;
}
this.logger.debug("WHMCS is available, attempting to load currencies");
await this.loadCurrencies();
} catch (error) {
this.logger.error("Failed to load WHMCS currencies on startup", {
error: getErrorMessage(error),
});
// Set fallback default
this.setFallbackCurrency();
}
}
/**
* Set fallback currency configuration when WHMCS is not available
*/
private setFallbackCurrency(): void {
this.defaultCurrency = {
id: 1,
code: "JPY",
prefix: "¥",
suffix: "",
format: "1",
rate: "1.00000",
};
this.currencies = [this.defaultCurrency];
this.logger.log("Using fallback currency configuration", {
defaultCurrency: this.defaultCurrency.code,
});
}
/**
* Get the default currency (first currency from WHMCS or JPY fallback)
*/
getDefaultCurrency(): WhmcsCurrency {
return (
this.defaultCurrency || {
id: 1,
code: "JPY",
prefix: "¥",
suffix: "",
format: "1",
rate: "1.00000",
}
);
}
/**
* Get all available currencies
*/
getAllCurrencies(): WhmcsCurrency[] {
return this.currencies;
}
/**
* Find currency by code
*/
getCurrencyByCode(code: string): WhmcsCurrency | null {
return this.currencies.find(c => c.code.toUpperCase() === code.toUpperCase()) || null;
}
/**
* Load currencies from WHMCS
*/
private async loadCurrencies(): Promise<void> {
try {
// The connection service returns the raw WHMCS API response data
// (the WhmcsResponse wrapper is unwrapped by the API methods service)
const response = (await this.connectionService.getCurrencies()) as WhmcsCurrenciesResponse;
// Check if response has currencies data (success case) or error fields
if (response.result === "success" || (response.currencies && !response.error)) {
// Parse the WHMCS response format into currency objects
this.currencies = this.parseWhmcsCurrenciesResponse(response);
if (this.currencies.length > 0) {
// Set first currency as default (WHMCS typically returns the primary currency first)
this.defaultCurrency = this.currencies[0];
this.logger.log(`Loaded ${this.currencies.length} currencies from WHMCS`, {
defaultCurrency: this.defaultCurrency?.code,
allCurrencies: this.currencies.map(c => c.code),
});
} else {
throw new Error("No currencies found in WHMCS response");
}
} else {
this.logger.error("WHMCS GetCurrencies returned error", {
result: response?.result,
message: response?.message,
error: response?.error,
errorcode: response?.errorcode,
fullResponse: JSON.stringify(response, null, 2),
});
throw new Error(
`WHMCS GetCurrencies error: ${response?.message || response?.error || "Unknown error"}`
);
}
} catch (error) {
this.logger.error("Failed to load currencies from WHMCS", {
error: getErrorMessage(error),
});
throw error;
}
}
/**
* Parse WHMCS response format into currency objects
* Handles both flat format (currencies[currency][0][id]) and nested format (currencies.currency[])
*/
private parseWhmcsCurrenciesResponse(response: WhmcsCurrenciesResponse): WhmcsCurrency[] {
const currencies: WhmcsCurrency[] = [];
// Check if response has nested currency structure
if (
response.currencies &&
typeof response.currencies === "object" &&
"currency" in response.currencies
) {
const currencyArray = Array.isArray(response.currencies.currency)
? response.currencies.currency
: [response.currencies.currency];
for (const currencyData of currencyArray) {
const currency: WhmcsCurrency = {
id: parseInt(String(currencyData.id)) || 0,
code: String(currencyData.code || ""),
prefix: String(currencyData.prefix || ""),
suffix: String(currencyData.suffix || ""),
format: String(currencyData.format || "1"),
rate: String(currencyData.rate || "1.00000"),
};
// Validate that we have essential currency data
if (currency.id && currency.code) {
currencies.push(currency);
}
}
} else {
// Fallback: try to parse flat format (currencies[currency][0][id], etc.)
const currencyKeys = Object.keys(response).filter(
key => key.startsWith("currencies[currency][") && key.includes("][id]")
);
// Extract currency indices
const currencyIndices = currencyKeys
.map(key => {
const match = key.match(/currencies\[currency\]\[(\d+)\]\[id\]/);
return match ? parseInt(match[1], 10) : null;
})
.filter((index): index is number => index !== null);
// Build currency objects from the flat response
for (const index of currencyIndices) {
const currency: WhmcsCurrency = {
id: parseInt(String(response[`currencies[currency][${index}][id]`])) || 0,
code: String(response[`currencies[currency][${index}][code]`] || ""),
prefix: String(response[`currencies[currency][${index}][prefix]`] || ""),
suffix: String(response[`currencies[currency][${index}][suffix]`] || ""),
format: String(response[`currencies[currency][${index}][format]`] || "1"),
rate: String(response[`currencies[currency][${index}][rate]`] || "1.00000"),
};
// Validate that we have essential currency data
if (currency.id && currency.code) {
currencies.push(currency);
}
}
}
return currencies;
}
/**
* Refresh currencies from WHMCS (can be called manually if needed)
*/
async refreshCurrencies(): Promise<void> {
await this.loadCurrencies();
}
}