From bfacdc8e58d8cb2ad55a944fb8f26885457eebb2 Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@hebeler.club>
Date: Wed, 21 Dec 2022 14:46:03 +0100
Subject: [PATCH] added refund form

---
 pass/public/images/money.svg                 | 167 +++++++++++++++++++
 pass/public/styles/orders/order.css          |   2 +-
 pass/public/styles/orders/order.less         |  10 +-
 pass/public/styles/orders/refund.css         |   1 +
 pass/public/styles/orders/refund.less        |  23 +++
 pass/routes/key.js                           |   2 +-
 pass/routes/{orders.js => orders/invoice.js} | 126 +-------------
 pass/routes/orders/orders.js                 | 123 ++++++++++++++
 pass/routes/orders/refund.js                 |  24 +++
 pass/views/orders/invoice.ejs                |  15 +-
 pass/views/orders/order.ejs                  |  39 ++---
 pass/views/orders/order_details.ejs          |  14 ++
 pass/views/orders/refund.ejs                 |  21 +++
 13 files changed, 405 insertions(+), 162 deletions(-)
 create mode 100644 pass/public/images/money.svg
 create mode 100644 pass/public/styles/orders/refund.css
 create mode 100644 pass/public/styles/orders/refund.less
 rename pass/routes/{orders.js => orders/invoice.js} (61%)
 create mode 100644 pass/routes/orders/orders.js
 create mode 100644 pass/routes/orders/refund.js
 create mode 100644 pass/views/orders/order_details.ejs
 create mode 100644 pass/views/orders/refund.ejs

