diff --git a/pass/app/Key.js b/pass/app/Key.js index f4743bfcb55830516ec230db4e6dc34c5ec77dc1..fc6c802ce7438c4b69685752b469cc32e07365a1 100644 --- a/pass/app/Key.js +++ b/pass/app/Key.js @@ -94,26 +94,19 @@ class Key { static async DISCHARGE_KEY(key, amount) { let redis_client = Key.REDIS_CLIENT; - // Check if key exists and get charge - redis_client.get(Key.DATABASE_PREFIX + key).then((current_amount) => { - if (current_amount && current_amount > 0) { - if (current_amount > amount) { - let expiration = require("dayjs")().add( - Key.EXPIRATION_AFTER_CHARGE_DAYS, - "day" + // Dicharge key and make sure it can't fall below 0 + return redis_client + .decrby(Key.DATABASE_PREFIX + key, amount) + .then((new_charge) => { + if (new_charge < 0) { + return redis_client.incrby( + Key.DATABASE_PREFIX + key, + Math.abs(new_charge) ); - return redis_client - .pipeline() - .set(Key.DATABASE_PREFIX + key, current_amount - amount) - .expireat(Key.DATABASE_PREFIX + key, expiration.unix()) - .exec(); } else { - return redis_client.del(Key.DATABASE_PREFIX + key); + return new_charge; } - } else { - throw "Key Does not exist or is not charged"; - } - }); + }); } } diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index c18dcd569dddc9038e34dcae01ca619c099a59df..1d6336292940557d12f5021b22b6e3f7313afb21 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -260,16 +260,20 @@ router.post("/webhook", async (req, res) => { } return Order.LOAD_ORDER_FROM_ID(order_id).then( /** @param {Order} order */ (order) => { - if (order.isPaymentComplete()) { + if (!order.isPaymentComplete()) { // Update Payment status let payment_link = order.getPaymentMethodLink(); - payment_link.payment_status = "REFUNDED"; - return order - .setPaymentMethodLink(payment_link) - .then(() => order.getKeyFromOrderLink()) - .then((key) => Key.DISCHARGE_KEY(key, order.getAmount())); + if (payment_link.payment_status !== "REFUNDED") { + payment_link.payment_status = "REFUNDED"; + return order + .setPaymentMethodLink(payment_link) + .then(() => order.getKeyFromOrderLink()) + .then((key) => Key.DISCHARGE_KEY(key, order.getAmount())); + } else { + throw "Order is already refunded"; + } } else { - throw "Order is already completed"; + throw "Order is already refunded"; } } ); diff --git a/pass/routes/orders/invoice.js b/pass/routes/orders/invoice.js index 822cda7478901f8a468452933e4dcf7c894b4977..3a699f7a9e66489bb9ac396ee83b84149dbae78b 100644 --- a/pass/routes/orders/invoice.js +++ b/pass/routes/orders/invoice.js @@ -82,7 +82,7 @@ router.post( let ejs = require("ejs"), fs = require("fs"), template = fs.readFileSync( - `${__dirname}/../views/orders/invoice_message.ejs`, + `${__dirname}/../../views/orders/invoice_message.ejs`, "utf-8" ); diff --git a/pass/routes/orders/refund.js b/pass/routes/orders/refund.js index 13b7a44dd26de1443305c8299d318def1a4ef06c..8dac122dc9c9ce43d841d20016c42a9f9d217b11 100644 --- a/pass/routes/orders/refund.js +++ b/pass/routes/orders/refund.js @@ -1,12 +1,13 @@ var express = require("express"); var router = express.Router({ mergeParams: true }); +const config = require("config"); + // 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.order.getAmount() ); req.data.order.refund = { count: refund_count, @@ -20,5 +21,74 @@ router.use("/", (req, res, next) => { router.get("/", (req, res) => { res.render("key", req.data); }); +router.post("/", (req, res, next) => { + req.data.order.refund.message = req.body.message; + // Refund values as sent back to us by the user. + // Used only to verify the amount we are refunding corresponds to what we showed the user + let user_amount = req.body.amount; // Amount of money refunded back to the user + let user_count = parseInt(req.body.count); // Amount of search request discharged from key + console.log(req.data.order.refund); + if ( + req.data.order.refund.amount.toFixed(2) !== user_amount || + req.data.order.refund.count !== user_count + ) { + // Something changed abort here + req.data.order.refund.error = "invalid_data"; + res.render("key", req.data); + } else { + let request_data = { + moderation: true, + amount: req.data.order.refund.amount, + count: req.data.order.refund.count, + }; + req.data.order.refund.moderation_url = `/key/${ + req.data.key.key + }/orders/${req.data.order.order.getOrderID()}/refund${new URLSearchParams( + request_data + ).toString()}`; + // Render the message + let ejs = require("ejs"), + fs = require("fs"), + template = fs.readFileSync( + `${__dirname}/../../views/orders/refund_message.ejs`, + "utf-8" + ); + + let message = ejs.render(template, req.data); + + // No validation errors. Try to create a new Ticket + fetch(`${config.get("app.osticket.url")}/api/tickets.json`, { + method: "post", + headers: { + "X-API-Key": config.get("app.osticket.api_key"), + "Content-Type": "application/json", + }, + body: JSON.stringify({ + alert: true, + autorespond: false, + source: "API", + name: "Metzgermeister Unbekannt", + email: "no-reply@metager.de", + subject: `MetaGer Schlüssel: Erstattung (${req.data.order.order.getOrderID()})`, + message: `data:text/html;charset=utf-8,${message}`, + topicId: 12, // ToDo change topic for english autoresponder + }), + }) + .then(async (response) => { + if (response.status != 201) { + console.error(await response.text()); + throw "Fehler beim Erstellen der Benachrichtigung,"; + } else { + req.data.order.refund.success = true; + res.render("key", req.data); + } + }) + .catch((reason) => { + console.log(reason); + req.data.order.refund.error = "send_email"; + res.render("key", req.data); + }); + } +}); module.exports = router; diff --git a/pass/views/orders/refund.ejs b/pass/views/orders/refund.ejs index 7d370ac1bfe9263b7fe27abdf849695631ad2c69..52ca0b4afe26e8963d50858b03a13ec1235bfcdc 100644 --- a/pass/views/orders/refund.ejs +++ b/pass/views/orders/refund.ejs @@ -6,16 +6,25 @@ <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 (typeof order.refund.success !== "undefined" && order.refund.success === true) { _%> + <p>Ihre Anfrage wurde uns erfolgreich zugestellt. Wir bearbeiten diese so schnell wie möglich. Je nach Zahlungsmethode kann es einige Tage dauern, bis eine Erstattung in Ihren Umsätzen sichtbar wird.</p> + <%_ } else { _%> + <form id="refund-form" method="post"> + <input type="hidden" name="amount" value="<%= order.refund.amount.toFixed(2) %>"> + <input type="hidden" name="count" value="<%= order.refund.count %>"> + <p>Sind Sie unzufrieden mit Ihrem Schlüssel? Das bedauern wir sehr! Selbstverständlich erstatten wir Ihnen in diesem Fall den Rechnungsbetrag. Eine Erstatung erfolgt stets auf das Gleiche Konto, welches bei der ursprünglichen Zahlung verwendet wurde. 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> + <%_ if(typeof order.refund.error !== "undefined") { _%> + <p class="error">Fehler beim Senden Ihrer Nachricht. Bitte versuchen Sie es später erneut.</p> + <%_ } _%> <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 diff --git a/pass/views/orders/refund_message.ejs b/pass/views/orders/refund_message.ejs new file mode 100644 index 0000000000000000000000000000000000000000..9d69456f58af2fb89e7c64391472930de2b35e8b --- /dev/null +++ b/pass/views/orders/refund_message.ejs @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> +</head> +<body> + <p>Eine Erstattung für die Bestellung mit der ID <span style="font-weight: bold"><%= order.order.getOrderID() %></span> wurde angefragt.</p> + <div style="display: grid; grid-template-columns: auto 1fr; gap: 1rem; align-items: center; justify-content: center;"> + <label for="count">Suchanfragen</label> + <input type="text" name="count" id="count" value="<%= order.refund.count %>" disabled> + <label for="amount">Erstattungsbetrag</label> + <input type="amount" name="amount" id="amount" value="<%= order.refund.amount.toFixed(2) %>" disabled> + <label for="message">Nachricht</label> + <textarea name="message" id="message" cols="30" rows="3" disabled><%= order.refund.message %></textarea> + <a href="<%= order.refund.moderation_url %>" target="_blank" style="display: block;text-decoration: none;border: 1px solid rgb(168, 84, 0);border-radius: 5px;padding: .2rem .5rem;background-color: rgb(185, 92, 0);color: white!important;font-weight: bold;grid-column: span 2;text-align: center;max-width: 15em;justify-self: center;">Erstattung prüfen</a> + </div> + <p style="margin-top: 1rem">Bitte Erstattungsdaten überprüfen und die Erstattung ggf. freigeben.</p> +</body> +</html> \ No newline at end of file