diff --git a/pass/public/styles/key/checkout-payment.less b/pass/public/styles/key/checkout-payment.less index 28f5e31febe6f72887fea92680aa45e6a4e385ac..d31ebbc92f63f85f07f78009116c78f9b7f42475 100644 --- a/pass/public/styles/key/checkout-payment.less +++ b/pass/public/styles/key/checkout-payment.less @@ -108,6 +108,18 @@ grid-column: span 3; padding: 0.5rem; justify-self: center; + display: flex; + white-space: nowrap; + align-items: center; + line-height: 1; + gap: 0.5rem; + + > img { + display: none; + } + &.loading > img { + display: block; + } } @media (max-width: @paypal-card-form-breakpoint-1) { grid-template-columns: 1fr; diff --git a/pass/public/styles/key/key.css b/pass/public/styles/key/key.css index 42d90aab92fd2177ab952e48e6b08ad8cd18e659..3ac3cab441ea2b25d83c18b29d2b12e0c79762f3 100644 --- a/pass/public/styles/key/key.css +++ b/pass/public/styles/key/key.css @@ -1 +1 @@ -#checkout-amount-template{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#checkout-amount-template>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#checkout-amount-template>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#checkout-amount-template>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}#checkout #checkout-amount{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;justify-items:center;align-items:center}#checkout #checkout-amount.single{grid-template-columns:1fr}#checkout #checkout-amount>a{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#checkout #checkout-amount>a>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#checkout #checkout-amount>a>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#checkout #checkout-amount>a>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}@media (max-width:800px){#checkout #checkout-amount{grid-template-columns:1fr 1fr}#checkout #checkout-amount.single{grid-template-columns:1fr}}@media (max-width:470px){#checkout #checkout-amount{grid-template-columns:1fr}#checkout #checkout-amount>a{width:100%;grid-template-columns:1fr 3em}}#payment{margin-bottom:1rem}#payment>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#payment>label[for="payment-group-paypal"]{border:1px solid #777;display:block;padding:.5rem;border-top-left-radius:5px;border-top-right-radius:5px;background-color:#f0f0f0;border-bottom-color:#ff7f00;border-bottom-width:2px}#payment>#payment-group-paypal{display:none}#payment>.payment-group{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:center;justify-items:center;gap:1rem;border:1px solid #777;padding:1rem;border-top:0;place-items:stretch}#payment>.payment-group>div{width:14em}@media (max-width:930px){#payment>.payment-group{grid-template-columns:1fr 1fr}}@media (max-width:470px){#payment>.payment-group{grid-template-columns:1fr}}#payment>.payment-group>div{min-width:10em}#payment>.payment-group#paypal-payments>.funding_source{display:flex;border:1px solid #777;padding:1rem;align-items:center;justify-content:center;height:2em;border-radius:5px;gap:.5rem;color:inherit;text-decoration:none}#payment>.payment-group#paypal-payments>.funding_source img{max-width:100%;max-height:100%}#paypal-checkout>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#paypal-checkout>#loading_paypal_funding_source{display:flex;align-items:center;justify-content:center;gap:1rem;color:#777;padding:2rem 0}#paypal-checkout>#loading_paypal_funding_source img{width:2rem}#paypal-checkout #paypal-payment-card #paypal-card-form{display:grid;grid-template-columns:1fr 5em 9em;place-items:stretch;gap:.5rem}#paypal-checkout #paypal-payment-card #paypal-card-form>div{display:flex;flex-direction:column;gap:.5rem}#paypal-checkout #paypal-payment-card #paypal-card-form>div>label{font-weight:bold;font-size:.8rem;white-space:nowrap}#paypal-checkout #paypal-payment-card #paypal-card-form>div>div{height:2.3rem;border:1px solid #777;border-radius:5px}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card{grid-column:span 3;padding:.5rem;justify-self:center}@media (max-width:920px){#paypal-checkout #paypal-payment-card #paypal-card-form{grid-template-columns:1fr}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card{grid-column:auto}}main{max-width:980px;margin:0 auto;display:grid;row-gap:1rem;padding-right:1rem;grid-template-columns:auto 1fr;grid-template-rows:auto 1fr auto auto;grid-template-areas:"qr key " "qr setting-url" "qr buttons" "amount charge"}main>#qr{grid-area:qr;justify-self:center}main #key{grid-area:key;font-size:clamp(.9rem, 4.8vw, 1.5rem);font-weight:bold;width:max-content;margin-top:11px;margin-right:1rem}main #setting-url{grid-area:setting-url;align-self:start;display:flex;padding:.5rem 1rem;border-radius:5px}main #setting-url>input{line-height:1.5;padding:.1rem .5rem;border-radius:5px;flex-grow:1}main>#buttons{grid-area:buttons;display:flex;gap:1rem;margin-bottom:17px;justify-content:flex-end}@media (max-width:435px){main>#buttons{display:grid;grid-template-columns:1fr}main>#buttons .button{width:auto}main>#buttons>*{display:grid;justify-items:center;text-align:center}}main>#amount{grid-area:amount;max-width:200px;justify-self:center;display:flex;flex-direction:column;align-items:center}main>#amount>h3{font-size:1.5rem;margin:0}main>#amount>div.amount{font-size:3rem}main>#charge{grid-area:charge}main>#charge>#store>p{line-height:1.5}@media (max-width:770px){main{padding:0 1rem;grid-template-rows:auto auto auto auto auto;grid-template-columns:auto auto;grid-template-areas:"amount qr " "key key" "setting-url setting-url" "buttons buttons " "charge charge "}main>#key{margin:0;justify-self:center}main>#setting-url{text-align:center}main>#buttons{justify-content:center;margin-bottom:0}main>#amount{align-self:center}main>#charge>#store{display:flex;flex-direction:column;align-items:center}}@media (max-width:430px){main{grid-template-rows:auto auto auto auto auto auto;grid-template-columns:auto;grid-template-areas:"qr" "key" "setting-url" "buttons" "amount" "charge"}}h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#summary{display:flex;align-items:center;gap:1rem;justify-content:center;margin-bottom:3rem}#summary>.checkout-amount{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#summary>.checkout-amount>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#summary>.checkout-amount>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#summary>.checkout-amount>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}#summary>.funding_source{height:4.5rem;width:11rem;display:grid;place-content:center;place-items:center;border:1px solid #777;border-radius:10px;color:inherit;text-decoration:none;gap:.5rem}#summary>.funding_source img{max-width:70%;max-height:100%}@media (max-width:430px){#summary{flex-direction:column}} \ No newline at end of file +#checkout-amount-template{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#checkout-amount-template>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#checkout-amount-template>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#checkout-amount-template>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}#checkout #checkout-amount{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;justify-items:center;align-items:center}#checkout #checkout-amount.single{grid-template-columns:1fr}#checkout #checkout-amount>a{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#checkout #checkout-amount>a>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#checkout #checkout-amount>a>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#checkout #checkout-amount>a>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}@media (max-width:800px){#checkout #checkout-amount{grid-template-columns:1fr 1fr}#checkout #checkout-amount.single{grid-template-columns:1fr}}@media (max-width:470px){#checkout #checkout-amount{grid-template-columns:1fr}#checkout #checkout-amount>a{width:100%;grid-template-columns:1fr 3em}}#payment{margin-bottom:1rem}#payment>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#payment>label[for="payment-group-paypal"]{border:1px solid #777;display:block;padding:.5rem;border-top-left-radius:5px;border-top-right-radius:5px;background-color:#f0f0f0;border-bottom-color:#ff7f00;border-bottom-width:2px}#payment>#payment-group-paypal{display:none}#payment>.payment-group{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:center;justify-items:center;gap:1rem;border:1px solid #777;padding:1rem;border-top:0;place-items:stretch}#payment>.payment-group>div{width:14em}@media (max-width:930px){#payment>.payment-group{grid-template-columns:1fr 1fr}}@media (max-width:470px){#payment>.payment-group{grid-template-columns:1fr}}#payment>.payment-group>div{min-width:10em}#payment>.payment-group#paypal-payments>.funding_source{display:flex;border:1px solid #777;padding:1rem;align-items:center;justify-content:center;height:2em;border-radius:5px;gap:.5rem;color:inherit;text-decoration:none}#payment>.payment-group#paypal-payments>.funding_source img{max-width:100%;max-height:100%}#paypal-checkout>h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#paypal-checkout>#loading_paypal_funding_source{display:flex;align-items:center;justify-content:center;gap:1rem;color:#777;padding:2rem 0}#paypal-checkout>#loading_paypal_funding_source img{width:2rem}#paypal-checkout #paypal-payment-card #paypal-card-form{display:grid;grid-template-columns:1fr 5em 9em;place-items:stretch;gap:.5rem}#paypal-checkout #paypal-payment-card #paypal-card-form>div{display:flex;flex-direction:column;gap:.5rem}#paypal-checkout #paypal-payment-card #paypal-card-form>div>label{font-weight:bold;font-size:.8rem;white-space:nowrap}#paypal-checkout #paypal-payment-card #paypal-card-form>div>div{height:2.3rem;border:1px solid #777;border-radius:5px}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card{grid-column:span 3;padding:.5rem;justify-self:center;display:flex;white-space:nowrap;align-items:center;line-height:1;gap:.5rem}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card>img{display:none}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card.loading>img{display:block}@media (max-width:920px){#paypal-checkout #paypal-payment-card #paypal-card-form{grid-template-columns:1fr}#paypal-checkout #paypal-payment-card #paypal-card-form>button#submit-credit-card{grid-column:auto}}main{max-width:980px;margin:0 auto;display:grid;row-gap:1rem;padding-right:1rem;grid-template-columns:auto 1fr;grid-template-rows:auto 1fr auto auto;grid-template-areas:"qr key " "qr setting-url" "qr buttons" "amount charge"}main>#qr{grid-area:qr;justify-self:center}main #key{grid-area:key;font-size:clamp(.9rem, 4.8vw, 1.5rem);font-weight:bold;width:max-content;margin-top:11px;margin-right:1rem}main #setting-url{grid-area:setting-url;align-self:start;display:flex;padding:.5rem 1rem;border-radius:5px}main #setting-url>input{line-height:1.5;padding:.1rem .5rem;border-radius:5px;flex-grow:1}main>#buttons{grid-area:buttons;display:flex;gap:1rem;margin-bottom:17px;justify-content:flex-end}@media (max-width:435px){main>#buttons{display:grid;grid-template-columns:1fr}main>#buttons .button{width:auto}main>#buttons>*{display:grid;justify-items:center;text-align:center}}main>#amount{grid-area:amount;max-width:200px;justify-self:center;display:flex;flex-direction:column;align-items:center}main>#amount>h3{font-size:1.5rem;margin:0}main>#amount>div.amount{font-size:3rem}main>#charge{grid-area:charge}main>#charge>#store>p{line-height:1.5}@media (max-width:770px){main{padding:0 1rem;grid-template-rows:auto auto auto auto auto;grid-template-columns:auto auto;grid-template-areas:"amount qr " "key key" "setting-url setting-url" "buttons buttons " "charge charge "}main>#key{margin:0;justify-self:center}main>#setting-url{text-align:center}main>#buttons{justify-content:center;margin-bottom:0}main>#amount{align-self:center}main>#charge>#store{display:flex;flex-direction:column;align-items:center}}@media (max-width:430px){main{grid-template-rows:auto auto auto auto auto auto;grid-template-columns:auto;grid-template-areas:"qr" "key" "setting-url" "buttons" "amount" "charge"}}h2{margin:0 0 1rem;text-align:center;border-bottom:1px solid #ff7f00}#summary{display:flex;align-items:center;gap:1rem;justify-content:center;margin-bottom:3rem}#summary>.checkout-amount{text-decoration:none;color:inherit;display:grid;grid-template-columns:7em 3.5em;grid-template-rows:2.5em 2em;place-items:stretch}#summary>.checkout-amount>.checkout-amount{font-size:2rem;font-weight:bold;line-height:1;display:flex;align-items:center;justify-content:center;border-top-left-radius:10px;border:1px solid #ff7f00;border-bottom:0;border-right:0}#summary>.checkout-amount>.checkout-duration{border-bottom-left-radius:10px;display:flex;align-items:center;justify-content:center;border:1px solid #ff7f00;border-top:0;border-right:0}#summary>.checkout-amount>.checkout-cost{grid-row:span 2;justify-content:center;background-color:#ff7f00;color:white;font-size:1.7rem;align-items:center;border-top-right-radius:10px;border-bottom-right-radius:10px;border:1px solid #ff7f00;border-left:0;display:flex}#summary>.funding_source{height:4.5rem;width:11rem;display:grid;place-content:center;place-items:center;border:1px solid #777;border-radius:10px;color:inherit;text-decoration:none;gap:.5rem}#summary>.funding_source img{max-width:70%;max-height:100%}@media (max-width:430px){#summary{flex-direction:column}} \ No newline at end of file diff --git a/pass/resources/js/checkout_paypal.js b/pass/resources/js/checkout_paypal.js index f6758938429f501e99a041cf5cd86a10e2b90c5b..7c059920c7cac25335060abd094a6428fd6b56be 100644 --- a/pass/resources/js/checkout_paypal.js +++ b/pass/resources/js/checkout_paypal.js @@ -83,15 +83,69 @@ function initialize_paypal_payments() { }, }, createOrder: checkout_data.createOrder, + onError: (error) => { + console.log("error"); + console.error(error); + }, }).then((cardFields) => { document .getElementById("paypal-card-form") .addEventListener("submit", (e) => { e.preventDefault(); - cardFields.submit().then(() => checkout_data.onApprove()); + + // Hide all errors + document + .querySelectorAll("#paypal-card-errors > p") + .forEach((element) => { + element.classList.add("hidden"); + }); + // Disable Button + let submit_button = document.getElementById("submit-credit-card"); + submit_button.disabled = true; + submit_button.classList.add("loading"); + + cardFields + .submit({ + cardholderName: + document.getElementById("card-holder-name").value, + contingencies: ["SCA_ALWAYS"], + }) + .then((payload) => { + console.log(payload); + if (payload.liabilityShift === "POSSIBLE") { + checkout_data.onApprove().catch((reason) => { + console.log("Fehlgeschlagen"); + }); + } + // Enable Button + submit_button.disabled = undefined; + submit_button.classList.remove("loading"); + }) + .catch((reason) => { + reason.details.forEach((detail) => { + console.log(detail); + if (detail.issue === "CARD_TYPE_NOT_SUPPORTED") { + document + .getElementById("paypal-card-errors-invalid-card") + .classList.remove("hidden"); + } else if (detail.issue === "VALIDATION_ERROR") { + let error = document.getElementById( + "paypal-card-errors-validation" + ); + error.textContent = detail.description; + error.classList.remove("hidden"); + } else if (detail.issue === "CARD_EXPIRED") { + document + .getElementById("paypal-card-errors-expired") + .classList.remove("hidden"); + } + // Enable Button + submit_button.disabled = undefined; + submit_button.classList.remove("loading"); + }); + }); }); }); - console.log("Hosted Fields"); } else if (funding_source === "paypal") { paypal .Buttons(get_paypal_checkout_data(null)) diff --git a/pass/routes/checkout/paypal.js b/pass/routes/checkout/paypal.js index 2d915362e2f3526bf84bc32460d73268c1e2968c..01e01adedbece02c3bfe9e6b1f2a828f213a9ace 100644 --- a/pass/routes/checkout/paypal.js +++ b/pass/routes/checkout/paypal.js @@ -204,7 +204,7 @@ router.post("/webhook", async (req, res) => { .then(() => { // Update Order status let payment_link = order.getPaymentMethodLink(); - payment_link.order_status = req.body.resource.status; + payment_link.payment_status = req.body.resource.status; return order.setPaymentMethodLink(payment_link); }) .then(() => order.getKeyFromOrderLink()) @@ -228,7 +228,7 @@ router.post("/webhook", async (req, res) => { if (order.isPaymentComplete()) { // Update Payment status let payment_link = order.getPaymentMethodLink(); - payment_link.order_status = "REFUNDED"; + payment_link.payment_status = "REFUNDED"; return order .setPaymentMethodLink(payment_link) .then(() => order.getKeyFromOrderLink()) @@ -250,8 +250,8 @@ router.post("/webhook", async (req, res) => { let payment_method_link = order.getPaymentMethodLink(); if ( payment_method_link && - payment_method_link.order_status && - payment_method_link.order_status !== "COMPLETED" + payment_method_link.payment_status && + payment_method_link.payment_status !== "COMPLETED" ) { return capturePayment(order); } else { @@ -367,7 +367,7 @@ async function createOrder(loaded_order) { .setPaymentMethodLink({ name: "paypal", order_id: data.id, - order_status: data.status, + payment_status: data.status, }) .then(() => { return { id: data.id, order_id: loaded_order.getOrderID() }; @@ -398,14 +398,20 @@ async function capturePayment(order) { }) .then((response) => response.json()) .then((response_data) => { - if (response_data.status !== "COMPLETED") { - console.error(response_data); + console.log(JSON.stringify(response_data)); + if ( + response_data.status !== "COMPLETED" || + response_data.purchase_units[0].payments.captures[0].status !== + "COMPLETED" + ) { + console.error(JSON.stringify(response_data)); throw "Couldn't create Payment"; } return order.setPaymentMethodLink({ name: "paypal", order_id: response_data.id, - order_status: response_data.status, + payment_status: + response_data.purchase_units[0].payments.captures[0].status, }); }); } @@ -455,6 +461,7 @@ async function verifyWebhook() { { name: "PAYMENT.CAPTURE.COMPLETED" }, { name: "PAYMENT.CAPTURE.REFUNDED" }, { name: "PAYMENT.CAPTURE.REVERSED" }, + { name: "CHECKOUT.ORDER.APPROVED" }, ], url: domain + "/webhooks/paypal/webhook", }), diff --git a/pass/views/key.ejs b/pass/views/key.ejs index 7685d5105cae28380ba8b34b1be07df29f7c814f..6f68fb85f3425daaffe05172f7d6085cc841a387 100644 --- a/pass/views/key.ejs +++ b/pass/views/key.ejs @@ -161,6 +161,12 @@ <%_ if(typeof checkout.payment.paypal.funding_source !== undefined) { _%> <%_ if(checkout.payment.paypal.funding_source === "card") { _%> <div id="paypal-payment-card" class="hidden"> + <div id="paypal-card-errors"> + <p id="paypal-card-errors-invalid-card" class="error hidden">Diese Karte wird leider nicht unterstützt</p> + <p id="paypal-card-errors-expired" class="error hidden">Diese Karte ist abgelaufen</p> + <p id="paypal-card-errors-validation" class="error hidden"></p> + <p id="paypal-card-errors-rejected" class="error hidden">Ihre Zahlung wurde von der Bank abgelehnt.</p> + </div> <form id="paypal-card-form"> <div> <label for="card-number">Kartennummer</label> @@ -174,8 +180,11 @@ <label for="cvv">Sicherheitscode (CVV)</label> <div id="cvv" class="card_field"></div> </div> - - <button type="submit" id="submit-credit-card" class="button">Jetzt bezahlen</button> + <div> + <label for="card-holder-name">Name</label> + <input type="text" id="card-holder-name" class="card_field" autocomplete="off" placeholder="Name"/> + </div> + <button type="submit" id="submit-credit-card" class="button"><img src="/images/loader.gif" alt="Loading symbol"> <span>Jetzt bezahlen</span></button> </form> </div> <%_ }else { _%>