diff --git a/pass/public/images/money.svg b/pass/public/images/money.svg
new file mode 100644
index 0000000..8c596ce
--- /dev/null
+++ b/pass/public/images/money.svg
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="900"
+   height="900"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="cash1.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.35"
+     inkscape:cx="350"
+     inkscape:cy="520"
+     inkscape:document-units="px"
+     inkscape:current-layer="g5229"
+     showgrid="false"
+     inkscape:window-width="1366"
+     inkscape:window-height="715"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Capa 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-152.36218)">
+    <g
+       transform="matrix(12.774324,0,0,13.452647,-604.34066,-4031.2319)"
+       id="g5229">
+      <g
+         id="g5859"
+         transform="matrix(0.6263025,0,0,0.6263025,33.712087,192.29508)">
+        <g
+           transform="matrix(-0.70666232,0,0,0.70666232,-6.0929638,-40.117016)"
+           id="g5231">
+          <g
+             id="g5257"
+             transform="translate(-6.4285711,-84.642857)" />
+          <g
+             transform="translate(-6.4285711,-92.64286)"
+             id="g5273">
+            <g
+               id="g5275">
+              <path
+                 style="fill:#ffffff;stroke:none"
+                 d="m -190.89286,425.75504 45,30.53571 0.53572,0.35715 8.92857,-3.39286 18.92857,-8.75 17.14286,-8.75 18.571426,-10 27.678571,-16.60714 7.857143,-4.82143 -43.571429,-21.96429 -36.785711,19.46429 -17.67857,8.39285 -24.28572,8.75 z"
+                 id="path5277"
+                 inkscape:connector-curvature="0"
+                 transform="translate(6.4285711,84.642857)" />
+              <path
+                 style="fill:#005500;stroke:none"
+                 d="m -83.203841,466.29464 c -55.310089,31.49436 -76.664149,35.62903 -103.590799,44.23291 l 47.49879,31.83852 c 35.8553,-14.36254 69.040859,-32.88026 101.072639,-53.21721 l -44.98063,-22.85422 z m -0.31477,1.63947 41.70702,21.18196 c -29.70502,18.85968 -63.729829,37.8978 -96.980629,51.21706 l -44.03632,-29.51047 c 24.97073,-7.97891 48.01751,-13.68191 99.309929,-42.88855 z"
+                 id="path5279"
+                 inkscape:connector-curvature="0" />
+            </g>
+          </g>
+          <g
+             id="g5281"
+             transform="translate(-6.4285711,-96.64286)">
+            <g
+               id="g5283">
+              <path
+                 transform="translate(6.4285711,84.642857)"
+                 inkscape:connector-curvature="0"
+                 id="path5285"
+                 d="m -190.89286,425.75504 45,30.53571 0.53572,0.35715 8.92857,-3.39286 18.92857,-8.75 17.14286,-8.75 18.571426,-10 27.678571,-16.60714 7.857143,-4.82143 -43.571429,-21.96429 -36.785711,19.46429 -17.67857,8.39285 -24.28572,8.75 z"
+                 style="fill:#ffffff;stroke:none" />
+              <path
+                 inkscape:connector-curvature="0"
+                 id="path5287"
+                 d="m -83.203841,466.29464 c -55.310089,31.49436 -76.664149,35.62903 -103.590799,44.23291 l 47.49879,31.83852 c 35.8553,-14.36254 69.040859,-32.88026 101.072639,-53.21721 l -44.98063,-22.85422 z m -0.31477,1.63947 41.70702,21.18196 c -29.70502,18.85968 -63.729829,37.8978 -96.980629,51.21706 l -44.03632,-29.51047 c 24.97073,-7.97891 48.01751,-13.68191 99.309929,-42.88855 z"
+                 style="fill:#005500;stroke:none" />
+            </g>
+          </g>
+          <g
+             transform="translate(-6.4285711,-100.64286)"
+             id="g5289">
+            <g
+               id="g5291">
+              <path
+                 style="fill:#ffffff;stroke:none"
+                 d="m -190.89286,425.75504 45,30.53571 0.53572,0.35715 8.92857,-3.39286 18.92857,-8.75 17.14286,-8.75 18.571426,-10 27.678571,-16.60714 7.857143,-4.82143 -43.571429,-21.96429 -36.785711,19.46429 -17.67857,8.39285 -24.28572,8.75 z"
+                 id="path5293"
+                 inkscape:connector-curvature="0"
+                 transform="translate(6.4285711,84.642857)" />
+              <path
+                 style="fill:#005500;stroke:none"
+                 d="m -83.203841,466.29464 c -55.310089,31.49436 -76.664149,35.62903 -103.590799,44.23291 l 47.49879,31.83852 c 35.8553,-14.36254 69.040859,-32.88026 101.072639,-53.21721 l -44.98063,-22.85422 z m -0.31477,1.63947 41.70702,21.18196 c -29.70502,18.85968 -63.729829,37.8978 -96.980629,51.21706 l -44.03632,-29.51047 c 24.97073,-7.97891 48.01751,-13.68191 99.309929,-42.88855 z"
+                 id="path5295"
+                 inkscape:connector-curvature="0" />
+            </g>
+          </g>
+          <g
+             id="g5297"
+             transform="translate(0,-14)">
+            <path
+               inkscape:connector-curvature="0"
+               id="path5299"
+               d="m -180.71429,422.00504 c 0,0 5.71429,3.92857 6.42858,7.14286 0.71428,3.21428 23.92857,16.78571 23.92857,16.78571 l 11.42857,0 7.85714,-1.78571 19.64286,-9.28572 15.714284,-6.78571 18.928572,-10.35715 17.857142,-10.71428 0,-11.42857 -25.714285,-12.14286 -12.857143,1.78571 -11.78571,7.14286 -28.92858,12.85714 -37.5,13.21429 z"
+               style="fill:#ffffff;stroke:none" />
+            <g
+               id="g5301"
+               style="fill:#165016">
+              <path
+                 style="fill:#165016;stroke:none"
+                 d="M -89.65625,378.78125 C -144.56747,408.797 -165.76753,412.73755 -192.5,420.9375 l 47.15625,30.34375 C -109.74702,437.59301 -76.800778,419.94466 -45,400.5625 L -89.65625,378.78125 z M -78.75,387.3125 -63.875,395 c -0.955399,1.91634 -1.5,4.03707 -1.5,6.28125 0,2.55026 0.692363,4.95727 1.90625,7.0625 -21.463826,13.90825 -45.38187,24.75337 -69.875,34.875 -2.7124,-2.49005 -6.6914,-4.0625 -11.125,-4.0625 -3.62367,0 -6.93009,1.06679 -9.5,2.8125 l -17.4375,-11.3125 c 1.12739,-1.77781 1.75,-3.80691 1.75,-5.96875 0,-2.267 -0.7069,-4.37955 -1.9375,-6.21875 19.45498,-5.13454 54.12354,-20.98019 72.03125,-29.9375 2.577326,1.27086 5.824082,2.03125 9.375,2.03125 4.567016,0 8.656963,-1.26067 11.4375,-3.25 z"
+                 id="path5303"
+                 inkscape:connector-curvature="0" />
+              <path
+                 style="color:#000000;fill:#165016;stroke:none;stroke-width:0.07914069;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+                 d="m -146.36813,410.11743 c -1.58551,7.11414 1.20048,14.03422 8.40277,18.38878 7.20233,4.35457 17.17174,5.14646 26.66229,2.81172 z"
+                 id="path5305"
+                 inkscape:connector-curvature="0"
+                 sodipodi:nodetypes="cscc" />
+              <path
+                 sodipodi:nodetypes="cscc"
+                 inkscape:connector-curvature="0"
+                 id="path5307"
+                 d="m -82.423075,417.96506 c 1.506079,-6.55188 -1.140337,-12.92505 -7.981804,-16.93546 -6.841505,-4.01042 -16.311461,-4.73972 -25.326551,-2.5895 z"
+                 style="color:#000000;fill:#165016;stroke:none;stroke-width:0.07402207;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+            </g>
+          </g>
+          <path
+             style="fill:#005500;stroke:none"
+             d="m -145.17857,393.79076 40.71428,24.99999 0.14986,10.67087 20.57879,-10.40022 0.164211,-11.87779 -39.285711,-23.39286 z"
+             id="path5309"
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="ccccccc" />
+        </g>
+        <path
+           sodipodi:nodetypes="cccccccccccccc"
+           inkscape:connector-curvature="0"
+           id="path5857"
+           d="m 127.8464,248.20324 -0.23853,1.7609 -1.53542,1.40864 2.01358,1.25366 -1.27645,1.61437 2.1288,1.2319 -25.32182,16.26715 12.76389,-10.1861 -8.72348,4.49246 11.73919,-9.55072 -8.71936,4.63275 10.98808,-8.90621 -8.96125,5.69782 z"
+           style="fill:#165016;stroke:none" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/pass/public/styles/orders/order.css b/pass/public/styles/orders/order.css
index 4961101..67d1c37 100644
--- a/pass/public/styles/orders/order.css
+++ b/pass/public/styles/orders/order.css
@@ -1 +1 @@
-#order{padding:1rem 0;grid-area:content}#order .breadcrumbs{display:flex;gap:.5rem;padding:0;list-style-type:disclosure-closed;font-size:clamp(.7rem, 4w, 1rem)}#order .breadcrumbs li{margin-left:1rem}#order .breadcrumbs li:first-child{margin-left:0;list-style-type:none}#order .breadcrumbs li a{color:#777}#order #order-details{display:grid;grid-template-columns:1fr 4rem 4rem;text-align:right;padding:2rem 0}#order #order-details>div.heading{text-align:left;border-bottom:1px solid #ff7f00;font-weight:bold;padding-bottom:.2rem}#order #order-details>div:not(.heading){padding:.5rem 0}#order #order-details>div.sum{font-weight:bold}#order>h3{text-align:center}#order #order-buttons{display:grid;grid-template-columns:1fr auto;justify-items:end;gap:.5rem}#order #order-buttons .button{font-size:.8rem;display:flex;align-items:center;justify-content:center;line-height:1;gap:.5rem;padding:.5rem}#order #order-buttons .button img{height:1rem;filter:brightness(0) invert(1)}@media (max-width:500px){#order #order-buttons{grid-template-columns:1fr}#order #order-buttons>a.button{width:100%;padding:1rem}}#order #invoice #download-receipt>a.button{display:flex;margin:0 auto;justify-content:center;align-items:center;gap:.5rem;padding:.5rem}#order #invoice #download-receipt>a.button>img{height:1.5em}#order #invoice #invoice-form #invoice-form-fields{display:grid;gap:1rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field{display:grid;gap:.5rem;font-weight:bold;color:#777;font-size:.7rem;place-content:center;text-align:center}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #email,#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #name{padding:.5rem;border-radius:5px;font-size:.8rem;width:13rem;text-align:center}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #address{padding:.5rem;border-radius:5px;line-height:2;text-align:center;width:13rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field button{display:flex;align-items:center;gap:.5rem;padding:.5rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field button img{height:1.2rem}#order #invoice #invoice-form .storage-time{font-size:.8rem;text-align:center}
\ No newline at end of file
+#order{padding:1rem 0;grid-area:content}#order .breadcrumbs{display:flex;gap:.5rem;padding:0;list-style-type:disclosure-closed;font-size:clamp(.7rem, 4vw, 1rem)}#order .breadcrumbs li{margin-left:1rem}#order .breadcrumbs li:first-child{margin-left:0;list-style-type:none}#order .breadcrumbs li a{color:#777}#order #order-details{display:grid;grid-template-columns:1fr 4rem 4rem;text-align:right;padding:2rem 0}#order #order-details>div.heading{text-align:left;border-bottom:1px solid #ff7f00;font-weight:bold;padding-bottom:.2rem}#order #order-details>div:not(.heading){padding:.5rem 0}#order #order-details>div.sum{font-weight:bold}#order>h3{text-align:center}#order #order-buttons{display:grid;grid-template-columns:1fr auto auto;justify-items:end;gap:.5rem}#order #order-buttons .button{font-size:.8rem;display:flex;align-items:center;justify-content:center;line-height:1;gap:.5rem;padding:.5rem}#order #order-buttons .button img{height:1rem}#order #order-buttons .button img.order-receipt{filter:brightness(0) invert(1)}@media (max-width:842px){#order #order-buttons{grid-template-columns:1fr}#order #order-buttons>a.button{width:100%;padding:1rem}}#order #invoice #download-receipt>a.button{display:flex;margin:0 auto;justify-content:center;align-items:center;gap:.5rem;padding:.5rem}#order #invoice #download-receipt>a.button>img{height:1.5em}#order #invoice #invoice-form #invoice-form-fields{display:grid;gap:1rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field{display:grid;gap:.5rem;font-weight:bold;color:#777;font-size:.7rem;place-content:center;text-align:center}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #email,#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #name{padding:.5rem;border-radius:5px;font-size:.8rem;width:13rem;text-align:center}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field #address{padding:.5rem;border-radius:5px;line-height:2;text-align:center;width:13rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field button{display:flex;align-items:center;gap:.5rem;padding:.5rem}#order #invoice #invoice-form #invoice-form-fields .invoice-form-field button img{height:1.2rem}#order #invoice #invoice-form .storage-time{font-size:.8rem;text-align:center}
\ No newline at end of file
diff --git a/pass/public/styles/orders/order.less b/pass/public/styles/orders/order.less
index 57589c8..3dafbb1 100644
--- a/pass/public/styles/orders/order.less
+++ b/pass/public/styles/orders/order.less
@@ -6,7 +6,7 @@
     gap: 0.5rem;
     padding: 0;
     list-style-type: disclosure-closed;
