gocommerce clone API

node v10.18.0
version: 2.0.0
endpointsharetweet
const express = require('express') const app = express() const port = 3000 const fetch = require('isomorphic-unfetch') const DomParser = require('dom-parser') const parser = new DomParser() const stripe = require('stripe')(process.env.JAMSTACK_SECRET_KEY) // Constants const SITE_URL = 'https://53806382.ngrok.io/' // Helper functions const getProductData = async url => { // Get the product meta from the static page const doc = await fetch(url).then(async res => { const htmlString = await res.text() return parser.parseFromString(htmlString, 'text/html') }) if (doc.getElementsByClassName('gocommerce-product').length !== 1) { throw new Error('None or too many product data declarations!') return } // Replace HTML special chars const jsonString = doc .getElementsByClassName('gocommerce-product')[0] .innerHTML.replace(/"/g, `"`) return JSON.parse(jsonString) } const calculateOrderAmount = ({ order, products }) => { let validatedAmount = 0 order.cart.forEach(item => { // Get product by sku const product = products.find(product => product.sku === item.sku) // Get price by currency const price = product.prices.find(price => { return price.currency === order.currency }) if (!price) throw new Error( `${product.sku} has no price info for currency ${order.currency}` ) validatedAmount += Number(price.amount) }) return validatedAmount } // use body-parser to automatically parse JSON-encoded request bodies const bodyParser = require('body-parser') app.use(bodyParser.json()) // Allow cors app.use('/', function(req, res, next) { // Allow requests from localhost. const allowedOrigins = ['http://localhost:9000'] // port that's used when running gatsby serve const origin = req.headers.origin if (allowedOrigins.indexOf(origin) > -1) { res.setHeader('Access-Control-Allow-Headers', '*') res.setHeader('Access-Control-Allow-Origin', origin) } next() }) app.post('/pay', async (req, res) => { const { order } = req.body const skuIds = order.cart.map(item => item.sku) const promises = skuIds.map( sku => new Promise(async (resolve, reject) => { try { const product = await getProductData(`${SITE_URL}${sku}`) resolve(product) } catch (error) { reject(error) } }) ) // Wait for all producst to be fetched const products = await Promise.all(promises) const totalAmount = calculateOrderAmount({ order, products }) if (totalAmount === Number(order.amount)) { // Create Payment try { const intent = await stripe.paymentIntents.create({ amount: totalAmount * 100, // TODO zero decimal currency detection currency: order.currency, payment_method: order.payment_method_id, setup_future_usage: 'off_session', // A PaymentIntent can be confirmed some time after creation, // but here we want to confirm (collect payment) immediately. confirm: true, // If the payment requires any follow-up actions from the // customer, like two-factor authentication, Stripe will error // and you will need to prompt them for a new payment method. error_on_requires_action: true, }) if (intent.status === 'succeeded') { // This creates a new Customer and attaches the PaymentMethod in one API call. const customer = await stripe.customers.create({ payment_method: intent.payment_method, email: order.email, address: order.address, }) // Handle post-payment fulfillment console.log( `Created Payment: ${intent.id} for Customer: ${customer.id}` ) // Now ship those goodies // await inventoryAPI.ship(order) res.json({ payment: intent, customer }) } else { // Any other status would be unexpected, so error res .status(400) .json({ error: { message: 'Unexpected status ' + intent.status } }) } } catch (error) { if (error.type === 'StripeCardError') { // Display error to customer res.status(400).json({ error: error.raw }) } else { // Something else happened res.status(400).json({ error }) } } } else { res .status(400) .json({ error: 'Order amount does not match product prices.' }) } }) app.listen(port, () => console.log(`Example API listening on port ${port}!`))
Loading…

no comments

    sign in to comment