fixed coupon redemptions
This commit is contained in:
parent
b209a77be9
commit
6e34a57bd6
1 changed files with 126 additions and 142 deletions
|
|
@ -922,123 +922,33 @@ module.exports = (pool, query, authMiddleware) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.post('/checkout', async (req, res, next) => {
|
||||
// Complete checkout after successful payment
|
||||
router.post('/complete-checkout', async (req, res, next) => {
|
||||
try {
|
||||
const { userId, shippingAddress, shippingMethod } = req.body;
|
||||
const { userId, orderId, sessionId } = req.body;
|
||||
console.log("Complete Checkout ", `${userId} ${orderId} ${sessionId}`)
|
||||
|
||||
if (req.user.id !== userId) {
|
||||
return res.status(403).json({
|
||||
error: true,
|
||||
message: 'You can only checkout your own cart'
|
||||
message: 'You can only complete your own checkout'
|
||||
});
|
||||
}
|
||||
|
||||
// Get cart
|
||||
const cartResult = await query(
|
||||
'SELECT * FROM carts WHERE user_id = $1',
|
||||
[userId]
|
||||
// Verify the order exists and belongs to the user
|
||||
const orderResult = await query(
|
||||
'SELECT * FROM orders WHERE id = $1 AND user_id = $2',
|
||||
[orderId, userId]
|
||||
);
|
||||
|
||||
if (cartResult.rows.length === 0) {
|
||||
if (orderResult.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
error: true,
|
||||
message: 'Cart not found'
|
||||
message: 'Order not found'
|
||||
});
|
||||
}
|
||||
|
||||
const cartId = cartResult.rows[0].id;
|
||||
|
||||
// Get cart items
|
||||
const cartItemsResult = await query(
|
||||
`SELECT ci.*, p.price, p.name, p.description, p.weight_grams, p.length_cm, p.width_cm, p.height_cm,
|
||||
(
|
||||
SELECT json_build_object(
|
||||
'path', pi.image_path,
|
||||
'isPrimary', pi.is_primary
|
||||
)
|
||||
FROM product_images pi
|
||||
WHERE pi.product_id = p.id AND pi.is_primary = true
|
||||
LIMIT 1
|
||||
) AS primary_image
|
||||
FROM cart_items ci
|
||||
JOIN products p ON ci.product_id = p.id
|
||||
WHERE ci.cart_id = $1`,
|
||||
[cartId]
|
||||
);
|
||||
|
||||
if (cartItemsResult.rows.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
message: 'Cart is empty'
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate subtotal
|
||||
const subtotal = cartItemsResult.rows.reduce((sum, item) => {
|
||||
return sum + (parseFloat(item.price) * item.quantity);
|
||||
}, 0);
|
||||
|
||||
// Determine shipping cost and create shipment if needed
|
||||
let shippingCost = 0;
|
||||
let shipmentData = null;
|
||||
|
||||
if (config.shipping.enabled) {
|
||||
// If a specific shipping method was selected
|
||||
if (shippingMethod && shippingMethod.id) {
|
||||
shippingCost = parseFloat(shippingMethod.rate) || 0;
|
||||
|
||||
// Check if this is a non-flat rate and we need to purchase a real shipment
|
||||
if (config.shipping.easypostEnabled &&
|
||||
!shippingMethod.id.includes('flat-rate') &&
|
||||
!shippingMethod.id.includes('free-shipping')) {
|
||||
|
||||
try {
|
||||
// Parse shipping address to object if it's a string
|
||||
const parsedAddress = typeof shippingAddress === 'string'
|
||||
? shippingService.parseAddressString(shippingAddress)
|
||||
: shippingAddress;
|
||||
|
||||
// Retrieve temporary shipment ID from cart metadata
|
||||
const cartMetadataResult = await query(
|
||||
'SELECT metadata FROM carts WHERE id = $1',
|
||||
[cartId]
|
||||
);
|
||||
|
||||
let shipmentId = null;
|
||||
if (cartMetadataResult.rows.length > 0 &&
|
||||
cartMetadataResult.rows[0].metadata &&
|
||||
cartMetadataResult.rows[0].metadata.temp_shipment_id) {
|
||||
shipmentId = cartMetadataResult.rows[0].metadata.temp_shipment_id;
|
||||
}
|
||||
|
||||
if (shipmentId) {
|
||||
// Purchase the shipment with the selected rate
|
||||
shipmentData = await shippingService.purchaseShipment(
|
||||
shipmentId,
|
||||
shippingMethod.id
|
||||
);
|
||||
|
||||
console.log('Shipment purchased successfully:', shipmentData.shipment_id);
|
||||
|
||||
// Use the actual rate from the purchased shipment
|
||||
shippingCost = shipmentData.selected_rate.rate;
|
||||
} else {
|
||||
console.log('No shipment ID found in cart metadata, using standard rate');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error purchasing shipment:', error);
|
||||
// Continue with the rate provided
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default to flat rate if no method selected
|
||||
const shippingRates = shippingService.getFlatRateShipping(subtotal);
|
||||
shippingCost = shippingRates[0].rate;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total with shipping
|
||||
const total = subtotal + shippingCost;
|
||||
const order = orderResult.rows[0];
|
||||
|
||||
// Begin transaction
|
||||
const client = await pool.connect();
|
||||
|
|
@ -1046,59 +956,133 @@ module.exports = (pool, query, authMiddleware) => {
|
|||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
// Create order
|
||||
const orderId = uuidv4();
|
||||
// Update order status and payment info
|
||||
await client.query(
|
||||
'INSERT INTO orders (id, user_id, status, total_amount, shipping_address, payment_completed, shipping_cost) VALUES ($1, $2, $3, $4, $5, $6, $7)',
|
||||
[orderId, userId, 'pending', total, shippingAddress, false, shippingCost]
|
||||
'UPDATE orders SET status = $1, payment_completed = true, payment_id = $2 WHERE id = $3',
|
||||
['processing', sessionId, orderId]
|
||||
);
|
||||
|
||||
// Create order items
|
||||
for (const item of cartItemsResult.rows) {
|
||||
await client.query(
|
||||
'INSERT INTO order_items (id, order_id, product_id, quantity, price_at_purchase) VALUES ($1, $2, $3, $4, $5)',
|
||||
[uuidv4(), orderId, item.product_id, item.quantity, item.price]
|
||||
);
|
||||
}
|
||||
|
||||
// If we have shipping details, save them with the order
|
||||
if (shippingMethod && shippingMethod.id) {
|
||||
const shippingInfo = shipmentData || {
|
||||
method_id: shippingMethod.id,
|
||||
carrier: shippingMethod.carrier,
|
||||
service: shippingMethod.service,
|
||||
rate: shippingMethod.rate,
|
||||
estimated_days: shippingMethod.delivery_days,
|
||||
tracking_code: null
|
||||
};
|
||||
|
||||
await client.query(
|
||||
'UPDATE orders SET shipping_info = $1 WHERE id = $2',
|
||||
[JSON.stringify(shippingInfo), orderId]
|
||||
);
|
||||
}
|
||||
|
||||
// Clear the temporary shipment ID from cart metadata
|
||||
await client.query(
|
||||
`UPDATE carts SET metadata = metadata - 'temp_shipment_id' WHERE id = $1`,
|
||||
[cartId]
|
||||
// Get cart
|
||||
const cartResult = await client.query(
|
||||
'SELECT * FROM carts WHERE user_id = $1',
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (cartResult.rows.length > 0) {
|
||||
const cartId = cartResult.rows[0].id;
|
||||
|
||||
// Check if cart has a coupon applied
|
||||
const cartMetadata = cartResult.rows[0].metadata;
|
||||
if (cartMetadata && cartMetadata.coupon) {
|
||||
const couponInfo = cartMetadata.coupon;
|
||||
|
||||
// Increment the coupon's current_redemptions
|
||||
await client.query(
|
||||
'UPDATE coupons SET current_redemptions = current_redemptions + 1 WHERE id = $1',
|
||||
[couponInfo.id]
|
||||
);
|
||||
|
||||
// Create a coupon redemption record
|
||||
await client.query(
|
||||
'INSERT INTO coupon_redemptions (coupon_id, order_id, user_id, discount_amount) VALUES ($1, $2, $3, $4)',
|
||||
[couponInfo.id, orderId, userId, couponInfo.discount_amount]
|
||||
);
|
||||
|
||||
// Update the order with coupon information
|
||||
await client.query(
|
||||
'UPDATE orders SET coupon_id = $1, discount_amount = $2 WHERE id = $3',
|
||||
[couponInfo.id, couponInfo.discount_amount, orderId]
|
||||
);
|
||||
}
|
||||
|
||||
// Get cart items to update product stock
|
||||
const cartItemsResult = await client.query(
|
||||
'SELECT * FROM cart_items WHERE cart_id = $1',
|
||||
[cartId]
|
||||
);
|
||||
|
||||
// Update product stock
|
||||
for (const item of cartItemsResult.rows) {
|
||||
await client.query(
|
||||
'UPDATE products SET stock_quantity = stock_quantity - $1 WHERE id = $2',
|
||||
[item.quantity, item.product_id]
|
||||
);
|
||||
|
||||
// Process stock notifications after updating stock
|
||||
try {
|
||||
const productWithNotification = await client.query(
|
||||
`SELECT id, name, stock_quantity, stock_notification
|
||||
FROM products
|
||||
WHERE id = $1`,
|
||||
[item.product_id]
|
||||
);
|
||||
|
||||
if (productWithNotification.rows.length > 0) {
|
||||
const product = productWithNotification.rows[0];
|
||||
let stockNotification;
|
||||
|
||||
// Handle different ways the JSON could be stored
|
||||
if (product.stock_notification) {
|
||||
try {
|
||||
// If it's a string, parse it
|
||||
// why are we doing this its been an obj for months now
|
||||
if (typeof product.stock_notification === 'string') {
|
||||
stockNotification = JSON.parse(product.stock_notification);
|
||||
} else {
|
||||
// Otherwise use as is
|
||||
stockNotification = product.stock_notification;
|
||||
}
|
||||
|
||||
console.log("Stock notification for product:", product.id, stockNotification);
|
||||
|
||||
// Check if notification is enabled and stock is below threshold
|
||||
if (stockNotification &&
|
||||
stockNotification.enabled === true &&
|
||||
stockNotification.email &&
|
||||
stockNotification.threshold &&
|
||||
product.stock_quantity <= parseInt(stockNotification.threshold)) {
|
||||
|
||||
// Log the notification with the order ID
|
||||
await client.query(
|
||||
`INSERT INTO notification_logs
|
||||
(order_id, notification_type, sent_at, status)
|
||||
VALUES ($1, $2, NOW(), $3)`,
|
||||
[orderId, 'low_stock_alert', 'pending']
|
||||
);
|
||||
|
||||
console.log(`Low stock notification queued for product ${product.id} - ${product.name}`);
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error("Error parsing stock notification JSON:", parseError);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (notificationError) {
|
||||
console.error("Error processing stock notification:", notificationError);
|
||||
// Continue with checkout even if notification processing fails
|
||||
}
|
||||
}
|
||||
|
||||
// Clear cart
|
||||
await client.query(
|
||||
'DELETE FROM cart_items WHERE cart_id = $1',
|
||||
[cartId]
|
||||
);
|
||||
|
||||
// Clear cart metadata (including coupon)
|
||||
await client.query(
|
||||
'UPDATE carts SET metadata = $1 WHERE id = $2',
|
||||
['{}', cartId]
|
||||
);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
|
||||
// Send back cart items for Stripe checkout
|
||||
res.status(201).json({
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Order created successfully, ready for payment',
|
||||
orderId,
|
||||
cartItems: cartItemsResult.rows,
|
||||
subtotal,
|
||||
shippingCost,
|
||||
total,
|
||||
shipmentData
|
||||
message: 'Order completed successfully',
|
||||
orderId
|
||||
});
|
||||
|
||||
// Note: We don't clear the cart here now - we'll do that after successful payment
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
throw error;
|
||||
|
|
|
|||
Loading…
Reference in a new issue