Initial commit
This commit is contained in:
197
backend/utils/helpers.js
Normal file
197
backend/utils/helpers.js
Normal 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
|
||||
};
|
||||
Reference in New Issue
Block a user