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