Add temporary debug endpoints and methods for WHMCS payment methods testing

- Introduced `testPaymentMethods` endpoint in InvoicesController to directly test WHMCS payment methods for a specific client ID.
- Added `testWhmcsPaymentMethods` method in InvoicesService for detailed logging and error handling during WHMCS API calls.
- Implemented cache bypassing and enhanced logging in `getPaymentMethods` method of WhmcsPaymentService for debugging purposes.
- Updated payment method transformation logic in WhmcsDataTransformer to handle variations in WHMCS API responses.
- Added debug information in Checkout and AddressConfirmation components to assist in troubleshooting address confirmation flow.
This commit is contained in:
tema 2025-08-30 15:41:08 +09:00
parent 1640fae457
commit a8f02ebc2b
7 changed files with 169 additions and 30 deletions

View File

@ -128,6 +128,16 @@ export class InvoicesController {
return this.invoicesService.getPaymentGateways();
}
@Get("test-payment-methods/:clientId")
@ApiOperation({
summary: "Test WHMCS payment methods API for specific client ID",
description: "Direct test of WHMCS GetPayMethods API - TEMPORARY DEBUG ENDPOINT",
})
@ApiParam({ name: "clientId", type: Number, description: "WHMCS Client ID to test" })
async testPaymentMethods(@Param("clientId", ParseIntPipe) clientId: number): Promise<any> {
return this.invoicesService.testWhmcsPaymentMethods(clientId);
}
@Post("payment-methods/refresh")
@HttpCode(HttpStatus.OK)
@ApiOperation({

View File

@ -466,6 +466,45 @@ export class InvoicesService {
}
}
/**
* TEMPORARY DEBUG METHOD: Test WHMCS payment methods API directly
*/
async testWhmcsPaymentMethods(clientId: number): Promise<any> {
try {
this.logger.log(`🔬 TESTING WHMCS GetPayMethods API for client ${clientId}`);
// Call WHMCS API directly with detailed logging
const result = await this.whmcsService.getPaymentMethods(clientId, `test-client-${clientId}`);
this.logger.log(`🔬 Test result for client ${clientId}:`, {
totalCount: result.totalCount,
paymentMethods: result.paymentMethods,
});
return {
clientId,
testTimestamp: new Date().toISOString(),
whmcsResponse: result,
summary: {
totalPaymentMethods: result.totalCount,
hasPaymentMethods: result.totalCount > 0,
paymentMethodTypes: result.paymentMethods.map(pm => pm.type),
}
};
} catch (error) {
this.logger.error(`🔬 Test failed for client ${clientId}`, {
error: getErrorMessage(error),
});
return {
clientId,
testTimestamp: new Date().toISOString(),
error: getErrorMessage(error),
errorType: error instanceof Error ? error.constructor.name : typeof error,
};
}
}
/**
* Create payment SSO link for invoice with specific payment method or gateway
*/

View File

@ -21,16 +21,29 @@ export class WhmcsPaymentService {
*/
async getPaymentMethods(clientId: number, userId: string): Promise<PaymentMethodList> {
try {
// Try cache first
const cached = await this.cacheService.getPaymentMethods(userId);
if (cached) {
this.logger.debug(`Cache hit for payment methods: user ${userId}`);
return cached;
}
// TEMPORARILY BYPASS CACHE for debugging
console.log(`🔍 BYPASSING CACHE for debugging - client ${clientId}, user ${userId}`);
// COMPLETELY BYPASS CACHE for debugging
console.log(`🔍 COMPLETELY BYPASSING CACHE for debugging - client ${clientId}, user ${userId}`);
// Try cache first - DISABLED FOR DEBUGGING
// const cached = await this.cacheService.getPaymentMethods(userId);
// if (cached && false) { // Force bypass cache
// this.logger.debug(`Cache hit for payment methods: user ${userId}`);
// return cached;
// }
const response = await this.connectionService.getPayMethods({ clientid: clientId });
// Debug logging to understand what WHMCS returns
console.log(`🔍 WHMCS GetPayMethods DEBUG for client ${clientId}:`);
console.log('Raw response:', JSON.stringify(response, null, 2));
console.log('Paymethods:', response.paymethods);
console.log('Paymethod array:', response.paymethods?.paymethod);
console.log('Is array:', Array.isArray(response.paymethods?.paymethod));
console.log('Length:', response.paymethods?.paymethod?.length);
this.logger.log(`WHMCS GetPayMethods response for client ${clientId}:`, {
rawResponse: response,
paymethods: response.paymethods,
@ -40,28 +53,71 @@ export class WhmcsPaymentService {
userId
});
const methods = (response.paymethods?.paymethod || []).map(pm => {
const transformed = this.dataTransformer.transformPaymentMethod(pm);
this.logger.log(`Transformed payment method:`, {
original: pm,
transformed,
clientId,
userId
});
return transformed;
});
// Handle different response structures
// According to WHMCS API docs, GetPayMethods returns { paymethods: [...] } directly
let paymentMethodsArray: any[] = [];
if (response.paymethods) {
if (Array.isArray(response.paymethods)) {
// Direct array response (correct for GetPayMethods)
paymentMethodsArray = response.paymethods;
} else if (response.paymethods.paymethod) {
// Nested structure (for GetPaymentMethods - payment gateways)
if (Array.isArray(response.paymethods.paymethod)) {
paymentMethodsArray = response.paymethods.paymethod;
} else {
paymentMethodsArray = [response.paymethods.paymethod];
}
}
}
console.log(`🔍 Payment methods array:`, paymentMethodsArray);
console.log(`🔍 Array length:`, paymentMethodsArray.length);
const methods = paymentMethodsArray.map(pm => {
console.log(`🔍 Processing payment method:`, pm);
try {
const transformed = this.dataTransformer.transformPaymentMethod(pm);
console.log(`🔍 Transformed to:`, transformed);
this.logger.log(`Transformed payment method:`, {
original: pm,
transformed,
clientId,
userId
});
return transformed;
} catch (error) {
console.error(`🚨 ERROR transforming payment method:`, error);
this.logger.error(`Failed to transform payment method`, {
error: getErrorMessage(error),
paymentMethod: pm,
clientId,
userId
});
return null; // Return null for failed transformations
}
}).filter(method => method !== null); // Filter out failed transformations
const result: PaymentMethodList = { paymentMethods: methods, totalCount: methods.length };
console.log(`🔍 FINAL RESULT for client ${clientId}:`, {
totalCount: result.totalCount,
hasPaymentMethods: result.totalCount > 0,
methods: result.paymentMethods
});
this.logger.log(`Final payment methods result for client ${clientId}:`, {
totalCount: result.totalCount,
methods: result.paymentMethods,
userId
});
await this.cacheService.setPaymentMethods(userId, result);
// DISABLE CACHE SAVING for debugging
// await this.cacheService.setPaymentMethods(userId, result);
console.log(`🔍 RETURNING RESULT WITHOUT CACHING for debugging`);
return result;
} catch (error) {
console.error(`🚨 ERROR fetching payment methods for client ${clientId}:`, error);
this.logger.error(`Failed to fetch payment methods for client ${clientId}`, {
error: getErrorMessage(error),
userId,

View File

@ -415,25 +415,33 @@ export class WhmcsDataTransformer {
/**
* Transform WHMCS payment method to shared PaymentMethod interface
*/
transformPaymentMethod(whmcsPayMethod: WhmcsPaymentMethod): PaymentMethod {
transformPaymentMethod(whmcsPayMethod: any): PaymentMethod {
try {
console.log(`🔧 TRANSFORMER DEBUG - Input:`, whmcsPayMethod);
// Handle field name variations between different WHMCS API responses
const transformed: PaymentMethod = {
id: whmcsPayMethod.id,
type: whmcsPayMethod.type,
description: whmcsPayMethod.description,
description: whmcsPayMethod.description || "",
gatewayName: whmcsPayMethod.gateway_name,
lastFour: whmcsPayMethod.last_four,
expiryDate: whmcsPayMethod.expiry_date,
// Handle both possible field names for lastFour
lastFour: whmcsPayMethod.last_four || whmcsPayMethod.card_last_four,
// Handle both possible field names for expiryDate
expiryDate: whmcsPayMethod.expiry_date || whmcsPayMethod.expiry_date,
bankName: whmcsPayMethod.bank_name,
accountType: whmcsPayMethod.account_type,
remoteToken: whmcsPayMethod.remote_token,
ccType: whmcsPayMethod.cc_type,
cardBrand: whmcsPayMethod.cc_type,
billingContactId: whmcsPayMethod.billing_contact_id,
createdAt: whmcsPayMethod.created_at,
updatedAt: whmcsPayMethod.updated_at,
// Handle both possible field names for card type
ccType: whmcsPayMethod.cc_type || whmcsPayMethod.card_type,
cardBrand: whmcsPayMethod.cc_type || whmcsPayMethod.card_type,
billingContactId: whmcsPayMethod.billing_contact_id || whmcsPayMethod.contact_id,
createdAt: whmcsPayMethod.created_at || whmcsPayMethod.last_updated,
updatedAt: whmcsPayMethod.updated_at || whmcsPayMethod.last_updated,
};
console.log(`🔧 TRANSFORMER DEBUG - Output:`, transformed);
// Optional validation hook
if (!this.validatePaymentMethod(transformed)) {
this.logger.warn("Transformed payment method failed validation", {

View File

@ -218,6 +218,13 @@ export class WhmcsService {
return this.paymentService.getPaymentGateways();
}
/**
* Invalidate payment methods cache for a user
*/
async invalidatePaymentMethodsCache(userId: string): Promise<void> {
return this.paymentService.invalidatePaymentMethodsCache(userId);
}
/**
* Create SSO token with payment method for invoice payment
*/

View File

@ -241,8 +241,16 @@ function CheckoutContent() {
};
const handleAddressConfirmed = (address?: Address) => {
console.log("🎯 PARENT: handleAddressConfirmed called with:", address);
console.log("🎯 PARENT: Current addressConfirmed state before:", addressConfirmed);
setAddressConfirmed(true);
setConfirmedAddress(address || null);
console.log("🎯 PARENT: addressConfirmed state set to true");
// Force a log after state update (in next tick)
setTimeout(() => {
console.log("🎯 PARENT: addressConfirmed state after update:", addressConfirmed);
}, 0);
};
const handleAddressIncomplete = () => {
@ -424,19 +432,20 @@ function CheckoutContent() {
)}
</div>
{/* Debug Info - Remove in production */}
{/* Debug Info - Remove in production */}
<div className="bg-gray-100 border rounded-lg p-3 mb-4 text-xs text-gray-600">
<strong>Debug Info:</strong> Address Confirmed: {addressConfirmed ? '✅' : '❌'} |
<strong>Debug Info:</strong> Address Confirmed: {addressConfirmed ? '✅' : '❌'} ({String(addressConfirmed)}) |
Payment Methods: {paymentMethodsLoading ? '⏳ Loading...' : paymentMethodsError ? '❌ Error' : paymentMethods ? `${paymentMethods.paymentMethods.length} found` : '❌ None'} |
Order Items: {checkoutState.orderItems.length} |
Can Submit: {!(
submitting ||
checkoutState.orderItems.length === 0 ||
checkoutState.orderItems.length === 0 ||
!addressConfirmed ||
paymentMethodsLoading ||
!paymentMethods ||
paymentMethods.paymentMethods.length === 0
) ? '✅' : '❌'}
) ? '✅' : '❌'} |
Render Time: {new Date().toLocaleTimeString()}
</div>
<div className="flex gap-4">

View File

@ -128,9 +128,19 @@ export function AddressConfirmation({
};
const handleConfirmAddress = () => {
console.log("🏠 CONFIRM ADDRESS CLICKED", {
billingInfo,
hasAddress: !!billingInfo?.address,
address: billingInfo?.address
});
if (billingInfo?.address) {
console.log("🏠 Calling onAddressConfirmed with:", billingInfo.address);
onAddressConfirmed(billingInfo.address);
setAddressConfirmed(true);
console.log("🏠 Address confirmed state set to true");
} else {
console.log("🏠 No billing info or address available");
}
};