const router = module.exports = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') const stripe = require('stripe')(process.env.STRIPE_PRIVATE_KEY); const easypost = new (require('@easypost/api'))(process.env.EASYPOST_API_KEY); const ensureAdmin = require('./middleware/ensureAdmin') const ensureCart = require('./middleware/ensureCart') const ensureUser = require('./middleware/ensureUser') const email = require('../utils/email') const validate = require('./middleware/validators') router.get('/', async (req, res) => { if(req.user) return res.json(await db.order.findAllForUser(req.user.uuid)) else return res.json(await db.order.findAllForSession(req.session.uuid)) }) router.get('/addresses', async (req, res) => { if(!req.user) return res.json(null) return res.json(await db.address.findAllForUser(req.user.uuid)) }) router.get('/all', ensureAdmin, async (req, res) => { const orders = await db.order.findAllComplete() res.json(orders) }) router.put('/', ensureCart, async (req, res) => { const order = await db.order.create(req.cart.uuid, req.sessionObj.uuid); res.json(order) }) router.get('/current', ensureCart, async (req, res) => { const order = await db.order.findForCart(req.cart.uuid); res.json(order) }) router.get('/:uuid', ensureAdmin, async (req, res) => { const order = await db.order.findByUUID(req.params.uuid) if(order && order.delivery && order.delivery.easypost_id) order.delivery.easypost = await easypost.Shipment.retrieve(order.delivery.easypost_id) res.json(order) }) router.post('/current/address/:uuid', ensureUser, ensureCart, async (req, res) => { const origOrder = await db.order.findForCart(req.cart.uuid); if(!origOrder) throw new Error("Unable to find current order"); const currentTransaction = origOrder .transactions.find(transaction => ( transaction.payment_state === 'started' )) const address = await db.address.findByUUID(req.params.uuid, req.user.uuid) if(!address) throw new Error("Unable to find address"); const order = await db.order.addAddress(currentTransaction, address) res.json(order) }) router.post('/current/address', ensureCart, parseJSON, validate.address, validate.handleApiError, async (req, res) => { const origOrder = await db.order.findForCart(req.cart.uuid); if(!origOrder) throw new Error("Unable to find current order"); const currentTransaction = origOrder .transactions.find(transaction => ( transaction.payment_state === 'started' )) const {name, street1, street2, city, state, zip, country} = req.body; // Create address, update order const address = await db.address.create(name, street1, street2, city, state, zip, country) const order = await db.order.addAddress(currentTransaction, address) res.json(order) }) router.post('/current/coupon', ensureCart, parseJSON, validate.coupon, validate.handleApiError, async (req, res) => { const origOrder = await db.order.findForCart(req.cart.uuid); if(!origOrder) throw new Error("Unable to find current order"); const coupon = await db.coupon.find(req.body.code) if(!coupon) throw new Error(`No such coupon: "${req.body.code}"`) const currentTransaction = origOrder .transactions.find(transaction => ( transaction.payment_state === 'started' )) const order = await db.order.addCoupon(currentTransaction, coupon) res.json(order) }) router.post('/current/checkout/stripe', ensureCart, async (req, res) => { const order = await db.order.findForCart(req.cart.uuid); if(!order) throw new Error("Unable to find current order"); const currentTransaction = order .transactions.find(transaction => ( transaction.payment_state === 'started' )) const {item_total_price, shipping_price, tax_price, coupon_effective_discount} = currentTransaction const {free_shipping} = currentTransaction.coupon || {} const numberOfItems = currentTransaction.cart.items.map(i => i.count).reduce((a,b) => a+b) const itemPriceWithDiscount = item_total_price - (coupon_effective_discount || 0) const shippingPrice = (free_shipping ? 0 : shipping_price) const line_items = [] if(itemPriceWithDiscount > 0) line_items.push({ name: `Cart Total (${numberOfItems} item${numberOfItems > 1 ? 's' : ''})`, amount: itemPriceWithDiscount, currency: 'usd', quantity: 1 }) if(shippingPrice > 0) line_items.push({ name: 'Shipping', amount: shippingPrice, currency: 'usd', quantity: 1 }) if(tax_price > 0) line_items.push({ name: 'Sales Tax (Utah)', amount: tax_price, currency: 'usd', quantity: 1 }) // TODO: We need to handle this case, mark it paid or something? if(line_items.length < 1) res.json(null) const session = await stripe.checkout.sessions.create({ client_reference_id: currentTransaction.uuid, customer_email: req.user ? req.user.email : undefined, payment_method_types: ['card'], line_items, success_url: `${process.env.EXTERNAL_URL}/store/checkout/verify?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.EXTERNAL_URL}/cancel`, }); res.json(session.id) }) router.post('/current/checkout/verify', ensureCart, parseJSON, async (req, res) => { const {session_id} = req.body if(!session_id) throw new Error("No session id given") let order = await db.order.findForCart(req.cart.uuid); if(!order) throw new Error("Unable to find current order"); const currentTransaction = order .transactions.find(transaction => ( transaction.payment_state === 'started' )) const {payment_intent} = await stripe.checkout.sessions.retrieve(session_id); const payment = await stripe.paymentIntents.retrieve(payment_intent) // Handle this better? if(payment.status !== "succeeded") return res.json({status: payment.status}) try { order = await db.order.addPayment(currentTransaction, payment) await db.session.clearCart(req.sessionObj.uuid) } catch (err) { console.error(err) throw new Error("Unable to complete order") } if(req.user) await db.order.setUser(order.uuid, req.user.uuid); res.json({status: payment.status, order}) }) router.post('/:uuid/ship/tracking', ensureAdmin, parseJSON, async (req, res) => { const order = await db.order.setTracking(req.params.uuid, req.body.code, req.body.date || null, req.body.price_cents) const user = await db.order.getUser(order.uuid) await email.sendShippingNotification(user, order) res.json(order) }) router.post('/:uuid/ship/delivery', ensureAdmin, parseJSON, async (req, res) => { const order = await db.order.setDelivery(req.params.uuid, req.body.description, req.body.date || null) res.json(order) }) router.post('/:uuid/ship/easypost', ensureAdmin, parseJSON, async (req, res) => { const order = await db.order.findByUUID(req.params.uuid) let itemsByUUID = {} for(const transaction of order.transactions) for(const item of transaction.cart.items) if(itemsByUUID[item.uuid]) itemsByUUID[item.uuid].count += item.count else itemsByUUID[item.uuid] = item const items = Object.values(itemsByUUID) const updatedOrder = await db.order.shipEasyPost( req.params.uuid, req.body.length, req.body.width, req.body.height, req.body.weight, items ) const user = await db.order.getUser(order.uuid) await email.sendShippingNotification(user, order) res.json(updatedOrder) })