From 6af8519a7dbc1c363eff1126e0a42b207765f8cf Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@suma-ev.de>
Date: Wed, 7 Dec 2022 14:50:07 +0100
Subject: [PATCH] added paypal payment buttons

---
 pass/public/styles/key/checkout-amount.less  |  82 ++++++
 pass/public/styles/key/checkout-payment.less |  46 ++++
 pass/public/styles/key/key.css               |   1 +
 pass/public/styles/{ => key}/key.less        |  81 +-----
 pass/resources/js/checkout.js                | 267 ++-----------------
 pass/routes/key.js                           |  64 ++++-
 pass/views/key.ejs                           |  47 ++--
 7 files changed, 251 insertions(+), 337 deletions(-)
 create mode 100644 pass/public/styles/key/checkout-amount.less
 create mode 100644 pass/public/styles/key/checkout-payment.less
 create mode 100644 pass/public/styles/key/key.css
 rename pass/public/styles/{ => key}/key.less (51%)

diff --git a/pass/public/styles/key/checkout-amount.less b/pass/public/styles/key/checkout-amount.less
new file mode 100644
index 0000000..990b70b
--- /dev/null
+++ b/pass/public/styles/key/checkout-amount.less
@@ -0,0 +1,82 @@
+#checkout {
+  > h2 {
+    margin: 0 0 1rem;
+    text-align: center;
+    border-bottom: 1px solid @color-main;
+  }
+  #checkout-amount {
+    @checkout-amount-breakpoint-1: 800px;
+    @checkout-amount-breakpoint-2: 470px;
+    display: grid;
+    grid-template-columns: 1fr 1fr 1fr;
+    &.single {
+      grid-template-columns: 1fr;
+    }
+    gap: 1rem;
+    justify-items: center;
+    align-items: center;
+    > a {
+      text-decoration: none;
+      color: inherit;
+      display: grid;
+      width: max-content;
+      grid-template-columns: 7em 3.5em;
+      grid-template-rows: 2.5em 2em;
+      height: max-content;
+      > .checkout-amount {
+        font-size: 2rem;
+        font-weight: bold;
+        line-height: 1;
+        display: flex;
+        width: 100%;
+        height: 100%;
+        align-items: center;
+        justify-content: center;
+        border-top-left-radius: 10px;
+        border: 1px solid rgb(255, 127, 0);
+        border-bottom: 0;
+        border-right: 0;
+      }
+      > .checkout-duration {
+        width: 100%;
+        height: 100%;
+        border-bottom-left-radius: 10px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border: 1px solid rgb(255, 127, 0);
+        border-top: 0;
+        border-right: 0;
+      }
+      > .checkout-cost {
+        grid-row: span 2;
+        justify-content: center;
+        background-color: rgb(255, 127, 0);
+        color: white;
+        font-size: 1.7rem;
+        align-items: center;
+        border-top-right-radius: 10px;
+        border-bottom-right-radius: 10px;
+        border: 1px solid rgb(255, 127, 0);
+        border-left: 0;
+        display: flex;
+        width: 100%;
+        height: 100%;
+        align-self: center;
+      }
+    }
+    @media (max-width: @checkout-amount-breakpoint-1) {
+      grid-template-columns: 1fr 1fr;
+      &.single {
+        grid-template-columns: 1fr;
+      }
+    }
+    @media (max-width: @checkout-amount-breakpoint-2) {
+      grid-template-columns: 1fr;
+      > a {
+        width: 100%;
+        grid-template-columns: 1fr 3em;
+      }
+    }
+  }
+}
diff --git a/pass/public/styles/key/checkout-payment.less b/pass/public/styles/key/checkout-payment.less
new file mode 100644
index 0000000..fc18c38
--- /dev/null
+++ b/pass/public/styles/key/checkout-payment.less
@@ -0,0 +1,46 @@
+#payment {
+  margin-bottom: 1rem;
+  @payment-group-breakpoint-1: 1050px;
+  @payment-group-breakpoint-2: 580px;
+  > h2 {
+    margin: 0 0 1rem;
+    text-align: center;
+    border-bottom: 1px solid @color-main;
+  }
+
+  > label[for="payment-group-paypal"] {
+    border: 1px solid #777;
+    display: block;
+    padding: 0.5rem;
+    border-top-left-radius: 5px;
+    border-top-right-radius: 5px;
+    background-color: #f0f0f0;
+    border-bottom-color: rgb(255, 127, 0);
+    border-bottom-width: 2px;
+  }
+  > #payment-group-paypal {
+    display: none;
+  }
+  > .payment-group {
+    display: grid;
+    grid-template-columns: 1fr 1fr 1fr;
+    align-items: center;
+    justify-items: center;
+    gap: 1rem;
+    border: 1px solid #777;
+    padding: 1rem;
+    border-top: 0;
+    > div {
+      width: 14em;
+    }
+    @media (max-width: @payment-group-breakpoint-1) {
+      grid-template-columns: 1fr 1fr;
+    }
+    @media (max-width: @payment-group-breakpoint-2) {
+      grid-template-columns: 1fr;
+      > div {
+        width: 100%;
+      }
+    }
+  }
+}
diff --git a/pass/public/styles/key/key.css b/pass/public/styles/key/key.css
new file mode 100644
index 0000000..847b32a
--- /dev/null
+++ b/pass/public/styles/key/key.css
@@ -0,0 +1 @@
+#checkout>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#checkout #checkout-amount{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;justify-items:center;align-items:center}#checkout #checkout-amount.single{grid-template-columns:1fr}#checkout #checkout-amount>a{text-decoration:none;color:inherit;display:grid;width:max-content;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;height:max-content}#checkout #checkout-amount>a>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;width:100%;height:100%;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#checkout #checkout-amount>a>.checkout-duration{width:100%;height:100%;border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#checkout #checkout-amount>a>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex;width:100%;height:100%;align-self:center}@media (max-width:800px){#checkout #checkout-amount{grid-template-columns:1fr 1fr}#checkout #checkout-amount.single{grid-template-columns:1fr}}@media (max-width:470px){#checkout #checkout-amount{grid-template-columns:1fr}#checkout #checkout-amount>a{width:100%;grid-template-columns:1fr 3em}}#payment{margin-bottom:1rem}#payment>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#payment>label[for="payment-group-paypal"]{border:1px solid #777;display:block;padding:.5rem;border-top-left-radius:5px;border-top-right-radius:5px;background-color:#f0f0f0;border-bottom-color:#ff7f00;border-bottom-width:2px}#payment>#payment-group-paypal{display:none}#payment>.payment-group{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:center;justify-items:center;gap:1rem;border:1px solid #777;padding:1rem;border-top:0}#payment>.payment-group>div{width:14em}@media (max-width:1050px){#payment>.payment-group{grid-template-columns:1fr 1fr}}@media (max-width:580px){#payment>.payment-group{grid-template-columns:1fr}#payment>.payment-group>div{width:100%}}main{max-width:980px;margin:0 auto;display:grid;row-gap:1rem;padding-right:1rem;grid-template-columns:auto 1fr;grid-template-rows:auto 1fr auto auto;grid-template-areas:"qr key        " "qr setting-url" "qr buttons" "amount charge"}main>#qr{grid-area:qr;justify-self:center}main #key{grid-area:key;font-size:clamp(.9rem, 4.8vw, 1.5rem);font-weight:bold;width:max-content;margin-top:11px;margin-right:1rem}main #setting-url{grid-area:setting-url;align-self:start;display:flex;padding:.5rem 1rem;border-radius:5px}main #setting-url>input{line-height:1.5;padding:.1rem .5rem;border-radius:5px;flex-grow:1}main>#buttons{grid-area:buttons;display:flex;gap:1rem;margin-bottom:17px;justify-content:flex-end}@media (max-width:435px){main>#buttons{display:grid;grid-template-columns:1fr}main>#buttons .button{width:auto}main>#buttons>*{display:grid;justify-items:center;text-align:center}}main>#amount{grid-area:amount;max-width:200px;justify-self:center;display:flex;flex-direction:column;align-items:center}main>#amount>h3{font-size:1.5rem;margin:0}main>#amount>div.amount{font-size:3rem}main>#charge{grid-area:charge}main>#charge>#store>p{line-height:1.5}@media (max-width:770px){main{padding:0 1rem;grid-template-rows:auto auto auto auto auto;grid-template-columns:auto auto;grid-template-areas:"amount          qr         " "key key" "setting-url setting-url" "buttons     buttons    " "charge      charge     "}main>#key{margin:0;justify-self:center}main>#setting-url{text-align:center}main>#buttons{justify-content:center;margin-bottom:0}main>#amount{align-self:center}main>#charge>#store{display:flex;flex-direction:column;align-items:center}}@media (max-width:430px){main{grid-template-rows:auto auto auto auto auto auto;grid-template-columns:auto;grid-template-areas:"qr" "key" "setting-url" "buttons" "amount" "charge"}}
\ No newline at end of file
diff --git a/pass/public/styles/key.less b/pass/public/styles/key/key.less
similarity index 51%
rename from pass/public/styles/key.less
rename to pass/public/styles/key/key.less
index 6bff593..907fd71 100644
--- a/pass/public/styles/key.less
+++ b/pass/public/styles/key/key.less
@@ -1,4 +1,6 @@
-@import "./misc/vars.less";
+@import "../misc/vars.less";
+@import "./checkout-amount.less";
+@import "./checkout-payment.less";
 
 @max-width: 980px;
 @key-breakpoint-1: 770px;
