Files
AluxPay/backend/routes/payment.js
2025-09-26 17:34:37 +02:00

269 lines
9.2 KiB
JavaScript

const express = require('express');
const { body, validationResult } = require('express-validator');
const paypalService = require('../services/paypal');
const woocommerceService = require('../services/woocommerce');
const { generateToken, verifyToken } = require('../utils/helpers');
const router = express.Router();
// Validation middleware
const validateCreateOrder = [
body('wc_order_id').isNumeric().withMessage('WooCommerce order ID must be numeric'),
body('total').isDecimal({ decimal_digits: '0,2' }).withMessage('Total must be a valid amount'),
body('currency').optional().isAlpha().isLength({ min: 3, max: 3 }).withMessage('Currency must be 3 letter code'),
];
const validateCaptureOrder = [
body('paypal_order_id').notEmpty().withMessage('PayPal order ID is required'),
body('wc_order_id').isNumeric().withMessage('WooCommerce order ID must be numeric'),
];
// Create PayPal order
router.post('/create-order', validateCreateOrder, async (req, res) => {
try {
// Check validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
error: 'Validation failed',
details: errors.array()
});
}
const { wc_order_id, total, currency, description, items } = req.body;
// In development, allow bypassing WooCommerce validation
let wcOrder = null;
const skipWooCommerce = process.env.SKIP_WOOCOMMERCE === 'true';
console.log('Skip WooCommerce:', skipWooCommerce, '| Env value:', process.env.SKIP_WOOCOMMERCE);
if (!skipWooCommerce) {
// Get WooCommerce order details for verification
wcOrder = await woocommerceService.getOrder(wc_order_id);
if (!wcOrder.success) {
return res.status(400).json({
success: false,
error: 'Invalid WooCommerce order',
details: wcOrder.error,
suggestion: 'For testing without WooCommerce, add SKIP_WOOCOMMERCE=true to your backend/.env file'
});
}
// Verify order total matches (security check)
if (parseFloat(wcOrder.order.total) !== parseFloat(total)) {
console.warn('Order total mismatch:', {
wc_total: wcOrder.order.total,
requested_total: total,
order_id: wc_order_id
});
return res.status(400).json({
success: false,
error: 'Order total mismatch'
});
}
} else {
console.log('Skipping WooCommerce validation - TEST MODE');
}
// Prepare order data for PayPal
const orderData = {
wc_order_id,
reference_id: `WC-${wc_order_id}-${Date.now()}`,
total: total,
currency: currency || 'USD',
description: description || `Order #${wc_order_id} from ${wcOrder?.order?.billing?.first_name || 'Test Customer'}`,
items: items || [],
brand_name: 'Your Store Name' // Customize this
};
// Create PayPal order
const paypalResult = await paypalService.createOrder(orderData);
if (!paypalResult.success) {
return res.status(500).json({
success: false,
error: 'Failed to create PayPal order',
details: paypalResult.error
});
}
// Generate secure token for this transaction
const token = generateToken({
wc_order_id,
paypal_order_id: paypalResult.order_id,
total,
created_at: Date.now()
});
res.json({
success: true,
paypal_order_id: paypalResult.order_id,
token: token,
approval_url: paypalResult.links?.find(link => link.rel === 'approve')?.href
});
} catch (error) {
console.error('Create Order Error:', error);
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Capture PayPal payment
router.post('/capture-order', validateCaptureOrder, async (req, res) => {
try {
// Check validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
error: 'Validation failed',
details: errors.array()
});
}
const { paypal_order_id, wc_order_id, token } = req.body;
console.log('Capture request received:', {
paypal_order_id,
wc_order_id,
hasToken: !!token,
tokenLength: token?.length
});
// Verify token (optional in development)
const skipTokenValidation = process.env.NODE_ENV === 'development' && process.env.SKIP_TOKEN_VALIDATION === 'true';
if (token && !skipTokenValidation) {
const tokenData = verifyToken(token);
console.log('Token verification result:', {
isValid: !!tokenData,
tokenData: tokenData
});
if (!tokenData || tokenData.wc_order_id !== parseInt(wc_order_id)) {
console.error('Token validation failed:', {
tokenData,
expectedOrderId: wc_order_id,
tokenOrderId: tokenData?.wc_order_id
});
return res.status(401).json({
success: false,
error: 'Invalid or expired token'
});
}
} else if (skipTokenValidation) {
console.warn('Skipping token validation - DEVELOPMENT MODE');
} else {
console.warn('No token provided in capture request');
}
// Capture the PayPal payment
const captureResult = await paypalService.captureOrder(paypal_order_id);
if (!captureResult.success) {
// Mark WooCommerce order as failed only if not skipping WooCommerce
if (process.env.SKIP_WOOCOMMERCE !== 'true') {
await woocommerceService.failPayment(wc_order_id, captureResult.error);
}
return res.status(400).json({
success: false,
error: 'Payment capture failed',
details: captureResult.error
});
}
// Update WooCommerce order (skip if in test mode)
let wcResult = { success: true, message: 'WooCommerce update skipped (test mode)' };
if (process.env.SKIP_WOOCOMMERCE !== 'true') {
wcResult = await woocommerceService.completePayment(wc_order_id, captureResult);
if (!wcResult.success) {
console.error('WooCommerce update failed after successful payment:', wcResult.error);
// Payment succeeded but WC update failed - this needs manual review
}
} else {
console.log('Skipping WooCommerce update - TEST MODE');
}
res.json({
success: true,
transaction_id: captureResult.transaction_id,
status: captureResult.status,
payer_email: captureResult.payer?.email_address,
woocommerce_updated: wcResult.success,
test_mode: process.env.SKIP_WOOCOMMERCE === 'true'
});
} catch (error) {
console.error('Capture Order Error:', error);
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
// Get order status
router.get('/order-status/:wc_order_id', async (req, res) => {
try {
const { wc_order_id } = req.params;
if (!wc_order_id || isNaN(wc_order_id)) {
return res.status(400).json({
success: false,
error: 'Invalid order ID'
});
}
// Skip WooCommerce check if in test mode
if (process.env.SKIP_WOOCOMMERCE === 'true') {
return res.json({
success: true,
order_id: wc_order_id,
status: 'completed',
total: '0.00',
currency: 'USD',
payment_method: 'paypal',
payment_method_title: 'PayPal (Test Mode)',
test_mode: true
});
}
const wcOrder = await woocommerceService.getOrder(wc_order_id);
if (!wcOrder.success) {
return res.status(404).json({
success: false,
error: 'Order not found'
});
}
res.json({
success: true,
order_id: wcOrder.order.id,
status: wcOrder.order.status,
total: wcOrder.order.total,
currency: wcOrder.order.currency,
payment_method: wcOrder.order.payment_method,
payment_method_title: wcOrder.order.payment_method_title
});
} catch (error) {
console.error('Get Order Status Error:', error);
res.status(500).json({
success: false,
error: 'Internal server error'
});
}
});
module.exports = router;