From 1434e0fbdc1f81aa81e5d73dc88cd9a05ac8bf5f Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Mon, 14 Nov 2022 16:25:21 +0100 Subject: [PATCH] moved paypal client payment to its own file --- pass/app/Order.js | 4 ++ pass/resources/js/checkout.js | 99 +++++--------------------- pass/resources/js/checkout_paypal.js | 101 +++++++++++++++++++++++++++ pass/routes/checkout/paypal.js | 51 +++++++++++--- 4 files changed, 163 insertions(+), 92 deletions(-) create mode 100644 pass/resources/js/checkout_paypal.js diff --git a/pass/app/Order.js b/pass/app/Order.js index cff4bb0..052720e 100644 --- a/pass/app/Order.js +++ b/pass/app/Order.js @@ -77,6 +77,10 @@ class Order { return this.#price_per_unit; } + getPaymentMethodLink() { + return this.#payment_method_link; + } + setPaymentMethodLink(payment_method_link) { this.#payment_method_link = payment_method_link; } diff --git a/pass/resources/js/checkout.js b/pass/resources/js/checkout.js index 532680f..835f5e1 100644 --- a/pass/resources/js/checkout.js +++ b/pass/resources/js/checkout.js @@ -1,10 +1,9 @@ const uuid_generator = require("uuid"); const BlindSignature = require("blind-signatures"); const BigInteger = require("jsbn").BigInteger; -const paypal_client = require("@paypal/paypal-js"); -var encrypted_sales_receipt = []; -var encrypted_sales_receipt_r = []; +var encrypted_sales_receipts = []; // Stores the encrypted sales receipts that get signed by our server after successful purchase +var encrypted_sales_receipts_r = []; // Those are the secrets for decrypting the signed sales_receipts; Should never leave the clients computer one_generate_encrypted_sales_receipt(); @@ -26,91 +25,29 @@ function one_generate_encrypted_sales_receipt() { N: N, E: E, }); - encrypted_sales_receipt.push(blinded.toString()); - encrypted_sales_receipt_r.push(r.toString()); + encrypted_sales_receipts.push(blinded.toString()); + encrypted_sales_receipts_r.push(r.toString()); } current_step_container.classList.remove("current"); current_step_container.classList.add("finished"); next_step_container.classList.add("current"); -} -function two_execute_payment(provider) {} + two_execute_payment(); +} -function execute_payment_paypal(e) { - let paypal_payment_option_button = document.getElementById( - "payment_method_paypal" - ); +function two_execute_payment() { + let current_step_container = document.getElementById("execute-payment"); - paypal_payment_option_button.dataset.active = true; - let client_id = paypal_payment_option_button.dataset.client_id; - paypal_client - .loadScript({ - "client-id": client_id, - currency: "EUR", - }) - .then((paypal) => { - paypal - .Buttons({ - createOrder: (data, actions) => { - return fetch("/checkout/payment/order/paypal", { - method: "POST", - headers: { - "Content-Type": "application/json;charset=utf-8", - }, - body: JSON.stringify({ - order_id: document.querySelector("input[name=order_id]").value, - amount: document.querySelector("input[name=amount]").value, - unit_size: document.querySelector("input[name=unit_size]") - .value, - price_per_unit: document.querySelector( - "input[name=price_per_unit]" - ).value, - public_key_n: document.querySelector("input[name=public_key_n]") - .value, - public_key_e: document.querySelector("input[name=public_key_e]") - .value, - integrity: document.querySelector("input[name=integrity]") - .value, - encrypted_sales_receipts: encrypted_sales_receipt, - }), - }) - .then((response) => response.json()) - .then((order) => order.id); - }, - onCancel: (data) => { - return fetch("/checkout/payment/order/paypal/cancel", { - method: "POST", - headers: { - "Content-Type": "application/json;charset=utf-8", - }, - body: JSON.stringify({ - order_id: document.querySelector("input[name=order_id]").value, - amount: document.querySelector("input[name=amount]").value, - unit_size: document.querySelector("input[name=unit_size]") - .value, - price_per_unit: document.querySelector( - "input[name=price_per_unit]" - ).value, - public_key_n: document.querySelector("input[name=public_key_n]") - .value, - public_key_e: document.querySelector("input[name=public_key_e]") - .value, - integrity: document.querySelector("input[name=integrity]") - .value, - encrypted_sales_receipts: encrypted_sales_receipt, - }), - }); - }, - }) - .render("#payment-information"); - }) - .catch((err) => { - // ToDo Handle error - console.error("failed to load the PayPal JS SDK script", err); + let paypal_handler = require("./checkout_paypal"); // Add handler for PayPal Checkout + document + .getElementById("payment_method_paypal") + .addEventListener("click", () => { + paypal_handler(encrypted_sales_receipts); }); -} -document - .getElementById("payment_method_paypal") - .addEventListener("click", execute_payment_paypal); + current_step_container.addEventListener("payment-complete", (e) => { + current_step_container.classList.remove("current"); + current_step_container.classList.add("finished"); + }); +} diff --git a/pass/resources/js/checkout_paypal.js b/pass/resources/js/checkout_paypal.js new file mode 100644 index 0000000..00fa75c --- /dev/null +++ b/pass/resources/js/checkout_paypal.js @@ -0,0 +1,101 @@ +const paypal_client = require("@paypal/paypal-js"); + +function execute_payment_paypal(encrypted_sales_receipts) { + let paypal_payment_option_button = document.getElementById( + "payment_method_paypal" + ); + + paypal_payment_option_button.dataset.active = true; + let client_id = paypal_payment_option_button.dataset.client_id; + paypal_client + .loadScript({ + "client-id": client_id, + currency: "EUR", + }) + .then((paypal) => { + paypal + .Buttons({ + createOrder: (data, actions) => { + return fetch("/checkout/payment/order/paypal", { + method: "POST", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + body: JSON.stringify({ + order_id: document.querySelector("input[name=order_id]").value, + amount: document.querySelector("input[name=amount]").value, + unit_size: document.querySelector("input[name=unit_size]") + .value, + price_per_unit: document.querySelector( + "input[name=price_per_unit]" + ).value, + public_key_n: document.querySelector("input[name=public_key_n]") + .value, + public_key_e: document.querySelector("input[name=public_key_e]") + .value, + integrity: document.querySelector("input[name=integrity]") + .value, + encrypted_sales_receipts: encrypted_sales_receipts, + }), + }) + .then((response) => response.json()) + .then((order) => order.id); + }, + onCancel: () => cancelPayment(encrypted_sales_receipts), + onError: () => cancelPayment(encrypted_sales_receipts), + onApprove: (data, actions) => { + return fetch("/checkout/payment/order/paypal/capture", { + method: "POST", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + body: JSON.stringify({ + order_id: document.querySelector("input[name=order_id]").value, + amount: document.querySelector("input[name=amount]").value, + unit_size: document.querySelector("input[name=unit_size]") + .value, + price_per_unit: document.querySelector( + "input[name=price_per_unit]" + ).value, + public_key_n: document.querySelector("input[name=public_key_n]") + .value, + public_key_e: document.querySelector("input[name=public_key_e]") + .value, + integrity: document.querySelector("input[name=integrity]") + .value, + encrypted_sales_receipts: encrypted_sales_receipts, + }), + }) + .then((response) => response.json()) + .then((orderData) => {}); + }, + }) + .render("#payment-information"); + }) + .catch((err) => { + // ToDo Handle error + console.error("failed to load the PayPal JS SDK script", err); + }); +} + +function cancelPayment(encrypted_sales_receipts) { + return fetch("/checkout/payment/order/paypal/cancel", { + method: "POST", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + body: JSON.stringify({ + order_id: document.querySelector("input[name=order_id]").value, + amount: document.querySelector("input[name=amount]").value, + unit_size: document.querySelector("input[name=unit_size]").value, + price_per_unit: document.querySelector("input[name=price_per_unit]") + .value, + public_key_n: document.querySelector("input[name=public_key_n]").value, + public_key_e: document.querySelector("input[name=public_key_e]").value, + integrity: document.querySelector("input[name=integrity]").value, + encrypted_sales_receipts: encrypted_sales_receipts, + }), + }); +} + +module.exports = execute_payment_paypal; diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index 044e64d..41e5fa6 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -35,6 +35,17 @@ router.post("/", async (req, res, next) => { }); }); +// capture payment & store order information or fullfill order +router.post("/capture", async (req, res) => { + Order.LOAD_ORDER_FROM_ID(req.body.order_id).then((loaded_order) => { + let paypal_order_id = loaded_order.getPaymentMethodLink().id; + console.log(paypal_order_id); + //const captureData = await capturePayment(orderID); + //res.json(captureData); + // TODO: store payment information such as the transaction ID + }); +}); + module.exports = router; ////////////////////// @@ -50,34 +61,39 @@ async function createOrder(loaded_order) { const accessToken = await generateAccessToken(); const url = `${base}/v2/checkout/orders`; - let tax_amount_per_unit = ( - loaded_order.getPricePerUnit().toFixed(2) * Order.PURCHASE_TAX_AMOUNT - ).toFixed(2); - let item_amount_per_unit = ( - loaded_order.getPricePerUnit().toFixed(2) - tax_amount_per_unit - ).toFixed(2); + let tax_amount_per_unit = fixRounding( + loaded_order.getPricePerUnit() * Order.PURCHASE_TAX_AMOUNT, + 2 + ); + let item_amount_per_unit = fixRounding( + loaded_order.getPricePerUnit() - tax_amount_per_unit, + 2 + ); let order = { intent: "CAPTURE", - purchase_units: [ { description: "MetaGer Pass Einkauf", amount: { currency_code: "EUR", - value: + value: fixRounding( item_amount_per_unit * loaded_order.getAmount() + - tax_amount_per_unit * loaded_order.getAmount(), + tax_amount_per_unit * loaded_order.getAmount(), + 2 + ), breakdown: { item_total: { currency_code: "EUR", - value: (item_amount_per_unit * loaded_order.getAmount()).toFixed( + value: fixRounding( + item_amount_per_unit * loaded_order.getAmount(), 2 ), }, tax_total: { currency_code: "EUR", - value: (tax_amount_per_unit * loaded_order.getAmount()).toFixed( + value: fixRounding( + tax_amount_per_unit * loaded_order.getAmount(), 2 ), }, @@ -167,3 +183,16 @@ async function generateAccessToken() { return data.access_token; } + +/** + * + * Javascripts calculations with floating points produce weird artifacts. This method is to fix that issue + * + * @param {Float} value + * @param {Int} precision + * @returns + */ +function fixRounding(value, precision) { + var power = Math.pow(10, precision || 0); + return Math.round(value * power) / power; +} -- GitLab