From a4196b910add12893a446dde2602f568c34de979 Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@hebeler.club>
Date: Mon, 17 Apr 2023 21:48:22 +0200
Subject: [PATCH] removed old order class

---
 pass/app/Order.js                             | 787 ------------------
 pass/app/payment_processor/Cash.js            |   4 +-
 pass/app/payment_processor/Manual.js          |  10 +-
 pass/app/payment_processor/Micropayment.js    |  36 +-
 .../app/payment_processor/PaymentProcessor.js |  20 +-
 pass/app/payment_processor/Paypal.js          |   8 +-
 pass/app/pdf/OrderReceipt.js                  |  16 +-
 pass/bin/cron                                 |   1 -
 pass/routes/admin/index.js                    |  82 +-
 pass/routes/api.js                            |  75 +-
 pass/routes/checkout/cash.js                  |   6 -
 pass/routes/checkout/checkout.js              | 184 ----
 pass/routes/checkout/development.js           |  56 --
 pass/routes/checkout/manual.js                |  24 +-
 pass/routes/checkout/micropayment.js          |  92 +-
 pass/routes/checkout/paypal.js                |  25 +-
 pass/routes/index.js                          |   3 +-
 pass/routes/orders/orders.js                  |  51 +-
 pass/routes/redeem.js                         | 247 ------
 19 files changed, 215 insertions(+), 1512 deletions(-)
 delete mode 100644 pass/app/Order.js
 delete mode 100644 pass/routes/checkout/checkout.js
 delete mode 100644 pass/routes/checkout/development.js
 delete mode 100644 pass/routes/redeem.js

