- 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.
209 lines
6.9 KiB
TypeScript
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();
|
|
}
|
|
}
|