From 3b32f8d0caa5996bf95ece48131761b06610fed1 Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@suma-ev.de>
Date: Wed, 9 Nov 2022 16:11:27 +0100
Subject: [PATCH] basic paypal checkout implementation (sandbox)

---
 pass/app.js                      |   2 +
 pass/package-lock.json           |  63 +++++++++++++++----
 pass/package.json                |   4 +-
 pass/public/js/index.js          |  35 ++++++-----
 pass/public/styles/checkout.css  |   2 +-
 pass/public/styles/checkout.less |   8 ++-
 pass/resources/js/checkout.js    |  73 ++++++++++++++++++++--
 pass/routes/checkout/checkout.js |  37 +++++-------
 pass/routes/checkout/paypal.js   | 100 +++++++++++++++++++++++++++++++
 pass/views/checkout/checkout.ejs |  46 +++++++-------
 pass/views/index.ejs             |   5 +-
 11 files changed, 296 insertions(+), 79 deletions(-)
 create mode 100644 pass/routes/checkout/paypal.js

diff --git a/pass/app.js b/pass/app.js
index ddb0596..8d67af5 100644
--- a/pass/app.js
+++ b/pass/app.js
@@ -8,6 +8,7 @@ var logger = require("morgan");
 
 var indexRouter = require("./routes/index");
 var checkoutRouter = require("./routes/checkout/checkout");
+var paypalRouter = require("./routes/checkout/paypal.js");
 
 var app = express();
 
@@ -24,6 +25,7 @@ app.use(express.static(path.join(__dirname, "public")));
 
 app.use("/", indexRouter);
 app.use("/checkout", checkoutRouter);
+app.use("/payment/paypal", paypalRouter);
 
 // Browserified Javascript files
 app.get(
diff --git a/pass/package-lock.json b/pass/package-lock.json
index 5defdb3..fa4a82d 100644
--- a/pass/package-lock.json
+++ b/pass/package-lock.json
@@ -8,6 +8,7 @@
       "name": "pass",
       "version": "0.0.0",
       "dependencies": {
+        "@paypal/paypal-js": "^5.1.1",
         "blind-signatures": "^1.0.7",
         "browserify-middleware": "^8.1.1",
         "config": "^3.3.8",
@@ -21,7 +22,8 @@
         "ioredis": "^5.2.4",
         "less-middleware": "~2.2.1",
         "morgan": "~1.9.1",
-        "node-forge": "^1.3.1"
+        "node-forge": "^1.3.1",
+        "uuid": "^9.0.0"
       },
       "devDependencies": {
         "nodemon": "^2.0.20"
@@ -32,6 +34,14 @@
       "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
       "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
     },
+    "node_modules/@paypal/paypal-js": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@paypal/paypal-js/-/paypal-js-5.1.1.tgz",
+      "integrity": "sha512-MMQ8TA048gTB43pzEOMzod8WY8hfzy+ahd7w29LtMvXduqzp7/29WxrTlsy4k6ARG6WGJ/uGqpc4+la4UZEQgw==",
+      "dependencies": {
+        "promise-polyfill": "^8.2.3"
+      }
+    },
     "node_modules/abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -3226,6 +3236,11 @@
         "asap": "~2.0.3"
       }
     },
+    "node_modules/promise-polyfill": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz",
+      "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg=="
+    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -3474,6 +3489,16 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/request/node_modules/uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+      "optional": true,
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
     "node_modules/resolve": {
       "version": "1.22.1",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@@ -4509,13 +4534,11 @@
       }
     },
     "node_modules/uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
-      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
-      "optional": true,
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
       "bin": {
-        "uuid": "bin/uuid"
+        "uuid": "dist/bin/uuid"
       }
     },
     "node_modules/validator": {
@@ -4798,6 +4821,14 @@
       "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
       "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
     },