diff --git a/pass/app/Order.js b/pass/app/Order.js
deleted file mode 100644
index 49b9c99..0000000
--- a/pass/app/Order.js
+++ /dev/null
@@ -1,787 +0,0 @@
-const config = require("config");
-const dayjs = require("dayjs");
-const path = require("path");
-const Key = require("./Key");
-const RedisClient = require("./RedisClient");
-const PaymentProcessor = require("./payment_processor/PaymentProcessor");
-const { CLIENT } = require("./RedisClient");
-const i18next = require("i18next");
-
-class Order {
-  static get STORAGE_MUTEX_KEY_PREFIX() {
-    return "order_mutex:";
-  }
-
-  static get STORAGE_KEY_PREFIX() {
-    return "order:";
-  }
-
-  static get PURCHASE_TAX_AMOUNT() {
-    return 0.07;
-  }
-
-  // How long is a link between order <=> key stored
-  static get PURCHASE_LINK_TIME_DAYS() {
-    return 31;
-  }
-  static get PURCHASE_LINK_ORDER_TO_KEY_PREFIX() {
-    return "order_link_order_key:";
-  }
-
-  // How many minutes is a user allowed to take for finishing the payment
-  static get PURCHASE_STORAGE_TIME_UNCOMPLETED_HOURS() {
-    return 6;
-  }
-
-  static GET_ORDER_FILE_BASE_PATH(order_date) {
-    let order_path = config.get("storage.data_path");
-    order_path = path.join(order_path, process.env.NODE_ENV, "orders");
-    order_path = path.join(
-      order_path,
-      order_date.format("YYYY"),
-      order_date.format("MM")
-    );
-
-    return order_path;
-  }
-  /**
-   * Data stored in Redis Database
-   */
-  #order_id;
-  #order_date;
-  #expires_at;
-  #amount;
-  #amount_refund_requested = 0;
-  #amount_refunded = 0;
-  #price;
-  #foreign_currency_price;
-  #foreign_currency;
-  #order_key_charged = false;
-  #payment_captured = false;
-  #order_key_changes = [];
-
-  #receipt_created = false;
-  #civicrm_contribution_id;
-  /** @type {PaymentProcessor} */
-  #payment_processor;
-
-  /**
-   * Data populated by context
-   */
-  #order_path;
-
-  constructor(
-    order_id,
-    order_date = null,
-    amount,
-    price,
-    foreign_currency_price,
-    foreign_currency,
-    payment_processor,
-    amount_refund_requested = 0,
-    order_key_charged = false,
-    payment_captured = false,
-    amount_refunded = 0,
-    receipt_created = false,
-    civicrm_contribution_id,
-    order_key_changes = []
-  ) {
-    this.#order_id = order_id;
-    if (order_date !== null) {
-      this.#order_date = order_date;
-    } else {
-      this.#order_date = dayjs.unix(this.#order_id.substr(0, 10));
-    }
-    this.#expires_at = dayjs().add(config.get("keys.expiration_days"), "days");
-
-
-    this.#order_path = Order.GET_ORDER_FILE_BASE_PATH(this.#order_date);
-
-    this.#amount = parseInt(amount);
-    this.#price = parseFloat(price);
-    if (foreign_currency_price !== undefined) {
-      this.#foreign_currency_price = parseFloat(foreign_currency_price);
-    }
-    if (foreign_currency !== undefined) {
-      this.#foreign_currency = foreign_currency;
-    }
-
-    this.#payment_processor = payment_processor;
-
-    this.#amount_refund_requested = amount_refund_requested;
-    this.#order_key_charged = order_key_charged;
-    this.#payment_captured = payment_captured;
-    this.#amount_refunded = amount_refunded;
-
-    this.#receipt_created = receipt_created;
-
-    this.#civicrm_contribution_id = civicrm_contribution_id;
-    this.#order_key_changes = order_key_changes;
-  }
-
-  getOrderID() {
-    return this.#order_id;
-  }
-
-  getAmount() {
-    return this.#amount;
-  }
-
-  getNettoPrice() {
-    let amount = this.getPrice();
-    let vat = config.get("price.vat") / 100; // Vat is configured in %
-    return amount / (1 + vat);
-  }
-  getVat() {
-    return config.get("price.vat");
-  }
-  getVatAmount() {
-    let vat = config.get("price.vat") / 100; // Vat is configured in %
-    let amount = this.getPrice();
-    return (amount / (1 + vat)) * vat;
-  }
-  getPrice() {
-    return this.#price;
-  }
-  getForeignCurrencyPrice() {
-    return this.#foreign_currency_price;
-  }
-  getForeignCurrency() {
-    return this.#foreign_currency;
-  }
-
-  /**
-   *
-   * @returns {PaymentProcessor}
-   */
-  getPaymentProcessor() {
-    return this.#payment_processor;
-  }
-
-  /**
-   *
-   * @param {dayjs.Dayjs} expiration
-   */
-  setExpiration(expiration) {
-    this.#expires_at = expiration;
-  }
-
-  async captureOrder() {
-    if (this.isPaymentCaptured()) {
-      throw new Error("Order already captured");
-    }
-    return this.getWriteLock()
-      .then(() => this.getPaymentProcessor().captureOrder())
-      .then(() => this.createCiviCRMOrder())
-      .then(() => {
-        this.#payment_captured = true;
-        this.#order_date = dayjs();
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock());
-  }
-
-  async chargeKey() {
-    if (this.isOrderKeyCharged()) {
-      throw new Error("Key already charged");
-    }
-    return this.getWriteLock()
-      .then(() => this.getKeyFromOrderLink())
-      .then((key) => Key.GET_KEY(key, true))
-      .then((key) => {
-        key.charge_key_order(
-          this.getAmount(),
-          this.getOrderID(),
-          this.#expires_at
-        );
-        return key.save();
-      })
-      .then(() => {
-        this.#order_key_charged = true;
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock());
-  }
-
-  async requestRefund(amount) {
-    if (this.#amount_refund_requested > 0) {
-      throw new Error("Refund is already requested");
-    }
-    let new_key = null;
-    return this.getWriteLock()
-      .then(() => this.getKeyFromOrderLink())
-      .then((key) => Key.GET_KEY(key, true))
-      .then((key) => {
-        key.discharge_key(amount, this.getOrderID());
-        return key.save();
-      })
-      .then((key) => {
-        new_key = key;
-        this.#amount_refund_requested = amount;
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock())
-      .then(() => new_key);
-  }
-
-  /**
-   * Processes a refund which was previously issued by the customer.
-   * Will issue a refund from the payment processor
-   *
-   * @returns
-   */
-  async refund() {
-    if (this.#amount_refunded > 0 || this.#amount_refund_requested === 0) {
-      throw "No refund requested or Refund already processed";
-    }
-
-    let refund_price = parseFloat(
-      (
-        Math.ceil(
-          (this.#amount_refund_requested / this.#amount) * this.#price * 100
-        ) / 100
-      ).toFixed(2)
-    );
-
-    return this.getWriteLock()
-      .then(() => this.getPaymentProcessor().refundOrder(refund_price))
-      .then(() => {
-        this.#amount_refunded = this.#amount_refund_requested;
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock());
-  }
-
-  /**
-   * Processes an external refund. I.e. when the refund was issued through Payment Gateway directly
-   *
-   * @param {float} price price that was refunded externally
-   */
-  async external_refund(price) {
-    // Create amount from refunded price
-    price = Math.min(this.#price, price);
-    let amount = Math.floor((price / this.#price) * this.#amount);
-
-    return this.getWriteLock()
-      .then(() => this.getKeyFromOrderLink())
-      .then((key) => Key.GET_KEY(key, true))
-      .then((key) => {
-        try {
-          key.discharge_key(amount, this.getOrderID());
-          return key.save();
-        } catch (err) {
-          throw err;
-        }
-      })
-      .then(() => {
-        this.#amount_refunded = amount;
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock());
-  }
-
-  async denyRefund() {
-    if (this.#amount_refunded > 0 || this.#amount_refund_requested === 0) {
-      return false;
-    }
-    return this.getWriteLock()
-      .then(() => this.getKeyFromOrderLink())
-      .then((key) => Key.GET_KEY(key, true))
-      .then((key) => {
-        key.charge_key_order(
-          this.#amount_refund_requested,
-          this.getOrderID(),
-          this.#expires_at
-        );
-        return key.save();
-      })
-      .then(() => {
-        this.#amount_refund_requested = 0;
-        return this.save();
-      })
-      .then(() => this.releaseWriteLock())
-      .then(() => {
-        return true;
-      })
-      .catch((reason) => {
-        console.debug(reason);
-        return false;
-      });
-  }
-
-  isReceiptCreated() {
-    return this.#receipt_created;
-  }
-
-  getAmountRefundRequested() {
-    return this.#amount_refund_requested;
-  }
-
-  isOrderKeyCharged() {
-    return this.#order_key_charged;
-  }
-  isPaymentCaptured() {
-    return this.#payment_captured;
-  }
-  getAmountRefunded() {
-    return this.#amount_refunded;
-  }
-
-  /**
-   *
-   * @param {int} amount
-   * @param {string} key
-   * @param {PaymentProcessor} payment_processor
-   * @param {i18next.TFunction} t
-   * @returns
-   */
-  static async CREATE_NEW_ORDER(amount, key, payment_processor, t) {
-    // Calculate price from amount
-    let price = (amount / 300) * config.get("price.per_300");
-    price = price.toFixed(2);
-
-    // Validate that Key can be charged with another order
-    /**
-     * @type {Key}
-     */
-    let loaded_key = null;
-    return Key.GET_KEY(key)
-      .then((key) => {
-        if (!key.isChargable()) {
-          throw "Key cannot be charged";
-        } else {
-          loaded_key = key;
-          return true;
-        }
-      })
-      .then(() => Order.GENERATE_UNIQUE_ORDER_ID())
-      .then((order_id) => {
-        let new_order = new Order(
-          order_id,
-          null,
-          amount,
-          price,
-          null,
-          null,
-          payment_processor
-        );
-        return new_order
-          .getWriteLock()
-          .then(() => new_order.getPaymentProcessor().createOrder(new_order, t)) // Create Order on PaymentProcessor side
-          .then(() => new_order.createOrderLink(loaded_key.get_key()))
-          .then(() => new_order.save())
-          .then(() => new_order.releaseWriteLock())
-          .then(() => {
-            return new_order;
-          });
-      });
-  }
-
-  /**
-   *
-   * @param {Number} order_id
-   * @returns {Promise<Order>}
-   */
-  static async LOAD_ORDER_FROM_ID(order_id) {
-    return new Promise((resolve, reject) => {
-      __redis_client
-        .get(Order.STORAGE_KEY_PREFIX + order_id)
-        .then((order_data) => {
-          let order_date = dayjs.unix(order_id.substr(0, 10));
-          if (order_data === null) {
-            let order_file = path.join(
-              Order.GET_ORDER_FILE_BASE_PATH(order_date),
-              order_id.toString() + ".json"
-            );
-            let fs = require("fs");
-            if (fs.existsSync(order_file)) {
-              order_data = JSON.parse(fs.readFileSync(order_file));
-              if ("order_date" in order_data && order_data.order_date) {
-                order_date = dayjs(order_data.order_date);
-              }
-            } else {
-              throw "Could not find Order in our database!";
-            }
-          } else {
-            order_data = JSON.parse(order_data);
-          }
-          let loaded_order = new Order(
-            order_data.order_id,
-            order_date,
-            order_data.amount,
-            order_data.price,
-            order_data.foreign_currency_price,
-            order_data.foreign_currency,
-            PaymentProcessor.LOAD_PAYMENT_PROCESSOR(
-              order_data.payment_processor
-            ),
-            order_data.amount_refund_requested,
-            order_data.order_key_charged,
-            order_data.payment_captured,
-            order_data.amount_refunded,
-            order_data.receipt_created,
-            order_data.civicrm_contribution_id,
-            order_data.order_key_changes
-          );
-          resolve(loaded_order);
-        })
-        .catch((reason) => {
-          reject(reason);
-        });
-    });
-  }
-
-  async save(redis_expiration = null) {
-    let stored_data = {
-      order_id: this.#order_id,
-      order_date: this.#order_date.unix(),
-      expires_at: this.#expires_at,
-      amount: this.#amount,
-      price: this.#price,
-      foreign_currency_price: this.#foreign_currency_price,
-      foreign_currency: this.#foreign_currency,
-      amount_refund_requested: this.#amount_refund_requested,
-      order_key_charged: this.#order_key_charged,
-      payment_captured: this.#payment_captured,
-      amount_refunded: this.#amount_refunded,
-      receipt_created: this.#receipt_created,
-      payment_processor: this.#payment_processor.serialize(),
-      civicrm_contribution_id: this.#civicrm_contribution_id,
-      order_key_changes: this.#order_key_changes,
-    };
-    /**
-     * Completed Orders will be stored in Filesystem
-     * Uncompleted Orders will be stored in Redis
-     */
-    if (redis_expiration === null) {
-      if (this.isPaymentCaptured()) {
-        redis_expiration = dayjs().add(Order.PURCHASE_LINK_TIME_DAYS, "day");
-      } else {
-        redis_expiration = dayjs().add(
-          Order.PURCHASE_STORAGE_TIME_UNCOMPLETED_HOURS,
-          "hour"
-        );
-      }
-    } else {
-      let min_expiration = dayjs().add(
-        Order.PURCHASE_STORAGE_TIME_UNCOMPLETED_HOURS,
-        "hour"
-      );
-      if (this.isPaymentCaptured()) {
-        min_expiration = dayjs().add(Order.PURCHASE_LINK_TIME_DAYS, "day");
-      }
-      if (min_expiration.isAfter(redis_expiration)) {
-        redis_expiration = min_expiration;
-      }
-    }
-
-    let redis_key = Order.STORAGE_KEY_PREFIX + this.#order_id;
-    if (this.isPaymentCaptured()) {
-      let fs = require("fs");
-      let order_file = path.join(
-        this.#order_path,
-        this.#order_id.toString() + ".json"
-      );
-      // Create directory if it does not exist
-      if (!fs.existsSync(path.dirname(order_file))) {
-        fs.mkdirSync(path.dirname(order_file), { recursive: true });
-      }
-      return __redis_client
-        .del(redis_key)
-        .then(() => this.updateOrderLinkTTL(redis_expiration))
-        .then(() =>
-          fs.writeFileSync(order_file, JSON.stringify(stored_data, null, 4))
-        );
-    } else {
-      // Store Order in Redis
-      return __redis_client
-        .pipeline()
-        .set(redis_key, JSON.stringify(stored_data))
-        .expireat(redis_key, redis_expiration.unix())
-        .exec()
-        .then(() => this.updateOrderLinkTTL(redis_expiration));
-    }
-  }
-
-  async delete() {
-    let redis_key = Order.STORAGE_KEY_PREFIX + this.#order_id;
-    if (!this.isPaymentCaptured()) {
-      return this.deleteOrderLink()
-        .then(() => __redis_client.del(redis_key))
-        .then(async (deleted_keys) => {
-          if (deleted_keys > 0) {
-            return true;
-          } else {
-            return false;
-          }
-        })
-        .catch(async () => {
-          return false;
-        });
-    } else {
-      return false;
-    }
-  }
-
-  async getWriteLock() {
-    let write_lock_key = Order.STORAGE_MUTEX_KEY_PREFIX + this.#order_id;
-
-    return new Promise(async (resolve, reject) => {
-      let timestart = dayjs();
-      let write_lock = 0;
-      do {
-        write_lock = await __redis_client
-          .pipeline()
-          .setnx(write_lock_key, 1)
-          .expiretime(write_lock_key)
-          .expire(write_lock_key, 15)
-          .exec();
-        let expire_at = write_lock[1][1];
-        write_lock = write_lock[0][1];
-        if (write_lock === 1) {
-          resolve(true);
-          break;
-        } else if (dayjs().diff(timestart, "second") >= 15) {
-          if (expire_at >= 0) {
-            await __redis_client.expireat(write_lock_key, expire_at);
-          }
-          reject("Timed out waiting for write lock for Order");
-          break;
-        } else {
-          if (expire_at >= 0) {
-            await __redis_client.expireat(write_lock_key, expire_at);
-          }
-          await new Promise((resolve) => setTimeout(resolve, 1000));
-        }
-      } while (true);
-    });
-  }
-
-  async releaseWriteLock() {
-    let write_lock_key = Order.STORAGE_MUTEX_KEY_PREFIX + this.#order_id;
-    return __redis_client.del(write_lock_key);
-  }
-
-  addOrderKeyChanges(changes) {
-    this.#order_key_changes.push(changes);
-  }
-
-  /**
-   * Creates a link between Order and Key that will automatically expire
-   * @param {string} key
-   */
-  async createOrderLink(key) {
-    let expiration = dayjs().add(Order.PURCHASE_LINK_TIME_DAYS, "day");
-    let redis_key_order_key =
-      Order.PURCHASE_LINK_ORDER_TO_KEY_PREFIX + this.#order_id;
-    return __redis_client
-      .pipeline()
-      .set(redis_key_order_key, key)
-      .expireat(redis_key_order_key, expiration.unix())
-      .exec();
-  }
-
-  async deleteOrderLink() {
-    let redis_key_order_key =
-      Order.PURCHASE_LINK_ORDER_TO_KEY_PREFIX + this.#order_id;
-    return this.getKeyFromOrderLink().then((key) => {
-      return __redis_client.pipeline().del(redis_key_order_key).exec();
-    });
-  }
-
-  async createCiviCRMOrder() {
-    if (this.getPaymentProcessor().serialize().processor_name === "Manual") {
-      // Manual Orders will not get stored in CiviCRM
-      return;
-    }
-
-    let civicrm_data = config.get("app.civicrm");
-    if (!civicrm_data.enabled) {
-      return;
-    }
-    return fetch(civicrm_data.url + "/Contribution/create", {
-      method: "post",
-      headers: {
-        "Content-Type": "application/x-www-form-urlencoded",
-        "X-Civi-Auth": `Bearer ${civicrm_data.api_key}`,
-      },
-      body:
-        "params=" +
-        encodeURIComponent(
-          JSON.stringify({
-            values: {
-              contact_id: civicrm_data.user_id,
-              financial_type_id: 5,
-              receive_date: this.#order_date.format("YYYY-MM-DD HH:mm:ss"),
-              total_amount: this.getPrice(),
-              is_test: process.env.NODE_ENV === "development" ? true : false,
-              is_pay_later: false,
-              tax_amount: this.getVatAmount(),
-              is_template: false,
-            },
-          })
-        ),
-    })
-      .then((response) => {
-        if (response.status !== 200) {
-          throw "Error Creating Civicrm Contribution";
-        } else {
-          return response.json();
-        }
-      })
-      .then((response_json) => {
-        this.#civicrm_contribution_id = response_json.values[0].id;
-      });
-  }
-
-  getOrderDate() {
-    return this.#order_date;
-  }
-
-  /**
-   *
-   * @param {number} price
-   * @param {number} foreign_price
-   * @param {string} foreign_currency
-   */
-  setPrice(price, foreign_price = 0, foreign_currency = "EUR") {
-    if (
-      config.get("price.allowed_currencies").indexOf(foreign_currency) === -1
-    ) {
-      throw new Error(`Currency ${foreign_currency} not allowed`);
-    }
-    if (this.isPaymentCaptured()) {
-      throw new Error("Cannot change details of processed Order");
-    }
-    this.#price = price;
-    this.#foreign_currency_price = foreign_price;
-    this.#foreign_currency = foreign_currency;
-
-    // Calculate new amount from price
-    this.#amount = parseInt(
-      Math.ceil((price / config.get("price.per_300")) * 300)
-    );
-  }
-
-  async attachReceipt(data, invoice_id) {
-    let civicrm_data = config.get("app.civicrm");
-    if (!civicrm_data.enabled) {
-      return;
-    }
-
-    return this.getWriteLock()
-      .then(() =>
-        fetch(civicrm_data.url + "/Contribution/addReceipt", {
-          method: "post",
-          headers: {
-            "Content-Type": "application/x-www-form-urlencoded",
-            "X-Civi-Auth": `Bearer ${civicrm_data.api_key}`,
-          },
-          body:
-            "params=" +
-            encodeURIComponent(
-              JSON.stringify({
-                receipt: data,
-                invoice_id,
-                id: this.#civicrm_contribution_id,
-              })
-            ),
-        })
-      )
-      .then((response) => {
-        if (response.status !== 200) {
-          throw "Error Creating Civicrm Contribution";
-        } else {
-          return response.json();
-        }
-      })
-      .then((response_json) => {
-        if (response_json["count"] !== 1) {
-          throw JSON.stringify(response_json, null, 4);
-        } else {
-          this.#receipt_created = true;
-          return response_json;
-        }
-      })
-      .then(() => this.save())
-      .then(() => this.releaseWriteLock());
-  }
-
-  async getReceipt() {
-    let civicrm_data = config.get("app.civicrm");
-    if (!civicrm_data.enabled) {
-      return Promise.reject("No Civicrm Config supplied");
-    }
-    return fetch(civicrm_data.url + "/Contribution/getReceipt", {
-      method: "post",
-      headers: {
-        "Content-Type": "application/x-www-form-urlencoded",
-        "X-Civi-Auth": `Bearer ${civicrm_data.api_key}`,
-      },
-      body:
-        "params=" +
-        encodeURIComponent(
-          JSON.stringify({
-            id: this.#civicrm_contribution_id,
-          })
-        ),
-    })
-      .then((result) => result.json())
-      .then((result_json) => {
-        if (
-          typeof result_json.error_code !== "undefined" &&
-          result_json.error_code !== 0
-        ) {
-          throw result_json;
-        } else {
-          return result_json;
-        }
-      });
-  }
-
-  /**
-   *
-   * @param {dayjs.Dayjs} redis_expiration
-   * @returns
-   */
-  async updateOrderLinkTTL(redis_expiration) {
-    let redis_key = Order.PURCHASE_LINK_ORDER_TO_KEY_PREFIX + this.#order_id;
-
-    return __redis_client.expireat(redis_key, redis_expiration.unix());
-  }
-
-  /**
-   * Tries to get a key for this order. If the order link already expired this function rejects the promise
-   */
-  async getKeyFromOrderLink() {
-    let redis_key = Order.PURCHASE_LINK_ORDER_TO_KEY_PREFIX + this.#order_id;
-    return __redis_client.get(redis_key).then(async (result) => {
-      if (result) {
-        return result;
-      } else {
-        throw "Order Link does not exist";
-      }
-    });
-  }
-
-  static async GENERATE_UNIQUE_ORDER_ID() {
-    // Generate Order ID => time in seconds since 1.1.1970 and and add a mutex to it to allow multiple order ids per second
-    let order_id = null;
-    let order_base = Math.round(new Date().getTime() / 1000);
-    let order_mutex = Math.floor(Math.random() * 10000);
-    do {
-      // make sure this order_id is not already registered
-      order_id = "" + order_base + order_mutex;
-      let order_lock = await __redis_client.setnx(order_id, true);
-      if (order_lock === 1) {
-        await __redis_client.expire(order_id, 5);
-      } else {
-        order_mutex++;
-        order_id = null;
-      }
-    } while (order_id === null);
-    return order_id;
-  }
-}
-
-module.exports = Order;
diff --git a/pass/app/payment_processor/Cash.js b/pass/app/payment_processor/Cash.js
index 1bd7c5f..6a730ff 100644
--- a/pass/app/payment_processor/Cash.js
+++ b/pass/app/payment_processor/Cash.js
@@ -16,10 +16,10 @@ class Cash extends PaymentProcessor {
 
   /**
    *
-   * @param {Order} order
+   * @param {PaymentReference} payment_reference
    * @param {i18next.TFunction} t
    */
-  async createOrder(order, t) {
+  async createOrder(payment_reference, t) {
     return Promise.resolve();
   }
 
diff --git a/pass/app/payment_processor/Manual.js b/pass/app/payment_processor/Manual.js
index f9f1b0a..780b58a 100644
--- a/pass/app/payment_processor/Manual.js
+++ b/pass/app/payment_processor/Manual.js
@@ -20,10 +20,10 @@ class Manual extends PaymentProcessor {
 
   /**
    *
-   * @param {Order} order
+   * @param {PaymentReference} payment_reference
    * @param {i18next.TFunction} t
    */
-  async createOrder(order, t) {
+  async createOrder(payment_reference, t) {
     return Promise.resolve();
   }
 
@@ -31,9 +31,9 @@ class Manual extends PaymentProcessor {
     return Promise.resolve();
   }
   /**
- * 
- * @returns {boolean}
- */
+   *
+   * @returns {boolean}
+   */
   isRefundSupported() {
     return false;
   }
diff --git a/pass/app/payment_processor/Micropayment.js b/pass/app/payment_processor/Micropayment.js
index b2f057b..eccfa43 100644
--- a/pass/app/payment_processor/Micropayment.js
+++ b/pass/app/payment_processor/Micropayment.js
@@ -1,10 +1,12 @@
-const Order = require("../Order");
 const PaymentProcessor = require("./PaymentProcessor");
 const i18next = require("i18next");
 const config = require("config");
+const PaymentReference = require("../PaymentReference");
 
 class Micropayment extends PaymentProcessor {
-  static get NAME() { return "Micropayment" };
+  static get NAME() {
+    return "Micropayment";
+  }
   /**
    * @var {string[]}
    */
@@ -38,29 +40,13 @@ class Micropayment extends PaymentProcessor {
    * Generates a URL for the customer where he is redirected to
    * to complete Payment.
    *
-   * @param {Order} order
+   * @param {PaymentReference} payment_reference
    * @param {i18next.TFunction} t
    *
    * @returns {Promise<URL>}
    */
-  async createOrder(order, t) {
-    let payment_window_url = new URL(
-      `https://${this.#service}.micropayment.de/${this.#service}/event`
-    );
-    let searchParams = new URLSearchParams({
-      project: config.get("payments.micropayment.project"),
-      amount: order.getPrice() * 100,
-      title: t("product.name", { ns: "order" }),
-      mp_user_id: order.getOrderID(),
-      producttype: "quantity",
-      paytext: t("product.name", { ns: "order", count: order.getAmount() }),
-      testmode: process.env.NODE_ENV === "development" ? "1" : "0",
-      orderid: order.getOrderID(),
-      vatinfo: "1",
-    });
-    payment_window_url.search = searchParams.toString();
-    this.#capture_url = payment_window_url;
-    return Promise.resolve(this.#capture_url);
+  async createOrder(payment_reference, t) {
+    return Promise.resolve();
   }
 
   /**
@@ -73,9 +59,9 @@ class Micropayment extends PaymentProcessor {
   }
 
   /**
- * 
- * @returns {boolean}
- */
+   *
+   * @returns {boolean}
+   */
   isRefundSupported() {
     return true;
   }
@@ -99,7 +85,7 @@ class Micropayment extends PaymentProcessor {
       return Promise.reject("Testmode Payment not accepted in production");
     }
     if (!("currency" in query) || query.currency !== "EUR") {
-      return Promise.reject("Can only receive Payments in EUR")
+      return Promise.reject("Can only receive Payments in EUR");
     }
     return Promise.resolve();
   }
diff --git a/pass/app/payment_processor/PaymentProcessor.js b/pass/app/payment_processor/PaymentProcessor.js
index aa5594f..704ed09 100644
--- a/pass/app/payment_processor/PaymentProcessor.js
+++ b/pass/app/payment_processor/PaymentProcessor.js
@@ -1,5 +1,5 @@
-const Order = require("../Order");
 const i18next = require("i18next");
+const PaymentReference = require("../PaymentReference");
 
 class PaymentProcessor {
   constructor() {
@@ -10,10 +10,10 @@ class PaymentProcessor {
 
   /**
    *
-   * @param {Order} order
+   * @param {PaymentReference} payment_reference
    * @param {i18next.TFunction} t
    */
-  async createOrder(order, t) {
+  async createOrder(payment_reference, t) {
     throw new Error("Function createOrder() must be implemented");
   }
 
@@ -21,9 +21,9 @@ class PaymentProcessor {
     throw new Error("Function captureOrder() must be implemented");
   }
   /**
-  * 
-  * @returns {boolean}
-  */
+   *
+   * @returns {boolean}
+   */
   isRefundSupported() {
     throw new Error("Function isRefundPossible() must be implemented");
   }
@@ -66,13 +66,9 @@ class PaymentProcessor {
     };
 
     if (!(processor_name in processors)) {
-      throw new Error(
-        `Cannot find Payment Processor ${processor_name}`
-      );
+      throw new Error(`Cannot find Payment Processor ${processor_name}`);
     }
-    return processors[processor_name].DESERIALIZE(
-      serialized_data
-    );
+    return processors[processor_name].DESERIALIZE(serialized_data);
   }
 }
 
diff --git a/pass/app/payment_processor/Paypal.js b/pass/app/payment_processor/Paypal.js
index c8d1341..7dbad29 100644
--- a/pass/app/payment_processor/Paypal.js
+++ b/pass/app/payment_processor/Paypal.js
@@ -1,11 +1,8 @@
-const Order = require("../Order");
 const PaymentReference = require("../PaymentReference");
 const Payment = require("../Payment");
 const dayjs = require("dayjs");
 const config = require("config");
 const PaymentProcessor = require("./PaymentProcessor");
-const Key = require("../Key");
-const RedisClient = require("../RedisClient");
 const webhook_redis_key = "checkout_paypal_webhook_id";
 const base = config.get(`payments.paypal.base`);
 const i18next = require("i18next");
@@ -156,7 +153,7 @@ class Paypal extends PaymentProcessor {
         if (
           response_data.status !== "COMPLETED" ||
           response_data.purchase_units[0].payments.captures[0].status !==
-          "COMPLETED"
+            "COMPLETED"
         ) {
           console.error(JSON.stringify(response_data));
           throw "PAYMENT_NOT_COMPLETED_ERROR";
@@ -290,7 +287,8 @@ class Paypal extends PaymentProcessor {
                 );
               }
             }
-          });
+          }
+        );
       } else {
         console.log(req.body);
         throw "Webhook not implemented";
diff --git a/pass/app/pdf/OrderReceipt.js b/pass/app/pdf/OrderReceipt.js
index d0c0583..a549175 100644
--- a/pass/app/pdf/OrderReceipt.js
+++ b/pass/app/pdf/OrderReceipt.js
@@ -1,4 +1,3 @@
-const Order = require("../Order");
 const dayjs = require("dayjs");
 const i18next = require("i18next");
 const config = require("config");
@@ -15,7 +14,12 @@ class OrderReceipt {
    * @param {Object} invoice
    * @param {i18next.TFunction} t
    */
-  static async CREATE_ORDER_RECEIPT(payment_reference, payment, receipt = undefined, t) {
+  static async CREATE_ORDER_RECEIPT(
+    payment_reference,
+    payment,
+    receipt = undefined,
+    t
+  ) {
     let letter_left_margin = OrderReceipt.CM_TO_POINTS(2.5, false);
     let letter_right_margin = OrderReceipt.CM_TO_POINTS(2, false);
 
@@ -256,10 +260,10 @@ class OrderReceipt {
         .fontSize(8)
         .text(
           " 1 " +
-          payment.converted_currency +
-          " = " +
-          (payment.price / payment.converted_price).toFixed(6) +
-          "€",
+            payment.converted_currency +
+            " = " +
+            (payment.price / payment.converted_price).toFixed(6) +
+            "€",
           {
             width: OrderReceipt.CM_TO_POINTS(3.25, false),
             align: "right",
diff --git a/pass/bin/cron b/pass/bin/cron
index efc8a84..be05d09 100644
--- a/pass/bin/cron
+++ b/pass/bin/cron
@@ -1,5 +1,4 @@
 const dayjs = require("dayjs");
-const Order = require("../app/Order");
 const RedisClient = require("../app/RedisClient");
 const config = require("config");
 const path = require("path");
diff --git a/pass/routes/admin/index.js b/pass/routes/admin/index.js
index 9465b4b..5573953 100644
--- a/pass/routes/admin/index.js
+++ b/pass/routes/admin/index.js
@@ -2,8 +2,7 @@ var express = require("express");
 var router = express.Router();
 const config = require("config");
 const dayjs = require("dayjs");
-const Order = require("../../app/Order");
-const { auth, requiresAuth, claimCheck } = require("express-openid-connect");
+const { auth } = require("express-openid-connect");
 const {
   validationResult,
   matchedData,
@@ -87,18 +86,21 @@ router.get(
 
     if (reqData.order && reqData.order.receipt_id !== null) {
       // There is already a receipt: Send it back
-      return Receipt.LOAD_RECEIPT_FROM_INTERNAL_ID(reqData.order.receipt_id).then(receipt => {
-        let receipt_data = Buffer.from(receipt.receipt, 'base64');
-        res.header({
-          "Content-Type": "application/pdf",
-          "Content-Disposition": `inline; filename=${receipt.public_id}.pdf`
-        }).send(receipt_data);
+      return Receipt.LOAD_RECEIPT_FROM_INTERNAL_ID(
+        reqData.order.receipt_id
+      ).then((receipt) => {
+        let receipt_data = Buffer.from(receipt.receipt, "base64");
+        res
+          .header({
+            "Content-Type": "application/pdf",
+            "Content-Disposition": `inline; filename=${receipt.public_id}.pdf`,
+          })
+          .send(receipt_data);
       });
     }
 
     if (data_complete) {
       // Create Invoice for preview
-      let receiptid = "xxxxxxxx";
       return OrderReceipt.CREATE_ORDER_RECEIPT(
         res.locals.payment_reference,
         reqData.order,
@@ -109,7 +111,7 @@ router.get(
           email: res.locals.email,
           address: res.locals.address,
           created_at: dayjs().format("YYYY-MM-DD HH:mm:ss"),
-          payment_id: reqData.order.id
+          payment_id: reqData.order.id,
         }),
         req.t
       )
@@ -119,9 +121,9 @@ router.get(
           let hasher = crypto.createHash("sha256");
           hasher.update(
             reqData.company +
-            res.locals.name +
-            res.locals.email +
-            res.locals.address
+              res.locals.name +
+              res.locals.email +
+              res.locals.address
           );
           res.locals.datahash = hasher.digest("hex");
           res.render("admin/payments/receipt");
@@ -198,29 +200,35 @@ router.post(
       email: reqData.email,
       address: reqData.address,
       payment_id: reqData.order.id,
-    }).then((receipt) => {
-      reqData.id = receipt.public_id;
-      let receipt_data;
-      return OrderReceipt.CREATE_ORDER_RECEIPT(
-        payment_reference,
-        reqData.order,
-        receipt,
-        req.t
-      )
-        .then((data) => {
-          receipt_data = Buffer.concat(data);
-          return receipt.attachReceipt(receipt_data.toString("base64"));
-        })
-        .then((receipt) => reqData.order.setReceipt(receipt.id)).then(() => {
-          res.type("pdf").header({
-            "Content-Disposition": `inline; filename=${receipt.public_id}.pdf`
-          }).send(receipt_data);
-        })
-    }).catch(reason => {
-      console.error(reason);
-      res.redirect(url.toString());
-      return;
-    });
+    })
+      .then((receipt) => {
+        reqData.id = receipt.public_id;
+        let receipt_data;
+        return OrderReceipt.CREATE_ORDER_RECEIPT(
+          payment_reference,
+          reqData.order,
+          receipt,
+          req.t
+        )
+          .then((data) => {
+            receipt_data = Buffer.concat(data);
+            return receipt.attachReceipt(receipt_data.toString("base64"));
+          })
+          .then((receipt) => reqData.order.setReceipt(receipt.id))
+          .then(() => {
+            res
+              .type("pdf")
+              .header({
+                "Content-Disposition": `inline; filename=${receipt.public_id}.pdf`,
+              })
+              .send(receipt_data);
+          });
+      })
+      .catch((reason) => {
+        console.error(reason);
+        res.redirect(url.toString());
+        return;
+      });
     reqData.id = await OrderReceipt.CREATE_UNIQUE_RECEIPT_ID();
     OrderReceipt.CREATE_ORDER_RECEIPT(reqData.order, reqData, req.t)
       .then((data) => {
@@ -297,7 +305,7 @@ router.post(
         converted_currency: price_data.converted_currency,
         payment_processor: Cash.NAME,
       })
-      .then((payment) => {
+      .then(() => {
         res.locals.orderid = payment_reference.public_id;
         res.render("admin/payments/cash_success");
       })
diff --git a/pass/routes/api.js b/pass/routes/api.js
index 4ec5e6f..36cfb85 100644
--- a/pass/routes/api.js
+++ b/pass/routes/api.js
@@ -5,13 +5,10 @@ const { body, validationResult } = require("express-validator");
 
 const config = require("config");
 const Key = require("../app/Key");
-const Order = require("../app/Order");
-const Manual = require("../app/payment_processor/Manual");
 const Crypto = require("../app/Crypto");
 const dayjs = require("dayjs");
 const NodeRSA = require("node-rsa");
 const PaymentReference = require("../app/PaymentReference");
-const RedisClient = require("../app/RedisClient");
 
 router.use("/key", authorizedOnly);
 
@@ -32,23 +29,29 @@ router.post("/key/create", (req, res) => {
     }
   }
 
-  return Key.GET_NEW_KEY().then(key => {
-    return PaymentReference.CREATE_NEW_REQUEST(amount, key.get_key(), dayjs().add("10", "years")).then(payment_reference => {
-      return payment_reference.chargeKey().then(() => {
-        return res.status(201).json({
-          key: key.get_key(),
-          payment_reference: payment_reference.public_id,
-          charged: payment_reference.amount,
+  return Key.GET_NEW_KEY()
+    .then((key) => {
+      return PaymentReference.CREATE_NEW_REQUEST(
+        amount,
+        key.get_key(),
+        dayjs().add("10", "years")
+      ).then((payment_reference) => {
+        return payment_reference.chargeKey().then(() => {
+          return res.status(201).json({
+            key: key.get_key(),
+            payment_reference: payment_reference.public_id,
+            charged: payment_reference.amount,
+          });
         });
-      })
-    });
-  }).catch((reason) => {
-    res.status(423).json({
-      code: 423,
-      error: reason,
-      charged: 0,
+      });
+    })
+    .catch((reason) => {
+      res.status(423).json({
+        code: 423,
+        error: reason,
+        charged: 0,
+      });
     });
-  });
 });
 
 router.get("/key/:key", (req, res) => {
@@ -111,23 +114,29 @@ router.post("/key/:key/charge", (req, res) => {
       return;
     }
   }
-  return Key.GET_KEY(req.params.key).then(key => {
-    return PaymentReference.CREATE_NEW_REQUEST(amount, key.get_key(), dayjs().add("10", "years")).then(payment_reference => {
-      return payment_reference.chargeKey().then(() => {
-        return res.status(201).json({
-          key: key.get_key(),
-          payment_reference: payment_reference.public_id,
-          charged: payment_reference.amount,
+  return Key.GET_KEY(req.params.key)
+    .then((key) => {
+      return PaymentReference.CREATE_NEW_REQUEST(
+        amount,
+        key.get_key(),
+        dayjs().add("10", "years")
+      ).then((payment_reference) => {
+        return payment_reference.chargeKey().then(() => {
+          return res.status(201).json({
+            key: key.get_key(),
+            payment_reference: payment_reference.public_id,
+            charged: payment_reference.amount,
+          });
         });
-      })
-    });
-  }).catch((reason) => {
-    res.status(423).json({
-      code: 423,
-      error: reason,
-      charged: 0,
+      });
+    })
+    .catch((reason) => {
+      res.status(423).json({
+        code: 423,
+        error: reason,
+        charged: 0,
+      });
     });
-  });
 });
 
 router.get("/token/pubkey", async (req, res) => {
diff --git a/pass/routes/checkout/cash.js b/pass/routes/checkout/cash.js
index 6e35901..909ba58 100644
--- a/pass/routes/checkout/cash.js
+++ b/pass/routes/checkout/cash.js
@@ -1,13 +1,7 @@
 var express = require("express");
-const Order = require("../../app/Order");
-const Manual = require("../../app/payment_processor/Manual");
-const config = require("config");
 var router = express.Router({ mergeParams: true });
 const { query, validationResult, matchedData } = require("express-validator");
 const dayjs = require("dayjs");
-const crypto = require("crypto");
-const RedisClient = require("../../app/RedisClient");
-const Cash = require("../../app/payment_processor/Cash");
 const PaymentReference = require("../../app/PaymentReference");
 
 router.use("/", (req, res, next) => {
diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js
deleted file mode 100644
index c035186..0000000
--- a/pass/routes/checkout/checkout.js
+++ /dev/null
@@ -1,184 +0,0 @@
-const Crypto = require("../../app/Crypto.js");
-const Order = require("../../app/Order.js");
-const config = require("config");
-const dayjs = require("dayjs");
-
-var express = require("express");
-var router = express.Router();
-const { query, body, validationResult } = require("express-validator");
-
-/* GET home page. */
-router.get(
-  "/",
-  query("amount")
-    .isInt({ min: -1, max: 12 })
-    .withMessage("Amount needs to be between 0 and 4.")
-    .toInt(),
-  async function (req, res, next) {
-    const errors = validationResult(req);
-    if (!errors.isEmpty()) {
-      return res.status(400).json({ errors: errors.array() });
-    }
-
-    /**
-     * The user interface allows either 100 searches or steps of 250 searches (up to 12 * 250 = 3000)
-     * 100 searches are a little bit more expensive than 250
-     *
-     * an amount of 0 corresponds to 100 searches
-     * an amount of 1-12 corresponds to 1-12 * 250 searches
-     */
-    let params = {
-      amount: req.query.amount === 0 ? 1 : req.query.amount,
-      unit_size: req.query.amount === 0 ? 100 : 250,
-      price_per_unit:
-        req.query.amount === 0 ? Order.PRICE_FOR_100 : Order.PRICE_FOR_250,
-      order_id: await generate_unique_order_id(),
-      payments: {
-        paypal: {
-          client_id: config.get(`payments.paypal.client_id`),
-        },
-      },
-    };
-
-    let crypto = new Crypto();
-    let order_date = dayjs();
-    let expiration_date = order_date.add(
-      Order.PURCHASE_STORAGE_TIME_MONTHS,
-      "month"
-    );
-    let private_key = await crypto.private_key_get(order_date, expiration_date);
-    params.crypto = {
-      N: private_key.keyPair.n,
-      E: private_key.keyPair.e,
-    };
-    params.expires_at = expiration_date.format("YYYY-MM-DD");
-
-    // Generate hmac hash of the payment data so we are able to verify them when the client submits them again
-    // Generate hmac hash of the payment data so we are able to verify them when the client submits them again
-    params.integrity = crypto.createIntegrityHash(
-      params.order_id,
-      params.expires_at,
-      params.amount,
-      params.unit_size,
-      params.price_per_unit,
-      params.crypto.N,
-      params.crypto.E
-    );
-
-    res.render("checkout/checkout", params);
-  }
-);
-
-router.use(
-  "/payment/order",
-  body("amount")
-    .isInt({ min: 1, max: 12 })
-    .withMessage("Invalid amount submitted")
-    .toInt(),
-  body("unit_size").isIn(["100", "250"]).toInt(),
-  body("price_per_unit")
-    .isCurrency({ symbol: "", allow_negatives: false, thousands_separator: "" })
-    .withMessage("Invalid Price Value.")
-    .toFloat(),
-  body("order_id").isInt({ min: 0 }).withMessage("Invalid `order_id` value"),
-  body("expires_at")
-    .isDate()
-    .matches(/^\d{4}-\d{2}-\d{2}$/)
-    .withMessage("Expiration date is not correct"),
-  body("public_key_e")
-    .isNumeric({ no_symbols: true })
-    .withMessage("Invalid Public Key"),
-  body("public_key_n")
-    .isNumeric({ no_symbols: true })
-    .withMessage("Invalid Public Key"),
-  body("integrity")
-    .isHash("sha256")
-    .custom((value, { req }) => {
-      let crypto = new Crypto();
-      if (
-        !crypto.validateIntegrityHash(
-          value,
-          req.body.order_id,
-          req.body.expires_at,
-          req.body.amount,
-          req.body.unit_size,
-          req.body.price_per_unit,
-          req.body.public_key_n,
-          req.body.public_key_e
-        )
-      ) {
-        console.log("Invalid integrity");
-        return Promise.reject("Integrity is not matching");
-      }
-      return true;
-    })
-    .withMessage(
-      "Value for `integrity` must be a SHA256 Hash and validate against the purchase data."
-    ),
-  body("encrypted_sales_receipts")
-    .custom((value, { req }) => {
-      if (!Array.isArray(value)) {
-        return Promise.reject("Parameter is not an array");
-      }
-      for (let i = 0; i < value.length; i++) {
-        if (!/^\d+$/.test(value[i])) {
-          return Promise.reject(
-            "Encrypted Sales Receipt contains invalid data"
-          );
-        }
-      }
-      let expected_ticket_count = (req.body.amount * req.body.unit_size) / 50;
-      if (expected_ticket_count % 1 !== 0) {
-        return Promise.reject("Expected ticket count is not an integer.");
-      }
-      if (value.length !== expected_ticket_count) {
-        return Promise.reject("Two many receipts compared to the order.");
-      }
-      return true;
-    })
-    .withMessage("Invalid Sales Receipt"),
-  (req, res, next) => {
-    // This route gets called when the user initiates a payment: Validate the submitted data here
-    const errors = validationResult(req);
-    if (!errors.isEmpty()) {
-      return res.status(400).json({ errors: errors.array() });
-    }
-
-    next("route");
-  }
-);
-
-/** Cancel is the same for all payment gateways */
-router.post("/payment/order/*/cancel", (req, res) => {
-  Order.LOAD_ORDER_FROM_ID(req.body.order_id).then((loaded_order) => {
-    if (loaded_order.isPaymentComplete()) {
-      res.status(400).json({
-        msg: "Cannot delete a completed order",
-      });
-      return;
-    }
-    loaded_order.delete().then((success) => {
-      if (success) {
-        res.status(200).json({
-          msg: "Order deleted",
-        });
-      } else {
-        res.status(400).json({
-          errors: [
-            {
-              msg: "Could not delete specified order",
-            },
-          ],
-        });
-      }
-    });
-  });
-});
-
-var developmentRouter = require("./development.js");
-router.use("/payment/order/development", developmentRouter);
-
-var paypalRouter = require("./paypal.js");
-router.use("/payment/order/paypal", paypalRouter);
-
-module.exports = router;
diff --git a/pass/routes/checkout/development.js b/pass/routes/checkout/development.js
deleted file mode 100644
index d373885..0000000
--- a/pass/routes/checkout/development.js
+++ /dev/null
@@ -1,56 +0,0 @@
-var express = require("express");
-var router = express.Router();
-
-const config = require("config");
-const Order = require("../../app/Order.js");
-
-router.post(
-  "/",
-  (req, res, next) => {
-    if (process.env.NODE_ENV !== "development") {
-      res.status(400).json({
-        errors: [
-          { msg: "Payment method only allowed in development environment." },
-        ],
-      });
-    } else {
-      next();
-    }
-  },
-  (req, res) => {
-    // Order data is validated: Create and store the order in the redis database
-    let order = new Order(
-      req.body.order_id,
-      req.body.expires_at,
-      req.body.amount,
-      req.body.unit_size,
-      req.body.price_per_unit,
-      req.body.encrypted_sales_receipts
-    );
-
-    order
-      .save()
-      .then(() => {
-        order.signOrder().then(() => {
-          order.save().then(() => {
-            res.json({
-              order_id: req.body.order_id,
-              expires_at: req.body.expires_at,
-              signatures: order.getSignatures(),
-            });
-          });
-        });
-      })
-      .catch((reason) => {
-        return res.status(400).json({
-          errors: [
-            {
-              msg: reason,
-            },
-          ],
-        });
-      });
-  }
-);
-
-module.exports = router;
diff --git a/pass/routes/checkout/manual.js b/pass/routes/checkout/manual.js
index b03b72e..b5ac5ce 100644
--- a/pass/routes/checkout/manual.js
+++ b/pass/routes/checkout/manual.js
@@ -1,7 +1,6 @@
 var express = require("express");
 const Order = require("../../app/Order");
-const Manual = require("../../app/payment_processor/Manual");
-const config = require("config");
+const PaymentReference = require("../../app/PaymentReference");
 var router = express.Router({ mergeParams: true });
 
 router.use("/", (req, res, next) => {
@@ -24,27 +23,14 @@ router.get("/", (req, res) => {
 });
 
 router.post("/", (req, res) => {
-  /**
-   * @type {Order}
-   */
-  let new_order = null;
-  Order.CREATE_NEW_ORDER(
+  return PaymentReference.CREATE_NEW_REQUEST(
     req.params.amount,
-    req.data.key.key.get_key(),
-    new Manual(req.body.note),
-    req.t
+    req.data.key.key.get_key()
   )
-    .then((order) => {
-      new_order = order;
-      return order.captureOrder();
-    })
-    .then(() => new_order.chargeKey())
+    .then((payment_reference) => payment_reference.chargeKey())
     .then(() => {
       let redirect_url =
-        `${res.locals.baseDir}/key/` +
-        req.data.key.key.get_key() +
-        "/orders/" +
-        new_order.getOrderID();
+        `${res.locals.baseDir}/key/` + req.data.key.key.get_key();
       res.redirect(redirect_url);
     });
 });
diff --git a/pass/routes/checkout/micropayment.js b/pass/routes/checkout/micropayment.js
index abebc4e..f3a5e4b 100644
--- a/pass/routes/checkout/micropayment.js
+++ b/pass/routes/checkout/micropayment.js
@@ -2,31 +2,39 @@ var express = require("express");
 var router = express.Router({ mergeParams: true });
 
 const config = require("config");
-const Order = require("../../app/Order");
 const PaymentReference = require("../../app/PaymentReference");
 const Micropayment = require("../../app/payment_processor/Micropayment");
 const crypto = require("crypto");
 
 router.get("/event", (req, res) => {
   Micropayment.VERIFY_WEBHOOK(req.query)
-    .then(() => PaymentReference.LOAD_FROM_PUBLIC_ID(req.query.paymentreference))
+    .then(() =>
+      PaymentReference.LOAD_FROM_PUBLIC_ID(req.query.paymentreference)
+    )
     .then((payment_reference) => {
       let price = (req.query.amount / 100).toFixed(2);
       if (req.query.function === "refund") {
         price *= -1;
       }
-      let redirect_url = new URL(`${res.locals.baseDir}/key/` + payment_reference.key.get_key() + "/orders/" + payment_reference.public_id);
+      let redirect_url = new URL(
+        `${res.locals.baseDir}/key/` +
+          payment_reference.key.get_key() +
+          "/orders/" +
+          payment_reference.public_id
+      );
       if (req.query.function !== "billing" && req.query.function !== "refund") {
         return redirect_url;
       }
-      return payment_reference.createPayment({
-        price: (req.query.amount / 100).toFixed(2),
-        converted_currency: req.query.currency,
-        converted_price: (req.query.amount / 100).toFixed(2),
-        payment_processor: Micropayment.NAME,
-        payment_processor_id: req.query.auth,
-        payment_processor_data: req.query
-      }).then(payment => redirect_url);
+      return payment_reference
+        .createPayment({
+          price: (req.query.amount / 100).toFixed(2),
+          converted_currency: req.query.currency,
+          converted_price: (req.query.amount / 100).toFixed(2),
+          payment_processor: Micropayment.NAME,
+          payment_processor_id: req.query.auth,
+          payment_processor_data: req.query,
+        })
+        .then(() => redirect_url);
     })
     .then((redirect_url) => {
       let response = {
@@ -82,39 +90,41 @@ router.get("/:service", (req, res) => {
 });
 
 router.post("/:service", async (req, res) => {
-  return PaymentReference.CREATE_NEW_REQUEST(req.data.checkout.amount,
-    req.data.key.key.get_key()).then(payment_reference => {
-      /**
-         * @type {URL}
-         */
-      let payment_window_url =
-        req.data.checkout.payment.micropayment.payment_window_url;
-      let searchParams = {
-        project: config.get("payments.micropayment.project"),
-        amount: payment_reference.price * 100,
-        title: "MetaGer Schlüssel",
-        mp_user_id: payment_reference.public_id,
-        producttype: "quantity",
-        paytext: `${payment_reference.amount} MetaGer Token`,
-        testmode: process.env.NODE_ENV === "development" ? "1" : "0",
-        paymentreference: payment_reference.public_id,
-        vatinfo: "1",
-      };
-      let seal = "";
-      for (let key in searchParams) {
-        seal += `${key}=${searchParams[key]}&`;
-      }
-      seal = seal.replace(/&$/, "");
-      seal += config.get("payments.micropayment.access_key");
+  return PaymentReference.CREATE_NEW_REQUEST(
+    req.data.checkout.amount,
+    req.data.key.key.get_key()
+  ).then((payment_reference) => {
+    /**
+     * @type {URL}
+     */
+    let payment_window_url =
+      req.data.checkout.payment.micropayment.payment_window_url;
+    let searchParams = {
+      project: config.get("payments.micropayment.project"),
+      amount: payment_reference.price * 100,
+      title: "MetaGer Schlüssel",
+      mp_user_id: payment_reference.public_id,
+      producttype: "quantity",
+      paytext: `${payment_reference.amount} MetaGer Token`,
+      testmode: process.env.NODE_ENV === "development" ? "1" : "0",
+      paymentreference: payment_reference.public_id,
+      vatinfo: "1",
+    };
+    let seal = "";
+    for (let key in searchParams) {
+      seal += `${key}=${searchParams[key]}&`;
+    }
+    seal = seal.replace(/&$/, "");
+    seal += config.get("payments.micropayment.access_key");
 
-      let hasher = crypto.createHash("md5");
-      seal = hasher.update(seal).digest("hex");
-      searchParams.seal = seal;
+    let hasher = crypto.createHash("md5");
+    seal = hasher.update(seal).digest("hex");
+    searchParams.seal = seal;
 
-      payment_window_url.search = (new URLSearchParams(searchParams)).toString();
+    payment_window_url.search = new URLSearchParams(searchParams).toString();
 
-      res.redirect(payment_window_url.toString());
-    });
+    res.redirect(payment_window_url.toString());
+  });
 });
 
 module.exports = router;
diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js
index c92397d..a337609 100644
--- a/pass/routes/checkout/paypal.js
+++ b/pass/routes/checkout/paypal.js
@@ -2,10 +2,7 @@ var express = require("express");
 var router = express.Router({ mergeParams: true });
 
 const config = require("config");
-const Order = require("../../app/Order.js");
 const PaymentReference = require("../../app/PaymentReference.js");
-const Payment = require("../../app/Payment");
-const Key = require("../../app/Key.js");
 const Paypal = require("../../app/payment_processor/Paypal.js");
 const { body } = require("express-validator");
 
@@ -73,7 +70,7 @@ router.post("/:funding_source/order/create", async (req, res) => {
             600,
             paypal.getOrderId()
           )
-          .then((result) => {
+          .then(() => {
             return res.status(200).json({
               payment_reference: payment_reference.public_id,
               paypal_order_id: paypal.getOrderId(),
@@ -224,24 +221,4 @@ module.exports = router;
 
 //////////////////////
 
-async function refundPayment(capture_ids) {
-  const accessToken = await generateAccessToken();
-  let promises = [];
-  capture_ids.forEach((capture_id) => {
-    promises.push(
-      fetch(`${base}/v2/payments/captures/${capture_id}/refund`, {
-        method: "post",
-        headers: {
-          Authorization: `Bearer ${accessToken}`,
-          "Content-Type": "application/json",
-        },
-        body: JSON.stringify({
-          note_to_payer: "Something went wrong when processing your payment.",
-        }),
-      })
-    );
-  });
-  return Promise.all(promises);
-}
-
 // generate an access token using client id and app secret
diff --git a/pass/routes/index.js b/pass/routes/index.js
index a377290..7740df2 100644
--- a/pass/routes/index.js
+++ b/pass/routes/index.js
@@ -1,5 +1,4 @@
 var express = require("express");
-const Order = require("../app/Order.js");
 var router = express.Router({ mergeParams: true });
 
 var lessMiddleware = require("less-middleware");
@@ -23,7 +22,7 @@ router.use("/admin", adminRouter);
 router.use("/help", helpRouter);
 
 /* GET home page. */
-router.get("/", function (req, res, next) {
+router.get("/", function (req, res) {
   req.i18n.setDefaultNamespace("index"); // Default NS for localized Strings
   res.render("index");
 });
diff --git a/pass/routes/orders/orders.js b/pass/routes/orders/orders.js
index 8365b07..028cd90 100644
--- a/pass/routes/orders/orders.js
+++ b/pass/routes/orders/orders.js
@@ -7,7 +7,6 @@ const {
   validationResult,
   matchedData,
 } = require("express-validator");
-const Order = require("../../app/Order");
 const OrderReceipt = require("../../app/pdf/OrderReceipt");
 const PaymentReference = require("../../app/PaymentReference");
 const Payment = require("../../app/Payment");
@@ -22,7 +21,7 @@ router.use("/", (req, res, next) => {
   }
   next();
 });
-router.get("/", (req, res, next) => {
+router.get("/", (req, res) => {
   req.data.page = "order";
   req.data.css.push(`${res.locals.baseDir}/styles/orders/orders.css`);
   res.render("key", req.data);
@@ -31,7 +30,7 @@ router.get("/", (req, res, next) => {
 router.post(
   "/",
   body("payment_reference").matches(/^(Z)?(\d+)$/),
-  (req, res, next) => {
+  (req, res) => {
     let errors = validationResult(req);
     if (!errors.isEmpty()) {
       req.data.page = "order";
@@ -60,7 +59,9 @@ router.post(
             res.render("key", req.data);
           } else {
             res.redirect(
-              `${res.locals.baseDir}/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference}#order`
+              `${res.locals.baseDir}/key/${req.data.key.key.get_key()}/orders/${
+                queryData.payment_reference
+              }#order`
             );
           }
         });
@@ -118,21 +119,31 @@ router.use(
         req.data.order.payments = payments;
 
         req.data.css.push(`${res.locals.baseDir}/styles/orders/order.css`);
-        req.data.links.order_url = `${res.locals.baseDir
-          }/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference.public_id
-          }#order`;
-        req.data.links.order_actions_base = `${res.locals.baseDir
-          }/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference.public_id
-          }`;
-        req.data.links.receipt_url = `${res.locals.baseDir
-          }/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference.public_id
-          }/pdf`;
-        req.data.links.invoice_url = `${res.locals.baseDir
-          }/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference.public_id
-          }/invoice#invoice-form`;
-        req.data.links.refund_url = `${res.locals.baseDir
-          }/key/${req.data.key.key.get_key()}/orders/${queryData.payment_reference.public_id
-          }/refund#refund-form`;
+        req.data.links.order_url = `${
+          res.locals.baseDir
+        }/key/${req.data.key.key.get_key()}/orders/${
+          queryData.payment_reference.public_id
+        }#order`;
+        req.data.links.order_actions_base = `${
+          res.locals.baseDir
+        }/key/${req.data.key.key.get_key()}/orders/${
+          queryData.payment_reference.public_id
+        }`;
+        req.data.links.receipt_url = `${
+          res.locals.baseDir
+        }/key/${req.data.key.key.get_key()}/orders/${
+          queryData.payment_reference.public_id
+        }/pdf`;
+        req.data.links.invoice_url = `${
+          res.locals.baseDir
+        }/key/${req.data.key.key.get_key()}/orders/${
+          queryData.payment_reference.public_id
+        }/invoice#invoice-form`;
+        req.data.links.refund_url = `${
+          res.locals.baseDir
+        }/key/${req.data.key.key.get_key()}/orders/${
+          queryData.payment_reference.public_id
+        }/refund#refund-form`;
         /*if (req.data.order.order.isReceiptCreated()) {
         req.data.order.download_invoice_url = `${res.locals.baseDir}/key/${req.data.key.key.get_key()
           }/orders/${req.data.order.order.getOrderID()}/invoice/download`;
@@ -177,7 +188,7 @@ router.get("/:payment_reference/:order_id/pdf", (req, res) => {
             .status(200)
             .header({
               "Content-Type": "application/pdf",
-              "Content-Disposition": `inline, filename=${req.params.order_id}.pdf`
+              "Content-Disposition": `inline, filename=${req.params.order_id}.pdf`,
             })
             .send(Buffer.concat(data));
         })
diff --git a/pass/routes/redeem.js b/pass/routes/redeem.js
deleted file mode 100644
index b11108d..0000000
--- a/pass/routes/redeem.js
+++ /dev/null
@@ -1,247 +0,0 @@
-var express = require("express");
-var router = express.Router();
-const { query, body, validationResult } = require("express-validator");
-
-const config = require("config");
-const dayjs = require("dayjs");
-const Crypto = require("../app/Crypto");
-var customParseFormat = require("dayjs/plugin/customParseFormat");
-const Key = require("../app/Key");
-const Order = require("../app/Order");
-const path = require('path');
-const fs = require('fs');
-const readline = require('readline');
-dayjs.extend(customParseFormat);
-
-/**
- * This routes are called after a purchase and intended to
- * exchange a purchase receipt against a MetaGer-Pass Key
- *
- * This middleware validates the purchase receipt for all of the other routes
- */
-router.use(
-  "/",
-  body("expiration_month")
-    .matches(/^\d{4}-\d{2}$/)
-    .custom((expiration_month, { req }) => {
-      dayjs(expiration_month + "-01");
-      return true;
-    })
-    .customSanitizer(expiration_month => {
-      return dayjs(expiration_month);
-    })
-    .withMessage("Invalid Expiration Month supplied"),
-  body("generation_month")
-    .matches(/^\d{4}-\d{2}$/)
-    .custom((generation_month, { req }) => {
-      return dayjs(generation_month + "-01");
-    })
-    .customSanitizer(generation_month => {
-      return dayjs(generation_month);
-    })
-    .withMessage("Invalid Generation Month supplied"),
-  body("receipts").custom(async (receipts, { req }) => {
-    return new Promise((resolve, reject) => {
-      new Crypto()
-        .validateMetaGerPassCode(
-          req.body.generation_month,
-          req.body.expiration_month,
-          req.body.metager_pass_codes
-        )
-        .then(() => {
-          resolve(true);
-        })
-        .catch((reason) => {
-          reject(reason);
-        });
-    });
-  }),
-  (req, res, next) => {
-    const errors = validationResult(req);
-    if (!errors.isEmpty()) {
-      return res.status(400).json({ errors: errors.array() });
-    }
-    next("route");
-  }
-);
-
-/* Recharge a MetaGer-Pass Key */
-router.post("/", body("metager_pass_key").isWhitelisted(Key.KEY_CHARSET).isLength({ min: 6, max: 20 }), async (req, res, next) => {
-  const errors = validationResult(req);
-  if (!errors.isEmpty()) {
-    return res.status(400).json({ errors: errors.array() });
-  }
-
-  let max_recharge_tries = 10;
-  let max_recharge_tries_hours = 1;   // Defines in which time frame the recharge tries are counted; recharge tries get reset after this amount of hours without recharge try have passed
-
-  let charge_amount = 0;
-
-  // Redis Client
-  let Redis = require("ioredis");
-  let redis_client = new Redis({
-    host: config.get("redis.host"),
-  });
-  // Dayjs
-  let dayjs = require("dayjs");
-
-  let key_recharge_cache_keys = [];
-  let key_recharge_cache_prefix = "recharge_key_cache";
-  for (let i = 0; i < req.body.metager_pass_codes.length; i++) {
-    let code = req.body.metager_pass_codes[i].code;
-    key_recharge_cache_keys.push(key_recharge_cache_prefix + "_" + code);
-    charge_amount += Order.PACKET_SIZE;
-  }
-
-  // Check one or more of the codes was already used to redeem a MetaGer-Pass Key
-  let order_month = dayjs(req.body.generation_month);
-  let redeem_file_path = path.join(
-    config.get("storage.data_path"),
-    process.env.NODE_ENV,
-    order_month.format("YYYY"),
-    order_month.format("MM"),
-    "redeemed.json");
-  if (!fs.existsSync(path.dirname(redeem_file_path))) {
-    fs.mkdirSync(path.dirname(redeem_file_path), { recursive: true });
-  }
-
-  if (fs.existsSync(redeem_file_path)) {
-    let rl = readline.createInterface({
-      input: fs.createReadStream(redeem_file_path),
-      output: process.stdout,
-      terminal: false
-    });
-    for await (const line of rl) {
-      for (let i = 0; i < req.body.metager_pass_codes.length; i++) {
-        if (req.body.metager_pass_codes[i].code === line.trim()) {
-          return res.status(400).json({
-            status: "FAILED",
-            msg: ["One or more of the provided MetaGer-Pass Codes are already redeemed."]
-          });
-        }
-      }
-    }
-  }
-
-
-  let recharge_tries = 0;
-  redis_client.mget(key_recharge_cache_keys).then(async response => {
-    for (let i = 0; i < response.length; i++) {
-      let tries = response[i];
-      if (tries && tries > recharge_tries) {
-        recharge_tries = tries;
-      }
-    }
-
-    if (recharge_tries >= max_recharge_tries) {
-      res.status(400).json({
-        status: "FAILED",
-        msg: "Too many failed attempts against non existing MetaGer-Pass Keys. Please try again later."
-      });
-    } else {
-      recharge_tries++;
-
-      let redis_pipeline = redis_client.pipeline();
-      let expiration = dayjs().add(max_recharge_tries_hours, "hour");
-      for (let i = 0; i < key_recharge_cache_keys.length; i++) {
-        redis_pipeline.set(key_recharge_cache_keys[i], recharge_tries);
-        redis_pipeline.expireat(key_recharge_cache_keys[i], expiration.unix());
-      }
-      await redis_pipeline.exec();
-
-      Key.CHARGE_EXISTING_KEY(req.body.metager_pass_key, charge_amount).then(result => {
-        // Key is charged store the redeem codes into filesystem so they can only be used once
-        for (let i = 0; i < req.body.metager_pass_codes.length; i++) {
-          fs.appendFileSync(redeem_file_path, req.body.metager_pass_codes[i].code + "\n");
-        }
-
-        res.json(result);
-      }).catch(reason => {
-        res.status(400).json({
-          status: "FAILED",
-          msg: [reason]
-        });
-      });
-    }
-  });
-});
-
-/* Create a new MetaGer-Pass Key from purchase receipt */
-router.post("/create", async (req, res, next) => {
-  let key = "";
-
-  // Redis Client
-  let Redis = require("ioredis");
-  let redis_client = new Redis({
-    host: config.get("redis.host"),
-  });
-  // Dayjs
-  let dayjs = require("dayjs");
-
-  let key_cache_redis_keys = [];
-  let key_redis_cache_prefix = "create_key_cache";
-  // Check if the user already created a key which is in redis
-  for (let i = 0; i < req.body.metager_pass_codes.length; i++) {
-    let code = req.body.metager_pass_codes[i].code;
-    key_cache_redis_keys.push(key_redis_cache_prefix + "_" + code);
-  }
-  redis_client.mget(key_cache_redis_keys).then(async response => {
-    let existing_key = null;
-    let existing_key_expiration = null;
-    // Check if there is at least one existing key associated with the pass codes
-    for (let i = 0; i < response.length; i++) {
-      if (response[i]) {
-        existing_key = response[i];
-        existing_key_expiration = await redis_client.expiretime(key_cache_redis_keys[i]);
-        break;
-      }
-    }
-    if (existing_key) {
-      // If there is we will return it and store this key to all the empty keys
-      for (let i = 0; i < response.length; i++) {
-        if (!response[i]) {
-          await redis_client.set(key_cache_redis_keys[i], existing_key);
-          await redis_client.expireat(key_cache_redis_keys[i], existing_key_expiration);
-        }
-      }
-      res.json({
-        status: "SUCCESS",
-        metager_pass_key: {
-          key: existing_key,
-          valid_until: dayjs.unix(existing_key_expiration).format()
-        }
-      });
-    } else {
-      // There is no Key yet. Let's generate one
-      let expiration = (new dayjs()).add(6, 'hour').unix();
-
-      Key.CREATE_NEW_KEY(expiration).then(async key => {
-        for (let i = 0; i < key_cache_redis_keys.length; i++) {
-          await redis_client.set(key_cache_redis_keys[i], key);
-          await redis_client.expireat(key_cache_redis_keys[i], expiration);
-        }
-        res.json({
-          status: "SUCCESS",
-          metager_pass_key: {
-            key: key,
-            valid_until: dayjs.unix(expiration).format()
-          }
-        });
-      }).catch(reason => {
-        res.status(400).json({
-          status: "FAILURE",
-          msg: reason
-        });
-      });
-    }
-  }).catch(reason => {
-    res.status(400).json({
-      status: "FAILURE",
-      msg: reason
-    });
-  });
-
-
-});
-
-module.exports = router;
-- 
GitLab