197 lines
5.1 KiB
JavaScript
197 lines
5.1 KiB
JavaScript
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
|
|
}; |