Initial commit

This commit is contained in:
2025-09-26 17:34:37 +02:00
commit b96ccfd112
37 changed files with 23138 additions and 0 deletions

197
backend/utils/helpers.js Normal file
View File

@@ -0,0 +1,197 @@
const jwt = require('jsonwebtoken');
// Generate secure token for transactions
function generateToken(data, expiresIn = '1h') {
try {
const secret = process.env.JWT_SECRET || 'your-fallback-secret-key';
return jwt.sign(data, secret, { expiresIn });
} catch (error) {
console.error('Token generation error:', error);
return null;
}
}
// Verify transaction token
function verifyToken(token) {
try {
const secret = process.env.JWT_SECRET || 'your-fallback-secret-key';
return jwt.verify(token, secret);
} catch (error) {
console.error('Token verification error:', error);
return null;
}
}
// Format currency amount
function formatCurrency(amount, currency = 'USD') {
try {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(parseFloat(amount));
} catch (error) {
return `${currency} ${parseFloat(amount).toFixed(2)}`;
}
}
// Validate email format
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Sanitize order data for logging
function sanitizeOrderData(orderData) {
const sanitized = { ...orderData };
// Remove sensitive data
delete sanitized.billing?.email;
delete sanitized.billing?.phone;
delete sanitized.customer_id;
return {
id: sanitized.id,
status: sanitized.status,
total: sanitized.total,
currency: sanitized.currency,
payment_method: sanitized.payment_method,
date_created: sanitized.date_created
};
}
// Generate reference ID
function generateReferenceId(prefix = 'REF') {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 8).toUpperCase();
return `${prefix}-${timestamp}-${random}`;
}
// Validate PayPal amount format
function validateAmount(amount) {
if (!amount) return false;
const numAmount = parseFloat(amount);
if (isNaN(numAmount)) return false;
if (numAmount <= 0) return false;
if (numAmount > 10000) return false; // Max amount check
// Check decimal places (max 2)
const decimalPlaces = (amount.toString().split('.')[1] || '').length;
if (decimalPlaces > 2) return false;
return true;
}
// Parse query parameters safely
function parseQueryParams(req) {
const {
wc_order_id,
total,
currency,
customer_email,
return_url,
cancel_url
} = req.query;
return {
wc_order_id: wc_order_id ? parseInt(wc_order_id) : null,
total: total ? parseFloat(total) : null,
currency: currency || 'USD',
customer_email: customer_email || null,
return_url: return_url || `${process.env.FRONTEND_URL}/success`,
cancel_url: cancel_url || `${process.env.FRONTEND_URL}/cancel`
};
}
// Log API request/response
function logApiCall(type, endpoint, data, response) {
console.log(`${type.toUpperCase()} API Call:`, {
endpoint,
timestamp: new Date().toISOString(),
data: sanitizeLogData(data),
response: sanitizeLogData(response),
success: response?.success || false
});
}
// Sanitize data for logging (remove sensitive info)
function sanitizeLogData(data) {
if (!data || typeof data !== 'object') return data;
const sanitized = { ...data };
const sensitiveFields = [
'password', 'secret', 'token', 'key', 'auth',
'email', 'phone', 'address', 'credit_card'
];
sensitiveFields.forEach(field => {
if (sanitized[field]) {
sanitized[field] = '[REDACTED]';
}
});
return sanitized;
}
// Error response helper
function createErrorResponse(message, details = null, statusCode = 500) {
const response = {
success: false,
error: message,
timestamp: new Date().toISOString()
};
if (details && process.env.NODE_ENV !== 'production') {
response.details = details;
}
return { response, statusCode };
}
// Success response helper
function createSuccessResponse(data, message = null) {
return {
success: true,
data: data,
message: message,
timestamp: new Date().toISOString()
};
}
// Retry mechanism for API calls
async function retryOperation(operation, maxRetries = 3, delayMs = 1000) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
console.warn(`Operation failed (attempt ${attempt}/${maxRetries}):`, error.message);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
}
}
}
throw lastError;
}
module.exports = {
generateToken,
verifyToken,
formatCurrency,
isValidEmail,
sanitizeOrderData,
generateReferenceId,
validateAmount,
parseQueryParams,
logApiCall,
sanitizeLogData,
createErrorResponse,
createSuccessResponse,
retryOperation
};