refactor: enhance error handling in WHMCS and signup workflows

- Introduced structured error codes in BadRequestException for better clarity in WHMCS and signup workflows.
- Updated error messages to include specific context, improving user feedback during account verification and migration processes.
- Refined validation logic to ensure consistent error handling across services.
This commit is contained in:
barsa 2026-03-02 18:15:13 +09:00
parent 230a61c520
commit 49e9dba3a3
5 changed files with 37 additions and 31 deletions

View File

@ -7,6 +7,7 @@ import { CacheService } from "@bff/infra/cache/cache.service.js";
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
import { SalesforceAccountService } from "@bff/integrations/salesforce/services/salesforce-account.service.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { ErrorCode } from "@customer-portal/domain/common";
import type { SignupRequest } from "@customer-portal/domain/auth";
import type { SignupAccountSnapshot, SignupAccountCacheEntry } from "./signup.types.js";
import { PORTAL_SOURCE_NEW_SIGNUP } from "@bff/modules/auth/constants/portal.constants.js";
@ -37,9 +38,10 @@ export class SignupAccountResolverService {
if (normalizedCustomerNumber) {
const resolved = await this.getAccountSnapshot(normalizedCustomerNumber);
if (!resolved) {
throw new BadRequestException(
`Salesforce account not found for Customer Number: ${normalizedCustomerNumber}`
);
throw new BadRequestException({
code: ErrorCode.CUSTOMER_NOT_FOUND,
message: `Salesforce account not found for Customer Number: ${normalizedCustomerNumber}`,
});
}
if (resolved.WH_Account__c && resolved.WH_Account__c.trim() !== "") {

View File

@ -13,6 +13,7 @@ import { WhmcsAccountDiscoveryService } from "@bff/integrations/whmcs/services/w
import { WhmcsClientService } from "@bff/integrations/whmcs/services/whmcs-client.service.js";
import { SalesforceFacade } from "@bff/integrations/salesforce/facades/salesforce.facade.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import { ErrorCode } from "@customer-portal/domain/common";
import { mapPrismaUserToDomain } from "@bff/infra/mappers/index.js";
import { getCustomFieldValue } from "@customer-portal/domain/customer/providers";
import { safeOperation, OperationCriticality } from "@bff/core/utils/safe-operation.util.js";
@ -79,9 +80,10 @@ export class WhmcsLinkWorkflowService {
getCustomFieldValue(clientDetails.customfields, "Customer Number")?.trim();
if (!customerNumber) {
throw new BadRequestException(
`Customer Number not found in WHMCS custom field 198. Please contact support.`
);
throw new BadRequestException({
code: ErrorCode.ACCOUNT_MAPPING_MISSING,
message: `Customer Number not found in WHMCS custom field 198 for client ${clientNumericId}`,
});
}
this.logger.log("Found Customer Number for WHMCS client", {
@ -91,7 +93,10 @@ export class WhmcsLinkWorkflowService {
const sfAccount = await this.salesforceService.findAccountByCustomerNumber(customerNumber);
if (!sfAccount) {
throw new BadRequestException("Salesforce account not found. Please contact support.");
throw new BadRequestException({
code: ErrorCode.CUSTOMER_NOT_FOUND,
message: `Salesforce account not found for Customer Number: ${customerNumber}`,
});
}
const createdUser = await this.usersService.create(

View File

@ -4,6 +4,7 @@ import { Logger } from "nestjs-pino";
import * as argon2 from "argon2";
import { type MigrateWhmcsAccountRequest } from "@customer-portal/domain/get-started";
import { ErrorCode } from "@customer-portal/domain/common";
import {
getCustomFieldValue,
serializeWhmcsKeyValueMap,
@ -112,7 +113,10 @@ export class WhmcsMigrationWorkflowService {
// Verify WHMCS client still exists and matches session
const whmcsClient = await this.whmcsDiscovery.findAccountByEmail(email);
if (!whmcsClient || whmcsClient.id !== whmcsClientId) {
throw new BadRequestException("WHMCS account verification failed. Please start over.");
throw new BadRequestException({
code: ErrorCode.VALIDATION_FAILED,
message: `WHMCS client mismatch for email ${email}: expected ${whmcsClientId}, got ${whmcsClient?.id ?? "none"}`,
});
}
// Check for existing portal user
@ -124,9 +128,10 @@ export class WhmcsMigrationWorkflowService {
// Find Salesforce account for mapping
const sfAccount = await this.findSalesforceAccountForMigration(email, whmcsClientId);
if (!sfAccount) {
throw new BadRequestException(
"Unable to find your Salesforce account. Please contact support."
);
throw new BadRequestException({
code: ErrorCode.CUSTOMER_NOT_FOUND,
message: `No Salesforce account found for migration: email=${email}, whmcsClientId=${whmcsClientId}`,
});
}
// Hash password for portal storage

View File

@ -293,11 +293,7 @@ export const whmcsCurrenciesResponseSchema = z
result: z.enum(["success", "error"]).optional(),
message: z.string().optional(),
errorcode: z.string().optional(),
totalresults: z
.string()
.transform(val => Number.parseInt(val, 10))
.or(z.number())
.optional(),
totalresults: whmcsOptionalNumber,
currencies: z
.object({
currency: z.array(whmcsCurrencySchema).or(whmcsCurrencySchema),

View File

@ -208,21 +208,19 @@ const nullableProfileOverrides = nullableProfileFields.reduce<Record<string, z.Z
{}
);
export const whmcsClientSchema = whmcsRawClientSchema
.extend({
...nullableProfileOverrides,
defaultpaymethodid: whmcsOptionalNumber.nullable(),
currency: whmcsOptionalNumber.nullable(),
allowSingleSignOn: whmcsOptionalBoolean.nullable(),
email_verified: whmcsOptionalBoolean.nullable(),
marketing_emails_opt_in: whmcsOptionalBoolean.nullable(),
address: addressSchema.nullable().optional(),
email_preferences: emailPreferencesSchema.nullable().optional(),
customfields: whmcsCustomFieldsSchema,
users: whmcsUsersSchema,
stats: statsSchema.optional(),
})
.transform(raw => ({ ...raw }));
export const whmcsClientSchema = whmcsRawClientSchema.extend({
...nullableProfileOverrides,
defaultpaymethodid: whmcsOptionalNumber.nullable(),
currency: whmcsOptionalNumber.nullable(),
allowSingleSignOn: whmcsOptionalBoolean.nullable(),
email_verified: whmcsOptionalBoolean.nullable(),
marketing_emails_opt_in: whmcsOptionalBoolean.nullable(),
address: addressSchema.nullable().optional(),
email_preferences: emailPreferencesSchema.nullable().optional(),
customfields: whmcsCustomFieldsSchema,
users: whmcsUsersSchema,
stats: statsSchema.optional(),
});
// ============================================================================
// User Schema (API Response - Normalized camelCase)