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: 'Fashion Store', // Customize this // ADD CUSTOMER DETAILS FROM WOOCOMMERCE: payer: wcOrder ? { email: wcOrder.order.billing.email, first_name: wcOrder.order.billing.first_name, last_name: wcOrder.order.billing.last_name, phone: wcOrder.order.billing.phone } : null, shipping: wcOrder ? { first_name: wcOrder.order.shipping.first_name, last_name: wcOrder.order.shipping.last_name, address_1: wcOrder.order.shipping.address_1, address_2: wcOrder.order.shipping.address_2, city: wcOrder.order.shipping.city, state: wcOrder.order.shipping.state, postcode: wcOrder.order.shipping.postcode, country: wcOrder.order.shipping.country } : null, // Optional: Add breakdown items_total: wcOrder ? wcOrder.order.total : total, shipping_total: wcOrder ? wcOrder.order.shipping_total : '0.00', tax_total: wcOrder ? wcOrder.order.total_tax : '0.00' }; // 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, line_items: wcOrder.order.line_items.map(item => ({ name: item.name, quantity: item.quantity, price: item.price, total: item.total, image: item.image?.src || null })), billing: { first_name: wcOrder.order.billing.first_name, last_name: wcOrder.order.billing.last_name, company: wcOrder.order.billing.company, address_1: wcOrder.order.billing.address_1, address_2: wcOrder.order.billing.address_2, city: wcOrder.order.billing.city, state: wcOrder.order.billing.state, postcode: wcOrder.order.billing.postcode, country: wcOrder.order.billing.country, email: wcOrder.order.billing.email, phone: wcOrder.order.billing.phone }, shipping: { total: wcOrder.order.shipping_total, method: wcOrder.order.shipping_lines[0]?.method_title, first_name: wcOrder.order.shipping.first_name, last_name: wcOrder.order.shipping.last_name, company: wcOrder.order.shipping.company, address_1: wcOrder.order.shipping.address_1, address_2: wcOrder.order.shipping.address_2, city: wcOrder.order.shipping.city, state: wcOrder.order.shipping.state, postcode: wcOrder.order.shipping.postcode, country: wcOrder.order.shipping.country } }); } catch (error) { console.error('Get Order Status Error:', error); res.status(500).json({ success: false, error: 'Internal server error' }); } }); module.exports = router;