From 0d18968beaceb8cf520e1382e78e1a37577fb753 Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Wed, 16 Nov 2022 16:08:24 +0100 Subject: [PATCH] added basics for acquiring a key --- pass/public/styles/checkout.css | 2 +- pass/public/styles/checkout.less | 70 ++++++++++++++++++++++- pass/resources/js/checkout.js | 39 +++++++++++-- pass/resources/js/checkout_development.js | 1 + pass/resources/js/checkout_paypal.js | 1 + pass/routes/checkout/checkout.js | 3 +- pass/routes/checkout/development.js | 1 + pass/routes/checkout/paypal.js | 1 + pass/views/checkout/checkout.ejs | 45 ++++++++++++++- 9 files changed, 152 insertions(+), 11 deletions(-) diff --git a/pass/public/styles/checkout.css b/pass/public/styles/checkout.css index 86871c3..e5fa58a 100644 --- a/pass/public/styles/checkout.css +++ b/pass/public/styles/checkout.css @@ -1 +1 @@ -#payment-container{width:max-content;border:1px solid #777;border-radius:.2rem}#payment-container>#heading{border:1px solid #777;background-color:#eaeaea;padding:.5rem}#payment-container #payment-provider-container{display:flex;align-items:center}#payment-container #payment-provider-container>#payment-information{margin:0 auto}#payment-container #payment-provider-container>#payment-providers{border-right:1px solid #777;padding:.5rem}#payment-container #payment-provider-container>#payment-providers>h1{margin:0;font-size:.7rem;font-weight:normal}#payment-container #payment-provider-container>#payment-providers>ul{list-style-type:none;margin:0;padding:0}#payment-container #payment-provider-container>#payment-providers>ul>li{cursor:pointer;padding:.5rem 1rem;background-color:#f5f5f5}#payment-container #payment-provider-container>#payment-providers>ul>li[data-active="true"]{background-color:orange;color:white;font-weight:bold;-webkit-text-stroke:#777 .01rem}#payment-container #payment-provider-container>#payment-providers>ul>li:not(:last-child){border-bottom:1px dashed #777}#payment-container>.step{border:1px solid #777;padding:1rem;color:rgba(120,120,120,0.314)}#payment-container>.step>.section-heading{display:flex;gap:.5rem}#payment-container>.step>.section-heading>.status{width:1rem}#payment-container>.step>.section-heading>.status>*{display:none}#payment-container>.step>.section-heading>.content{flex-grow:1}#payment-container>.step>.section-heading>.location{text-align:right;border-left:1px solid rgba(120,120,120,0.314);padding-left:.6rem}#payment-container>.step>.section-body{display:none}#payment-container>.step.current{color:inherit}#payment-container>.step.current .loading{display:block}#payment-container>.step.current .location{color:#777;border-color:#777}#payment-container>.step.current>.section-body{display:block}#payment-container>.step.finished .finished{display:block} \ No newline at end of file +#payment-container{width:max-content;max-width:980px;border:1px solid #777;border-radius:.2rem}#payment-container>#heading{border:1px solid #777;background-color:#eaeaea;padding:.5rem}#payment-container #payment-provider-container{display:flex;align-items:center}#payment-container #payment-provider-container>#payment-information{margin:0 auto}#payment-container #payment-provider-container>#payment-providers{border-right:1px solid #777;padding:.5rem}#payment-container #payment-provider-container>#payment-providers>h1{margin:0;font-size:.7rem;font-weight:normal}#payment-container #payment-provider-container>#payment-providers>ul{list-style-type:none;margin:0;padding:0}#payment-container #payment-provider-container>#payment-providers>ul>li{cursor:pointer;padding:.5rem 1rem;background-color:#f5f5f5}#payment-container #payment-provider-container>#payment-providers>ul>li[data-active="true"]{background-color:orange;color:white;font-weight:bold;-webkit-text-stroke:#777 .01rem}#payment-container #payment-provider-container>#payment-providers>ul>li:not(:last-child){border-bottom:1px dashed #777}#payment-container>.step{border:1px solid #777;padding:1rem;color:rgba(120,120,120,0.5)}#payment-container>.step>.section-heading{display:flex;gap:.5rem}#payment-container>.step>.section-heading>.status{width:1rem}#payment-container>.step>.section-heading>.status>*{display:none}#payment-container>.step>.section-heading>.content{flex-grow:1}#payment-container>.step>.section-heading>.location{text-align:right;border-left:1px solid rgba(120,120,120,0.314);padding-left:.6rem}#payment-container>.step>.section-body{display:none}#payment-container>.step.current{color:inherit}#payment-container>.step.current .loading{display:block}#payment-container>.step.current .location{color:#777;border-color:#777}#payment-container>.step.current>.section-body{display:block}#payment-container>.step.finished{color:#40da50}#payment-container>.step.finished .finished{display:block}#finish-purchase .section-body>.options{display:flex;gap:2rem;justify-content:space-around}#finish-purchase .section-body>.options>label{display:block;cursor:pointer;padding:1rem;border:1px solid #777;border-radius:.8rem}#finish-purchase .section-body>input[type="radio"]{display:none}#finish-purchase .section-body>div.option-body{display:none}#finish-purchase .section-body .option-selected{background-color:#ff8000;color:white;-webkit-text-stroke:#777 .01rem;font-weight:bold;border-color:#fb8d21}#finish-purchase .section-body>#create-voucher-radio:checked~div#create-voucher.option-body{display:block}#finish-purchase .section-body>#create-voucher-radio:checked~.options>label[for="create-voucher-radio"]{background-color:#ff8000;color:white;-webkit-text-stroke:#777 .01rem;font-weight:bold;border-color:#fb8d21}#finish-purchase .section-body>#create-key-radio:checked~div#create-key.option-body{display:block}#finish-purchase .section-body>#create-key-radio:checked~.options>label[for="create-key-radio"]{background-color:#ff8000;color:white;-webkit-text-stroke:#777 .01rem;font-weight:bold;border-color:#fb8d21}#finish-purchase .section-body>#recharge-key-radio:checked~div#recharge-key.option-body{display:block}#finish-purchase .section-body>#recharge-key-radio:checked~.options>label[for="recharge-key-radio"]{background-color:#ff8000;color:white;-webkit-text-stroke:#777 .01rem;font-weight:bold;border-color:#fb8d21} \ No newline at end of file diff --git a/pass/public/styles/checkout.less b/pass/public/styles/checkout.less index 6afed0b..57333ea 100644 --- a/pass/public/styles/checkout.less +++ b/pass/public/styles/checkout.less @@ -1,5 +1,6 @@ #payment-container { width: max-content; + max-width: 980px; border: 1px solid #777; border-radius: 0.2rem; @@ -49,7 +50,7 @@ > .step { border: 1px solid #777; padding: 1rem; - color: hsla(0, 0%, 47%, 0.314); + color: hsla(0, 0%, 47%, 0.5); > .section-heading { display: flex; gap: 0.5rem; @@ -84,8 +85,71 @@ display: block; } } - &.finished .finished { - display: block; + &.finished { + color: rgb(64, 218, 80); + .finished { + display: block; + } + } + } +} + +// Finish Purchase Styles +#finish-purchase { + .section-body { + > .options { + display: flex; + gap: 2rem; + justify-content: space-around; + > label { + display: block; + cursor: pointer; + padding: 1rem; + border: 1px solid #777; + border-radius: 0.8rem; + } + } + + // Styles to change content based on selected option + > input[type="radio"] { + display: none; + } + > div.option-body { + display: none; + } + + .option-selected { + background-color: rgb(255, 128, 0); + background-color: #ff8000; + color: white; + -webkit-text-stroke: #777 0.01rem; + font-weight: bold; + border-color: rgb(251, 141, 33); + } + > #create-voucher-radio:checked { + ~ div#create-voucher.option-body { + display: block; + } + ~ .options > label[for="create-voucher-radio"] { + .option-selected(); + } + } + + > #create-key-radio:checked { + ~ div#create-key.option-body { + display: block; + } + ~ .options > label[for="create-key-radio"] { + .option-selected(); + } + } + > #recharge-key-radio:checked { + ~ div#recharge-key.option-body { + display: block; + } + ~ .options > label[for="recharge-key-radio"] { + .option-selected(); + } } } } diff --git a/pass/resources/js/checkout.js b/pass/resources/js/checkout.js index bf8342b..49d90d3 100644 --- a/pass/resources/js/checkout.js +++ b/pass/resources/js/checkout.js @@ -1,6 +1,7 @@ const uuid_generator = require("uuid"); const BlindSignature = require("blind-signatures"); +var metager_pass_order_id = null; var metager_pass_sales_receipts = []; var metager_pass_encrypted_sales_receipts = []; // Stores the encrypted sales receipts that get signed by our server after successful purchase var metager_pass_encrypted_sales_receipts_r = []; // Those are the secrets for decrypting the signed sales_receipts; Should never leave the clients computer @@ -41,8 +42,6 @@ function one_generate_encrypted_sales_receipt() { metager_pass_encrypted_sales_receipts_r.push(r.toString()); } - console.log(metager_pass_encrypted_sales_receipts); - current_step_container.classList.remove("current"); current_step_container.classList.add("finished"); next_step_container.classList.add("current"); @@ -52,7 +51,6 @@ function one_generate_encrypted_sales_receipt() { function two_execute_payment() { let current_step_container = document.getElementById("execute-payment"); - let next_step_container = document.getElementById("verify-and-decrypt"); let development_handler_button = document.getElementById( "payment_method_development" ); @@ -73,12 +71,18 @@ function two_execute_payment() { current_step_container.addEventListener("payment-complete", (e) => { current_step_container.classList.remove("current"); current_step_container.classList.add("finished"); - next_step_container.classList.add("current"); three_verify_and_decrypt(e.detail); }); } function three_verify_and_decrypt(signatures) { + let current_step_container = document.getElementById("verify-and-decrypt"); + let next_step_container = document.getElementById("finish-purchase"); + + current_step_container.classList.add("current"); + + metager_pass_order_id = signatures.order_id; + let N = document.querySelector("input[name=public_key_n]").value; let E = document.querySelector("input[name=public_key_e]").value; // Decrypting/Unblinding Signatures @@ -93,6 +97,7 @@ function three_verify_and_decrypt(signatures) { } // Verify Signatures + // ToDo do something with invalid signatures for (let i = 0; i < metager_pass_sales_receipts.length; i++) { let verification = BlindSignature.verify({ unblinded: metager_pass_signatures[i], @@ -102,4 +107,30 @@ function three_verify_and_decrypt(signatures) { }); console.log(metager_pass_sales_receipts[i] + " => " + verification); } + + current_step_container.classList.remove("current"); + current_step_container.classList.add("finished"); + four_finish_purchase(); +} + +function four_finish_purchase() { + let current_step_container = document.getElementById("finish-purchase"); + current_step_container.classList.add("current"); + + // Prepare Voucher download + let dayjs = require("dayjs"); + let order_date = dayjs.unix(metager_pass_order_id.substr(0, 10)); + let voucher_link = document.getElementById("voucher-link"); + let voucher_data = { + month: order_date.format("MM/YYYY"), + receipt: { + receipts: metager_pass_sales_receipts, + signatures: metager_pass_signatures, + }, + }; + voucher_data = + "data:application/mgpass;charset=utf-8;base64," + + btoa(JSON.stringify(voucher_data, null, 4)); + voucher_link.href = voucher_data; + voucher_link.download = "mgpass_coupon.json"; } diff --git a/pass/resources/js/checkout_development.js b/pass/resources/js/checkout_development.js index 8dac4ce..f1ede9c 100644 --- a/pass/resources/js/checkout_development.js +++ b/pass/resources/js/checkout_development.js @@ -47,6 +47,7 @@ function execute_payment_paypal(encrypted_sales_receipts) { .then((data) => { let paymentEvent = new CustomEvent("payment-complete", { detail: { + order_id: data.order_id, signatures: data.signatures, }, bubbles: true, diff --git a/pass/resources/js/checkout_paypal.js b/pass/resources/js/checkout_paypal.js index b677204..dc6bbe3 100644 --- a/pass/resources/js/checkout_paypal.js +++ b/pass/resources/js/checkout_paypal.js @@ -80,6 +80,7 @@ function execute_payment_paypal(encrypted_sales_receipts) { .then((orderData) => { let paymentEvent = new CustomEvent("payment-complete", { detail: { + order_id: orderData.order_id, signatures: orderData.signatures, }, bubbles: true, diff --git a/pass/routes/checkout/checkout.js b/pass/routes/checkout/checkout.js index 5562a3f..2e4cdce 100644 --- a/pass/routes/checkout/checkout.js +++ b/pass/routes/checkout/checkout.js @@ -12,7 +12,8 @@ router.get( "/", query("amount") .isInt({ min: 0, max: 12 }) - .withMessage("Amount needs to be between 0 and 4."), + .withMessage("Amount needs to be between 0 and 4.") + .toInt(), async function (req, res, next) { const errors = validationResult(req); if (!errors.isEmpty()) { diff --git a/pass/routes/checkout/development.js b/pass/routes/checkout/development.js index 65129ad..a9c2dfa 100644 --- a/pass/routes/checkout/development.js +++ b/pass/routes/checkout/development.js @@ -33,6 +33,7 @@ router.post( order.signOrder().then(() => { order.save().then(() => { res.json({ + order_id: req.body.order_id, signatures: order.getSignatures(), }); }); diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index e7f30cc..dd466f2 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -51,6 +51,7 @@ router.post("/capture", async (req, res) => { .save() .then(() => { captureData.signatures = loaded_order.getSignatures(); + captureData.order_id = req.body.order_id; res.json(captureData); }) .catch((error) => { diff --git a/pass/views/checkout/checkout.ejs b/pass/views/checkout/checkout.ejs index f070d00..0f15b66 100644 --- a/pass/views/checkout/checkout.ejs +++ b/pass/views/checkout/checkout.ejs @@ -17,7 +17,7 @@ <img src="/images/loader.gif" alt="Loading" class="loading"> <div class="finished">✓</div> </div> - <div class="content">MetaGer Pass Code erstellen und verschlüsseln</div> + <div class="content">Kaufbeleg erstellen und verschlüsseln</div> <div class="location">Lokal</div> </div> <div class="section-body"> @@ -60,10 +60,51 @@ <img src="/images/loader.gif" alt="Loading" class="loading"> <div class="finished">✓</div> </div> - <div class="content">MetaGer Pass Code verifizieren und entschlüsseln</div> + <div class="content">Kaufbeleg verifizieren und entschlüsseln</div> <div class="location">Lokal</div> </div> </div> + <div id="finish-purchase" class="step"> + <div class="section-heading"> + <div class="status"> + <img src="/images/loader.gif" alt="Loading" class="loading"> + <div class="finished">✓</div> + </div> + <div class="content">MetaGer-Pass Schlüssel erstellen/aufladen</div> + <div class="location">Server</div> + </div> + <div class="section-body"> + <input type="radio" name="finish-purchase-options" value="create-voucher" id="create-voucher-radio"> + <input type="radio" name="finish-purchase-options" value="create-key" id="create-key-radio"> + <input type="radio" name="finish-purchase-options" value="recharge-key" id="recharge-key-radio"> + <div class="options"> + <label for="create-voucher-radio">Gutschein herunterladen</label> + <label for="create-key-radio">Neuen Schlüssel erstellen</label> + <label for="recharge-key-radio">Bestehenden Schlüssel aufladen</label> + </div> + <div id="create-voucher" class="option-body"> + <p> + Mit einem MetaGer-Pass Coupon können Sie Ihren Einkauf zu einem späteren Zeitpunkt einlösen. Der Vorteil wäre, dass es dadurch keinen zeitlichen Bezug zwischen Einkauf und dem verwendeten Schlüssel gibt. Außerdem können Sie den Coupon bei Bedarf auch an andere Personen zur Verwendung weiter geben. Alternativ können Sie die gekauften Suchanfragen auch sofort einlösen. + </p> + <div> + <a href="data:application/mgpass;charset=utf-8;base64,dGVzdHRlYWU=" download="voucher.mgpass" target="_blank" id="voucher-link">Gutschein herunterladen</a> + </div> + </div> + <div id="create-key" class="option-body"> + <p> + Erstellen Sie sich nachfolgend einen neuen anonymen MetaGer-Pass Schlüssel. Auf diesem werden Ihre gekauften Suchanfragen anschließend gutgeschrieben. + </p> + <div> + <form action="" method="post"></form> + </div> + </div> + <div id="recharge-key" class="option-body"> + <p> + Haben Sie bereits einen MetaGer-Pass Schlüssel und möchten diesen weiter verwenden? Geben Sie nachfolgend Ihren bekannten Schlüssel ein um die gekauften Suchanfragen darauf gutzuschreiben. + </p> + </div> + </div> + </div> </div> </main> -- GitLab