mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 10:53:23 +00:00
security: Address critical security vulnerabilities and improve code quality
🔒 Security Fixes: - Make webhook signature validation required for production - Prevent information disclosure by returning 200 for all webhook responses - Sanitize external error messages while preserving internal logging 🔧 Code Quality Improvements: - Add URL validation to prevent localhost usage in production - Create currency utilities for proper handling of non-centesimal currencies - Replace unsafe 'any' types with type-safe ProviderData wrapper - Add comprehensive input validation for amounts, currencies, and descriptions - Set default Stripe API version for consistency 📦 New Features: - Currency conversion utilities supporting JPY, KRW, and other special cases - Type-safe provider data structure with metadata - Enhanced validation functions for payment data 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
updateInvoiceOnPaymentSuccess,
|
||||
handleWebhookError
|
||||
} from './utils'
|
||||
import { formatAmountForProvider, isValidAmount, isValidCurrencyCode } from './currency'
|
||||
|
||||
const symbol = Symbol('mollie')
|
||||
export type MollieProviderConfig = Parameters<typeof createMollieClient>[0]
|
||||
@@ -105,20 +106,48 @@ export const mollieProvider = (mollieConfig: MollieProviderConfig & {
|
||||
singleton.set(payload, mollieClient)
|
||||
},
|
||||
initPayment: async (payload, payment) => {
|
||||
// Validate required fields
|
||||
if (!payment.amount) {
|
||||
throw new Error('Amount is required')
|
||||
}
|
||||
if (!payment.currency) {
|
||||
throw new Error('Currency is required')
|
||||
}
|
||||
|
||||
// Validate amount
|
||||
if (!isValidAmount(payment.amount)) {
|
||||
throw new Error('Invalid amount: must be a positive integer within reasonable limits')
|
||||
}
|
||||
|
||||
// Validate currency code
|
||||
if (!isValidCurrencyCode(payment.currency)) {
|
||||
throw new Error('Invalid currency: must be a 3-letter ISO code')
|
||||
}
|
||||
|
||||
// Validate URLs in production
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
const redirectUrl = mollieConfig.redirectUrl ||
|
||||
(!isProduction ? 'https://localhost:3000/payment/success' : undefined)
|
||||
const webhookUrl = mollieConfig.webhookUrl ||
|
||||
`${process.env.PAYLOAD_PUBLIC_SERVER_URL || (!isProduction ? 'https://localhost:3000' : '')}/api/payload-billing/mollie/webhook`
|
||||
|
||||
if (isProduction) {
|
||||
if (!redirectUrl || redirectUrl.includes('localhost')) {
|
||||
throw new Error('Valid redirect URL is required for production')
|
||||
}
|
||||
if (!webhookUrl || webhookUrl.includes('localhost')) {
|
||||
throw new Error('Valid webhook URL is required for production')
|
||||
}
|
||||
}
|
||||
|
||||
const molliePayment = await singleton.get(payload).payments.create({
|
||||
amount: {
|
||||
value: (payment.amount / 100).toFixed(2),
|
||||
currency: payment.currency
|
||||
value: formatAmountForProvider(payment.amount, payment.currency),
|
||||
currency: payment.currency.toUpperCase()
|
||||
},
|
||||
description: payment.description || '',
|
||||
redirectUrl: mollieConfig.redirectUrl || 'https://localhost:3000/payment/success',
|
||||
webhookUrl: mollieConfig.webhookUrl || `${process.env.PAYLOAD_PUBLIC_SERVER_URL || 'https://localhost:3000'}/api/payload-billing/mollie/webhook`,
|
||||
redirectUrl,
|
||||
webhookUrl,
|
||||
});
|
||||
payment.providerId = molliePayment.id
|
||||
payment.providerData = molliePayment.toPlainObject()
|
||||
|
||||
Reference in New Issue
Block a user