+    "@paypal/paypal-js": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/@paypal/paypal-js/-/paypal-js-5.1.1.tgz",
+      "integrity": "sha512-MMQ8TA048gTB43pzEOMzod8WY8hfzy+ahd7w29LtMvXduqzp7/29WxrTlsy4k6ARG6WGJ/uGqpc4+la4UZEQgw==",
+      "requires": {
+        "promise-polyfill": "^8.2.3"
+      }
+    },
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -7354,6 +7385,11 @@
         "asap": "~2.0.3"
       }
     },
+    "promise-polyfill": {
+      "version": "8.2.3",
+      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz",
+      "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg=="
+    },
     "proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -7561,6 +7597,12 @@
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.1.tgz",
           "integrity": "sha512-LQy1Q1fcva/UsnP/6Iaa4lVeM49WiOitu2T4hZCyA/elLKu37L99qcBJk4VCCk+rdLvnMzfKyiN3SZTqdAZGSQ==",
           "optional": true
+        },
+        "uuid": {
+          "version": "3.4.0",
+          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+          "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+          "optional": true
         }
       }
     },
@@ -8391,10 +8433,9 @@
       "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
     },
     "uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
-      "optional": true
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
+      "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
     },
     "validator": {
       "version": "13.7.0",
diff --git a/pass/package.json b/pass/package.json
index 5196544..fac9384 100644
--- a/pass/package.json
+++ b/pass/package.json
@@ -7,6 +7,7 @@
     "dev": "nodemon --exec node ./bin/www"
   },
   "dependencies": {
+    "@paypal/paypal-js": "^5.1.1",
     "blind-signatures": "^1.0.7",
     "browserify-middleware": "^8.1.1",
     "config": "^3.3.8",
@@ -20,7 +21,8 @@
     "ioredis": "^5.2.4",
     "less-middleware": "~2.2.1",
     "morgan": "~1.9.1",
-    "node-forge": "^1.3.1"
+    "node-forge": "^1.3.1",
+    "uuid": "^9.0.0"
   },
   "devDependencies": {
     "nodemon": "^2.0.20"
diff --git a/pass/public/js/index.js b/pass/public/js/index.js
index a62ba01..cf14611 100644
--- a/pass/public/js/index.js
+++ b/pass/public/js/index.js
@@ -1,19 +1,22 @@
-const default_searches = 250;
-const default_price = 3;
+const default_searches = 300;
+const default_price = 4;
 const default_estimate_months = 1;
 
-document.getElementById("multiplier").addEventListener("input", multiplierChanged);
-multiplierChanged()
+document.getElementById("amount").addEventListener("input", multiplierChanged);
+multiplierChanged();
 
-function multiplierChanged(){
-    let multiplier = document.getElementById("multiplier").value;
-    let searches_element = document.querySelector("#offers > .offer.default > h1");
-    let estimate_element = document.querySelector("#offers > .offer.default > p.hint > span");
-    let price_element = document.querySelector("#offers > .offer.default > button.select");
-    let amount_form_element = document.querySelector("#offers > .offer.default > input[name=amount]");
-    searches_element.textContent = default_searches * multiplier;
-    price_element.textContent = (default_price * multiplier) + " €";
-    estimate_element.textContent = default_estimate_months * multiplier;
-    amount_form_element.value = default_searches * multiplier;
-
-}
\ No newline at end of file
+function multiplierChanged() {
+  let multiplier = document.getElementById("amount").value;
+  let searches_element = document.querySelector(
+    "#offers > .offer.default > h1"
+  );
+  let estimate_element = document.querySelector(
+    "#offers > .offer.default > p.hint > span"
+  );
+  let price_element = document.querySelector(
+    "#offers > .offer.default > button.select"
+  );
+  searches_element.textContent = default_searches * multiplier;
+  price_element.textContent = default_price * multiplier + " €";
+  estimate_element.textContent = default_estimate_months * multiplier;
+}
diff --git a/pass/public/styles/checkout.css b/pass/public/styles/checkout.css
index f64239c..fa00378 100644
--- a/pass/public/styles/checkout.css
+++ b/pass/public/styles/checkout.css
@@ -1 +1 @@
-#payment-container{width:max-content;border:1px solid #777;border-radius:.2rem}#payment-container>#heading{border:1px solid #777;background-color:#eaeaea;padding:.5rem}#payment-container>#payment-provider-container{display:flex;align-items:center}#payment-container>#payment-provider-container>#payment-providers{border-right:1px solid #777;padding:.5rem}#payment-container>#payment-provider-container>#payment-providers>h1{margin:0;font-size:.7rem;font-weight:normal}#payment-container>#payment-provider-container>#payment-providers>ul{list-style-type:none;margin:0;padding:0}#payment-container>#payment-provider-container>#payment-providers>ul>li{cursor:pointer;padding:.5rem 1rem;background-color:#f5f5f5}#payment-container>#payment-provider-container>#payment-providers>ul>li[data-active="true"]{background-color:orange;color:white;font-weight:bold;-webkit-text-stroke:#777 .01rem}#payment-container>#payment-provider-container>#payment-providers>ul>li:not(:last-child){border-bottom:1px dashed #777}#payment-container>.step{border:1px solid #777;padding:1rem;color:rgba(120,120,120,0.314)}#payment-container>.step>.section-heading{display:flex;gap:.5rem}#payment-container>.step>.section-heading>.status{width:1rem}#payment-container>.step>.section-heading>.status>*{display:none}#payment-container>.step>.section-heading>.content{flex-grow:1}#payment-container>.step>.section-heading>.location{text-align:right;border-left:1px solid rgba(120,120,120,0.314);padding-left:.6rem}#payment-container>.step.current{color:inherit}#payment-container>.step.current .loading{display:block}#payment-container>.step.current .location{color:#777;border-color:#777}#payment-container>.step.finished .finished{display:block}
\ No newline at end of file
+#payment-container{width:max-content;border:1px solid #777;border-radius:.2rem}#payment-container>#heading{border:1px solid #777;background-color:#eaeaea;padding:.5rem}#payment-container #payment-provider-container{display:flex;align-items:center}#payment-container #payment-provider-container>#payment-providers{border-right:1px solid #777;padding:.5rem}#payment-container #payment-provider-container>#payment-providers>h1{margin:0;font-size:.7rem;font-weight:normal}#payment-container #payment-provider-container>#payment-providers>ul{list-style-type:none;margin:0;padding:0}#payment-container #payment-provider-container>#payment-providers>ul>li{cursor:pointer;padding:.5rem 1rem;background-color:#f5f5f5}#payment-container #payment-provider-container>#payment-providers>ul>li[data-active="true"]{background-color:orange;color:white;font-weight:bold;-webkit-text-stroke:#777 .01rem}#payment-container #payment-provider-container>#payment-providers>ul>li:not(:last-child){border-bottom:1px dashed #777}#payment-container>.step{border:1px solid #777;padding:1rem;color:rgba(120,120,120,0.314)}#payment-container>.step>.section-heading{display:flex;gap:.5rem}#payment-container>.step>.section-heading>.status{width:1rem}#payment-container>.step>.section-heading>.status>*{display:none}#payment-container>.step>.section-heading>.content{flex-grow:1}#payment-container>.step>.section-heading>.location{text-align:right;border-left:1px solid rgba(120,120,120,0.314);padding-left:.6rem}#payment-container>.step>.section-body{display:none}#payment-container>.step.current{color:inherit}#payment-container>.step.current .loading{display:block}#payment-container>.step.current .location{color:#777;border-color:#777}#payment-container>.step.current>.section-body{display:block}#payment-container>.step.finished .finished{display:block}
\ No newline at end of file
diff --git a/pass/public/styles/checkout.less b/pass/public/styles/checkout.less
index 7a84bcc..ec62288 100644
--- a/pass/public/styles/checkout.less
+++ b/pass/public/styles/checkout.less
@@ -9,7 +9,7 @@
     padding: 0.5rem;
   }
 
