From 8e1e14965a91bb83374ed4ca65767fed1a7db039 Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Fri, 11 Nov 2022 16:57:12 +0100 Subject: [PATCH] saving order details in redis --- pass/app/Order.js | 91 ++++++++++++++++++++++++++++++++ pass/routes/checkout/checkout.js | 25 ++++++++- pass/routes/checkout/paypal.js | 53 ++++++++----------- 3 files changed, 138 insertions(+), 31 deletions(-) create mode 100644 pass/app/Order.js diff --git a/pass/app/Order.js b/pass/app/Order.js new file mode 100644 index 0000000..39db876 --- /dev/null +++ b/pass/app/Order.js @@ -0,0 +1,91 @@ +const config = require("config"); +const dayjs = require("dayjs"); + +class Order { + static get STORAGE_KEY_PREFIX() { + return "order_"; + } + static get PURCHASE_STORAGE_TIME_MONTHS() { + return 6; + } + // How many minutes is a user allowed to take for finishing the payment + static get PURCHASE_TIME_LIMIT_MINUTES() { + return 15; + } + + /** + * Data stored in Redis Database + */ + #order_id; + #amount; + #price; + #encrypted_sales_receipts; + #payment_completed; + + /** + * Data populated by context + */ + #create_mode; + #redis_client; + + constructor(order_id, amount, price, encrypted_sales_receipts) { + this.#order_id = order_id; + this.#amount = amount; + this.#price = price; + this.#encrypted_sales_receipts = encrypted_sales_receipts; + + this.#payment_completed = false; + this.#create_mode = true; + + let Redis = require("ioredis"); + this.#redis_client = new Redis({ + host: config.get("redis.host"), + }); + } + + static async LOAD_ORDER_FROM_ID(order_id) { + let Redis = require("ioredis"); + let redis_client = new Redis({ + host: config.get("redis.host"), + }); + + let redis_key = Order.STORAGE_KEY_PREFIX + order_id; + let order_data = await redis_client.hgetall(redis_key); + console.log(order_data); + } + + async save() { + let redis_key = Order.STORAGE_KEY_PREFIX + this.#order_id; + let expiration = new dayjs(); + if (this.#create_mode) { + expiration = expiration.add(Order.PURCHASE_TIME_LIMIT_MINUTES, "minute"); + } else { + expiration = expiration.add(Order.PURCHASE_STORAGE_TIME_MONTHS, "month"); + } + expiration = Math.round(expiration.diff() / 1000); + + let key_exists = await this.#redis_client.exists(redis_key); + if (this.#create_mode && key_exists) { + return Promise.reject("Cannot create a Order that already exists!"); + } + + let storage_promise = this.#redis_client + .hmset(redis_key, { + order_id: this.#order_id, + amount: this.#amount, + price: this.#price, + encrypted_sales_receipts: this.#encrypted_sales_receipts, + payment_completed: this.#payment_completed, + }) + .then((result) => { + this.#create_mode = false; + }) + .then((result) => { + this.#redis_client.expire(redis_key, expiration); + }); + + await storage_promise; + } +} + +module.exports = Order; diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js index 9d20e91..7498e1f 100644 --- a/pass/routes/checkout/checkout.js +++ b/pass/routes/checkout/checkout.js @@ -1,5 +1,6 @@ const BlindSignature = require("blind-signatures"); const Crypto = require("../../app/Crypto.js"); +const Order = require("../../app/Order.js"); const config = require("config"); const price_for_100 = 1; @@ -115,7 +116,29 @@ router.use( if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } - next("route"); + + // Order data is validated: Create and store the order in the redis database + let order = new Order( + req.body.order_id, + req.body.amount, + req.body.price, + req.body.encrypted_sales_receipts + ); + order + .save() + .then(() => { + // Order created on our side. Continue the payment with the selected provider + next("route"); + }) + .catch((reason) => { + return res.status(400).json({ + errors: [ + { + msg: reason, + }, + ], + }); + }); } ); diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index 8a4df36..9b1ff56 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -9,10 +9,8 @@ const base = "https://api-m.sandbox.paypal.com"; /* Client initiates payment */ router.post("/", async (req, res, next) => { - res.status(500); - res.json(["Disabling Order creating temporarily"]); - return; - const order = await createOrder(); + // All Submitted data is verified at this point + const order = await createOrder(req); res.json(order); }); @@ -26,11 +24,30 @@ module.exports = router; // use the orders api to create an order -async function createOrder() { +async function createOrder(req) { const accessToken = await generateAccessToken(); const url = `${base}/v2/checkout/orders`; + let order = { + intent: "CAPTURE", + + purchase_units: [ + { + description: "MetaGer Pass Einkauf", + amount: { + currency_code: "EUR", + value: req.body.price.toFixed(2), + }, + }, + ], + application_context: { + brand_name: "SUMA-EV", + shipping_preference: "NO_SHIPPING", + }, + }; + console.log(order.purchase_units[0].amount); + const response = await fetch(url, { method: "post", @@ -39,31 +56,7 @@ async function createOrder() { Authorization: `Bearer ${accessToken}`, }, - body: JSON.stringify({ - intent: "CAPTURE", - - purchase_units: [ - { - description: "Test Payment", - amount: { - currency_code: "EUR", - - value: "4.00", - }, - }, - ], - payment_source: { - paypal: { - experience_context: { - payment_method_preference: "IMMEDIATE_PAYMENT_REQUIRED", - payment_method_selected: "PAYPAL", - user_action: "PAY_NOW", - brand_name: "EXAMPLE INC", - shipping_preference: "NO_SHIPPING", - }, - }, - }, - }), + body: JSON.stringify(order), }); const data = await response.json(); -- GitLab