3 Commits

Author SHA1 Message Date
1eb9d282b3 fix: use testmode boolean parameter for Mollie payments
Changed from mode: 'test' | 'live' to testmode: boolean as per Mollie
API requirements. The testmode parameter is set to true when the API
key starts with 'test_', false otherwise.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:12:09 +01:00
291ce255b4 feat: add automatic mode detection for Mollie payments
Automatically set Mollie payment mode to 'test' or 'live' based on
the API key prefix (test_ or live_). This ensures payments are
created in the correct mode and helps prevent configuration errors.

Changes:
- Add getMollieMode() helper to detect mode from API key
- Include mode parameter in payment creation
- Use type assertion for Mollie client compatibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:45:04 +01:00
2904d30a5c fix: improve error logging with detailed messages and stack traces
Previously, error objects were passed directly to the logger without
proper serialization, resulting in empty error messages like "Error:"
with no details. This made debugging production issues impossible.

Changes:
- Extract error message and stack trace before logging
- Format errors consistently across all providers
- Add stack trace logging for better debugging
- Update test provider error handling

This fixes the issue where webhook and payment update errors showed
no useful information in production logs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:39:52 +01:00
4 changed files with 32 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@xtr-dev/payload-billing",
"version": "0.1.23",
"version": "0.1.26",
"description": "PayloadCMS plugin for billing and payment provider integrations with tracking and local testing",
"license": "MIT",
"type": "module",

View File

@@ -17,6 +17,13 @@ import { createContextLogger } from '../utils/logger'
const symbol = Symbol.for('@xtr-dev/payload-billing/mollie')
export type MollieProviderConfig = Parameters<typeof createMollieClient>[0]
/**
* Determine if testmode should be enabled based on API key prefix
*/
function isTestMode(apiKey: string): boolean {
return apiKey.startsWith('test_')
}
/**
* Type-safe mapping of Mollie payment status to internal status
*/
@@ -161,6 +168,9 @@ export const mollieProvider = (mollieConfig: MollieProviderConfig & {
validateProductionUrl(redirectUrl, 'Redirect')
validateProductionUrl(webhookUrl, 'Webhook')
// Determine testmode from API key (test_ prefix = true)
const testmode = isTestMode(mollieConfig.apiKey)
const molliePayment = await singleton.get(payload).payments.create({
amount: {
value: formatAmountForProvider(payment.amount, payment.currency),
@@ -169,7 +179,8 @@ export const mollieProvider = (mollieConfig: MollieProviderConfig & {
description: payment.description || '',
redirectUrl,
webhookUrl,
});
testmode,
} as any);
payment.providerId = molliePayment.id
// Use toPlainObject if available, otherwise spread the object (for compatibility with different Mollie client versions)
payment.providerData = typeof molliePayment.toPlainObject === 'function'

View File

@@ -424,7 +424,8 @@ export const testProvider = (testConfig: TestProviderConfig) => {
setTimeout(() => {
processTestPayment(payload, session, pluginConfig).catch(async (error) => {
const logger = createContextLogger(payload, 'Test Provider')
logger.error('Failed to process payment:', error)
const errorMessage = error instanceof Error ? error.message : String(error)
logger.error(`Failed to process payment: ${errorMessage}`)
// Ensure session status is updated consistently
session.status = 'failed'

View File

@@ -16,7 +16,7 @@ export const webhookResponses = {
// Log error internally but don't expose details
if (payload) {
const logger = createContextLogger(payload, 'Webhook')
logger.error('Error:', message)
logger.error(`Error: ${message}`)
} else {
console.error('[Webhook] Error:', message)
}
@@ -126,7 +126,12 @@ export async function updatePaymentStatus(
}
} catch (error) {
const logger = createContextLogger(payload, 'Payment Update')
logger.error(`Failed to update payment ${paymentId}:`, error)
const errorMessage = error instanceof Error ? error.message : String(error)
const errorStack = error instanceof Error ? error.stack : undefined
logger.error(`Failed to update payment ${paymentId}: ${errorMessage}`)
if (errorStack) {
logger.error(`Stack trace: ${errorStack}`)
}
return false
}
}
@@ -165,15 +170,22 @@ export function handleWebhookError(
context?: string,
payload?: Payload
): Response {
const message = error instanceof Error ? error.message : 'Unknown error'
const message = error instanceof Error ? error.message : String(error)
const stack = error instanceof Error ? error.stack : undefined
const fullContext = context ? `${provider} Webhook - ${context}` : `${provider} Webhook`
// Log detailed error internally for debugging
if (payload) {
const logger = createContextLogger(payload, fullContext)
logger.error('Error:', error)
logger.error(`Error: ${message}`)
if (stack) {
logger.error(`Stack trace: ${stack}`)
}
} else {
console.error(`[${fullContext}] Error:`, error)
console.error(`[${fullContext}] Error: ${message}`)
if (stack) {
console.error(`[${fullContext}] Stack trace:`, stack)
}
}
// Return generic response to avoid information disclosure