-  > #payment-provider-container {
+  #payment-provider-container {
     display: flex;
     align-items: center;
 
@@ -65,6 +65,9 @@
         padding-left: 0.6rem;
       }
     }
+    > .section-body {
+      display: none;
+    }
     &.current {
       color: inherit;
       .loading {
@@ -74,6 +77,9 @@
         color: #777;
         border-color: #777;
       }
+      > .section-body {
+        display: block;
+      }
     }
     &.finished .finished {
       display: block;
diff --git a/pass/resources/js/checkout.js b/pass/resources/js/checkout.js
index 241951a..88cec47 100644
--- a/pass/resources/js/checkout.js
+++ b/pass/resources/js/checkout.js
@@ -1,5 +1,70 @@
-one_generate_private_key();
+const uuid_generator = require("uuid");
+const BlindSignature = require("blind-signatures");
+const BigInteger = require("jsbn").BigInteger;
+const paypal_client = require("@paypal/paypal-js");
 
-function one_generate_private_key() {
-    
-}
\ No newline at end of file
+var encrypted_sales_receipt = [];
+var encrypted_sales_receipt_r = [];
+
+one_generate_encrypted_sales_receipt();
+
+function one_generate_encrypted_sales_receipt() {
+  let current_step_container = document.getElementById(
+    "generate-sales-receipt"
+  );
+  let next_step_container = document.getElementById("execute-payment");
+
+  let amount = document.getElementById("payment-container").dataset.amount;
+  let N = document.getElementById("payment-container").dataset.public_n;
+  let E = document.getElementById("payment-container").dataset.public_e;
+
+  for (let i = 0; i < amount; i++) {
+    let uuid = uuid_generator.v4();
+
+    let { blinded, r } = BlindSignature.blind({
+      message: uuid,
+      N: N,
+      E: E,
+    });
+    encrypted_sales_receipt.push(blinded.toString());
+    encrypted_sales_receipt_r.push(r.toString());
+  }
+
+  current_step_container.classList.remove("current");
+  current_step_container.classList.add("finished");
+  next_step_container.classList.add("current");
+}
+
+function two_execute_payment(provider) {}
+
+function execute_payment_paypal(e) {
+  let paypal_payment_option_button = document.getElementById(
+    "payment_method_paypal"
+  );
+
+  paypal_payment_option_button.dataset.active = true;
+  let client_id = paypal_payment_option_button.dataset.client_id;
+  paypal_client
+    .loadScript({
+      "client-id": client_id,
+    })
+    .then((paypal) => {
+      paypal
+        .Buttons({
+          createOrder: (data, actions) => {
+            return fetch("/payment/paypal/order", {})
+              .then((response) => response.json())
+              .then((order) => order.id);
+          },
+        })
+        .render("#payment-information");
+    })
+    .catch((err) => {
+      // ToDo Handle error
+      console.error("failed to load the PayPal JS SDK script", err);
+    });
+}
+
+document
+  .getElementById("payment_method_paypal")
+  .addEventListener("click", execute_payment_paypal);
diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js
index 9e04a5c..0c59f53 100644
--- a/pass/routes/checkout/checkout.js
+++ b/pass/routes/checkout/checkout.js
@@ -1,8 +1,8 @@
 const BlindSignature = require("blind-signatures");
 const Crypto = require("../../app/Crypto.js");
+const config = require("config");
 
 const price_for_100 = 1;
-const price_for_250 = 3;
 
 var express = require("express");
 var router = express.Router();
@@ -12,17 +12,8 @@ const { query, validationResult } = require("express-validator");
 router.get(
   "/",
   query("amount")
-    .isInt({ min: 100, max: 1000 })
-    .withMessage("Amount needs to be between 100 and 1000."),
-  query("amount")
-    .toInt()
-    .custom((value, { req }) => {
-      if (value !== 100 && value % 250 !== 0) {
-        throw new Error("Amount needs to be either 100 or divisble by 250.");
-      }
-      return true;
-    })
-    .withMessage("Amount needs to be either 100 or divisble by 250."),
+    .isInt({ min: 1, max: 4 })
+    .withMessage("Amount needs to be between 1 and 4."),
   function (req, res, next) {
     const errors = validationResult(req);
     if (!errors.isEmpty()) {
@@ -31,22 +22,24 @@ router.get(
 
     let params = {
       amount: req.query.amount,
-      price:
-        req.query.amount === 100
-          ? price_for_100
-          : price_for_250 * (req.query.amount / 250),
+      unit_size: 100,
+      price: req.query.amount * price_for_100,
+      payments: {
+        paypal: {
+          client_id: config.get("payments.paypal.testing.client_id"),
+        },
+      },
     };
 
     let crypto = new Crypto();
-    crypto.private_key_get_current().then((keypair) => {
-      console.log(keypair);
+    crypto.private_key_get_current().then((private_key) => {
+      params.crypto = {
+        N: private_key.n,
+        E: private_key.e,
+      };
       res.render("checkout/checkout", params);
     });
   }
 );
 
-function get_current_private_key() {
-  let dayjs = require("dayjs");
-}
-
 module.exports = router;
diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js
new file mode 100644
index 0000000..8bfd7a4
--- /dev/null
+++ b/pass/routes/checkout/paypal.js
@@ -0,0 +1,100 @@
+var express = require("express");
+var router = express.Router();
+
+const config = require("config");
+
+const CLIENT_ID = config.get("payments.paypal.testing.client_id");
+const APP_SECRET = config.get("payments.paypal.testing.secret");
+const base = "https://api-m.sandbox.paypal.com";
+
+/* GET home page. */
+router.get("/order", async (req, res, next) => {
+  const order = await createOrder();
+  res.json(order);
+});
+
+module.exports = router;
+
+//////////////////////
+
+// PayPal API helpers
+
+//////////////////////
+
+// use the orders api to create an order
+
+async function createOrder() {
+  const accessToken = await generateAccessToken();
+
+  const url = `${base}/v2/checkout/orders`;
+
+  const response = await fetch(url, {
+    method: "post",
+
+    headers: {
+      "Content-Type": "application/json",
+
+      Authorization: `Bearer ${accessToken}`,
+    },
+
+    body: JSON.stringify({
+      intent: "CAPTURE",
+
+      purchase_units: [
+        {
+          amount: {
+            currency_code: "USD",
+
+            value: "100.00",
+          },
+        },
+      ],
+    }),
+  });
+
+  const data = await response.json();
+
+  return data;
+}
+
+// use the orders api to capture payment for an order
+
+async function capturePayment(orderId) {
+  const accessToken = await generateAccessToken();
+
+  const url = `${base}/v2/checkout/orders/${orderId}/capture`;
+
+  const response = await fetch(url, {
+    method: "post",
+
+    headers: {
+      "Content-Type": "application/json",
+
+      Authorization: `Bearer ${accessToken}`,
+    },
+  });
+
+  const data = await response.json();
+
+  return data;
+}
+
+// generate an access token using client id and app secret
+
+async function generateAccessToken() {
+  const auth = Buffer.from(CLIENT_ID + ":" + APP_SECRET).toString("base64");
+
+  const response = await fetch(`${base}/v1/oauth2/token`, {
+    method: "post",
+
+    body: "grant_type=client_credentials",
+
+    headers: {
+      Authorization: `Basic ${auth}`,
+    },
+  });
+
+  const data = await response.json();
+
+  return data.access_token;
+}
diff --git a/pass/views/checkout/checkout.ejs b/pass/views/checkout/checkout.ejs
index 38fdbcd..21f2b8f 100644
--- a/pass/views/checkout/checkout.ejs
+++ b/pass/views/checkout/checkout.ejs
@@ -1,40 +1,46 @@
 <%- include('../templates/page_header', {css: ['/styles/checkout.css'], js: ['/js/checkout.js']}); %>
 
 <main>
-    <div id="payment-container">
-        <div id="heading">Ihr Einkauf: <%- amount %>  Suchanfragen für <%- price %>€</div>
-        <div id="generate-private-key" class="step current">
+    <div id="payment-container" data-amount="<%- amount _%>" data-public_e="<%- crypto.E _%>" data-public_n="<%- crypto.N %>">
+        <div id="heading">Ihr Einkauf: <%- amount * unit_size %>  Suchanfragen für <%- price %>€</div>
+        <div id="generate-sales-receipt" class="step current">
             <div class="section-heading">
                 <div class="status">
                     <img src="/images/loader.gif" alt="Loading" class="loading">
                     <div class="finished">✓</div>
                 </div>
-                <div class="content">Privaten Schlüssel generieren</div>
+                <div class="content">Verschlüsselten Kaufbeleg generieren</div>
                 <div class="location">Lokal</div>
             </div>
+            <div class="section-body">
+
+            </div>
         </div>
-        <div id="generate-auth-keys" class="step">
+        
+        <div id="execute-payment" class="step">
             <div class="section-heading">
                 <div class="status">
                     <img src="/images/loader.gif" alt="Loading" class="loading">
                     <div class="finished">✓</div>
                 </div>
-                <div class="content">Auth Codes generieren</div>
-                <div class="location">Lokal</div>
+                <div class="content">Zahlung durchführen</div>
+                <div class="location">Extern</div>
             </div>
-        </div>
-        <div id="payment-provider-container">
-            <div id="payment-providers">
-                <h1>Zahlungsmethode</h1>
-                <ul>
-                    <li data-active="true">Paypal</li>
-                    <li>Google Pay</li>
-                    <li>Apple Pay</li>
-                    <li>Paysafe Card</li>
-                </ul>
-            </div>
-            <div id="payment-information">
-                <button>Bezahlen</button>
+            <div class="section-body">
+                <div id="payment-provider-container">
+                    <div id="payment-providers">
+                        <h1>Zahlungsmethode</h1>
+                        <ul>
+                            <li id="payment_method_paypal" data-active="false" data-client_id="<%- payments.paypal.client_id %>">Paypal</li>
+                            <li>Google Pay</li>
+                            <li>Apple Pay</li>
+                            <li>Paysafe Card</li>
+                        </ul>
+                    </div>
+                    <div id="payment-information">
+                        <div class="no-provider">Bitte Zahlungsmethode auswählen</div>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
diff --git a/pass/views/index.ejs b/pass/views/index.ejs
index e6c9556..e3640ad 100644
--- a/pass/views/index.ejs
+++ b/pass/views/index.ejs
@@ -4,16 +4,15 @@
   </p>
   <div id="offers">
     <form class="offer" action="/checkout">
-      <input type="hidden" name="amount" value="100">
+      <input type="hidden" name="amount" value="1">
       <h1>100</h1>
       <div class="spacer"></div>
       <button type="submit" class="select">1€</button>
     </form>
     <form class="offer default" action="/checkout">
-      <input type="hidden" name="amount" value="0">
       <h1>-</h1>
       <p class="hint">reicht üblicherweise für <span>-</span> Monat</p>
-      <input form="nosubmit" type="range" name="multiplier" id="multiplier" min="1" max="4" value="1">
+      <input type="range" name="amount" id="amount" min="1" max="4" value="1">
       <div class="spacer"></div>
       <button type="submit" class="select">- €</button>
     </form>
-- 
GitLab