From 40e9d3b66a46d209b20d3ddae06f466b9a56d31e Mon Sep 17 00:00:00 2001 From: David Remer Date: Wed, 8 Apr 2020 14:57:35 +0300 Subject: [PATCH 1/5] add cancel mechanism for paypal order --- modules.local/ppcp-session/services.php | 12 +++++ .../src/Cancellation/CancelController.php | 45 +++++++++++++++++++ .../src/Cancellation/CancelView.php | 13 ++++++ .../ppcp-session/src/SessionHandler.php | 7 +++ .../ppcp-session/src/SessionModule.php | 7 ++- 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 modules.local/ppcp-session/src/Cancellation/CancelController.php create mode 100644 modules.local/ppcp-session/src/Cancellation/CancelView.php diff --git a/modules.local/ppcp-session/services.php b/modules.local/ppcp-session/services.php index 22f231a7b..d743a4a58 100644 --- a/modules.local/ppcp-session/services.php +++ b/modules.local/ppcp-session/services.php @@ -4,9 +4,12 @@ declare(strict_types=1); namespace Inpsyde\PayPalCommerce\Session; use Dhii\Data\Container\ContainerInterface; +use Inpsyde\PayPalCommerce\Session\Cancellation\CancelController; +use Inpsyde\PayPalCommerce\Session\Cancellation\CancelView; return [ 'session.handler' => function (ContainerInterface $container) : SessionHandler { + if (is_admin()) { return new SessionHandler(); } @@ -18,4 +21,13 @@ return [ WC()->session->set(SessionHandler::ID, $sessionHandler); return $sessionHandler; }, + 'session.cancellation.view' => function (ContainerInterface $container) : CancelView { + return new CancelView(); + }, + 'session.cancellation.controller' => function (ContainerInterface $container) : CancelController { + return new CancelController( + $container->get('session.handler'), + $container->get('session.cancellation.view') + ); + }, ]; diff --git a/modules.local/ppcp-session/src/Cancellation/CancelController.php b/modules.local/ppcp-session/src/Cancellation/CancelController.php new file mode 100644 index 000000000..450bfb5c5 --- /dev/null +++ b/modules.local/ppcp-session/src/Cancellation/CancelController.php @@ -0,0 +1,45 @@ +view = $view; + $this->sessionHandler = $sessionHandler; + } + + public function run() + { + $paramName = 'ppcp-cancel'; + $nonce = 'ppcp-cancel-' . get_current_user_id(); + if (isset($_GET[$paramName]) && // Input var ok. + wp_verify_nonce( + sanitize_text_field(wp_unslash($_GET[$paramName])), // Input var ok. + $nonce + ) + ) { // Input var ok. + $this->sessionHandler->cancelOrder(); + } + if (! $this->sessionHandler->order()) { + return; + } + + add_action( + 'woocommerce_review_order_after_submit', + function () use ($paramName, $nonce) { + $this->view->renderSessionCancelation($paramName, wp_create_nonce($nonce)); + } + ); + } +} diff --git a/modules.local/ppcp-session/src/Cancellation/CancelView.php b/modules.local/ppcp-session/src/Cancellation/CancelView.php new file mode 100644 index 000000000..d090d7492 --- /dev/null +++ b/modules.local/ppcp-session/src/Cancellation/CancelView.php @@ -0,0 +1,13 @@ + $nonce], wc_get_checkout_url())) . '">' . esc_html('Cancel', 'woocommerce-paypal-commerce-gateway') . ''; + } +} diff --git a/modules.local/ppcp-session/src/SessionHandler.php b/modules.local/ppcp-session/src/SessionHandler.php index 6ecdca2f7..12ab0cb68 100644 --- a/modules.local/ppcp-session/src/SessionHandler.php +++ b/modules.local/ppcp-session/src/SessionHandler.php @@ -22,6 +22,13 @@ class SessionHandler return $this; } + public function cancelOrder() : SessionHandler + { + $this->order = null; + $this->storeSession(); + return $this; + } + private function storeSession() { WC()->session->set(self::ID, $this); diff --git a/modules.local/ppcp-session/src/SessionModule.php b/modules.local/ppcp-session/src/SessionModule.php index e71c5d89f..0f398b39a 100644 --- a/modules.local/ppcp-session/src/SessionModule.php +++ b/modules.local/ppcp-session/src/SessionModule.php @@ -5,6 +5,7 @@ namespace Inpsyde\PayPalCommerce\Session; use Dhii\Container\ServiceProvider; use Dhii\Modular\Module\ModuleInterface; +use Inpsyde\PayPalCommerce\Session\Cancellation\CancelController; use Interop\Container\ServiceProviderInterface; use Psr\Container\ContainerInterface; @@ -24,7 +25,11 @@ class SessionModule implements ModuleInterface add_action( 'woocommerce_init', function () use ($container) { - $container->get('session.handler'); + $controller = $container->get('session.cancellation.controller'); + /** + * @var CancelController $controller + */ + $controller->run(); } ); } From 1ca7904742aee64cd3e617fc4e0327a8a7734645 Mon Sep 17 00:00:00 2001 From: David Remer Date: Wed, 8 Apr 2020 15:04:20 +0300 Subject: [PATCH 2/5] text-align cancel button, some small refactor --- .../ppcp-button/src/Assets/SmartButton.php | 2 +- .../src/Cancellation/CancelController.php | 5 +++-- .../ppcp-session/src/Cancellation/CancelView.php | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/modules.local/ppcp-button/src/Assets/SmartButton.php b/modules.local/ppcp-button/src/Assets/SmartButton.php index 65ba7b692..16855d144 100644 --- a/modules.local/ppcp-button/src/Assets/SmartButton.php +++ b/modules.local/ppcp-button/src/Assets/SmartButton.php @@ -49,7 +49,7 @@ class SmartButton add_action( 'woocommerce_widget_shopping_cart_buttons', - function() { + function () { echo ''; }, 30 diff --git a/modules.local/ppcp-session/src/Cancellation/CancelController.php b/modules.local/ppcp-session/src/Cancellation/CancelController.php index 450bfb5c5..bf68d8993 100644 --- a/modules.local/ppcp-session/src/Cancellation/CancelController.php +++ b/modules.local/ppcp-session/src/Cancellation/CancelController.php @@ -35,10 +35,11 @@ class CancelController return; } + $url = add_query_arg([$paramName => wp_create_nonce($nonce)], wc_get_checkout_url()); add_action( 'woocommerce_review_order_after_submit', - function () use ($paramName, $nonce) { - $this->view->renderSessionCancelation($paramName, wp_create_nonce($nonce)); + function () use ($url) { + $this->view->renderSessionCancelation($url); } ); } diff --git a/modules.local/ppcp-session/src/Cancellation/CancelView.php b/modules.local/ppcp-session/src/Cancellation/CancelView.php index d090d7492..d9e2a0bf7 100644 --- a/modules.local/ppcp-session/src/Cancellation/CancelView.php +++ b/modules.local/ppcp-session/src/Cancellation/CancelView.php @@ -6,8 +6,18 @@ namespace Inpsyde\PayPalCommerce\Session\Cancellation; class CancelView { - public function renderSessionCancelation(string $paramName, string $nonce) + public function renderSessionCancelation(string $url) { - echo '' . esc_html('Cancel', 'woocommerce-paypal-commerce-gateway') . ''; + ?> +

+ +

+ Date: Wed, 8 Apr 2020 15:31:22 +0300 Subject: [PATCH 3/5] destroy session data --- .../ppcp-session/src/Cancellation/CancelController.php | 2 +- modules.local/ppcp-session/src/SessionHandler.php | 2 +- modules.local/ppcp-wc-gateway/src/Gateway/WcGateway.php | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules.local/ppcp-session/src/Cancellation/CancelController.php b/modules.local/ppcp-session/src/Cancellation/CancelController.php index bf68d8993..89ee3eb79 100644 --- a/modules.local/ppcp-session/src/Cancellation/CancelController.php +++ b/modules.local/ppcp-session/src/Cancellation/CancelController.php @@ -29,7 +29,7 @@ class CancelController $nonce ) ) { // Input var ok. - $this->sessionHandler->cancelOrder(); + $this->sessionHandler->destroySessionData(); } if (! $this->sessionHandler->order()) { return; diff --git a/modules.local/ppcp-session/src/SessionHandler.php b/modules.local/ppcp-session/src/SessionHandler.php index 12ab0cb68..4b0cb8fa9 100644 --- a/modules.local/ppcp-session/src/SessionHandler.php +++ b/modules.local/ppcp-session/src/SessionHandler.php @@ -22,7 +22,7 @@ class SessionHandler return $this; } - public function cancelOrder() : SessionHandler + public function destroySessionData() : SessionHandler { $this->order = null; $this->storeSession(); diff --git a/modules.local/ppcp-wc-gateway/src/Gateway/WcGateway.php b/modules.local/ppcp-wc-gateway/src/Gateway/WcGateway.php index 5a90908d3..2def773fd 100644 --- a/modules.local/ppcp-wc-gateway/src/Gateway/WcGateway.php +++ b/modules.local/ppcp-wc-gateway/src/Gateway/WcGateway.php @@ -101,6 +101,7 @@ class WcGateway extends \WC_Payment_Gateway $wcOrder->update_status('processing', __('Payment received.', 'woocommerce-paypal-gateway')); } $woocommerce->cart->empty_cart(); + $this->sessionHandler->destroySessionData(); return [ 'result' => 'success', From 7c2bc5a8dafe2bf90314c2b9517b030197412005 Mon Sep 17 00:00:00 2001 From: David Remer Date: Wed, 8 Apr 2020 16:22:54 +0300 Subject: [PATCH 4/5] remove sessionHandler from orderEndpoint in api --- modules.local/ppcp-api-client/services.php | 2 -- .../ppcp-api-client/src/Endpoint/OrderEndpoint.php | 7 ------- 2 files changed, 9 deletions(-) diff --git a/modules.local/ppcp-api-client/services.php b/modules.local/ppcp-api-client/services.php index b8f821096..69f0167e5 100644 --- a/modules.local/ppcp-api-client/services.php +++ b/modules.local/ppcp-api-client/services.php @@ -37,13 +37,11 @@ return [ ); }, 'api.endpoint.order' => function (ContainerInterface $container) : OrderEndpoint { - $sessionHandler = $container->get('session.handler'); $orderFactory = $container->get('api.factory.order'); $patchCollectionFactory = $container->get('api.factory.patch-collection-factory'); return new OrderEndpoint( $container->get('api.host'), $container->get('api.bearer'), - $sessionHandler, $orderFactory, $patchCollectionFactory ); diff --git a/modules.local/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules.local/ppcp-api-client/src/Endpoint/OrderEndpoint.php index de4bd3062..3721683da 100644 --- a/modules.local/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules.local/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -16,20 +16,17 @@ class OrderEndpoint private $host; private $bearer; - private $sessionHandler; private $orderFactory; private $patchCollectionFactory; public function __construct( string $host, Bearer $bearer, - SessionHandler $sessionHandler, OrderFactory $orderFactory, PatchCollectionFactory $patchCollectionFactory ) { $this->host = $host; $this->bearer = $bearer; - $this->sessionHandler = $sessionHandler; $this->orderFactory = $orderFactory; $this->patchCollectionFactory = $patchCollectionFactory; } @@ -37,7 +34,6 @@ class OrderEndpoint public function createForPurchaseUnits(PurchaseUnit ...$items) : Order { $bearer = $this->bearer->bearer(); - $data = [ 'intent' => 'CAPTURE', 'purchase_units' => array_map( @@ -62,7 +58,6 @@ class OrderEndpoint } $json = json_decode($response['body']); $order = $this->orderFactory->fromPayPalResponse($json); - $this->sessionHandler->replaceOrder($order); return $order; } @@ -90,7 +85,6 @@ class OrderEndpoint } $json = json_decode($response['body']); $order = $this->orderFactory->fromPayPalResponse($json); - $this->sessionHandler->replaceOrder($order); return $order; } @@ -136,7 +130,6 @@ class OrderEndpoint } $newOrder = $this->order($orderToUpdate->id()); - $this->sessionHandler->replaceOrder($newOrder); return $newOrder; } } From 5824a40a082c14810c80ff869ba575bf55f7b3a9 Mon Sep 17 00:00:00 2001 From: David Remer Date: Wed, 8 Apr 2020 16:23:33 +0300 Subject: [PATCH 5/5] add onApprove endpoint to store order in session --- modules.local/ppcp-button/assets/js/button.js | 2 +- .../ppcp-button/assets/js/button.js.map | 2 +- .../resources/js/modules/CartConfig.js | 7 +- .../js/modules/SingleProductConfig.js | 8 +- .../resources/js/modules/onApprove.js | 22 ++++++ modules.local/ppcp-button/services.php | 7 ++ .../ppcp-button/src/Assets/SmartButton.php | 5 ++ .../ppcp-button/src/ButtonModule.php | 12 +++ .../src/Endpoint/ApproveOrderEndpoint.php | 74 +++++++++++++++++++ .../src/Endpoint/CreateOrderEndpoint.php | 2 +- 10 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 modules.local/ppcp-button/resources/js/modules/onApprove.js create mode 100644 modules.local/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php diff --git a/modules.local/ppcp-button/assets/js/button.js b/modules.local/ppcp-button/assets/js/button.js index 1867bb3ed..28deccdd4 100644 --- a/modules.local/ppcp-button/assets/js/button.js +++ b/modules.local/ppcp-button/assets/js/button.js @@ -1,2 +1,2 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t);var n=class{constructor(e){this.wrapper=e}render(e){paypal.Buttons(e).render(this.wrapper)}hideButtons(){document.querySelector(this.wrapper).style.display="none"}showButtons(){document.querySelector(this.wrapper).style.display="block"}};var o=class{constructor(e,t,r){this.element=e,this.showCallback=t,this.hideCallback=r,this.observer=null}init(){this.observer=new MutationObserver(()=>{this.element.classList.contains("disabled")?this.hideCallback():this.showCallback()}),this.observer.observe(this.element,{attributes:!0})}disconnect(){this.observer.disconnect()}};var a=class{constructor(e,t,r){this.id=e,this.quantity=t,this.variations=r}data(){return{id:this.id,quantity:this.quantity,variations:this.variations}}};var i=class{constructor(e,t,r,n,o,a){this.config=e,this.updateCart=t,this.showButtonCallback=r,this.hideButtonCallback=n,this.formElement=o,this.errorHandler=a}configuration(){if(this.hasVariations()){new o(this.formElement.querySelector(".single_add_to_cart_button"),this.showButtonCallback,this.hideButtonCallback).init()}return{createOrder:this.createOrder(),onApprove:(e,t)=>t.redirect(this.config.redirect),onError:e=>{this.errorHandler.message(e)}}}createOrder(){var e=null;e=this.isGroupedProduct()?()=>{const e=[];return this.formElement.querySelectorAll('input[type="number"]').forEach(t=>{if(!t.value)return;const r=t.getAttribute("name").match(/quantity\[([\d]*)\]/);if(2!==r.length)return;const n=parseInt(r[1]),o=parseInt(t.value);e.push(new a(n,o,null))}),e}:()=>{const e=document.querySelector('[name="add-to-cart"]').value,t=document.querySelector('[name="quantity"]').value,r=this.variations();return[new a(e,t,r)]};return(t,r)=>{this.errorHandler.clear();return this.updateCart.update(e=>fetch(this.config.ajax.create_order.endpoint,{method:"POST",body:JSON.stringify({nonce:this.config.ajax.create_order.nonce,purchase_units:e})}).then((function(e){return e.json()})).then((function(e){if(e.success)return e.data.id})),e())}}variations(){if(!this.hasVariations())return null;return[...this.formElement.querySelectorAll("[name^='attribute_']")].map(e=>({value:e.value,name:e.name}))}hasVariations(){return this.formElement.classList.contains("variations_form")}isGroupedProduct(){return this.formElement.classList.contains("grouped_form")}};var s=class{constructor(e,t){this.endpoint=e,this.nonce=t}update(e,t){return new Promise((r,n)=>{fetch(this.endpoint,{method:"POST",body:JSON.stringify({nonce:this.nonce,products:t})}).then(e=>e.json()).then(t=>{if(!t.success)return void n(t.data);const o=e(t.data);r(o)})})}};var c=class{constructor(){this.wrapper=document.querySelector(".woocommerce-notices-wrapper")}message(e){this.wrapper.classList.add("woocommerce-error"),this.wrapper.innerText=this.sanitize(e)}sanitize(e){const t=document.createElement("textarea");return t.innerHTML=e,t.value}clear(){this.wrapper.classList.contains("woocommerce-error")&&(this.wrapper.classList.remove("woocommerce-error"),this.wrapper.innerText="")}};var u=class{constructor(e,t){this.config=e,this.errorHandler=t}configuration(){return{createOrder:(e,t)=>fetch(this.config.ajax.create_order.endpoint,{method:"POST",body:JSON.stringify({nonce:this.config.ajax.create_order.nonce,purchase_units:[]})}).then((function(e){return e.json()})).then((function(e){if(e.success)return e.data.id})),onApprove:(e,t)=>t.redirect(this.config.redirect),onError:e=>{this.errorHandler.message(e)}}}};document.addEventListener("DOMContentLoaded",()=>{const e=document.createElement("script");e.setAttribute("src",PayPalCommerceGateway.button.url),e.addEventListener("load",e=>{(()=>{const e=PayPalCommerceGateway.context,t=new c,r=new u(PayPalCommerceGateway,t);if(document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)){new n(PayPalCommerceGateway.button.mini_cart_wrapper).render(r.configuration())}if(jQuery(document.body).on("wc_fragments_loaded wc_fragments_refreshed",()=>{if(!document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper))return;new n(PayPalCommerceGateway.button.mini_cart_wrapper).render(r.configuration())}),!document.querySelector(PayPalCommerceGateway.button.wrapper))return;const o=new n(PayPalCommerceGateway.button.wrapper);let a=null;if("product"===e){if(!document.querySelector("form.cart"))return;const e=new s(PayPalCommerceGateway.ajax.change_cart.endpoint,PayPalCommerceGateway.ajax.change_cart.nonce);a=new i(PayPalCommerceGateway,e,o.showButtons.bind(o),o.hideButtons.bind(o),document.querySelector("form.cart"),t)}"cart"===e&&(a=r,jQuery(document.body).on("updated_cart_totals updated_checkout",()=>{o.render(a.configuration())})),a?o.render(a.configuration()):console.error("No context for button found.")})()}),document.body.append(e)})}]); +!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";r.r(t);var n=class{constructor(e){this.wrapper=e}render(e){paypal.Buttons(e).render(this.wrapper)}hideButtons(){document.querySelector(this.wrapper).style.display="none"}showButtons(){document.querySelector(this.wrapper).style.display="block"}};var o=class{constructor(e,t,r){this.element=e,this.showCallback=t,this.hideCallback=r,this.observer=null}init(){this.observer=new MutationObserver(()=>{this.element.classList.contains("disabled")?this.hideCallback():this.showCallback()}),this.observer.observe(this.element,{attributes:!0})}disconnect(){this.observer.disconnect()}};var a=class{constructor(e,t,r){this.id=e,this.quantity=t,this.variations=r}data(){return{id:this.id,quantity:this.quantity,variations:this.variations}}};var i=e=>(t,r)=>fetch(e.config.ajax.approve_order.endpoint,{method:"POST",body:JSON.stringify({nonce:e.config.ajax.approve_order.nonce,order_id:t.orderID})}).then(e=>e.json()).then(t=>{t.success&&(location.href=e.config.redirect)});var s=class{constructor(e,t,r,n,o,a){this.config=e,this.updateCart=t,this.showButtonCallback=r,this.hideButtonCallback=n,this.formElement=o,this.errorHandler=a}configuration(){if(this.hasVariations()){new o(this.formElement.querySelector(".single_add_to_cart_button"),this.showButtonCallback,this.hideButtonCallback).init()}return{createOrder:this.createOrder(),onApprove:i(this),onError:e=>{this.errorHandler.message(e)}}}createOrder(){var e=null;e=this.isGroupedProduct()?()=>{const e=[];return this.formElement.querySelectorAll('input[type="number"]').forEach(t=>{if(!t.value)return;const r=t.getAttribute("name").match(/quantity\[([\d]*)\]/);if(2!==r.length)return;const n=parseInt(r[1]),o=parseInt(t.value);e.push(new a(n,o,null))}),e}:()=>{const e=document.querySelector('[name="add-to-cart"]').value,t=document.querySelector('[name="quantity"]').value,r=this.variations();return[new a(e,t,r)]};return(t,r)=>{this.errorHandler.clear();return this.updateCart.update(e=>fetch(this.config.ajax.create_order.endpoint,{method:"POST",body:JSON.stringify({nonce:this.config.ajax.create_order.nonce,purchase_units:e})}).then((function(e){return e.json()})).then((function(e){if(e.success)return e.data.id})),e())}}variations(){if(!this.hasVariations())return null;return[...this.formElement.querySelectorAll("[name^='attribute_']")].map(e=>({value:e.value,name:e.name}))}hasVariations(){return this.formElement.classList.contains("variations_form")}isGroupedProduct(){return this.formElement.classList.contains("grouped_form")}};var c=class{constructor(e,t){this.endpoint=e,this.nonce=t}update(e,t){return new Promise((r,n)=>{fetch(this.endpoint,{method:"POST",body:JSON.stringify({nonce:this.nonce,products:t})}).then(e=>e.json()).then(t=>{if(!t.success)return void n(t.data);const o=e(t.data);r(o)})})}};var u=class{constructor(){this.wrapper=document.querySelector(".woocommerce-notices-wrapper")}message(e){this.wrapper.classList.add("woocommerce-error"),this.wrapper.innerText=this.sanitize(e)}sanitize(e){const t=document.createElement("textarea");return t.innerHTML=e,t.value}clear(){this.wrapper.classList.contains("woocommerce-error")&&(this.wrapper.classList.remove("woocommerce-error"),this.wrapper.innerText="")}};var d=class{constructor(e,t){this.config=e,this.errorHandler=t}configuration(){return{createOrder:(e,t)=>fetch(this.config.ajax.create_order.endpoint,{method:"POST",body:JSON.stringify({nonce:this.config.ajax.create_order.nonce,purchase_units:[]})}).then((function(e){return e.json()})).then((function(e){if(e.success)return e.data.id})),onApprove:i(this),onError:e=>{this.errorHandler.message(e)}}}};document.addEventListener("DOMContentLoaded",()=>{const e=document.createElement("script");e.setAttribute("src",PayPalCommerceGateway.button.url),e.addEventListener("load",e=>{(()=>{const e=PayPalCommerceGateway.context,t=new u,r=new d(PayPalCommerceGateway,t);if(document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)){new n(PayPalCommerceGateway.button.mini_cart_wrapper).render(r.configuration())}if(jQuery(document.body).on("wc_fragments_loaded wc_fragments_refreshed",()=>{if(!document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper))return;new n(PayPalCommerceGateway.button.mini_cart_wrapper).render(r.configuration())}),!document.querySelector(PayPalCommerceGateway.button.wrapper))return;const o=new n(PayPalCommerceGateway.button.wrapper);let a=null;if("product"===e){if(!document.querySelector("form.cart"))return;const e=new c(PayPalCommerceGateway.ajax.change_cart.endpoint,PayPalCommerceGateway.ajax.change_cart.nonce);a=new s(PayPalCommerceGateway,e,o.showButtons.bind(o),o.hideButtons.bind(o),document.querySelector("form.cart"),t)}"cart"===e&&(a=r,jQuery(document.body).on("updated_cart_totals updated_checkout",()=>{o.render(a.configuration())})),a?o.render(a.configuration()):console.error("No context for button found.")})()}),document.body.append(e)})}]); //# sourceMappingURL=button.js.map \ No newline at end of file diff --git a/modules.local/ppcp-button/assets/js/button.js.map b/modules.local/ppcp-button/assets/js/button.js.map index ae6d2f3b4..ea1812d43 100644 --- a/modules.local/ppcp-button/assets/js/button.js.map +++ b/modules.local/ppcp-button/assets/js/button.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./resources/js/modules/Renderer.js","webpack:///./resources/js/modules/ButtonsToggleListener.js","webpack:///./resources/js/modules/Product.js","webpack:///./resources/js/modules/SingleProductConfig.js","webpack:///./resources/js/modules/UpdateCart.js","webpack:///./resources/js/modules/ErrorHandler.js","webpack:///./resources/js/modules/CartConfig.js","webpack:///./resources/js/button.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","Renderer","constructor","wrapper","this","render","buttonConfig","paypal","Buttons","hideButtons","document","querySelector","style","display","showButtons","ButtonsToggleListener","element","showCallback","hideCallback","observer","init","MutationObserver","classList","contains","observe","attributes","disconnect","Product","id","quantity","variations","data","SingleProductConfig","config","updateCart","showButtonCallback","hideButtonCallback","formElement","errorHandler","configuration","hasVariations","createOrder","onApprove","actions","redirect","onError","error","message","getProducts","isGroupedProduct","products","querySelectorAll","forEach","elementName","getAttribute","match","length","parseInt","push","qty","clear","update","purchase_units","fetch","ajax","create_order","endpoint","method","body","JSON","stringify","nonce","then","res","json","success","map","UpdateCart","onResolve","Promise","resolve","reject","result","resolved","ErrorHandler","text","add","innerText","sanitize","textarea","createElement","innerHTML","remove","CartConfig","addEventListener","script","setAttribute","PayPalCommerceGateway","button","url","event","context","defaultConfigurator","mini_cart_wrapper","jQuery","on","renderer","configurator","change_cart","console","bootstrap","append"],"mappings":"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","file":"js/button.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","class Renderer {\n\n constructor(wrapper)\n {\n this.wrapper = wrapper;\n }\n\n render(buttonConfig)\n {\n\n paypal.Buttons(\n buttonConfig\n ).render(this.wrapper);\n }\n\n hideButtons()\n {\n document.querySelector(this.wrapper).style.display = 'none';\n }\n\n showButtons()\n {\n document.querySelector(this.wrapper).style.display = 'block';\n }\n}\n\nexport default Renderer;","/**\n * When you can't add something to the cart, the PayPal buttons should not show.\n * Therefore we listen for changes on the add to cart button and show/hide the buttons accordingly.\n */\n\nclass ButtonsToggleListener {\n constructor(element, showCallback, hideCallback)\n {\n this.element = element;\n this.showCallback = showCallback;\n this.hideCallback = hideCallback;\n this.observer = null;\n }\n\n init()\n {\n const config = { attributes : true };\n const callback = () => {\n if (this.element.classList.contains('disabled')) {\n this.hideCallback();\n return;\n }\n this.showCallback();\n }\n this.observer = new MutationObserver(callback);\n this.observer.observe(this.element, config);\n }\n\n disconnect()\n {\n this.observer.disconnect();\n }\n}\n\nexport default ButtonsToggleListener;","class Product {\n\n constructor(id, quantity, variations) {\n this.id = id;\n this.quantity = quantity;\n this.variations = variations;\n }\n\n data() {\n return {\n id:this.id,\n quantity:this.quantity,\n variations:this.variations\n }\n }\n}\n\nexport default Product;","import ButtonsToggleListener from \"./ButtonsToggleListener\";\nimport Product from \"./Product\";\nclass SingleProductConfig {\n\n constructor(\n config,\n updateCart,\n showButtonCallback,\n hideButtonCallback,\n formElement,\n errorHandler\n ) {\n this.config = config;\n this.updateCart = updateCart;\n this.showButtonCallback = showButtonCallback;\n this.hideButtonCallback = hideButtonCallback;\n this.formElement = formElement;\n this.errorHandler = errorHandler;\n }\n\n configuration()\n {\n\n if ( this.hasVariations() ) {\n const observer = new ButtonsToggleListener(\n this.formElement.querySelector('.single_add_to_cart_button'),\n this.showButtonCallback,\n this.hideButtonCallback\n );\n observer.init();\n }\n const onApprove = (data, actions) => {\n return actions.redirect(this.config.redirect);\n }\n return {\n createOrder: this.createOrder(),\n onApprove,\n onError: (error) => {\n this.errorHandler.message(error);\n }\n }\n }\n\n createOrder()\n {\n var getProducts = null;\n if (! this.isGroupedProduct() ) {\n getProducts = () => {\n const id = document.querySelector('[name=\"add-to-cart\"]').value;\n const qty = document.querySelector('[name=\"quantity\"]').value;\n const variations = this.variations();\n return [new Product(id, qty, variations)];\n }\n } else {\n getProducts = () => {\n const products = [];\n this.formElement.querySelectorAll('input[type=\"number\"]').forEach((element) => {\n if (! element.value) {\n return;\n }\n const elementName = element.getAttribute('name').match(/quantity\\[([\\d]*)\\]/);\n if (elementName.length !== 2) {\n return;\n }\n const id = parseInt(elementName[1]);\n const quantity = parseInt(element.value);\n products.push(new Product(id, quantity, null));\n })\n return products;\n }\n }\n const createOrder = (data, actions) => {\n this.errorHandler.clear();\n\n const onResolve = (purchase_units) => {\n return fetch(this.config.ajax.create_order.endpoint, {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.config.ajax.create_order.nonce,\n purchase_units\n })\n }).then(function (res) {\n return res.json();\n }).then(function (data) {\n if (!data.success) {\n //Todo: Error handling\n return;\n }\n return data.data.id;\n });\n };\n\n const promise = this.updateCart.update(onResolve, getProducts());\n return promise;\n };\n return createOrder;\n }\n\n variations()\n {\n\n if (! this.hasVariations()) {\n return null;\n }\n const attributes = [...this.formElement.querySelectorAll(\"[name^='attribute_']\")].map(\n (element) => {\n return {\n value:element.value,\n name:element.name\n }\n }\n );\n return attributes;\n }\n\n hasVariations()\n {\n return this.formElement.classList.contains('variations_form');\n }\n\n isGroupedProduct()\n {\n return this.formElement.classList.contains('grouped_form');\n }\n}\n\nexport default SingleProductConfig;","import Product from \"./Product\";\nclass UpdateCart {\n\n constructor(endpoint, nonce)\n {\n this.endpoint = endpoint;\n this.nonce = nonce;\n }\n\n /**\n *\n * @param onResolve\n * @param {Product[]} products\n * @returns {Promise}\n */\n update(onResolve, products)\n {\n return new Promise((resolve, reject) => {\n fetch(\n this.endpoint,\n {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.nonce,\n products,\n })\n }\n ).then(\n (result) => {\n return result.json();\n }\n ).then((result) => {\n if (! result.success) {\n reject(result.data);\n return;\n }\n\n const resolved = onResolve(result.data);\n resolve(resolved);\n })\n });\n }\n}\n\nexport default UpdateCart;","class ErrorHandler {\n\n constructor()\n {\n this.wrapper = document.querySelector('.woocommerce-notices-wrapper');\n }\n\n message(text)\n {\n this.wrapper.classList.add('woocommerce-error');\n this.wrapper.innerText = this.sanitize(text);\n }\n\n sanitize(text)\n {\n const textarea = document.createElement('textarea');\n textarea.innerHTML = text;\n return textarea.value;\n }\n\n clear()\n {\n if (! this.wrapper.classList.contains('woocommerce-error')) {\n return;\n }\n this.wrapper.classList.remove('woocommerce-error');\n this.wrapper.innerText = '';\n }\n}\n\nexport default ErrorHandler;","\n\nclass CartConfig {\n\n constructor(config, errorHandler) {\n this.config = config;\n this.errorHandler = errorHandler;\n }\n\n configuration() {\n\n const createOrder = (data, actions) => {\n return fetch(this.config.ajax.create_order.endpoint, {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.config.ajax.create_order.nonce,\n purchase_units:[]\n })\n }).then(function (res) {\n return res.json();\n }).then(function (data) {\n if (!data.success) {\n //Todo: Error handling\n return;\n }\n return data.data.id;\n });\n }\n const onApprove = (data, actions) => {\n return actions.redirect(this.config.redirect);\n }\n return {\n createOrder,\n onApprove,\n onError: (error) => {\n this.errorHandler.message(error);\n }\n }\n }\n}\n\nexport default CartConfig;","import Renderer from './modules/Renderer';\nimport SingleProductConfig from './modules/SingleProductConfig';\nimport UpdateCart from './modules/UpdateCart';\nimport ErrorHandler from './modules/ErrorHandler';\nimport CartConfig from \"./modules/CartConfig\";\n\nconst bootstrap = ()=> {\n const context = PayPalCommerceGateway.context;\n const errorHandler = new ErrorHandler();\n const defaultConfigurator = new CartConfig(\n PayPalCommerceGateway,\n errorHandler\n );\n // Configure mini cart buttons\n if (document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)) {\n\n const renderer = new Renderer(\n PayPalCommerceGateway.button.mini_cart_wrapper\n );\n renderer.render(defaultConfigurator.configuration())\n }\n jQuery( document.body ).on( 'wc_fragments_loaded wc_fragments_refreshed', () => {\n if (! document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)) {\n return;\n }\n const renderer = new Renderer(\n PayPalCommerceGateway.button.mini_cart_wrapper\n );\n renderer.render(defaultConfigurator.configuration())\n } );\n\n // Configure context buttons\n if (! document.querySelector(PayPalCommerceGateway.button.wrapper)) {\n return;\n }\n const renderer = new Renderer(\n PayPalCommerceGateway.button.wrapper\n );\n let configurator = null;\n if (context === 'product') {\n if (! document.querySelector('form.cart')) {\n return;\n }\n const updateCart = new UpdateCart(\n PayPalCommerceGateway.ajax.change_cart.endpoint,\n PayPalCommerceGateway.ajax.change_cart.nonce\n );\n configurator = new SingleProductConfig(\n PayPalCommerceGateway,\n updateCart,\n renderer.showButtons.bind(renderer),\n renderer.hideButtons.bind(renderer),\n document.querySelector('form.cart'),\n errorHandler\n );\n }\n if (context === 'cart') {\n configurator = defaultConfigurator;\n\n jQuery( document.body ).on( 'updated_cart_totals updated_checkout', () => {\n renderer.render(configurator.configuration())\n });\n }\n if (! configurator) {\n console.error('No context for button found.');\n return;\n }\n\n renderer.render(configurator.configuration());\n}\ndocument.addEventListener(\n 'DOMContentLoaded',\n () => {\n\n if (! typeof(PayPalCommerceGateway)) {\n console.error('PayPal button could not be configured.');\n return;\n }\n\n const script = document.createElement('script');\n script.setAttribute('src', PayPalCommerceGateway.button.url);\n script.addEventListener('load', (event) => {\n bootstrap();\n })\n document.body.append(script);\n\n\n }\n);"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./resources/js/modules/Renderer.js","webpack:///./resources/js/modules/ButtonsToggleListener.js","webpack:///./resources/js/modules/Product.js","webpack:///./resources/js/modules/onApprove.js","webpack:///./resources/js/modules/SingleProductConfig.js","webpack:///./resources/js/modules/UpdateCart.js","webpack:///./resources/js/modules/ErrorHandler.js","webpack:///./resources/js/modules/CartConfig.js","webpack:///./resources/js/button.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","Renderer","constructor","wrapper","this","render","buttonConfig","paypal","Buttons","hideButtons","document","querySelector","style","display","showButtons","ButtonsToggleListener","element","showCallback","hideCallback","observer","init","MutationObserver","classList","contains","observe","attributes","disconnect","Product","id","quantity","variations","data","onApprove","context","actions","fetch","config","ajax","approve_order","endpoint","method","body","JSON","stringify","nonce","order_id","orderID","then","res","json","success","location","href","redirect","SingleProductConfig","updateCart","showButtonCallback","hideButtonCallback","formElement","errorHandler","configuration","hasVariations","createOrder","onError","error","message","getProducts","isGroupedProduct","products","querySelectorAll","forEach","elementName","getAttribute","match","length","parseInt","push","qty","clear","update","purchase_units","create_order","map","UpdateCart","onResolve","Promise","resolve","reject","result","resolved","ErrorHandler","text","add","innerText","sanitize","textarea","createElement","innerHTML","remove","CartConfig","addEventListener","script","setAttribute","PayPalCommerceGateway","button","url","event","defaultConfigurator","mini_cart_wrapper","jQuery","on","renderer","configurator","change_cart","console","bootstrap","append"],"mappings":"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","file":"js/button.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","class Renderer {\n\n constructor(wrapper)\n {\n this.wrapper = wrapper;\n }\n\n render(buttonConfig)\n {\n\n paypal.Buttons(\n buttonConfig\n ).render(this.wrapper);\n }\n\n hideButtons()\n {\n document.querySelector(this.wrapper).style.display = 'none';\n }\n\n showButtons()\n {\n document.querySelector(this.wrapper).style.display = 'block';\n }\n}\n\nexport default Renderer;","/**\n * When you can't add something to the cart, the PayPal buttons should not show.\n * Therefore we listen for changes on the add to cart button and show/hide the buttons accordingly.\n */\n\nclass ButtonsToggleListener {\n constructor(element, showCallback, hideCallback)\n {\n this.element = element;\n this.showCallback = showCallback;\n this.hideCallback = hideCallback;\n this.observer = null;\n }\n\n init()\n {\n const config = { attributes : true };\n const callback = () => {\n if (this.element.classList.contains('disabled')) {\n this.hideCallback();\n return;\n }\n this.showCallback();\n }\n this.observer = new MutationObserver(callback);\n this.observer.observe(this.element, config);\n }\n\n disconnect()\n {\n this.observer.disconnect();\n }\n}\n\nexport default ButtonsToggleListener;","class Product {\n\n constructor(id, quantity, variations) {\n this.id = id;\n this.quantity = quantity;\n this.variations = variations;\n }\n\n data() {\n return {\n id:this.id,\n quantity:this.quantity,\n variations:this.variations\n }\n }\n}\n\nexport default Product;","const onApprove = (context) => {\n return (data, actions) => {\n return fetch(context.config.ajax.approve_order.endpoint, {\n method: 'POST',\n body: JSON.stringify({\n nonce: context.config.ajax.approve_order.nonce,\n order_id:data.orderID\n })\n }).then((res)=>{\n return res.json();\n }).then((data)=>{\n if (!data.success) {\n //Todo: Error handling\n return;\n }\n location.href = context.config.redirect;\n });\n\n }\n}\n\nexport default onApprove;\n","import ButtonsToggleListener from \"./ButtonsToggleListener\";\nimport Product from \"./Product\";\nimport onApprove from \"./onApprove\";\nclass SingleProductConfig {\n\n constructor(\n config,\n updateCart,\n showButtonCallback,\n hideButtonCallback,\n formElement,\n errorHandler\n ) {\n this.config = config;\n this.updateCart = updateCart;\n this.showButtonCallback = showButtonCallback;\n this.hideButtonCallback = hideButtonCallback;\n this.formElement = formElement;\n this.errorHandler = errorHandler;\n }\n\n configuration()\n {\n\n if ( this.hasVariations() ) {\n const observer = new ButtonsToggleListener(\n this.formElement.querySelector('.single_add_to_cart_button'),\n this.showButtonCallback,\n this.hideButtonCallback\n );\n observer.init();\n }\n\n return {\n createOrder: this.createOrder(),\n onApprove: onApprove(this),\n onError: (error) => {\n this.errorHandler.message(error);\n }\n }\n }\n\n createOrder()\n {\n var getProducts = null;\n if (! this.isGroupedProduct() ) {\n getProducts = () => {\n const id = document.querySelector('[name=\"add-to-cart\"]').value;\n const qty = document.querySelector('[name=\"quantity\"]').value;\n const variations = this.variations();\n return [new Product(id, qty, variations)];\n }\n } else {\n getProducts = () => {\n const products = [];\n this.formElement.querySelectorAll('input[type=\"number\"]').forEach((element) => {\n if (! element.value) {\n return;\n }\n const elementName = element.getAttribute('name').match(/quantity\\[([\\d]*)\\]/);\n if (elementName.length !== 2) {\n return;\n }\n const id = parseInt(elementName[1]);\n const quantity = parseInt(element.value);\n products.push(new Product(id, quantity, null));\n })\n return products;\n }\n }\n const createOrder = (data, actions) => {\n this.errorHandler.clear();\n\n const onResolve = (purchase_units) => {\n return fetch(this.config.ajax.create_order.endpoint, {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.config.ajax.create_order.nonce,\n purchase_units\n })\n }).then(function (res) {\n return res.json();\n }).then(function (data) {\n if (!data.success) {\n //Todo: Error handling\n return;\n }\n return data.data.id;\n });\n };\n\n const promise = this.updateCart.update(onResolve, getProducts());\n return promise;\n };\n return createOrder;\n }\n\n variations()\n {\n\n if (! this.hasVariations()) {\n return null;\n }\n const attributes = [...this.formElement.querySelectorAll(\"[name^='attribute_']\")].map(\n (element) => {\n return {\n value:element.value,\n name:element.name\n }\n }\n );\n return attributes;\n }\n\n hasVariations()\n {\n return this.formElement.classList.contains('variations_form');\n }\n\n isGroupedProduct()\n {\n return this.formElement.classList.contains('grouped_form');\n }\n}\nexport default SingleProductConfig;","import Product from \"./Product\";\nclass UpdateCart {\n\n constructor(endpoint, nonce)\n {\n this.endpoint = endpoint;\n this.nonce = nonce;\n }\n\n /**\n *\n * @param onResolve\n * @param {Product[]} products\n * @returns {Promise}\n */\n update(onResolve, products)\n {\n return new Promise((resolve, reject) => {\n fetch(\n this.endpoint,\n {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.nonce,\n products,\n })\n }\n ).then(\n (result) => {\n return result.json();\n }\n ).then((result) => {\n if (! result.success) {\n reject(result.data);\n return;\n }\n\n const resolved = onResolve(result.data);\n resolve(resolved);\n })\n });\n }\n}\n\nexport default UpdateCart;","class ErrorHandler {\n\n constructor()\n {\n this.wrapper = document.querySelector('.woocommerce-notices-wrapper');\n }\n\n message(text)\n {\n this.wrapper.classList.add('woocommerce-error');\n this.wrapper.innerText = this.sanitize(text);\n }\n\n sanitize(text)\n {\n const textarea = document.createElement('textarea');\n textarea.innerHTML = text;\n return textarea.value;\n }\n\n clear()\n {\n if (! this.wrapper.classList.contains('woocommerce-error')) {\n return;\n }\n this.wrapper.classList.remove('woocommerce-error');\n this.wrapper.innerText = '';\n }\n}\n\nexport default ErrorHandler;","import onApprove from \"./onApprove\";\n\nclass CartConfig {\n\n constructor(config, errorHandler) {\n this.config = config;\n this.errorHandler = errorHandler;\n }\n\n configuration() {\n\n const createOrder = (data, actions) => {\n return fetch(this.config.ajax.create_order.endpoint, {\n method: 'POST',\n body: JSON.stringify({\n nonce: this.config.ajax.create_order.nonce,\n purchase_units:[]\n })\n }).then(function (res) {\n return res.json();\n }).then(function (data) {\n if (!data.success) {\n //Todo: Error handling\n return;\n }\n return data.data.id;\n });\n }\n return {\n createOrder,\n onApprove:onApprove(this),\n onError: (error) => {\n this.errorHandler.message(error);\n }\n }\n }\n}\n\nexport default CartConfig;","import Renderer from './modules/Renderer';\nimport SingleProductConfig from './modules/SingleProductConfig';\nimport UpdateCart from './modules/UpdateCart';\nimport ErrorHandler from './modules/ErrorHandler';\nimport CartConfig from \"./modules/CartConfig\";\n\nconst bootstrap = ()=> {\n const context = PayPalCommerceGateway.context;\n const errorHandler = new ErrorHandler();\n const defaultConfigurator = new CartConfig(\n PayPalCommerceGateway,\n errorHandler\n );\n // Configure mini cart buttons\n if (document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)) {\n\n const renderer = new Renderer(\n PayPalCommerceGateway.button.mini_cart_wrapper\n );\n renderer.render(defaultConfigurator.configuration())\n }\n jQuery( document.body ).on( 'wc_fragments_loaded wc_fragments_refreshed', () => {\n if (! document.querySelector(PayPalCommerceGateway.button.mini_cart_wrapper)) {\n return;\n }\n const renderer = new Renderer(\n PayPalCommerceGateway.button.mini_cart_wrapper\n );\n renderer.render(defaultConfigurator.configuration())\n } );\n\n // Configure context buttons\n if (! document.querySelector(PayPalCommerceGateway.button.wrapper)) {\n return;\n }\n const renderer = new Renderer(\n PayPalCommerceGateway.button.wrapper\n );\n let configurator = null;\n if (context === 'product') {\n if (! document.querySelector('form.cart')) {\n return;\n }\n const updateCart = new UpdateCart(\n PayPalCommerceGateway.ajax.change_cart.endpoint,\n PayPalCommerceGateway.ajax.change_cart.nonce\n );\n configurator = new SingleProductConfig(\n PayPalCommerceGateway,\n updateCart,\n renderer.showButtons.bind(renderer),\n renderer.hideButtons.bind(renderer),\n document.querySelector('form.cart'),\n errorHandler\n );\n }\n if (context === 'cart') {\n configurator = defaultConfigurator;\n\n jQuery( document.body ).on( 'updated_cart_totals updated_checkout', () => {\n renderer.render(configurator.configuration())\n });\n }\n if (! configurator) {\n console.error('No context for button found.');\n return;\n }\n\n renderer.render(configurator.configuration());\n}\ndocument.addEventListener(\n 'DOMContentLoaded',\n () => {\n\n if (! typeof(PayPalCommerceGateway)) {\n console.error('PayPal button could not be configured.');\n return;\n }\n\n const script = document.createElement('script');\n script.setAttribute('src', PayPalCommerceGateway.button.url);\n script.addEventListener('load', (event) => {\n bootstrap();\n })\n document.body.append(script);\n\n\n }\n);"],"sourceRoot":""} \ No newline at end of file diff --git a/modules.local/ppcp-button/resources/js/modules/CartConfig.js b/modules.local/ppcp-button/resources/js/modules/CartConfig.js index 1c3504594..d41a546b7 100644 --- a/modules.local/ppcp-button/resources/js/modules/CartConfig.js +++ b/modules.local/ppcp-button/resources/js/modules/CartConfig.js @@ -1,4 +1,4 @@ - +import onApprove from "./onApprove"; class CartConfig { @@ -26,12 +26,9 @@ class CartConfig { return data.data.id; }); } - const onApprove = (data, actions) => { - return actions.redirect(this.config.redirect); - } return { createOrder, - onApprove, + onApprove:onApprove(this), onError: (error) => { this.errorHandler.message(error); } diff --git a/modules.local/ppcp-button/resources/js/modules/SingleProductConfig.js b/modules.local/ppcp-button/resources/js/modules/SingleProductConfig.js index 7f98e605b..774909ee5 100644 --- a/modules.local/ppcp-button/resources/js/modules/SingleProductConfig.js +++ b/modules.local/ppcp-button/resources/js/modules/SingleProductConfig.js @@ -1,5 +1,6 @@ import ButtonsToggleListener from "./ButtonsToggleListener"; import Product from "./Product"; +import onApprove from "./onApprove"; class SingleProductConfig { constructor( @@ -29,12 +30,10 @@ class SingleProductConfig { ); observer.init(); } - const onApprove = (data, actions) => { - return actions.redirect(this.config.redirect); - } + return { createOrder: this.createOrder(), - onApprove, + onApprove: onApprove(this), onError: (error) => { this.errorHandler.message(error); } @@ -123,5 +122,4 @@ class SingleProductConfig { return this.formElement.classList.contains('grouped_form'); } } - export default SingleProductConfig; \ No newline at end of file diff --git a/modules.local/ppcp-button/resources/js/modules/onApprove.js b/modules.local/ppcp-button/resources/js/modules/onApprove.js new file mode 100644 index 000000000..c2ecb4967 --- /dev/null +++ b/modules.local/ppcp-button/resources/js/modules/onApprove.js @@ -0,0 +1,22 @@ +const onApprove = (context) => { + return (data, actions) => { + return fetch(context.config.ajax.approve_order.endpoint, { + method: 'POST', + body: JSON.stringify({ + nonce: context.config.ajax.approve_order.nonce, + order_id:data.orderID + }) + }).then((res)=>{ + return res.json(); + }).then((data)=>{ + if (!data.success) { + //Todo: Error handling + return; + } + location.href = context.config.redirect; + }); + + } +} + +export default onApprove; diff --git a/modules.local/ppcp-button/services.php b/modules.local/ppcp-button/services.php index ec10942c9..bfb492e7f 100644 --- a/modules.local/ppcp-button/services.php +++ b/modules.local/ppcp-button/services.php @@ -5,6 +5,7 @@ namespace Inpsyde\PayPalCommerce\Button; use Inpsyde\PayPalCommerce\Button\Assets\SmartButton; use Dhii\Data\Container\ContainerInterface; +use Inpsyde\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\RequestData; @@ -40,4 +41,10 @@ return [ $apiClient = $container->get('api.endpoint.order'); return new CreateOrderEndpoint($requestData, $repository, $apiClient); }, + 'button.endpoint.approve-order' => function (ContainerInterface $container) : ApproveOrderEndpoint { + $requestData = $container->get('button.request-data'); + $apiClient = $container->get('api.endpoint.order'); + $sessionHandler = $container->get('session.handler'); + return new ApproveOrderEndpoint($requestData, $apiClient, $sessionHandler); + }, ]; diff --git a/modules.local/ppcp-button/src/Assets/SmartButton.php b/modules.local/ppcp-button/src/Assets/SmartButton.php index 16855d144..554a49a46 100644 --- a/modules.local/ppcp-button/src/Assets/SmartButton.php +++ b/modules.local/ppcp-button/src/Assets/SmartButton.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace Inpsyde\PayPalCommerce\Button\Assets; +use Inpsyde\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint; @@ -82,6 +83,10 @@ class SmartButton 'endpoint' => home_url(\WC_AJAX::get_endpoint(CreateOrderEndpoint::ENDPOINT)), 'nonce' => wp_create_nonce(CreateOrderEndpoint::nonce()), ], + 'approve_order' => [ + 'endpoint' => home_url(\WC_AJAX::get_endpoint(ApproveOrderEndpoint::ENDPOINT)), + 'nonce' => wp_create_nonce(ApproveOrderEndpoint::nonce()), + ], ], 'button' => [ 'wrapper' => '#ppc-button', diff --git a/modules.local/ppcp-button/src/ButtonModule.php b/modules.local/ppcp-button/src/ButtonModule.php index 855496fa6..9090ee670 100644 --- a/modules.local/ppcp-button/src/ButtonModule.php +++ b/modules.local/ppcp-button/src/ButtonModule.php @@ -6,6 +6,7 @@ namespace Inpsyde\PayPalCommerce\Button; use Dhii\Container\ServiceProvider; use Dhii\Modular\Module\ModuleInterface; use Inpsyde\PayPalCommerce\Button\Assets\SmartButton; +use Inpsyde\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint; use Inpsyde\PayPalCommerce\Button\Endpoint\RequestData; @@ -56,6 +57,17 @@ class ButtonModule implements ModuleInterface } ); + add_action( + 'wc_ajax_' . ApproveOrderEndpoint::ENDPOINT, + function () use ($container) { + $endpoint = $container->get('button.endpoint.approve-order'); + /** + * @var ChangeCartEndpoint $endpoint + */ + $endpoint->handleRequest(); + } + ); + add_action( 'wc_ajax_' . CreateOrderEndpoint::ENDPOINT, function () use ($container) { diff --git a/modules.local/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php b/modules.local/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php new file mode 100644 index 000000000..202c28eeb --- /dev/null +++ b/modules.local/ppcp-button/src/Endpoint/ApproveOrderEndpoint.php @@ -0,0 +1,74 @@ +requestData = $requestData; + $this->apiEndpoint = $apiEndpoint; + $this->sessionHandler = $sessionHandler; + } + + public static function nonce(): string + { + return self::ENDPOINT . get_current_user_id(); + } + + public function handleRequest(): bool + { + try { + $data = $this->requestData->readRequest($this->nonce()); + if (! isset($data['order_id'])) { + throw new RuntimeException(__("No order id given", "woocommerce-paypal-commerce-gateway")); + } + + $order = $this->apiEndpoint->order($data['order_id']); + if (! $order) { + throw new RuntimeException( + sprintf( + // translators: %s is the id of the order. + __('Order %s not found.', 'woocommerce-paypal-commerce-gateway'), + $data['order_id'] + ) + ); + } + + if (! $order->status()->is(OrderStatus::APPROVED)) { + + throw new RuntimeException( + sprintf( + // translators: %s is the id of the order. + __('Order %s is not approved yet.', 'woocommerce-paypal-commerce-gateway'), + $data['order_id'] + ) + ); + } + + $this->sessionHandler->replaceOrder($order); + wp_send_json_success($order); + return true; + } catch (\RuntimeException $error) { + wp_send_json_error($error->getMessage()); + return false; + } + } +} \ No newline at end of file diff --git a/modules.local/ppcp-button/src/Endpoint/CreateOrderEndpoint.php b/modules.local/ppcp-button/src/Endpoint/CreateOrderEndpoint.php index 9dce98e8e..a09910529 100644 --- a/modules.local/ppcp-button/src/Endpoint/CreateOrderEndpoint.php +++ b/modules.local/ppcp-button/src/Endpoint/CreateOrderEndpoint.php @@ -40,7 +40,7 @@ class CreateOrderEndpoint implements EndpointInterface ...$purchaseUnits ); wp_send_json_success($order->toArray()); - return false; + return true; } catch (\RuntimeException $error) { wp_send_json_error($error->getMessage()); return false;