@@ -92,83 +94,6 @@ main {
         line-height: 1.5;
       }
     }
-
-    > #checkout {
-      > h2 {
-        margin: 0 0 1rem;
-        text-align: center;
-        border-bottom: 1px solid @color-main;
-      }
-      #checkout-amount {
-        @checkout-amount-breakpoint-1: 800px;
-        @checkout-amount-breakpoint-2: 470px;
-        display: grid;
-        grid-template-columns: 1fr 1fr 1fr;
-        gap: 1rem;
-        justify-items: center;
-        align-items: center;
-        > a {
-          text-decoration: none;
-          color: inherit;
-          display: grid;
-          width: max-content;
-          grid-template-columns: 7em 3.5em;
-          grid-template-rows: 2.5em 2em;
-          height: max-content;
-          > .checkout-amount {
-            font-size: 2rem;
-            font-weight: bold;
-            line-height: 1;
-            display: flex;
-            width: 100%;
-            height: 100%;
-            align-items: center;
-            justify-content: center;
-            border-top-left-radius: 10px;
-            border: 1px solid rgb(255, 127, 0);
-            border-bottom: 0;
-            border-right: 0;
-          }
-          > .checkout-duration {
-            width: 100%;
-            height: 100%;
-            border-bottom-left-radius: 10px;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            border: 1px solid rgb(255, 127, 0);
-            border-top: 0;
-            border-right: 0;
-          }
-          > .checkout-cost {
-            grid-row: span 2;
-            justify-content: center;
-            background-color: rgb(255, 127, 0);
-            color: white;
-            font-size: 1.7rem;
-            align-items: center;
-            border-top-right-radius: 10px;
-            border-bottom-right-radius: 10px;
-            border: 1px solid rgb(255, 127, 0);
-            border-left: 0;
-            display: flex;
-            width: 100%;
-            height: 100%;
-            align-self: center;
-          }
-        }
-        @media (max-width: @checkout-amount-breakpoint-1) {
-          grid-template-columns: 1fr 1fr;
-        }
-        @media (max-width: @checkout-amount-breakpoint-2) {
-          grid-template-columns: 1fr;
-          > a {
-            width: 100%;
-            grid-template-columns: 1fr 3em;
-          }
-        }
-      }
-    }
   }
 
   @media (max-width: @key-breakpoint-1) {
diff --git a/pass/resources/js/checkout.js b/pass/resources/js/checkout.js
index 111b137..fad6fc3 100644
--- a/pass/resources/js/checkout.js
+++ b/pass/resources/js/checkout.js
@@ -1,242 +1,29 @@
-const uuid_generator = require("uuid");
-const BlindSignature = require("blind-signatures");
-
-var metager_pass_order_id = null;
-var metager_pass_expires_at = null;
-var metager_pass_sales_receipts = [];
-var metager_pass_encrypted_sales_receipts = []; // Stores the encrypted sales receipts that get signed by our server after successful purchase
-var metager_pass_encrypted_sales_receipts_r = []; // Those are the secrets for decrypting the signed sales_receipts; Should never leave the clients computer
-var metager_pass_signatures = [];
-
-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 = parseInt(document.querySelector("input[name=amount]").value);
-  let unit_size = parseInt(
-    document.querySelector("input[name=unit_size]").value
-  );
-  let N = document.querySelector("input[name=public_key_n]").value;
-  let E = document.querySelector("input[name=public_key_e]").value;
-
-  let ticket_count = (amount * unit_size) / 50;
-  if (ticket_count % 1 !== 0) {
-    console.error(
-      "This should not happen. Ticket count should only produce integer values"
-    );
-    return;
-  }
-
-  for (let i = 0; i < ticket_count; i++) {
-    let uuid = uuid_generator.v4();
-    metager_pass_sales_receipts.push(uuid);
-    let { blinded, r } = BlindSignature.blind({
-      message: uuid,
-      N: N,
-      E: E,
-    });
-    metager_pass_encrypted_sales_receipts.push(blinded.toString());
-    metager_pass_encrypted_sales_receipts_r.push(r.toString());
-  }
-
-  current_step_container.classList.remove("current");
-  current_step_container.classList.add("finished");
-  next_step_container.classList.add("current");
-
-  two_execute_payment();
-}
-
-function two_execute_payment() {
-  let current_step_container = document.getElementById("execute-payment");
-  let development_handler_button = document.getElementById(
-    "payment_method_development"
-  );
-  if (development_handler_button) {
-    let development_handler = require("./checkout_development");
-    development_handler_button.addEventListener("click", () => {
-      development_handler(metager_pass_encrypted_sales_receipts);
-    });
-  }
-
-  let paypal_handler = require("./checkout_paypal"); // Add handler for PayPal Checkout
-  document
-    .getElementById("payment_method_paypal")
-    .addEventListener("click", () => {
-      paypal_handler(metager_pass_encrypted_sales_receipts);
-    });
-
-  current_step_container.addEventListener("payment-complete", (e) => {
-    current_step_container.classList.remove("current");
-    current_step_container.classList.add("finished");
-    three_verify_and_decrypt(e.detail);
-  });
-}
-
-function three_verify_and_decrypt(signatures) {
-  let current_step_container = document.getElementById("verify-and-decrypt");
-  let next_step_container = document.getElementById("finish-purchase");
-
-  current_step_container.classList.add("current");
-  metager_pass_order_id = signatures.order_id;
-  let dayjs = require("dayjs");
-  metager_pass_expires_at = dayjs(signatures.expires_at).format("YYYY-MM");
-
-  let N = document.querySelector("input[name=public_key_n]").value;
-  let E = document.querySelector("input[name=public_key_e]").value;
-  // Decrypting/Unblinding Signatures
-  for (let i = 0; i < signatures.signatures.length; i++) {
-    metager_pass_signatures.push(
-      BlindSignature.unblind({
-        signed: signatures.signatures[i],
-        N: N,
-        r: metager_pass_encrypted_sales_receipts_r[i],
-      }).toString()
-    );
-  }
-
-  // Verify Signatures
-  // ToDo do something with invalid signatures
-  for (let i = 0; i < metager_pass_sales_receipts.length; i++) {
-    let verification = BlindSignature.verify({
-      unblinded: metager_pass_signatures[i],
-      N: N,
-      E: E,
-      message: metager_pass_sales_receipts[i],
-    });
-    console.log(metager_pass_sales_receipts[i] + " => " + verification);
-  }
-
-  current_step_container.classList.remove("current");
-  current_step_container.classList.add("finished");
-  four_finish_purchase();
-}
-
-function four_finish_purchase() {
-  let current_step_container = document.getElementById("finish-purchase");
-  current_step_container.classList.add("current");
-
-  // Prepare Voucher download
-  let voucher_link = document.getElementById("voucher-link");
-  let voucher_data = {
-    expiration_month: metager_pass_expires_at,
-    receipts: [],
-  };
-  for (let i = 0; i < metager_pass_sales_receipts.length; i++) {
-    voucher_data.receipts.push({
-      receipt: metager_pass_sales_receipts[i],
-      signature: metager_pass_signatures[i],
-    });
-  }
-  voucher_data =
-    "data:application/mgpass;charset=utf-8;base64," +
-    btoa(JSON.stringify(voucher_data, null, 4));
-  voucher_link.href = voucher_data;
-  voucher_link.download = "mgpass_coupon.json";
-
-  // Create redeem data
-  let order_month = require("dayjs")
-    .unix(metager_pass_order_id.substr(0, 10))
-    .format("YYYY-MM");
-  let redeem_data = {
-    expiration_month: metager_pass_expires_at,
-    generation_month: order_month,
-    metager_pass_codes: [],
-  };
-  for (let i = 0; i < metager_pass_sales_receipts.length; i++) {
-    redeem_data.metager_pass_codes.push({
-      code: metager_pass_sales_receipts[i],
-      signature: metager_pass_signatures[i],
-    });
-  }
-
-  // Make Create Key button work
-  let create_key_button = document.getElementById("create-key-button");
-  create_key_button.addEventListener("pointerdown", e => {
-    if (e.target.disabled) {
-      return;
-    } else {
-      e.target.disabled = true;
-    }
-
-    fetch("/redeem/create", {
-      method: "POST",
-      headers: {
-        "Content-Type": "application/json;charset=utf-8",
-      },
-      body: JSON.stringify(redeem_data),
-    }).then(response => {
-      if (!response.ok) {
-        return response.json().then(json_response => {
-          console.error(json_response);
-          for (let i = 0; i < json_response.errors.length; i++) {
-            let error_message = document.createElement("li");
-            error_message.textContent = json_response.errors[i].msg
-            document.querySelector("#create-key .errors ul").appendChild(error_message);
-          }
-          e.target.disabled = false;
-          return Promise.reject("Error while creating a new MetaGer-Pass Key");
-        });
-      } else {
-        return response.json()
-      }
-    })
-      .then(response => {
-        let key = response.metager_pass_key;
-        // Prefill the recharge existing key button
-        let metager_pass_key_input = document.getElementById("metager-pass-key");
-        metager_pass_key_input.value = key.key;
-        document.getElementById("metager-pass-key").addEventListener("change", e => {
-          // When the user changes the generated key unlock the generate button again
-          create_key_button.disabled = false;
-        });
-        // Select The recharge existing key tab and show the correct text
-        document.getElementById("recharge-existing-key").classList.add("hidden");
-        document.getElementById("recharge-new-key").classList.remove("hidden");
-        document.getElementById("recharge-key-radio").checked = true;
-      });
-  });
-
-  // Make Redeem Button Work
-  let redeem_button = document.getElementById("recharge-key-button");
-  redeem_button.addEventListener("pointerdown", e => {
-    if (e.target.disabled) {
-      return;
-    } else {
-      e.target.disabled = true;
-      document.getElementById("metager-pass-key").disabled = true;
-    }
-    let post_data = { ...redeem_data };
-    post_data.metager_pass_key = document.getElementById("metager-pass-key").value;
-    fetch("/redeem", {
-      method: "POST",
-      headers: {
-        "Content-Type": "application/json;charset=utf-8",
-      },
-      body: JSON.stringify(post_data),
-    }).then(response => response.json())
-      .then(response => {
-        // Show Summary and fill data for it
-        let dayjs = require('dayjs');
-        document.querySelector("#redeem-successful .key").textContent = response.metager_pass_key.key;
-        document.querySelector("#redeem-successful .searches").textContent = response.metager_pass_key.searches;
-        document.querySelector("#redeem-successful .valid-until").textContent = dayjs(response.metager_pass_key.expire_at).format("DD.MM.YYYY HH:mm:ss");
-        current_step_container.classList.remove("current");
-        current_step_container.classList.add("finished");
-        five_summary();
-        console.log(response);
-      }).catch(reason => {
-        e.target.disabled = false;
-        document.getElementById("metager-pass-key").disabled = false;
-        console.error(reason);
+const paypal_client = require("@paypal/paypal-js");
+console.log(paypal_client);
+
+paypal_client
+  .loadScript({
+    "client-id": document.querySelector("input[name=paypal-client-id]").value,
+    components: ["buttons", "funding-eligibility"],
+  })
+  .then((paypal) => {
+    paypal.getFundingSources().forEach((fundingSource) => {
+      let button = paypal.Buttons({
+        style: {
+          color: "white",
+          height: 50,
+        },
+        fundingSource: fundingSource,
       });
+      if (button.isEligible()) {
+        console.log("eligible");
+        let funding_source_element = document.createElement("div");
+        funding_source_element.classList.add("funding_source");
+        funding_source_element.id = fundingSource;
+        document
+          .getElementById("paypal-payments")
+          .appendChild(funding_source_element);
+        button.render("#" + fundingSource);
+      }
+    });
   });
-}
-
-function five_summary() {
-  let current_step_container = document.getElementById("summary");
-  current_step_container.classList.add("current");
-}
diff --git a/pass/routes/key.js b/pass/routes/key.js
index 43382dc..7f3938b 100644
--- a/pass/routes/key.js
+++ b/pass/routes/key.js
@@ -1,5 +1,7 @@
 var express = require("express");
 var router = express.Router();
+const { param, validationResult } = require("express-validator");
+const config = require("config");
 
 var Key = require("../app/Key");
 
@@ -9,8 +11,65 @@ router.get("/create", function (req, res, next) {
   });
 });
 
-router.get("/:key/:amount?", async (req, res) => {
+router.use("/:key", param("key").isUUID(4), (req, res, next) => {
+  // Input Validation
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(400).json({ errors: errors.array() });
+  }
+  next("route");
+});
+
+// Basic account page
+router.get("/:key", async (req, res) => {
   let key = req.params.key;
+  let metager_url =
+    "https://metager.de/meta/settings/load-settings?key=" +
+    encodeURIComponent(key);
+  let QRCode = require("qrcode");
+
+  let qr_data_uri = await QRCode.toDataURL(metager_url);
+  res.render("key", {
+    created_new: req.query.new === "true" ? true : false,
+    key: {
+      key: key,
+      settings_url: metager_url,
+      qr: qr_data_uri,
+    },
+    js: [],
+  });
+});
+
+/**
+ * Validate Amount field for checkout process
+ */
+router.use(
+  "/:key/checkout/:amount?",
+  param("amount")
+    .optional({ checkFalsy: true })
+    .isInt()
+    .toInt()
+    .isIn([300, 600, 900, 1200, 1800, 3600]),
+  (req, res, next) => {
+    // Input Validation
+    const errors = validationResult(req);
+    if (!errors.isEmpty()) {
+      return res.status(400).json({ errors: errors.array() });
+    }
+    next("route");
+  }
+);
+
+router.get("/:key/checkout/:amount?", async (req, res) => {
+  let key = req.params.key;
+  let checkout_data = {
+    amount: req.params.amount === 0 ? undefined : req.params.amount,
+    paypal: {
+      client_id: config.get(
+        `payments.paypal.${process.env.NODE_ENV}.client_id`
+      ),
+    },
+  };
 
   let metager_url =
     "https://metager.de/meta/settings/load-settings?key=" +
@@ -18,7 +77,6 @@ router.get("/:key/:amount?", async (req, res) => {
   let QRCode = require("qrcode");
 
   let qr_data_uri = await QRCode.toDataURL(metager_url);
-  console.log(req.query.new === "true");
   res.render("key", {
     created_new: req.query.new === "true" ? true : false,
     key: {
@@ -26,6 +84,8 @@ router.get("/:key/:amount?", async (req, res) => {
       settings_url: metager_url,
       qr: qr_data_uri,
     },
+    js: checkout_data.amount !== undefined ? ["/js/checkout.js"] : [],
+    checkout: checkout_data,
   });
 });
 
diff --git a/pass/views/key.ejs b/pass/views/key.ejs
index f5daf02..9b23df9 100644
--- a/pass/views/key.ejs
+++ b/pass/views/key.ejs
@@ -1,4 +1,4 @@
-<%- include('templates/page_header', {css: ["/styles/key.css"], js: []}); %>
+<%- include('templates/page_header', {css: ["/styles/key/key.css"], js: js}); %>
 
     <img id="qr" src="<%= key.qr %> "></img>
     <div id="key">
@@ -24,9 +24,8 @@
         <div class="amount">0</div>
         <div>Suchanfragen</div>
     </div>
-
     <div id="charge">
-        <% if (created_new) { %>
+        <%_ if (created_new) { _%>
         <div id="store">
             <h2>So gehts weiter:</h2>
             <p>
@@ -37,51 +36,65 @@
             </p>
             <a class="button" href="/key/<%= key.key %>">Schlüssel jetzt aufladen</a>
         </div>
-        <% }else { %>
+        <%_ }else { _%>
         <div id="checkout">
             <h2>Suchanfragen auffüllen</h2>
             <div id="charge-steps">
                 <div id="charge-steps-amount"></div>
             </div>
-            <% if (typeof checkout === "undefined" || !checkout.amount) { %>
-            <div id="checkout-amount">
-                <a href="/key/<%= key.key %>/300">
+            <div id="checkout-amount" <%_ if (typeof checkout !== "undefined" && typeof checkout.amount !== "undefined") { _%>class="single"<%_ } _%>>
+                <%_ if (typeof checkout !== "undefined" && typeof checkout.amount !== "undefined") { _%>
+                <a href="#checkout-amount">
+                    <span class="checkout-amount"><%= checkout.amount %></span>
+                    <span class="checkout-cost"><%= checkout.amount / 300 * 5%>€</span>
+                    <span class="checkout-duration">~<%= checkout.amount / 300 %> <% if(checkout.amount > 300) { %> Monate<% }else { %>Monat<% } %>*</span>
+                </a>
+                <%_ }else { _%>
+                <a href="/key/<%= key.key %>/checkout/300#payment">
                     <span class="checkout-amount">300</span>
                     <span class="checkout-cost">5€</span>
                     <span class="checkout-duration">~1 Monat*</span>
                 </a>
-                <a href="/key/<%= key.key %>/600">
+                <a href="/key/<%= key.key %>/checkout/600#payment">
                     <span class="checkout-amount">600</span>
                     <span class="checkout-cost">10€</span>
                     <span class="checkout-duration">~2 Monate*</span>
                 </a>
-                <a href="/key/<%= key.key %>/900">
+                <a href="/key/<%= key.key %>/checkout/900#payment">
                     <span class="checkout-amount">900</span>
                     <span class="checkout-cost">15€</span>
                     <span class="checkout-duration">~3 Monate*</span>
                 </a>
-                <a href="/key/<%= key.key %>/1200">
+                <a href="/key/<%= key.key %>/checkout/1200#payment">
                     <span class="checkout-amount">1200</span>
                     <span class="checkout-cost">20€</span>
                     <span class="checkout-duration">~4 Monate*</span>
                 </a>
-                <a href="/key/<%= key.key %>/1800">
+                <a href="/key/<%= key.key %>/checkout/1800#payment">
                     <span class="checkout-amount">1800</span>
                     <span class="checkout-cost">30€</span>
                     <span class="checkout-duration">~6 Monate*</span>
                 </a>
-                <a href="/key/<%= key.key %>/3600">
+                <a href="/key/<%= key.key %>/checkout/3600#payment">
                     <span class="checkout-amount">3600</span>
                     <span class="checkout-cost">60€</span>
                     <span class="checkout-duration">~1 Jahr*</span>
                 </a>
+                <%_ } _%>
             </div>
             <p>* Die angegebenen Zeiträume sind Schätzungen, die auf unseren Erfahrungswerten basieren und sollen einen Anhaltspunkt geben, wie viele Suchanfragen benötigt werden.</p>
-            <% } %>
         </div>
-        <% } %>
+        <%_ if (typeof checkout !== "undefined" && typeof checkout.amount !== "undefined") { _%>
+        <div id="payment">
+            <h2>Zahlung durchführen</h2>
+            <input type="hidden" name="paypal-client-id" value="<%= checkout.paypal.client_id %>">
+            <label for="payment-group-paypal">
+                <div class="info">*PayPal, Kreditkarte, Girokarte, ...</div>
+            </label>
+            <input type="radio" name="payment-group" id="payment-group-paypal" selected>
+            <div id="paypal-payments" class="payment-group"></div>
+        </div>
+        <%_ } _%>
+        <%_ } _%>
     </div>
-
-
-
     <%- include('templates/page_footer'); -%>
\ No newline at end of file
-- 
GitLab