diff --git a/pass/app/Crypto.js b/pass/app/Crypto.js index 46853a04a9ac291241edf65e864b76904ddcc004..72f0b1ffad15b786d6bbb9b60bed3ff7c410cc1e 100644 --- a/pass/app/Crypto.js +++ b/pass/app/Crypto.js @@ -66,6 +66,24 @@ class Crypto { return private_key; } + + /** + * Creates an hmac hash for purchase data so we can check it later + */ + createIntegrityHash(order_id, amount, price, public_key_n, public_key_e) { + let data_to_hash = JSON.stringify({ + order_id: order_id, + amount: amount, + price: price, + public_key_n: public_key_n, + public_key_e: public_key_e, + }); + let forge = require("node-forge"); + let hmac = forge.hmac.create(); + hmac.start("sha256", config.get("crypto.hmac_integrity_seed")); + hmac.update(data_to_hash); + return hmac.digest().toHex(); + } } module.exports = Crypto; diff --git a/pass/resources/js/checkout.js b/pass/resources/js/checkout.js index 88cec473df9bfaefa63e2980962602b9f56cfd99..b60c0739fb1d09c38d016ad014348f75811a4741 100644 --- a/pass/resources/js/checkout.js +++ b/pass/resources/js/checkout.js @@ -14,9 +14,9 @@ function one_generate_encrypted_sales_receipt() { ); let next_step_container = document.getElementById("execute-payment"); - let amount = document.getElementById("payment-container").dataset.amount; - let N = document.getElementById("payment-container").dataset.public_n; - let E = document.getElementById("payment-container").dataset.public_e; + let amount = document.querySelector("input[name=amount]").value; + let N = document.querySelector("input[name=public_key_n]").value; + let E = document.querySelector("input[name=public_key_e]").value; for (let i = 0; i < amount; i++) { let uuid = uuid_generator.v4(); @@ -47,6 +47,7 @@ function execute_payment_paypal(e) { paypal_client .loadScript({ "client-id": client_id, + currency: "EUR", }) .then((paypal) => { paypal diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js index 0c59f539e5b4b7f0bf6ff3b9de1ed71508a42b6e..1bf5ee9d524c748da8de19bb444c230f4841cd4a 100644 --- a/pass/routes/checkout/checkout.js +++ b/pass/routes/checkout/checkout.js @@ -14,7 +14,7 @@ router.get( query("amount") .isInt({ min: 1, max: 4 }) .withMessage("Amount needs to be between 1 and 4."), - function (req, res, next) { + async function (req, res, next) { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); @@ -24,6 +24,7 @@ router.get( amount: req.query.amount, unit_size: 100, price: req.query.amount * price_for_100, + order_id: await generate_unique_order_id(), payments: { paypal: { client_id: config.get("payments.paypal.testing.client_id"), @@ -32,14 +33,51 @@ router.get( }; let crypto = new Crypto(); - crypto.private_key_get_current().then((private_key) => { - params.crypto = { - N: private_key.n, - E: private_key.e, - }; - res.render("checkout/checkout", params); - }); + let private_key = await crypto.private_key_get_current(); + params.crypto = { + N: private_key.n, + E: private_key.e, + }; + + // 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.amount, + params.price, + params.crypto.N, + params.crypto.E + ); + + res.render("checkout/checkout", params); } ); module.exports = router; + +async function 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 Redis = require("ioredis"); + let redis_connection = new Redis({ + host: config.get("redis.host"), + }); + 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 + let order_lock = await redis_connection.setnx( + "" + order_base + order_mutex, + true + ); + if (order_lock === 1) { + await redis_connection.expire(order_id, 5); + order_id = "" + order_base + order_mutex; + console.log(order_id); + } else { + console.log("Couldn't acquire lock"); + order_mutex++; + } + } while (order_id === null); + return order_id; +} diff --git a/pass/views/checkout/checkout.ejs b/pass/views/checkout/checkout.ejs index 21f2b8fa972fcaf0301f5c408c0f9ecfdc103d26..b7c6bdc16c81cc72dccc315c91894e3220a4f137 100644 --- a/pass/views/checkout/checkout.ejs +++ b/pass/views/checkout/checkout.ejs @@ -1,7 +1,13 @@ <%- include('../templates/page_header', {css: ['/styles/checkout.css'], js: ['/js/checkout.js']}); %> <main> - <div id="payment-container" data-amount="<%- amount _%>" data-public_e="<%- crypto.E _%>" data-public_n="<%- crypto.N %>"> + <input type="hidden" name="order_id" value="<%- order_id %>"> + <input type="hidden" name="amount" value="<%- amount %>"> + <input type="hidden" name="price" value="<%- price %>"> + <input type="hidden" name="public_key_e" value="<%- crypto.E %>"> + <input type="hidden" name="public_key_n" value="<%- crypto.N %>"> + <input type="hidden" name="integrity" value="<%- integrity %>"> + <div id="payment-container"> <div id="heading">Ihr Einkauf: <%- amount * unit_size %> Suchanfragen für <%- price %>€</div> <div id="generate-sales-receipt" class="step current"> <div class="section-heading">