-    font-size: clamp(0.7rem, 4w, 1rem);
+    font-size: clamp(0.7rem, 4vw, 1rem);
     li {
       margin-left: 1rem;
       &:first-child {
@@ -45,7 +45,7 @@
 
   #order-buttons {
     display: grid;
-    grid-template-columns: 1fr auto;
+    grid-template-columns: 1fr auto auto;
     justify-items: end;
     gap: 0.5rem;
     .button {
@@ -58,10 +58,12 @@
       padding: 0.5rem;
       img {
         height: 1rem;
-        filter: brightness(0) invert(1);
+        &.order-receipt {
+          filter: brightness(0) invert(1);
+        }
       }
     }
-    @media (max-width: 500px) {
+    @media (max-width: 842px) {
       grid-template-columns: 1fr;
       > a.button {
         width: 100%;
diff --git a/pass/public/styles/orders/refund.css b/pass/public/styles/orders/refund.css
new file mode 100644
index 0000000..fee50cd
--- /dev/null
+++ b/pass/public/styles/orders/refund.css
@@ -0,0 +1 @@
+#refund{grid-area:content}#refund #refund-form textarea{width:100%;max-width:25rem}#refund #refund-form button{display:flex;text-align:center;width:100%;max-width:25rem;align-items:center;justify-content:center;gap:.5rem;padding:.5rem;font-size:.8rem}#refund #refund-form button>img{height:2em}
\ No newline at end of file
diff --git a/pass/public/styles/orders/refund.less b/pass/public/styles/orders/refund.less
new file mode 100644
index 0000000..e58f62f
--- /dev/null
+++ b/pass/public/styles/orders/refund.less
@@ -0,0 +1,23 @@
+#refund {
+  grid-area: content;
+  #refund-form {
+    textarea {
+      width: 100%;
+      max-width: 25rem;
+    }
+    button {
+      display: flex;
+      text-align: center;
+      width: 100%;
+      max-width: 25rem;
+      align-items: center;
+      justify-content: center;
+      gap: 0.5rem;
+      padding: 0.5rem;
+      font-size: 0.8rem;
+      > img {
+        height: 2em;
+      }
+    }
+  }
+}
diff --git a/pass/routes/key.js b/pass/routes/key.js
index 958cfb9..cc93c47 100644
--- a/pass/routes/key.js
+++ b/pass/routes/key.js
@@ -3,7 +3,7 @@ var router = express.Router();
 const { param, validationResult } = require("express-validator");
 const config = require("config");
 
-var orderRouter = require("./orders");
+var orderRouter = require("./orders/orders");
 var checkout_router_paypal = require("./checkout/paypal");
 
 var Key = require("../app/Key");
diff --git a/pass/routes/orders.js b/pass/routes/orders/invoice.js
similarity index 61%
rename from pass/routes/orders.js
rename to pass/routes/orders/invoice.js
index 71b732d..822cda7 100644
--- a/pass/routes/orders.js
+++ b/pass/routes/orders/invoice.js
@@ -1,121 +1,11 @@
 var express = require("express");
 var router = express.Router({ mergeParams: true });
-const { param, body, validationResult } = require("express-validator");
-const Order = require("../app/Order");
-const config = require("config");
-
-const dayjs = require("dayjs");
-const OrderReceipt = require("../app/pdf/OrderReceipt");
-
-router.use("/", (req, res, next) => {
-  if (typeof req.cookies.order !== "undefined") {
-    req.data.form = {
-      "order-id": req.cookies.order,
-    };
-  }
-  console.log(req.cookies);
-  next();
-});
-router.get("/", (req, res, next) => {
-  req.data.page = "order";
-  req.data.css.push("/styles/orders/orders.css");
-  res.render("key", req.data);
-});
-
-router.post("/", (req, res, next) => {
-  console.log(req.body["order-id"]);
-  let matches = req.body["order-id"].match(/^(INV_)?(\d{14})$/);
-  if (!matches) {
-    req.data.page = "order";
-    req.data.css.push("/styles/orders/orders.css");
-    req.data.error = 400;
-    req.data.form = {
-      "order-id": req.body["order-id"],
-    };
-    res.render("key", req.data);
-  } else {
-    let order_id = matches[2];
-    Order.LOAD_ORDER_FROM_ID(order_id)
-      .then(/** @param {Order} order */ (order) => order.getKeyFromOrderLink())
-      .then((key) => {
-        if (key !== req.data.key.key) {
-          throw "Order is not connected to this key";
-        }
-        res.redirect(`/key/${req.data.key.key}/orders/${order_id}`);
-      })
-      .catch((reason) => {
-        req.data.page = "order";
-        req.data.css.push("/styles/orders/orders.css");
-        req.data.error = 404;
-        req.data.form = {
-          "order-id": req.body["order-id"],
-        };
-        res.render("key", req.data);
-      });
-  }
-});
-
-router.use("/:order_id", param("order_id").isInt(), (req, res, next) => {
-  const errors = validationResult(req);
-  if (!errors.isEmpty()) {
-    return res.status(404).render("error", {
-      message: "Order not found",
-      error: {
-        status: 404,
-      },
-    });
-  }
-  Order.LOAD_ORDER_FROM_ID(req.params.order_id)
-    .then((order) => {
-      res.cookie("order", order.getOrderID());
-      req.data.page = "order";
-      req.data.css.push("/styles/orders/order.css");
-      req.data.order = { order: order };
-      req.data.links.order_url = `/key/${req.data.key.key}/orders/${req.params.order_id}#order`;
-      req.data.links.receipt_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/pdf`;
-      req.data.links.invoice_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/invoice#invoice-form`;
-      if (req.data.order.order.isReceiptCreated()) {
-        req.data.order.download_invoice_url = `/key/${
-          req.data.key.key
-        }/orders/${req.data.order.order.getOrderID()}/invoice/download`;
-      }
-      next("route");
-    })
-    .catch((reason) => {
-      res.status(404).render("error", {
-        message: "Order not found",
-        error: {
-          status: 404,
-        },
-      });
-    });
-});
-
-router.get("/:order_id", (req, res) => {
-  res.render("key", req.data);
-});
 
-router.get("/:order_id/pdf", (req, res) => {
-  OrderReceipt.CREATE_ORDER_RECEIPT(req.data.order.order)
-    .then((data) => {
-      res
-        .status(200)
-        .header({
-          "Content-Type": "application/pdf",
-        })
-        .send(Buffer.concat(data));
-    })
-    .catch((reason) => {
-      res.locals.error = {
-        status: 500,
-        stack: JSON.stringify(reason, null, 4),
-      };
-      res.locals.message = "Error generating Order Receipt";
-      res.status(500).render("error");
-    });
-});
+const config = require("config");
+const { body, validationResult } = require("express-validator");
+const OrderReceipt = require("../../app/pdf/OrderReceipt");
 
-router.get("/:order_id/invoice", (req, res) => {
+router.get("/", (req, res) => {
   req.data.order.invoice = {
     params: {
       name: req.query.name || "",
@@ -132,7 +22,7 @@ router.get("/:order_id/invoice", (req, res) => {
 });
 
 router.post(
-  "/:order_id/invoice*",
+  "/*",
   body("email").isEmail({ domain_specific_validation: true }),
   body("address").isLength({ max: 1000 }),
   body("name").isLength({ max: 500 }),
@@ -159,7 +49,7 @@ router.post(
 );
 
 router.post(
-  "/:order_id/invoice",
+  "/",
   (req, res, next) => {
     // Check if admin parameter is set and if so if the user is authenticated
     if (req.body.admin) {
@@ -235,7 +125,7 @@ router.post(
 );
 
 router.post(
-  "/:order_id/invoice/create",
+  "/create",
   (req, res, next) => {
     // Check if admin parameter is set and if so if the user is authenticated
     if (req.data.admin) {
@@ -280,7 +170,7 @@ router.post(
   }
 );
 
-router.get("/:order_id/invoice/download", (req, res) => {
+router.get("/download", (req, res) => {
   if (req.data.order.order.isReceiptCreated()) {
     req.data.order.order
       .getReceipt()
diff --git a/pass/routes/orders/orders.js b/pass/routes/orders/orders.js
new file mode 100644
index 0000000..60b2372
--- /dev/null
+++ b/pass/routes/orders/orders.js
@@ -0,0 +1,123 @@
+var express = require("express");
+var router = express.Router({ mergeParams: true });
+
+const { param, validationResult } = require("express-validator");
+const Order = require("../../app/Order");
+const OrderReceipt = require("../../app/pdf/OrderReceipt");
+
+router.use("/", (req, res, next) => {
+  if (typeof req.cookies.order !== "undefined") {
+    req.data.form = {
+      "order-id": req.cookies.order,
+    };
+  }
+  console.log(req.cookies);
+  next();
+});
+router.get("/", (req, res, next) => {
+  req.data.page = "order";
+  req.data.css.push("/styles/orders/orders.css");
+  res.render("key", req.data);
+});
+
+router.post("/", (req, res, next) => {
+  console.log(req.body["order-id"]);
+  let matches = req.body["order-id"].match(/^(INV_)?(\d{14})$/);
+  if (!matches) {
+    req.data.page = "order";
+    req.data.css.push("/styles/orders/orders.css");
+    req.data.error = 400;
+    req.data.form = {
+      "order-id": req.body["order-id"],
+    };
+    res.render("key", req.data);
+  } else {
+    let order_id = matches[2];
+    Order.LOAD_ORDER_FROM_ID(order_id)
+      .then(/** @param {Order} order */ (order) => order.getKeyFromOrderLink())
+      .then((key) => {
+        if (key !== req.data.key.key) {
+          throw "Order is not connected to this key";
+        }
+        res.redirect(`/key/${req.data.key.key}/orders/${order_id}`);
+      })
+      .catch((reason) => {
+        req.data.page = "order";
+        req.data.css.push("/styles/orders/orders.css");
+        req.data.error = 404;
+        req.data.form = {
+          "order-id": req.body["order-id"],
+        };
+        res.render("key", req.data);
+      });
+  }
+});
+
+router.use("/:order_id", param("order_id").isInt(), (req, res, next) => {
+  const errors = validationResult(req);
+  if (!errors.isEmpty()) {
+    return res.status(404).render("error", {
+      message: "Order not found",
+      error: {
+        status: 404,
+      },
+    });
+  }
+  Order.LOAD_ORDER_FROM_ID(req.params.order_id)
+    .then((order) => {
+      res.cookie("order", order.getOrderID());
+      req.data.page = "order";
+      req.data.css.push("/styles/orders/order.css");
+      req.data.order = { order: order };
+      req.data.links.order_url = `/key/${req.data.key.key}/orders/${req.params.order_id}#order`;
+      req.data.links.receipt_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/pdf`;
+      req.data.links.invoice_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/invoice#invoice-form`;
+      req.data.links.refund_url = `/key/${req.data.key.key}/orders/${req.params.order_id}/refund#refund-form`;
+      if (req.data.order.order.isReceiptCreated()) {
+        req.data.order.download_invoice_url = `/key/${
+          req.data.key.key
+        }/orders/${req.data.order.order.getOrderID()}/invoice/download`;
+      }
+      next("route");
+    })
+    .catch((reason) => {
+      res.status(404).render("error", {
+        message: "Order not found",
+        error: {
+          status: 404,
+        },
+      });
+    });
+});
+
+router.get("/:order_id", (req, res) => {
+  res.render("key", req.data);
+});
+
+router.get("/:order_id/pdf", (req, res) => {
+  OrderReceipt.CREATE_ORDER_RECEIPT(req.data.order.order)
+    .then((data) => {
+      res
+        .status(200)
+        .header({
+          "Content-Type": "application/pdf",
+        })
+        .send(Buffer.concat(data));
+    })
+    .catch((reason) => {
+      res.locals.error = {
+        status: 500,
+        stack: JSON.stringify(reason, null, 4),
+      };
+      res.locals.message = "Error generating Order Receipt";
+      res.status(500).render("error");
+    });
+});
+
+let invoiceRouter = require("./invoice");
+router.use("/:order_id/invoice", invoiceRouter);
+
+let refundRouter = require("./refund");
+router.use("/:order_id/refund", refundRouter);
+
+module.exports = router;
diff --git a/pass/routes/orders/refund.js b/pass/routes/orders/refund.js
new file mode 100644
index 0000000..13b7a44
--- /dev/null
+++ b/pass/routes/orders/refund.js
@@ -0,0 +1,24 @@
+var express = require("express");
+var router = express.Router({ mergeParams: true });
+
+// Base URLÖ: /key/:key/orders/:order/refund
+router.use("/", (req, res, next) => {
+  let refund_count = Math.min(
+    req.data.key.charge,
+    req.data.order.order.getAmount(),
+    408
+  );
+  req.data.order.refund = {
+    count: refund_count,
+    amount:
+      (req.data.order.order.getPrice() / req.data.order.order.getAmount()) *
+      refund_count,
+  };
+  req.data.css.push("/styles/orders/refund.css");
+  next();
+});
+router.get("/", (req, res) => {
+  res.render("key", req.data);
+});
+
+module.exports = router;
diff --git a/pass/views/orders/invoice.ejs b/pass/views/orders/invoice.ejs
index 79343f4..2c584e0 100644
--- a/pass/views/orders/invoice.ejs
+++ b/pass/views/orders/invoice.ejs
@@ -5,20 +5,7 @@
         <li><a href="<%= links.order_url %>"><%= order.order.getOrderID() %></a></li>
         <li>Rechnung</li>
     </ul>
-    <div id="order-details">
-        <div class="heading item">Bestelldetails</div>
-        <div class="heading count">Anzahl</div>
-        <div class="heading price">Preis</div>
-        <div class="item">MetaGer Schlüssel: Suchanfragen (300x)</div>
-        <div class="count"><%= order.order.getAmount() / 300 %></div>
-        <div class="price"><%= order.order.getNettoPrice().toFixed(2) %> €</div>
-        <div class="item">MwSt. (<%= order.order.getVat() %> %)</div>
-        <div class="count"></div>
-        <div class="price"><%= order.order.getVatAmount().toFixed(2) %> €</div>
-        <div class="item sum">Gesamtbetrag</div>
-        <div class="count"></div>
-        <div class="price"><%= order.order.getPrice().toFixed(2) %> €</div>
-    </div>
+    <%- include("order_details") %>
 
     <form <% if(admin) { %>action="<%= order.invoice.create_invoice_url %>"<%_ } _%> method="POST" id="invoice-form">
         <%_ if(!order.invoice.success) { _%>
diff --git a/pass/views/orders/order.ejs b/pass/views/orders/order.ejs
index 48d0fc0..b8e0e6b 100644
--- a/pass/views/orders/order.ejs
+++ b/pass/views/orders/order.ejs
@@ -1,45 +1,36 @@
 <div id="order">
-    <%_ if(!order.invoice) { _%>
+    <%_ if(!order.invoice && !order.refund) { _%>
     <h2>Ihre Bestellung Nr. <%= order.order.getOrderID() %></h2>
     <ul class="breadcrumbs">
         <li><a href="<%= links.orders_url %>">Bestellungen</a></li>
         <li><%= order.order.getOrderID() %></li>
     </ul>
-    <div id="order-details">
-        <div class="heading item">Bestelldetails</div>
-        <div class="heading count">Anzahl</div>
-        <div class="heading price">Preis</div>
-        <div class="item">MetaGer Schlüssel: Suchanfragen (300x)</div>
-        <div class="count"><%= order.order.getAmount() / 300 %></div>
-        <div class="price"><%= order.order.getNettoPrice().toFixed(2) %> €</div>
-        <div class="item">MwSt. (<%= order.order.getVat() %> %)</div>
-        <div class="count"></div>
-        <div class="price"><%= order.order.getVatAmount().toFixed(2) %> €</div>
-        <div class="item sum">Gesamtbetrag</div>
-        <div class="count"></div>
-        <div class="price"><%= order.order.getPrice().toFixed(2) %> €</div>
-    </div>
+    <%- include("order_details") %>
     <h3>Vielen Dank für Ihren Einkauf!</h3>
     <div id="order-buttons">
         <a href="<%= links.receipt_url %>" target="_blank" class="button">
-            <img src="/images/download.svg" alt="" />
+            <img src="/images/download.svg" alt="" class="order-receipt" />
             <span>Auftragsbestätigung herunterladen</span>
         </a>
         <%_ if(!order.order.isReceiptCreated()) { _%>
         <a href="<%= links.invoice_url %>" class="button">
             <img src="/images/invoice.svg" alt="" />
-            <span>Rechnung beantragen</span>
+            <span>Rechnung anfragen</span>
         </a>
         <%_ } else { _%>
-        <div id="download-receipt">
-            <a href="<%= links.download_invoice_url %>" target="_blank" class="button">
-                <img src="/images/invoice.svg" alt="" />
-                <span>Rechnung herunterladen</span>
-            </a>
-        </div>
+        <a href="<%= links.download_invoice_url %>" target="_blank" class="button">
+            <img src="/images/invoice.svg" alt="" />
+            <span>Rechnung herunterladen</span>
+        </a>
         <%_ } _%>
+        <a href="<%= links.refund_url %>" class="button">
+            <img src="/images/money.svg" alt="" />
+            <span>Erstattung anfragen</span>
+        </a>
     </div>
-    <%_ } else { _%>
+    <%_ } else if(order.invoice) { _%>
     <%- include('./invoice', {}); %>
+    <%_ } else if(typeof order.refund !== "undefined") { _%>
+    <%- include('./refund') %>
     <%_ } _%>
 </div>
\ No newline at end of file
diff --git a/pass/views/orders/order_details.ejs b/pass/views/orders/order_details.ejs
new file mode 100644
index 0000000..24a1cce
--- /dev/null
+++ b/pass/views/orders/order_details.ejs
@@ -0,0 +1,14 @@
+<div id="order-details">
+    <div class="heading item">Bestelldetails</div>
+    <div class="heading count">Anzahl</div>
+    <div class="heading price">Preis</div>
+    <div class="item">MetaGer Schlüssel: Suchanfragen (300x)</div>
+    <div class="count"><%= order.order.getAmount() / 300 %></div>
+    <div class="price"><%= order.order.getNettoPrice().toFixed(2) %> €</div>
+    <div class="item">MwSt. (<%= order.order.getVat() %> %)</div>
+    <div class="count"></div>
+    <div class="price"><%= order.order.getVatAmount().toFixed(2) %> €</div>
+    <div class="item sum">Gesamtbetrag</div>
+    <div class="count"></div>
+    <div class="price"><%= order.order.getPrice().toFixed(2) %> €</div>
+</div>
\ No newline at end of file
diff --git a/pass/views/orders/refund.ejs b/pass/views/orders/refund.ejs
new file mode 100644
index 0000000..7d370ac
--- /dev/null
+++ b/pass/views/orders/refund.ejs
@@ -0,0 +1,21 @@
+<div id="refund">
+    <h2>Erstattung</h2>
+    <ul class="breadcrumbs">
+        <li><a href="<%= links.orders_url %>">Bestellungen</a></li>
+        <li><a href="<%= links.order_url %>"><%= order.order.getOrderID() %></a></li>
+        <li>Erstattung</li>
+    </ul>
+    <%- include("order_details") %>
+    <form id="refund-form">
+        <p>Sind Sie unzufrieden mit Ihrem Schlüssel? Das bedauern wir sehr! Selbstverständlich erstatten wir Ihnen in diesem Fall den Rechnungsbetrag. Gerne nehmen wir auch Ihre Kritik entgegen.</p>
+        <%_ if(order.refund.count < order.order.getAmount()) { _%>
+        <p>Hinweis: Ein Teil Ihres gekauften Guthabens wurde bereits verbraucht. Wir können Ihnen deshalb lediglich <span class="bold"><%= order.refund.count %>/<%= order.order.getAmount() %></span> Suchanfragen erstatten.</p>
+        <%_ } _%>
+        <h3>Ihre Erstattung</h3>
+        <textarea name="message" id="message" cols="1" rows="10" placeholder="Ihre Nachricht (optional)" size="1" autofocus></textarea>
+        <button class="button">
+            <img src="/images/money.svg" alt="" aria-hidden="true">
+            <span><%= order.refund.amount.toFixed(2) %>€ Erstattung anfragen</span>
+        </button>
+    </form>
+</div>
\ No newline at end of file
-- 
GitLab