diff --git a/assets/build/unzer_apple_pay_v2.asset.php b/assets/build/unzer_apple_pay_v2.asset.php
index 867a1a1..f9fb8a2 100644
--- a/assets/build/unzer_apple_pay_v2.asset.php
+++ b/assets/build/unzer_apple_pay_v2.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => '916afb7af7c9d1103569',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => '60784a2d960f6cc7d201');
diff --git a/assets/build/unzer_apple_pay_v2.js b/assets/build/unzer_apple_pay_v2.js
index 5c21ce7..1a78821 100644
--- a/assets/build/unzer_apple_pay_v2.js
+++ b/assets/build/unzer_apple_pay_v2.js
@@ -1 +1 @@
-(()=>{"use strict";const e=window.wp.htmlEntities,n=window.wc.wcBlocksRegistry,t=window.ReactJSXRuntime,s=window.wc.wcSettings,o=window.wp.element,r="apple_pay_v2",c=(0,s.getSetting)("unzer_"+r+"_data",{}),a=c.title||r,p=c.id||r,i=c?.description||"",m=c?.publicKey||"",d=c?.locale||"",u=c.paymentComponentId||p+"-payment-component",y=c.checkoutComponentId||p+"-checkout-component",l=({eventRegistration:e,emitResponse:n})=>{const{onPaymentSetup:s}=e;return(0,o.useEffect)(()=>{const e=s(function(e,n,t,s){return async()=>{try{const o=document.getElementById(e);if(o||console.error("unzer payment component does not exist: "+e),o.woocommercePaymentTypeId){const e={};e.unzer_nonce=s.nonce,e[n]=o.woocommercePaymentTypeId;const r={type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}};return console.log("return previously generated type id (button payment methods)",r),r}const r=await o.submit();if(r.submitResponse){if(!0===r.submitResponse.success){const e={};return e.unzer_nonce=s.nonce,e[n]=r.submitResponse.data.id,{type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}}}return{type:t?.responseTypes?.ERROR,message:r.submitResponse.message||"General Error"}}return{type:t?.responseTypes?.ERROR,message:"General Error 002"}}catch(e){return{type:t?.responseTypes?.ERROR,message:e.message||e.name||"General Error 003"}}}}(u,"unzer-apple-pay-v2-id",n,c));return e},[s]),(0,t.jsxs)("div",{children:[i&&(0,t.jsx)("p",{children:i}),(0,t.jsx)("div",{style:{display:"none"},children:(0,t.jsxs)("div",{id:u+"-container-for-button",children:[(0,t.jsx)("unzer-payment",{id:u,publicKey:m,locale:d,children:(0,t.jsx)("unzer-apple-pay",{})}),(0,t.jsx)("unzer-checkout",{id:y})]})})]})};!function(s,o,r){(0,n.registerPaymentMethod)({name:s,label:(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"wc-block-components-payment-method-label",children:o})}),content:r,edit:r,canMakePayment:()=>!0,ariaLabel:(0,e.decodeEntities)(o),supports:{features:["products"]}})}(p,a,(0,t.jsx)(l,{}))})();
\ No newline at end of file
+(()=>{"use strict";const e=window.wp.htmlEntities,n=window.wc.wcBlocksRegistry,t=window.ReactJSXRuntime;function s(s,o,r){(0,n.registerPaymentMethod)({name:s,label:(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"wc-block-components-payment-method-label",children:o})}),content:r,edit:r,canMakePayment:()=>!0,ariaLabel:(0,e.decodeEntities)(o),supports:{features:["products"]}})}const o=window.wc.wcSettings,r=window.wp.element,c="apple_pay_v2",i=(0,o.getSetting)("unzer_"+c+"_data",{}),a=i.title||c,p=i.id||c,d=i?.description||"",m=i?.publicKey||"",u=i?.locale||"",l=i.paymentComponentId||p+"-payment-component",y=i.checkoutComponentId||p+"-checkout-component",w=({eventRegistration:e,emitResponse:n})=>{const{onPaymentSetup:s}=e;return(0,r.useEffect)(()=>{const e=s(function(e,n,t,s){return async()=>{try{const o=document.getElementById(e);if(o||console.error("unzer payment component does not exist: "+e),o.woocommercePaymentTypeId){const e={};e.unzer_nonce=s.nonce,e[n]=o.woocommercePaymentTypeId;const r={type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}};return console.log("return previously generated type id (button payment methods)",r),r}const r=await o.submit();if(r.submitResponse){if(!0===r.submitResponse.success){const e={};return e.unzer_nonce=s.nonce,e[n]=r.submitResponse.data.id,{type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}}}return{type:t?.responseTypes?.ERROR,message:r.submitResponse.message||"General Error"}}return{type:t?.responseTypes?.ERROR,message:"General Error 002"}}catch(e){return{type:t?.responseTypes?.ERROR,message:e.message||e.name||"General Error 003"}}}}(l,"unzer-apple-pay-v2-id",n,i));return e},[s]),(0,t.jsxs)("div",{children:[d&&(0,t.jsx)("p",{children:d}),(0,t.jsx)("div",{style:{display:"none"},children:(0,t.jsxs)("div",{id:l+"-container-for-button",children:[(0,t.jsx)("unzer-payment",{id:l,publicKey:m,locale:u,children:(0,t.jsx)("unzer-apple-pay",{})}),(0,t.jsx)("unzer-checkout",{id:y})]})})]})};unzer_parameters.express.is_express?function(e){const n=(0,o.getSetting)("unzer_"+e+"_data",{}),r=n.title||e,c=n.id||e,i=n?.description||"",a=()=>i?(0,t.jsx)("p",{children:i}):null;s(c,r,(0,t.jsx)(a,{}))}(c):s(p,a,(0,t.jsx)(w,{}))})();
\ No newline at end of file
diff --git a/assets/build/unzer_card.asset.php b/assets/build/unzer_card.asset.php
index 6dfb35f..7df6fc9 100644
--- a/assets/build/unzer_card.asset.php
+++ b/assets/build/unzer_card.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => '63fd7aaf04a14116e59a',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => '63fd7aaf04a14116e59a');
diff --git a/assets/build/unzer_direct_debit.asset.php b/assets/build/unzer_direct_debit.asset.php
index 7a7d798..06cfbba 100644
--- a/assets/build/unzer_direct_debit.asset.php
+++ b/assets/build/unzer_direct_debit.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => '32d79f5351f92c11ec07',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => '32d79f5351f92c11ec07');
diff --git a/assets/build/unzer_direct_debit_secured.asset.php b/assets/build/unzer_direct_debit_secured.asset.php
index d05763f..973d1ba 100644
--- a/assets/build/unzer_direct_debit_secured.asset.php
+++ b/assets/build/unzer_direct_debit_secured.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => 'ce8fb575ec826f41efeb',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => 'ce8fb575ec826f41efeb');
diff --git a/assets/build/unzer_global.asset.php b/assets/build/unzer_global.asset.php
index de580bd..c7d9407 100644
--- a/assets/build/unzer_global.asset.php
+++ b/assets/build/unzer_global.asset.php
@@ -1,4 +1 @@
- array( 'wp-data' ),
- 'version' => '8b3cc5a0ca561d16a328',
-);
+ array('wp-data'), 'version' => '287c75ea7e2278feac67');
diff --git a/assets/build/unzer_global.js b/assets/build/unzer_global.js
index bcb5c5f..711e089 100644
--- a/assets/build/unzer_global.js
+++ b/assets/build/unzer_global.js
@@ -1 +1 @@
-(()=>{"use strict";const e=window.wp.data;function t(e,t){e.woocommercePaymentTypeId=t;const o=document.querySelectorAll(".wc-block-components-checkout-place-order-button");o[o.length-1].click(),e.style.display="none",setTimeout(()=>{n(!0)},3e3)}window.unzerCurrentPaymentMethod="",window.unzerIsInitial=!0,window.unzerCurrentAmount=0,window.unzerInitiatePaymentMethod=(e,o,n)=>{const a=document.getElementById(o.paymentComponentId),c=document.getElementById(o.paymentComponentId+"-container-for-button"),r=document.querySelectorAll(".wc-block-components-checkout-place-order-button");r.forEach(e=>{e.classList.remove("unzer-place-order-button-hidden")}),document.querySelectorAll(".unzer-payment-method-checkout-button-container").forEach(e=>{e.remove()}),a&&(a.style.display="",Promise.all([customElements.whenDefined("unzer-payment")]).then(()=>{try{if(r.length&&c){r.forEach(e=>{e.classList.add("unzer-place-order-button-hidden")});const e=r[r.length-1];c.classList.add("unzer-payment-method-checkout-button-container"),e.after(c)}if("unzer_google_pay"===e&&a.setGooglePayData){const e=o.options,c={gatewayMerchantId:e.gatewayMerchantId,merchantInfo:e.merchantInfo,transactionInfo:{currencyCode:n.totals.currency_code,countryCode:n.billingAddress.country,totalPriceStatus:"ESTIMATED",totalPrice:(n.totals.total_price/100).toFixed(2)},buttonOptions:e.buttonOptions,allowedCardNetworks:e.allowedCardNetworks,allowCreditCards:e.allowCreditCards,allowPrepaidCards:e.allowPrepaidCards};setTimeout(function(){a.setGooglePayData(c)},1e3);const r=document.getElementById("unzer-google-pay-checkout-component");r&&(r.onPaymentSubmit=function(e){e.submitResponse&&e.submitResponse.success?t(a,e.submitResponse.data.id):console.error(e)})}if("unzer_apple_pay_v2"===e&&a.setApplePayData){const e={countryCode:o.storeCountry,currencyCode:n.totals.currency_code,supportedNetworks:["visa","masterCard"],merchantCapabilities:["supports3DS"],total:{label:o.storeName,amount:n.totals.total_price/100}};setTimeout(function(){a.setApplePayData(e)},1e3);const c=document.getElementById("unzer-apple-pay-v2-checkout-component");c&&(c.onPaymentSubmit=function(e){e.submitResponse&&e.submitResponse.data&&e.submitResponse.data.id&&-1===e.submitResponse.data.id.indexOf("-apl-")||(e.submitResponse&&e.submitResponse.success?t(a,e.submitResponse.data.id):console.error(e))})}const s=new FormData;s.append("data",JSON.stringify(n)),s.append("payment_method",e),s.append("unzer_nonce",o.nonce),fetch(o.getCustomerDataUrl,{method:"POST",body:s}).then(e=>e.json()).then(e=>{const t=e.customer;if(t&&a.setCustomerData(t),e.publicKey!==a.publicKey&&(a.publicKey=e.publicKey,a.initOnFirstUpdate()),a.setBasketData){a.setBasketData({amount:n.totals.total_price/100,currencyType:n.totals.currency_code,country:n.billingAddress.country});const e=document.getElementById("unzer-paylater-installment-component");e&&e.reloadPlansWithRetainedData&&(e.updateDataFromStore(),e.onDataReady())}})}catch(e){console.error(e)}}))};const o=function(e,t){const o=window.wc.wcSettings.getSetting(e+"_data",{});if(console.log(o,t),window.unzerIsInitial){const n=()=>{document.querySelector(".wc-block-components-checkout-place-order-button")&&document.querySelector(".wp-block-woocommerce-checkout-payment-block .wc-block-components-radio-control-accordion-option")?setTimeout(async()=>{window.unzerInitiatePaymentMethod(e,o,t)},1e3):setTimeout(n,100)};n()}else setTimeout(async()=>{window.unzerInitiatePaymentMethod(e,o,t)},1e3)};function n(t=!1){(0,e.select)(window.wc.wcBlocksData.CHECKOUT_STORE_KEY);const n=(0,e.select)(window.wc.wcBlocksData.CART_STORE_KEY),a=(0,e.select)(window.wc.wcBlocksData.PAYMENT_STORE_KEY),c=n.getCartData();let r=!1;window.unzerCurrentPaymentMethod!==a.getActivePaymentMethod()&&(r=!0),r||window.unzerCurrentAmount===c.totals.total_price||(r=!0),(r||t)&&(console.log("payment method changed to: "+a.getActivePaymentMethod()),window.unzerCurrentPaymentMethod=a.getActivePaymentMethod(),window.unzerCurrentAmount=c.totals.total_price,o(window.unzerCurrentPaymentMethod,c))}(0,e.subscribe)(n,"wc/store/cart")})();
\ No newline at end of file
+(()=>{"use strict";const e=window.wp.data;function t(e,t){e.woocommercePaymentTypeId=t;const o=document.querySelectorAll(".wc-block-components-checkout-place-order-button");o[o.length-1].click(),e.style.display="none",setTimeout(()=>{a(!0)},3e3)}window.unzerCurrentPaymentMethod="",window.unzerIsInitial=!0,window.unzerCurrentAmount=0,window.unzerInitiatePaymentMethod=(e,o,n)=>{const a=document.getElementById(o.paymentComponentId),c=document.getElementById(o.paymentComponentId+"-container-for-button"),r=document.querySelectorAll(".wc-block-components-checkout-place-order-button");r.forEach(e=>{e.classList.remove("unzer-place-order-button-hidden")}),document.querySelectorAll(".unzer-payment-method-checkout-button-container").forEach(e=>{e.remove()}),a&&(a.style.display="",Promise.all([customElements.whenDefined("unzer-payment")]).then(()=>{try{if(r.length&&c){r.forEach(e=>{e.classList.add("unzer-place-order-button-hidden")});const e=r[r.length-1];c.classList.add("unzer-payment-method-checkout-button-container"),e.after(c)}if("unzer_google_pay"===e&&a.setGooglePayData){const e=o.options,c={gatewayMerchantId:e.gatewayMerchantId,merchantInfo:e.merchantInfo,transactionInfo:{currencyCode:n.totals.currency_code,countryCode:n.billingAddress.country,totalPriceStatus:"ESTIMATED",totalPrice:(n.totals.total_price/100).toFixed(2)},buttonOptions:e.buttonOptions,allowedCardNetworks:e.allowedCardNetworks,allowCreditCards:e.allowCreditCards,allowPrepaidCards:e.allowPrepaidCards};setTimeout(function(){a.setGooglePayData(c)},1e3);const r=document.getElementById("unzer-google-pay-checkout-component");r&&(r.onPaymentSubmit=function(e){e.submitResponse&&e.submitResponse.success?t(a,e.submitResponse.data.id):console.error(e)})}if("unzer_apple_pay_v2"===e&&a.setApplePayData&&!unzer_parameters.express.is_express){const e={countryCode:o.storeCountry,currencyCode:n.totals.currency_code,supportedNetworks:["visa","masterCard"],merchantCapabilities:["supports3DS"],total:{label:o.storeName,amount:n.totals.total_price/100}};setTimeout(function(){a.setApplePayData(e)},1e3);const c=document.getElementById("unzer-apple-pay-v2-checkout-component");c&&(c.onPaymentSubmit=function(e){e.submitResponse&&e.submitResponse.data&&e.submitResponse.data.id&&-1===e.submitResponse.data.id.indexOf("-apl-")||(e.submitResponse&&e.submitResponse.success?t(a,e.submitResponse.data.id):console.error(e))})}const s=new FormData;s.append("data",JSON.stringify(n)),s.append("payment_method",e),s.append("unzer_nonce",o.nonce),fetch(o.getCustomerDataUrl,{method:"POST",body:s}).then(e=>e.json()).then(e=>{const t=e.customer;if(t&&a.setCustomerData(t),e.publicKey!==a.publicKey&&(a.publicKey=e.publicKey,a.initOnFirstUpdate()),a.setBasketData){a.setBasketData({amount:n.totals.total_price/100,currencyType:n.totals.currency_code,country:n.billingAddress.country});const e=document.getElementById("unzer-paylater-installment-component");e&&e.reloadPlansWithRetainedData&&(e.updateDataFromStore(),e.onDataReady())}})}catch(e){console.error(e)}}))};const o=function(e,t){const o=window.wc.wcSettings.getSetting(e+"_data",{});if(console.log(o,t),window.unzerIsInitial){const n=()=>{document.querySelector(".wc-block-components-checkout-place-order-button")&&document.querySelector(".wp-block-woocommerce-checkout-payment-block .wc-block-components-radio-control-accordion-option")?setTimeout(async()=>{window.unzerInitiatePaymentMethod(e,o,t)},1e3):setTimeout(n,100)};n()}else setTimeout(async()=>{window.unzerInitiatePaymentMethod(e,o,t)},1e3)},n=function(e){ExpressButtons.refresh()};function a(t=!1){(0,e.select)(window.wc.wcBlocksData.CHECKOUT_STORE_KEY);const a=(0,e.select)(window.wc.wcBlocksData.CART_STORE_KEY),c=(0,e.select)(window.wc.wcBlocksData.PAYMENT_STORE_KEY),r=a.getCartData();let s=!1;window.unzerCurrentPaymentMethod!==c.getActivePaymentMethod()&&(s=!0),s||window.unzerCurrentAmount===r.totals.total_price||(s=!0),(s||t)&&(console.log("payment method changed to: "+c.getActivePaymentMethod()),window.unzerCurrentPaymentMethod=c.getActivePaymentMethod(),window.unzerCurrentAmount=r.totals.total_price,o(window.unzerCurrentPaymentMethod,r),n(r))}(0,e.subscribe)(a,"wc/store/cart")})();
\ No newline at end of file
diff --git a/assets/build/unzer_google_pay.asset.php b/assets/build/unzer_google_pay.asset.php
index 649f38f..6a38bb6 100644
--- a/assets/build/unzer_google_pay.asset.php
+++ b/assets/build/unzer_google_pay.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => 'ed95cfcadf7c4c532301',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => 'd4bd75af8e5657fae8ba');
diff --git a/assets/build/unzer_google_pay.js b/assets/build/unzer_google_pay.js
index 5d8c331..1762f66 100644
--- a/assets/build/unzer_google_pay.js
+++ b/assets/build/unzer_google_pay.js
@@ -1 +1 @@
-(()=>{"use strict";const e=window.wp.htmlEntities,n=window.wc.wcBlocksRegistry,t=window.ReactJSXRuntime,s=window.wc.wcSettings,o=window.wp.element,r="google_pay",c=(0,s.getSetting)("unzer_"+r+"_data",{}),a=c.title||r,i=c.id||r,p=c?.description||"",m=c?.publicKey||"",d=c?.locale||"",u=c.paymentComponentId||i+"-payment-component",y=c.checkoutComponentId||i+"-checkout-component",l=({eventRegistration:e,emitResponse:n})=>{const{onPaymentSetup:s}=e;return(0,o.useEffect)(()=>{const e=s(function(e,n,t,s){return async()=>{try{const o=document.getElementById(e);if(o||console.error("unzer payment component does not exist: "+e),o.woocommercePaymentTypeId){const e={};e.unzer_nonce=s.nonce,e[n]=o.woocommercePaymentTypeId;const r={type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}};return console.log("return previously generated type id (button payment methods)",r),r}const r=await o.submit();if(r.submitResponse){if(!0===r.submitResponse.success){const e={};return e.unzer_nonce=s.nonce,e[n]=r.submitResponse.data.id,{type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}}}return{type:t?.responseTypes?.ERROR,message:r.submitResponse.message||"General Error"}}return{type:t?.responseTypes?.ERROR,message:"General Error 002"}}catch(e){return{type:t?.responseTypes?.ERROR,message:e.message||e.name||"General Error 003"}}}}(u,"unzer-google-pay-id",n,c));return e},[s]),(0,t.jsxs)("div",{children:[p&&(0,t.jsx)("p",{children:p}),(0,t.jsx)("div",{style:{display:"none"},children:(0,t.jsxs)("div",{id:u+"-container-for-button",children:[(0,t.jsx)("unzer-payment",{id:u,publicKey:m,locale:d,children:(0,t.jsx)("unzer-google-pay",{})}),(0,t.jsx)("unzer-checkout",{id:y})]})})]})};!function(s,o,r){(0,n.registerPaymentMethod)({name:s,label:(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"wc-block-components-payment-method-label",children:o})}),content:r,edit:r,canMakePayment:()=>!0,ariaLabel:(0,e.decodeEntities)(o),supports:{features:["products"]}})}(i,a,(0,t.jsx)(l,{}))})();
\ No newline at end of file
+(()=>{"use strict";const e=window.wp.htmlEntities,n=window.wc.wcBlocksRegistry,t=window.ReactJSXRuntime;function s(s,o,r){(0,n.registerPaymentMethod)({name:s,label:(0,t.jsx)("div",{children:(0,t.jsx)("span",{className:"wc-block-components-payment-method-label",children:o})}),content:r,edit:r,canMakePayment:()=>!0,ariaLabel:(0,e.decodeEntities)(o),supports:{features:["products"]}})}const o=window.wc.wcSettings,r=window.wp.element,c="google_pay",i=(0,o.getSetting)("unzer_"+c+"_data",{}),a=i.title||c,p=i.id||c,d=i?.description||"",m=i?.publicKey||"",u=i?.locale||"",l=i.paymentComponentId||p+"-payment-component",y=i.checkoutComponentId||p+"-checkout-component",g=({eventRegistration:e,emitResponse:n})=>{const{onPaymentSetup:s}=e;return(0,r.useEffect)(()=>{const e=s(function(e,n,t,s){return async()=>{try{const o=document.getElementById(e);if(o||console.error("unzer payment component does not exist: "+e),o.woocommercePaymentTypeId){const e={};e.unzer_nonce=s.nonce,e[n]=o.woocommercePaymentTypeId;const r={type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}};return console.log("return previously generated type id (button payment methods)",r),r}const r=await o.submit();if(r.submitResponse){if(!0===r.submitResponse.success){const e={};return e.unzer_nonce=s.nonce,e[n]=r.submitResponse.data.id,{type:t?.responseTypes?.SUCCESS,meta:{paymentMethodData:e}}}return{type:t?.responseTypes?.ERROR,message:r.submitResponse.message||"General Error"}}return{type:t?.responseTypes?.ERROR,message:"General Error 002"}}catch(e){return{type:t?.responseTypes?.ERROR,message:e.message||e.name||"General Error 003"}}}}(l,"unzer-google-pay-id",n,i));return e},[s]),(0,t.jsxs)("div",{children:[d&&(0,t.jsx)("p",{children:d}),(0,t.jsx)("div",{style:{display:"none"},children:(0,t.jsxs)("div",{id:l+"-container-for-button",children:[(0,t.jsx)("unzer-payment",{id:l,publicKey:m,locale:u,children:(0,t.jsx)("unzer-google-pay",{})}),(0,t.jsx)("unzer-checkout",{id:y})]})})]})};unzer_parameters.express.is_express?function(e){const n=(0,o.getSetting)("unzer_"+e+"_data",{}),r=n.title||e,c=n.id||e,i=n?.description||"",a=()=>i?(0,t.jsx)("p",{children:i}):null;s(c,r,(0,t.jsx)(a,{}))}(c):s(p,a,(0,t.jsx)(g,{}))})();
\ No newline at end of file
diff --git a/assets/build/unzer_installment.asset.php b/assets/build/unzer_installment.asset.php
index fb0a52f..4becc8d 100644
--- a/assets/build/unzer_installment.asset.php
+++ b/assets/build/unzer_installment.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => '10ed83671a1cfe909449',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => '10ed83671a1cfe909449');
diff --git a/assets/build/unzer_invoice.asset.php b/assets/build/unzer_invoice.asset.php
index a8a211b..b0f5178 100644
--- a/assets/build/unzer_invoice.asset.php
+++ b/assets/build/unzer_invoice.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
- 'version' => 'f3d80eedde3556f6c679',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities'), 'version' => 'f3d80eedde3556f6c679');
diff --git a/assets/build/unzer_open_banking.asset.php b/assets/build/unzer_open_banking.asset.php
index 999cf91..6901bab 100644
--- a/assets/build/unzer_open_banking.asset.php
+++ b/assets/build/unzer_open_banking.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wp-html-entities' ),
- 'version' => '659fc40d4c203c7a7228',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wp-html-entities'), 'version' => '659fc40d4c203c7a7228');
diff --git a/assets/build/unzer_simple.asset.php b/assets/build/unzer_simple.asset.php
index 3141f4f..b9ea53a 100644
--- a/assets/build/unzer_simple.asset.php
+++ b/assets/build/unzer_simple.asset.php
@@ -1,4 +1 @@
- array( 'react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-html-entities' ),
- 'version' => '61276055c948fa0fa3b2',
-);
+ array('react-jsx-runtime', 'wc-blocks-registry', 'wc-settings', 'wp-html-entities'), 'version' => '61276055c948fa0fa3b2');
diff --git a/assets/css/checkout.css b/assets/css/checkout.css
index 557b3f6..1450e04 100644
--- a/assets/css/checkout.css
+++ b/assets/css/checkout.css
@@ -42,4 +42,17 @@ apple-pay-button {
.unzer-payment-instrument-new-form{
display: none;
+}
+
+#unzer-express-buttons-container {
+ margin-top: 10px;
+}
+
+.unzer-paypal-express-container {
+ margin-bottom: 10px;
+}
+
+#unzer-express-buttons-container #google-pay-container button.gpay-button,
+#unzer-express-buttons-container #google-pay-container #gpay-button-online-api-id {
+ width: 100%;
}
\ No newline at end of file
diff --git a/assets/js/checkout.js b/assets/js/checkout.js
index 175479f..1f90fad 100755
--- a/assets/js/checkout.js
+++ b/assets/js/checkout.js
@@ -12,6 +12,7 @@ const UnzerManager = {
UnzerManager.initTimeout = setTimeout(
function () {
UnzerManager.currency = unzer_parameters.currency;
+ UnzerManager.hideNoneExpressMethods();
UnzerManager.initCard();
UnzerManager.initDirectDebit();
UnzerManager.initDirectDebitSecured();
@@ -48,6 +49,16 @@ const UnzerManager = {
}
},
+ hideNoneExpressMethods() {
+ if (unzer_parameters.express.current_express_method != '' && unzer_parameters.express.is_express) {
+ document.querySelectorAll('.wc_payment_method').forEach((element) => {
+ if (!element.classList.contains('payment_method_' + unzer_parameters.express.current_express_method)) {
+ element.style.display = 'none';
+ }
+ });
+ }
+ },
+
initSavedPaymentInstruments() {
const updateFunction = function () {
const containers = document.querySelectorAll('.unzer-saved-payment-instruments-container');
@@ -171,6 +182,9 @@ const UnzerManager = {
initGooglePay() {
+ if (unzer_parameters.express.is_express) {
+ return;
+ }
const options = unzer_parameters.google_pay_options;
if (!options) {
@@ -229,6 +243,10 @@ const UnzerManager = {
},
initApplePay() {
+ if (unzer_parameters.express.is_express) {
+ return;
+ }
+
UnzerManager.createApplePayButtonContainer();
const unzerPaymentElement = document.getElementById('unzer-apple-pay-payment-component');
if (unzerPaymentElement && unzerPaymentElement.setApplePayData) {
@@ -270,6 +288,9 @@ const UnzerManager = {
if (document.getElementById('unzer-apple-pay-v2-id').value) {
return true;
}
+ if (unzer_parameters.express.is_express) {
+ return true;
+ }
console.error('Apple Pay: Checkout triggered without payment data.');
UnzerManager.error(unzer_parameters.generic_error_message);
}
@@ -415,6 +436,7 @@ const UnzerManager = {
return selectedRadio.value;
},
renderCurrentPaymentMethod() {
+ this.hideNoneExpressMethods();
const method = UnzerManager.getSelectedPaymentMethod();
document.querySelectorAll('.unzer-ui-container').forEach(
function (el) {
diff --git a/assets/js/express.js b/assets/js/express.js
new file mode 100644
index 0000000..b12629e
--- /dev/null
+++ b/assets/js/express.js
@@ -0,0 +1,451 @@
+const ExpressButtons = {
+ interval: null,
+ intervalCounter: 0,
+ cartTotal: null,
+ attemptToPlaceButton: function () {
+ let container = document.querySelector( '.wc-block-mini-cart__footer');
+
+ if ( ! container ) {
+ container = document.querySelector( '.wc-block-cart__submit-container' );
+ }
+ if ( ! container) {
+ container = document.querySelector('.wc-proceed-to-checkout');
+ if ( ! container) {
+ return false;
+ }
+ container = container.parentElement;
+ }
+ if (document.getElementById( 'unzer-express-buttons-container' )) {
+ return false;
+ }
+
+ const button_container = document.createElement( 'div' );
+ button_container.id = 'unzer-express-buttons-container';
+
+ const unzer_express_error_container = document.createElement('div');
+ unzer_express_error_container.classList.add('unzer-express-buttons-error');
+ unzer_express_error_container.classList.add('woocommerce-error');
+ unzer_express_error_container.hidden = true;
+ unzer_express_error_container.innerHTML = unzer_parameters.generic_error_message_express;
+ button_container.appendChild(unzer_express_error_container);
+
+ const unzer_payment_container = document.createElement( 'unzer-payment' );
+ unzer_payment_container.classList.add('unzer-express-payment');
+ unzer_payment_container.setAttribute('publicKey', unzer_parameters.publicKey);
+ unzer_payment_container.setAttribute('locale', unzer_parameters.locale);
+
+ if (unzer_parameters.express.paypal) {
+ const unzer_paypal_express_container = document.createElement('div');
+ unzer_paypal_express_container.classList.add('unzer-paypal-express-container');
+ const unzer_paypal_express_element = document.createElement('unzer-paypal-express');
+ unzer_paypal_express_element.classList.add('unzer-paypal-express');
+ unzer_paypal_express_container.appendChild(unzer_paypal_express_element);
+ unzer_payment_container.appendChild(unzer_paypal_express_container);
+ }
+
+ if (unzer_parameters.express.googlepay) {
+ const unzer_google_pay_express_container = document.createElement('div');
+ unzer_google_pay_express_container.classList.add('unzer-google-pay-express-container');
+ const unzer_google_pay_express_element = document.createElement('unzer-google-pay');
+ unzer_google_pay_express_element.classList.add('unzer-google-pay');
+ unzer_google_pay_express_container.appendChild(unzer_google_pay_express_element);
+ unzer_payment_container.appendChild(unzer_google_pay_express_container);
+ }
+
+ if (unzer_parameters.express.applepay) {
+ const unzer_applepay_express_container = document.createElement('div');
+ unzer_applepay_express_container.classList.add('unzer-applepay-express-container');
+ const unzer_applepay_express_element = document.createElement('unzer-apple-pay');
+ unzer_applepay_express_element.classList.add('unzer-apple-pay');
+ unzer_applepay_express_container.appendChild(unzer_applepay_express_element);
+ unzer_payment_container.appendChild(unzer_applepay_express_container);
+ }
+
+ button_container.appendChild(unzer_payment_container);
+
+ container.appendChild( button_container );
+ return true;
+ },
+
+ getTotals: async function() {
+ const cartTotal = await getWooCommerceCartAmount();
+
+ if (!cartTotal) {
+ console.warn('Warenkorb-Betrag konnte nicht ermittelt werden.');
+ return null;
+ }
+
+ ExpressButtons.cartTotal = cartTotal;
+ return cartTotal;
+ },
+
+ init: async function () {
+ if (!unzer_parameters.express.paypal && !unzer_parameters.express.googlepay && !unzer_parameters.express.applepay) {
+ return false;
+ }
+ if (unzer_parameters.express.is_express) {
+ return false;
+ }
+
+ const cartTotal = await this.getTotals();
+
+ if (!cartTotal || !cartTotal.amount) {
+ return false;
+ }
+ if ( ! ExpressButtons.attemptToPlaceButton()) {
+ return false;
+ }
+ Promise.all([
+ customElements.whenDefined('unzer-payment'),
+ customElements.whenDefined('unzer-google-pay'),
+ customElements.whenDefined('unzer-paypal-express'),
+ customElements.whenDefined('unzer-apple-pay'),
+ ]).then(() => {
+ const unzerExpressPayment = document.querySelector(
+ '.unzer-express-payment'
+ );
+
+ // setMerchantConfigData?
+
+ const unzerPaypalExpress = document.querySelector(
+ '.unzer-paypal-express'
+ );
+ const unzerGooglePay = document.querySelector('.unzer-google-pay');
+ const unzerApplePay = document.querySelector('.unzer-apple-pay');
+
+ if (!unzerExpressPayment) {
+ return;
+ }
+
+ if (unzerPaypalExpress) {
+ this.registerPaypalExpress(
+ unzerPaypalExpress,
+ unzerExpressPayment
+ );
+ }
+
+ if (unzerGooglePay) {
+ this.registerGooglePay(unzerGooglePay, unzerExpressPayment);
+ const container = unzerGooglePay.parentNode;
+ unzerGooglePay.remove();
+ container.appendChild(unzerGooglePay);
+ }
+
+ if (unzerApplePay) {
+ this.registerApplePay(unzerApplePay, unzerExpressPayment);
+ const container = unzerApplePay.parentNode;
+ unzerApplePay.remove();
+ container.appendChild(unzerApplePay);
+ }
+ });
+ return true;
+ },
+
+ refresh: function()
+ {
+ if (window.unzerCurrentAmount && this.cartTotal) {
+ this.cartTotal.amount = window.unzerCurrentAmount;
+ } else {
+ this.getTotals();
+ }
+ if (document.getElementById('unzer-express-buttons-container')) {
+ document.getElementById('unzer-express-buttons-container').remove();
+ }
+ this.init();
+ },
+
+ showError: function() {
+ const errorElement = document.querySelector(
+ '.unzer-express-buttons-error'
+ );
+ if (errorElement) {
+ errorElement.removeAttribute('hidden');
+ }
+ },
+
+ registerPaypalExpress: function (paypalButton, unzerExpressPayment) {
+ paypalButton.id =
+ 'unzer-paypal-button-' + Math.floor(Math.random() * 10000);
+ paypalButton.addEventListener('click', async (event) => {
+ event.stopPropagation();
+ const response = await unzerExpressPayment.submit();
+
+ if (!response?.submitResponse?.success) {
+ this.showError();
+ return;
+ }
+ const paymentTypeId = response.submitResponse.data.id;
+ fetch(unzer_parameters.urls.paypal, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ paymentTypeId: paymentTypeId,
+ }),
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ location.href = json.redirectUrl;
+ })
+ .catch(() => {
+ this.showError();
+ });
+ });
+ },
+
+ registerGooglePay: function(googlePayButton, unzerExpressPayment) {
+ const googlePayData = {
+ gatewayMerchantId: unzer_parameters.google_pay_options.gatewayMerchantId,
+ merchantInfo: unzer_parameters.google_pay_options.merchantInfo,
+ transactionInfo: {
+ currencyCode: unzer_parameters.currency,
+ countryCode: unzer_parameters.google_pay_options.countryCode,
+ totalPriceStatus: 'ESTIMATED',
+ checkoutOption: 'DEFAULT',
+ totalPrice: String(ExpressButtons.cartTotal.amount),
+ },
+ buttonOptions: unzer_parameters.google_pay_options.buttonOptions,
+ allowedCardNetworks: unzer_parameters.google_pay_options.allowedCardNetworks,
+ allowCreditCards: unzer_parameters.google_pay_options.allowCreditCards,
+ allowPrepaidCards: unzer_parameters.google_pay_options.allowPrepaidCards,
+ billingAddressParameters: { format: 'MIN' },
+ billingAddressRequired: true,
+ emailRequired: true,
+ onPaymentDataChangedCallback: () => {
+ return {};
+ },
+ shippingOptionParameters: {},
+ onPaymentAuthorizedCallback: async (
+ paymentData,
+ approve,
+ reject
+ ) => {
+ // You can create customer here based on paymentData
+ const response = await unzerExpressPayment.submit();
+ if (!response?.submitResponse?.success) {
+ this.showError();
+ return;
+ }
+ const paymentTypeId = response.submitResponse.data.id;
+
+ fetch(unzer_parameters.urls.googlePay, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ paymentTypeId,
+ paymentData,
+ }),
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ location.href = json.redirectUrl;
+ })
+ .catch(() => {
+ this.showError();
+ });
+ },
+ shippingAddressRequired: true,
+ shippingOptionRequired: false,
+ };
+ unzerExpressPayment.setGooglePayData(googlePayData);
+ },
+
+ registerApplePay: function(applePayButton, unzerExpressPayment) {
+ console.log('register amount', ExpressButtons.cartTotal.amount);
+ const applePayPaymentRequest = {
+ countryCode: unzer_parameters.applepay_options.countryCode,
+ currencyCode: unzer_parameters.currency,
+ supportedNetworks: unzer_parameters.applepay_options.supportedNetworks,
+ merchantCapabilities: unzer_parameters.applepay_options.merchantCapabilities,
+ total: {
+ label: unzer_parameters.applepay_options.shopName,
+ amount: String(ExpressButtons.cartTotal.amount),
+ },
+ requiredShippingContactFields: [
+ 'postalAddress',
+ 'name',
+ 'email',
+ 'phone',
+ ],
+ requiredBillingContactFields: [
+ 'postalAddress',
+ 'name',
+ 'email',
+ 'phone',
+ ],
+
+ onPaymentAuthorizedCallback: async (
+ paymentData,
+ approve,
+ reject,
+ event
+ ) => {
+ let shippingContact = event.payment.shippingContact; // Store the shipping contact data for express checkout
+ let billingContact = event.payment.billingContact; // Store the billing contact data for express checkout
+
+ // You can create customer here based on paymentData
+ const response = await unzerExpressPayment.submit();
+ if (response.submitResponse.success) {
+ approve();
+ } else {
+ reject();
+ this.showError();
+ return;
+ }
+ const paymentTypeId = response.submitResponse.data.id;
+
+ fetch(unzer_parameters.urls.applePay, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ paymentTypeId,
+ paymentData,
+ shippingContact,
+ billingContact,
+ }),
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ location.href = json.redirectUrl;
+ })
+ .catch(() => {
+ this.showError();
+ });
+ },
+ };
+ applePayPaymentRequest.initApplePaySession = (applePaySession) => {
+ const session = applePaySession;
+ };
+ unzerExpressPayment?.setApplePayData(applePayPaymentRequest);
+ }
+}
+
+ExpressButtons.interval = setInterval(
+ async function () {
+ ExpressButtons.intervalCounter++;
+ if (ExpressButtons.init() || ExpressButtons.intervalCounter > 10) {
+ clearInterval( ExpressButtons.interval );
+ }
+ },
+ 500
+);
+
+async function getWooCommerceCartAmount() {
+
+ try {
+ if (
+ window.wp &&
+ window.wp.data &&
+ window.wp.data.select('wc/store/cart')
+ ) {
+ const cart = window.wp.data.select('wc/store/cart').getCartData();
+
+ if (cart?.totals?.total_price) {
+ return {
+ amount: parseInt(cart.totals.total_price, 10) / 100,
+ currency: cart.totals.currency_code || null,
+ source: 'wc-blocks'
+ };
+ }
+ }
+ } catch (e) {}
+
+ try {
+ const response = await fetch('/wp-json/wc/store/v1/cart');
+ const cart = await response.json();
+
+ if (cart?.totals?.total_price) {
+ return {
+ amount: parseInt(cart.totals.total_price, 10) / 100,
+ currency: cart.totals.currency_code || null,
+ source: 'store-api'
+ };
+ }
+ } catch (e) {}
+
+ const selectors = [
+ '.woocommerce-checkout .order-total .woocommerce-Price-amount bdi',
+ '.wc-block-mini-cart__footer .wc-block-formatted-money-amount',
+ '.woocommerce-cart .cart_totals .order-total .woocommerce-Price-amount bdi',
+ '.order-total .woocommerce-Price-amount bdi'
+ ];
+
+ for (const selector of selectors) {
+ const element = document.querySelector(selector);
+
+ if (element) {
+ const amount = parseWooCommercePrice(element.textContent);
+
+ if (!Number.isNaN(amount)) {
+ return {
+ amount,
+ currency: getCurrencyFromPriceText(element.textContent),
+ source: 'dom'
+ };
+ }
+ }
+ }
+
+ return null;
+}
+
+function parseWooCommercePrice(priceText) {
+ return parseFloat(
+ priceText
+ .replace(/\s/g, '')
+ .replace(/[^\d,.-]/g, '')
+ .replace(/\./g, '')
+ .replace(',', '.')
+ );
+}
+
+function getCurrencyFromPriceText(priceText) {
+ if (priceText.includes('€')) return 'EUR';
+ if (priceText.includes('$')) return 'USD';
+ if (priceText.includes('£')) return 'GBP';
+
+ return null;
+}
+
+jQuery( function( $ ) {
+ $( document.body ).on( 'updated_cart_totals', function() {
+ ExpressButtons.refresh();
+ });
+});
+
+let miniCartRefreshTimeout = null;
+
+function observeMiniCartSubtotal() {
+ const target = document.querySelector('.wc-block-mini-cart__footer-subtotal');
+
+ if (!target) {
+ return false;
+ }
+
+ const observer = new MutationObserver(() => {
+ clearTimeout(miniCartRefreshTimeout);
+
+ miniCartRefreshTimeout = setTimeout(() => {
+ ExpressButtons.refresh();
+ }, 200);
+ });
+
+ observer.observe(target, {
+ childList: true,
+ subtree: true,
+ characterData: true
+ });
+
+ return true;
+}
+
+let miniCartObserverInterval = setInterval(function () {
+ if (observeMiniCartSubtotal()) {
+ clearInterval(miniCartObserverInterval);
+ }
+}, 500);
\ No newline at end of file
diff --git a/assets/src/payment_methods/unzer_apple_pay_v2.jsx b/assets/src/payment_methods/unzer_apple_pay_v2.jsx
index 4d3b2b2..e8b31c0 100644
--- a/assets/src/payment_methods/unzer_apple_pay_v2.jsx
+++ b/assets/src/payment_methods/unzer_apple_pay_v2.jsx
@@ -1,4 +1,5 @@
import unzerRegisterMethod from "../util/register-method";
+import unzerRegisterSimpleMethod from "../util/register-simple-method";
import {getSetting} from '@woocommerce/settings'
import {useEffect} from '@wordpress/element'
import onPaymentSetupFactory from "../util/on-payment-setup-factory";
@@ -39,4 +40,9 @@ const PaymentContent = ({eventRegistration, emitResponse}) => {
)
}
-unzerRegisterMethod(gatewayName, gatewayTitle, );
+if (unzer_parameters.express.is_express) {
+ unzerRegisterSimpleMethod(nameInSnakeCase);
+} else {
+ unzerRegisterMethod(gatewayName, gatewayTitle, );
+}
+
diff --git a/assets/src/payment_methods/unzer_google_pay.jsx b/assets/src/payment_methods/unzer_google_pay.jsx
index e2578fd..a6c128c 100644
--- a/assets/src/payment_methods/unzer_google_pay.jsx
+++ b/assets/src/payment_methods/unzer_google_pay.jsx
@@ -2,6 +2,7 @@ import unzerRegisterMethod from "../util/register-method";
import {getSetting} from '@woocommerce/settings'
import {useEffect} from '@wordpress/element'
import onPaymentSetupFactory from "../util/on-payment-setup-factory";
+import unzerRegisterSimpleMethod from "../util/register-simple-method";
const nameInSnakeCase = 'google_pay';
const settings = getSetting('unzer_' + nameInSnakeCase + '_data', {})
@@ -39,4 +40,8 @@ const PaymentContent = ({eventRegistration, emitResponse}) => {
)
}
-unzerRegisterMethod(gatewayName, gatewayTitle, );
+if (unzer_parameters.express.is_express) {
+ unzerRegisterSimpleMethod(nameInSnakeCase);
+} else {
+ unzerRegisterMethod(gatewayName, gatewayTitle, );
+}
diff --git a/assets/src/unzer_global.jsx b/assets/src/unzer_global.jsx
index ecfb1f1..8068bd0 100644
--- a/assets/src/unzer_global.jsx
+++ b/assets/src/unzer_global.jsx
@@ -87,36 +87,38 @@ window.unzerInitiatePaymentMethod = (
}
if (longNameInSnakeCase === 'unzer_apple_pay_v2' && paymentComponent.setApplePayData) {
- const applePayPaymentRequest = {
- countryCode: settings.storeCountry,
- currencyCode: cartData.totals.currency_code,
- supportedNetworks: ['visa', 'masterCard'],
- merchantCapabilities: ['supports3DS'],
- total: {
- label: settings.storeName,
- amount: cartData.totals.total_price / 100
- }
- };
- // for some reason this does not get applied without timeout?
- setTimeout(
- function () {
- paymentComponent.setApplePayData(applePayPaymentRequest);
- },
- 1000
- );
-
- const unzerCheckout = document.getElementById('unzer-apple-pay-v2-checkout-component');
- if (unzerCheckout) {
- unzerCheckout.onPaymentSubmit = function (response) {
- if (response.submitResponse && response.submitResponse.data && response.submitResponse.data.id && response.submitResponse.data.id.indexOf('-apl-') === -1) {
- return
- }
- if (response.submitResponse && response.submitResponse.success) {
- triggerSubmitForPaymentTypeId(paymentComponent, response.submitResponse.data.id);
- } else {
- console.error(response);
+ if (!unzer_parameters.express.is_express) {
+ const applePayPaymentRequest = {
+ countryCode: settings.storeCountry,
+ currencyCode: cartData.totals.currency_code,
+ supportedNetworks: ['visa', 'masterCard'],
+ merchantCapabilities: ['supports3DS'],
+ total: {
+ label: settings.storeName,
+ amount: cartData.totals.total_price / 100
}
};
+ // for some reason this does not get applied without timeout?
+ setTimeout(
+ function () {
+ paymentComponent.setApplePayData(applePayPaymentRequest);
+ },
+ 1000
+ );
+
+ const unzerCheckout = document.getElementById('unzer-apple-pay-v2-checkout-component');
+ if (unzerCheckout) {
+ unzerCheckout.onPaymentSubmit = function (response) {
+ if (response.submitResponse && response.submitResponse.data && response.submitResponse.data.id && response.submitResponse.data.id.indexOf('-apl-') === -1) {
+ return
+ }
+ if (response.submitResponse && response.submitResponse.success) {
+ triggerSubmitForPaymentTypeId(paymentComponent, response.submitResponse.data.id);
+ } else {
+ console.error(response);
+ }
+ };
+ }
}
}
@@ -190,8 +192,10 @@ const updatePaymentMethodData = function (longNameInSnakeCase, cartData) {
}, 1000
)
}
+}
-
+const updateExpressMethods = function (cartData) {
+ ExpressButtons.refresh();
}
function unzerCheckForPaymentMethodChanges(force = false) {
@@ -214,6 +218,7 @@ function unzerCheckForPaymentMethodChanges(force = false) {
window.unzerCurrentPaymentMethod = paymentStore.getActivePaymentMethod();
window.unzerCurrentAmount = cartData.totals.total_price;
updatePaymentMethodData(window.unzerCurrentPaymentMethod, cartData);
+ updateExpressMethods(cartData);
}
}
diff --git a/includes/Main.php b/includes/Main.php
index 7ba2fba..444b2e6 100755
--- a/includes/Main.php
+++ b/includes/Main.php
@@ -6,6 +6,7 @@
use UnzerPayments\Controllers\AccountController;
use UnzerPayments\Controllers\AdminController;
use UnzerPayments\Controllers\CheckoutController;
+use UnzerPayments\Controllers\ExpressController;
use UnzerPayments\Controllers\WebhookController;
use UnzerPayments\Gateways\AbstractGateway;
use UnzerPayments\Gateways\Alipay;
@@ -30,8 +31,10 @@
use UnzerPayments\Gateways\WeChatPay;
use UnzerPayments\Gateways\Wero;
use UnzerPayments\Services\DashboardService;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\OrderService;
use UnzerPayments\Services\PaymentService;
+use UnzerSDK\Resources\PaymentTypes\Applepay;
use WC_Payment_Gateway;
class Main {
@@ -87,6 +90,12 @@ public function registerEvents(): void {
add_action( 'woocommerce_api_' . WebhookController::WEBHOOK_ROUTE_SLUG, array( new WebhookController(), 'receiveWebhook' ) );
add_action( 'woocommerce_api_' . AccountController::DELETE_PAYMENT_INSTRUMENT_URL_SLUG, array( new AccountController(), 'deletePaymentInstrument' ) );
add_action( 'woocommerce_api_' . CheckoutController::GET_UNZER_CUSTOMER_SLUG, array( new CheckoutController(), 'getUnzerCustomerData' ) );
+
+ add_action( 'woocommerce_api_' . ExpressController::PAYPAL_EXPRESS_SLUG, array( new ExpressController(), 'paypalExpress' ) );
+ add_action( 'woocommerce_api_' . ExpressController::PAYPAL_EXPRESS_RETURN_SLUG, array( new ExpressController(), 'paypalExpressReturn' ) );
+ add_action( 'woocommerce_api_' . ExpressController::GOOGLE_PAY_EXPRESS_SLUG, array( new ExpressController(), 'googleExpress' ) );
+ add_action( 'woocommerce_api_' . ExpressController::APPLE_PAY_EXPRESS_SLUG, array( new ExpressController(), 'applepayExpress' ) );
+
add_filter( 'plugin_action_links_' . plugin_basename( UNZER_PLUGIN_PATH . 'unzer-payments.php' ), array( $this, 'addPluginSettingsLink' ) );
add_action( 'add_meta_boxes', array( $this, 'addMetaBoxes' ), 40 );
add_action( 'woocommerce_settings_checkout', array( AdminController::class, 'renderGlobalSettingsStart' ) );
@@ -104,6 +113,32 @@ public function registerEvents(): void {
add_action( 'woocommerce_blocks_loaded', array( $this, 'addCheckoutBlocks' ) );
add_action( 'before_woocommerce_pay_form', array( $this, 'orderPayPaymentMethod' ), 20, 4 );
add_action( 'woocommerce_order_status_changed', array( $this, 'unzerPaymentStatusChange' ), 10, 4 );
+ add_filter(
+ 'woocommerce_available_payment_gateways',
+ function (array $gateways): array {
+ if (is_admin() && !wp_doing_ajax()) {
+ return $gateways;
+ }
+ if (!is_checkout()) {
+ return $gateways;
+ }
+ if (empty($_GET['isExpressCheckout']) || $_GET['isExpressCheckout'] !== 'true') {
+ return $gateways;
+ }
+ $expressMethod = WC()->session->get(
+ ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD
+ );
+ if (!$expressMethod) {
+ return $gateways;
+ }
+ foreach ($gateways as $id => $gateway) {
+ if ($id !== $expressMethod) {
+ unset($gateways[$id]);
+ }
+ }
+ return $gateways;
+ }
+ );
add_action(
'admin_enqueue_scripts',
function () {
@@ -141,7 +176,14 @@ function ( $tags, $context ) {
10,
2
);
- }
+
+ add_action('woocommerce_admin_order_data_after_payment_info', function ($order) {
+ if (in_array($order->get_payment_method(), [Paypal::GATEWAY_ID, GooglePay::GATEWAY_ID, ApplePayV2::GATEWAY_ID]) &&
+ strpos($order->get_payment_method_title(), '(Express)') !== false) {
+ echo '
Express Checkout
';
+ }
+ });
+ }
public function addOrderEmailData( $order ) {
( new OrderService() )->printPaymentInstructionsHtml( $order );
@@ -412,17 +454,17 @@ public function unzerPaymentStatusChange( $order_id, $from, $to, $order ) {
try {
if ( in_array( $to, array( str_replace( 'wc-', '', get_option( 'unzer_capture_trigger_order_status' ) ) ), true ) ) {
- if ( ! $order->is_paid() ) {
- $unzer = ( new PaymentService() )->getUnzerManagerForOrder( $order );
- $paymentId = $order->get_meta( self::ORDER_META_KEY_PAYMENT_ID, true );
- $payment = $unzer->fetchPayment( $paymentId );
- if ( $payment->getAmount()->getCharged() == 0 ) {
- $order->update_meta_data( self::ORDER_CHARGE_AUTOMATICALLY_DONE, 'yes' );
- $order->save_meta_data();
- ( new PaymentService() )->performChargeOnAuthorization( $order_id );
- }
- }
- }
+
+ $unzer = ( new PaymentService() )->getUnzerManagerForOrder( $order );
+ $paymentId = $order->get_meta( self::ORDER_META_KEY_PAYMENT_ID, true );
+ $payment = $unzer->fetchPayment( $paymentId );
+ if ( $payment->getAmount()->getCharged() == 0 ) {
+ $order->update_meta_data( self::ORDER_CHARGE_AUTOMATICALLY_DONE, 'yes' );
+ $order->save_meta_data();
+ ( new PaymentService() )->performChargeOnAuthorization( $order_id );
+ }
+ }
+
} catch ( \Exception $e ) {
// silent
}
diff --git a/includes/controllers/CheckoutController.php b/includes/controllers/CheckoutController.php
index 61b8240..98c7026 100644
--- a/includes/controllers/CheckoutController.php
+++ b/includes/controllers/CheckoutController.php
@@ -4,8 +4,12 @@
use Exception;
use UnzerPayments\Gateways\AbstractGateway;
+use UnzerPayments\Gateways\ApplePayV2;
+use UnzerPayments\Gateways\GooglePay;
+use UnzerPayments\Gateways\Paypal;
use UnzerPayments\Main;
use UnzerPayments\Services\CustomerService;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\LogService;
use UnzerPayments\Services\OrderService;
use UnzerPayments\Services\PaymentService;
@@ -82,10 +86,19 @@ public function confirm() {
$paymentGateway->maybeSavePaymentInstrument( $transaction->getPayment()->getPaymentType()->getId() );
}
}
+
+ if ((string)WC()->session->get(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD) !== '') {
+ if (in_array($order->get_payment_method(), [Paypal::GATEWAY_ID, GooglePay::GATEWAY_ID, ApplePayV2::GATEWAY_ID])) {
+ $order->set_payment_method_title($order->get_payment_method_title() . ' (Express)');
+ $order->save();
+ }
+ }
+
$orderService = new OrderService();
try {
$orderService->processPaymentStatus( $transaction, $order );
self::clearSessionData();
+ self::resetExpressCheckoutSessionData();
wp_redirect( $order->get_checkout_order_received_url() );
} catch ( Exception $e ) {
$logger->error(
@@ -101,6 +114,7 @@ public function confirm() {
wp_redirect( wc_get_checkout_url() );
}
WC()->session->set( 'save_payment_instrument', false );
+ self::resetExpressCheckoutSessionData();
die;
}
@@ -131,6 +145,7 @@ public function getUnzerCustomerData() {
*/
public static function checkoutSuccess( $order ) {
self::clearSessionData();
+ self::resetExpressCheckoutSessionData();
( new OrderService() )->printPaymentInstructionsHtml( $order );
}
@@ -140,6 +155,14 @@ protected function renderJson( array $data ) {
die;
}
+ public static function resetExpressCheckoutSessionData() {
+ WC()->session->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, false);
+ WC()->session->set(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID, false);
+ WC()->session->set(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID, false);
+ WC()->session->set(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID, false);
+ WC()->session->set(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_TYPE_ID, false);
+ }
+
protected static function clearSessionData() {
setcookie( CustomerService::SESSION_KEY_USER_ID, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN );
}
diff --git a/includes/controllers/ExpressController.php b/includes/controllers/ExpressController.php
new file mode 100644
index 0000000..6d9b376
--- /dev/null
+++ b/includes/controllers/ExpressController.php
@@ -0,0 +1,238 @@
+getRequestValue('paymentTypeId', '')
+ );
+
+ if (empty($paymentTypeId)) {
+ wp_send_json_error([
+ 'message' => 'paymentTypeId is missing',
+ ], 400);
+ }
+
+ if (!WC()->cart) {
+ wc_load_cart();
+ }
+
+ $cartTotal = (float) WC()->cart->get_total('edit');
+ $currency = get_woocommerce_currency();
+
+ $paymentService = new PaymentService();
+
+ $basket = (new Basket())
+ ->setTotalValueGross($cartTotal)
+ ->setCurrencyCode($currency);
+
+ $basketItem = (new BasketItem())
+ ->setAmountPerUnitGross($cartTotal)
+ ->setTitle('-');
+
+ $basket->addBasketItem($basketItem);
+ $basketResult = $paymentService->getUnzerManager()->createBasket($basket);
+
+ $shopservice = new ShopService();
+ $metadata = $shopservice->getMetadata();
+ $shopservice->setIsExpress($metadata, true);
+
+ $bookingMode = (new Paypal())->get_option( 'transaction_type' );
+
+ $returnUrl = WC()->api_request_url(self::PAYPAL_EXPRESS_RETURN_SLUG);
+
+ if ($bookingMode === AbstractGateway::TRANSACTION_TYPE_AUTHORIZE) {
+ $authorization = (new Authorization(
+ $cartTotal,
+ $currency,
+ $returnUrl
+ ))->setCheckoutType('express', $paymentTypeId);
+
+ $resultTransaction = $paymentService->getUnzerManager()->performAuthorization(
+ $authorization,
+ $paymentTypeId,
+ null,
+ $metadata,
+ $basketResult
+ );
+
+ } else {
+ $charge = (new Charge(
+ $cartTotal,
+ $currency,
+ $returnUrl
+ ))->setCheckoutType('express', $paymentTypeId);
+
+ $resultTransaction = $paymentService->getUnzerManager()->performCharge(
+ $charge,
+ $paymentTypeId,
+ null,
+ $metadata,
+ $basketResult
+ );
+ }
+
+ WC()->session->set(
+ ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID,
+ $resultTransaction->getPaymentId()
+ );
+
+ WC()->session->set(
+ ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_TYPE_ID,
+ $paymentTypeId
+ );
+
+ wp_send_json([
+ 'paymentId' => $resultTransaction->getPaymentId(),
+ 'redirectUrl' => $resultTransaction->getRedirectUrl(),
+
+ ]);
+
+ } catch (Exception $e) {
+
+ wp_send_json_error([
+ 'message' => $e->getMessage(),
+ ], 500);
+
+ }
+ }
+
+ public function paypalExpressReturn()
+ {
+ $paymentId = WC()->session->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID);
+
+ if (empty($paymentId)) {
+ wp_safe_redirect(wc_get_cart_url());
+ exit;
+ }
+ $paymentService = new PaymentService();
+
+ try {
+ $payment = $paymentService->getUnzerManager()->fetchPayment($paymentId);
+ (new ExpressCheckoutService())->startCheckoutSessionFromUnzerPayment(
+ $payment,
+ Paypal::GATEWAY_ID
+ );
+
+ } catch (\Exception $e) {
+ wc_add_notice( __( 'Payment error', 'unzer-payments' ), 'error' );
+ wp_safe_redirect(wc_get_cart_url());
+ exit;
+ }
+
+ WC()->session->set(
+ ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD,
+ Paypal::GATEWAY_ID
+ );
+
+ wp_redirect(
+ add_query_arg(
+ 'isExpressCheckout',
+ 'true',
+ wc_get_checkout_url()
+ )
+ );
+ exit;
+ }
+
+ public function googleExpress()
+ {
+ $paymentTypeId = sanitize_text_field(
+ $this->getRequestValue('paymentTypeId', '')
+ );
+ $paymentData = $this->getRequestValue('paymentData', '');
+ try {
+ (new ExpressCheckoutService())->startCheckoutSessionFromGooglePayData($paymentData, GooglePay::GATEWAY_ID);
+ } catch (\Exception $e) {
+ wc_add_notice( __( 'Payment error', 'unzer-payments' ), 'error' );
+ wp_safe_redirect(wc_get_cart_url());
+ }
+ WC()->session->set(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID, $paymentTypeId);
+ WC()->session->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, GooglePay::GATEWAY_ID);
+
+ wp_send_json(
+ [
+ 'redirectUrl' => add_query_arg(
+ 'isExpressCheckout',
+ 'true',
+ wc_get_checkout_url()
+ )
+ ]
+ );
+ exit();
+ }
+
+ public function applepayExpress()
+ {
+ $logger = ( new LogService() );
+ $paymentTypeId = sanitize_text_field(
+ $this->getRequestValue('paymentTypeId', '')
+ );
+ $paymentData = $this->getRequestValue('paymentData', '');
+ $shippingContact = $this->getRequestValue('shippingContact', '');
+ $billingContact = $this->getRequestValue('billingContact', '');
+ try {
+ (new ExpressCheckoutService())->startCheckoutSessionFromApplePayData($paymentData, $shippingContact, $billingContact, ApplePayV2::GATEWAY_ID);
+ } catch (\Exception $e) {
+ wc_add_notice( __( 'Payment error', 'unzer-payments' ), 'error' );
+ wp_safe_redirect(wc_get_cart_url());
+ }
+ WC()->session->set(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID, $paymentTypeId);
+ WC()->session->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, ApplePayV2::GATEWAY_ID);
+
+ wp_send_json(
+ [
+ 'redirectUrl' => add_query_arg(
+ 'isExpressCheckout',
+ 'true',
+ wc_get_checkout_url()
+ )
+ ]
+ );
+ exit();
+ }
+
+ private function getRequestValue(string $key, $default = null)
+ {
+ if (isset($_REQUEST[$key])) {
+ return wp_unslash($_REQUEST[$key]);
+ }
+ $rawBody = file_get_contents('php://input');
+ $payload = json_decode($rawBody, true);
+ if (is_array($payload) && array_key_exists($key, $payload)) {
+ return $payload[$key];
+ }
+ return $default;
+ }
+
+}
\ No newline at end of file
diff --git a/includes/gateways/AbstractGateway.php b/includes/gateways/AbstractGateway.php
index e156859..563092f 100644
--- a/includes/gateways/AbstractGateway.php
+++ b/includes/gateways/AbstractGateway.php
@@ -3,8 +3,10 @@
namespace UnzerPayments\Gateways;
use UnzerPayments\Controllers\AdminController;
+use UnzerPayments\Controllers\ExpressController;
use UnzerPayments\Main;
use UnzerPayments\Services\CustomerService;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\LogService;
use UnzerPayments\Services\PaymentService;
use UnzerPayments\Util;
@@ -80,6 +82,7 @@ abstract class AbstractGateway extends WC_Payment_Gateway {
const TRANSACTION_TYPE_CHARGE = 'charge';
const SETTINGS_KEY_SAVE_INSTRUMENTS = 'save_instruments';
+ const SETTINGS_KEY_EXPRESS_OPTION = 'express_option';
/**
* @var string
*/
@@ -89,6 +92,8 @@ abstract class AbstractGateway extends WC_Payment_Gateway {
*/
protected $logger;
+ public static $haveDoneScriptsOnce = false;
+
/**
* @var null|array
*/
@@ -110,7 +115,7 @@ public function __construct() {
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
- }
+ }
protected function get_allowed_html_tags() {
$response = array_merge( wp_kses_allowed_html( 'post' ), self::ALLOWED_HTML );
@@ -118,10 +123,37 @@ protected function get_allowed_html_tags() {
return $response;
}
+ public function has_at_least_one_express_checkout()
+ {
+ foreach (['ApplePayV2', 'Paypal', 'GooglePay'] as $expressMethod) {
+ $class = '\\UnzerPayments\\Gateways\\' . $expressMethod;
+ if (!class_exists($class)) {
+ continue;
+ }
+ $gateway = new $class;
+ if ($gateway->has_express_checkout()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function has_express_checkout()
+ {
+ if ($this->is_enabled()) {
+ if ($this->get_option( AbstractGateway::SETTINGS_KEY_EXPRESS_OPTION ) === 'yes') {
+ return true;
+ }
+ }
+ return false;
+ }
+
public function payment_scripts() {
- if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) ) {
- return;
- }
+ if (!$this->has_at_least_one_express_checkout()) {
+ if (! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) ) {
+ return;
+ }
+ }
if ( ! $this->is_enabled() ) {
return;
@@ -448,6 +480,10 @@ protected function get_checkout_customer_json_encoded() {
}
protected function addCheckoutAssets() {
+ if (self::$haveDoneScriptsOnce) {
+ return;
+ }
+
global $wp;
// TODO replace when minimum WP version is 6.5 (wp_enqueue_script_module)
add_filter(
@@ -474,11 +510,15 @@ function ( $tag, $handle, $src ) {
}
wp_register_script( 'woocommerce_unzer', UNZER_PLUGIN_URL . '/assets/js/checkout.js', array( 'jquery' ), UNZER_VERSION, array( 'in_footer' => false ) );
+ wp_register_script( 'woocommerce_unzer_express', UNZER_PLUGIN_URL . '/assets/js/express.js', array( 'jquery' ), UNZER_VERSION, array( 'in_footer' => false ) );
+
// for separate api keys
$paylaterGateway = new Invoice();
$installmentGateway = new Installment();
$directDebitSecuredGateway = new DirectDebitSecured();
$googlePayGateway = new GooglePay();
+ $applePayGateway = new ApplePayV2();
+ $paypalGateway = new PayPal();
wp_localize_script(
'woocommerce_unzer',
@@ -493,12 +533,27 @@ function ( $tag, $handle, $src ) {
'publicKey_installment_chf_b2c' => $installmentGateway->get_option( 'public_key_chf_b2c' ),
'publicKey_directdebitsecured_eur_b2c' => $directDebitSecuredGateway->get_option( 'public_key_eur_b2c' ),
'generic_error_message' => __( 'An error occurred while processing your payment. Please try another payment method.', 'unzer-payments' ),
+ 'generic_error_message_express' => __( 'An error occurred while processing your request. Please try again.', 'unzer-payments' ),
'locale' => get_locale(),
'store_name' => get_bloginfo( 'name' ),
'store_country' => strtoupper( substr( get_option( 'woocommerce_default_country' ), 0, 2 ) ),
'is_order_pay' => self::isOrderPay() ? 'true' : 'false',
'currency' => get_woocommerce_currency(),
'google_pay_options' => $googlePayGateway->getPublicOptions(),
+ 'applepay_options' => $applePayGateway->getPublicOptions(),
+ 'urls' => [
+ 'paypal' => WC()->api_request_url(ExpressController::PAYPAL_EXPRESS_SLUG),
+ 'paypalReturn' => WC()->api_request_url(ExpressController::PAYPAL_EXPRESS_RETURN_SLUG),
+ 'googlePay' => WC()->api_request_url(ExpressController::GOOGLE_PAY_EXPRESS_SLUG),
+ 'applePay' => WC()->api_request_url(ExpressController::APPLE_PAY_EXPRESS_SLUG),
+ ],
+ 'express' => [
+ 'is_express' => (empty($_GET['isExpressCheckout']) || $_GET['isExpressCheckout'] !== 'true') ? false : true,
+ 'current_express_method' => WC()->session->get(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD),
+ 'paypal' => $paypalGateway->has_express_checkout(),
+ 'googlepay' => $googlePayGateway->has_express_checkout(),
+ 'applepay' => $applePayGateway->has_express_checkout(),
+ ]
)
);
wp_localize_script(
@@ -509,6 +564,8 @@ function ( $tag, $handle, $src ) {
)
);
wp_enqueue_script( 'woocommerce_unzer' );
+ wp_enqueue_script( 'woocommerce_unzer_express' );
+ self::$haveDoneScriptsOnce = true;
}
public static function isOrderPay() {
@@ -536,4 +593,5 @@ public static function addRiskDataToAuthorization( Authorization $authorization,
public static function isUnzerPaymentMethod( string $paymentMethodId ) {
return substr( $paymentMethodId, 0, 6 ) === 'unzer_';
}
+
}
diff --git a/includes/gateways/ApplePayV2.php b/includes/gateways/ApplePayV2.php
index b90e875..bba9129 100644
--- a/includes/gateways/ApplePayV2.php
+++ b/includes/gateways/ApplePayV2.php
@@ -3,6 +3,7 @@
namespace UnzerPayments\Gateways;
use UnzerPayments\Gateways\Blocks\ApplePayBlock;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\PaymentService;
use UnzerPayments\Util;
@@ -95,6 +96,17 @@ public function get_form_fields() {
),
'default' => 'charge',
),
+ AbstractGateway::SETTINGS_KEY_EXPRESS_OPTION => array(
+ 'title' => __( 'Offer Apple Pay Express Checkout', 'unzer-payments' ),
+ 'label' => __( ' ', 'unzer-payments' ),
+ 'type' => 'select',
+ 'description' => '',
+ 'default' => 'no',
+ 'options' => array(
+ 'no' => __( 'No', 'unzer-payments' ),
+ 'yes' => __( 'Yes', 'unzer-payments' ),
+ ),
+ ),
)
);
}
@@ -105,10 +117,24 @@ public function process_payment( $order_id ) {
'result' => 'success',
);
- $applePayId = Util::getNonceCheckedPostValue( 'unzer-apple-pay-v2-id' );
+ $applePayId = Util::getNonceCheckedPostValue( 'unzer-apple-pay-v2-id' );
+ $this->logger->debug( 'fetched applePayId ' . $applePayId, array( ) );
+ try {
+ if (!empty(WC()->session->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID))) {
+ $applePayId = (WC()->session->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID));
+
+ $client = ( new PaymentService() )->getUnzerManagerForOrder($order_id);
+ $paymentType = $client->fetchPaymentType(WC()->session->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID));
+ $this->logger->debug( 'fetched applePayId from Session ' . $applePayId, array( 'paymentType' => $paymentType ) );
+ }
+ } catch (\Throwable) {
+ }
+
if ( $this->get_option( 'transaction_type' ) === AbstractGateway::TRANSACTION_TYPE_AUTHORIZE ) {
+ $this->logger->debug( 'apple pay authorization for order ' . $order_id, array( $applePayId ) );
$transaction = ( new PaymentService() )->performAuthorizationForOrder( $order_id, $this, $applePayId );
} else {
+ $this->logger->debug( 'apple pay charge for order ' . $order_id, array( $applePayId ) );
$transaction = ( new PaymentService() )->performChargeForOrder( $order_id, $this, $applePayId );
}
@@ -123,4 +149,14 @@ public function process_payment( $order_id ) {
}
return $return;
}
+
+ public function getPublicOptions() {
+ return array(
+ 'countryCode' => 'DE',
+ 'allowedCardNetworks' => ['masterCard', 'visa'],
+ 'merchantCapabilities' => ['supports3DS'],
+ 'shopName' => get_bloginfo('name'),
+ );
+ }
+
}
diff --git a/includes/gateways/GooglePay.php b/includes/gateways/GooglePay.php
index cc5a1da..fe289ac 100644
--- a/includes/gateways/GooglePay.php
+++ b/includes/gateways/GooglePay.php
@@ -3,6 +3,7 @@
namespace UnzerPayments\Gateways;
use UnzerPayments\Gateways\Blocks\GooglePayBlock;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\PaymentService;
use UnzerPayments\Util;
use UnzerSDK\Unzer;
@@ -221,6 +222,17 @@ public function get_form_fields() {
),
'default' => 'fill',
),
+ AbstractGateway::SETTINGS_KEY_EXPRESS_OPTION => array(
+ 'title' => __( 'Offer Google Pay Express Checkout', 'unzer-payments' ),
+ 'label' => __( ' ', 'unzer-payments' ),
+ 'type' => 'select',
+ 'description' => '',
+ 'default' => 'no',
+ 'options' => array(
+ 'no' => __( 'No', 'unzer-payments' ),
+ 'yes' => __( 'Yes', 'unzer-payments' ),
+ ),
+ ),
)
);
}
@@ -231,7 +243,14 @@ public function process_payment( $order_id ) {
'result' => 'success',
);
- $googlePayId = Util::getNonceCheckedPostValue( 'unzer-google-pay-id' );
+ $googlePayId = Util::getNonceCheckedPostValue( 'unzer-google-pay-id' );
+ try {
+ if (!empty(WC()->session->get(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID))) {
+ $googlePayId = (WC()->session->get(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID));
+ }
+ } catch (\Throwable) {
+ }
+
if ( $this->get_option( 'transaction_type' ) === AbstractGateway::TRANSACTION_TYPE_AUTHORIZE ) {
$transaction = ( new PaymentService() )->performAuthorizationForOrder( $order_id, $this, $googlePayId );
} else {
diff --git a/includes/gateways/Paypal.php b/includes/gateways/Paypal.php
index d94ad3c..e04fb1d 100644
--- a/includes/gateways/Paypal.php
+++ b/includes/gateways/Paypal.php
@@ -2,11 +2,18 @@
namespace UnzerPayments\Gateways;
+use UnzerPayments\Controllers\CheckoutController;
use UnzerPayments\Gateways\Blocks\PaypalBlock;
+use UnzerPayments\Main;
+use UnzerPayments\Services\CustomerService;
+use UnzerPayments\Services\ExpressCheckoutService;
+use UnzerPayments\Services\OrderService;
use UnzerPayments\Services\PaymentService;
use UnzerPayments\Traits\SavePaymentInstrumentTrait;
use UnzerPayments\Util;
use UnzerSDK\Resources\PaymentTypes\Paypal as PaypalResource;
+use UnzerSDK\Resources\TransactionTypes\Authorization;
+use UnzerSDK\Resources\TransactionTypes\Charge;
if ( ! defined( 'ABSPATH' ) ) {
exit;
@@ -14,9 +21,7 @@
class Paypal extends AbstractGateway {
-
-
- use SavePaymentInstrumentTrait;
+ use SavePaymentInstrumentTrait;
public $paymentTypeResource = PaypalResource::class;
const GATEWAY_ID = 'unzer_paypal';
@@ -91,11 +96,35 @@ public function get_form_fields() {
'yes' => __( 'Yes', 'unzer-payments' ),
),
),
+ AbstractGateway::SETTINGS_KEY_EXPRESS_OPTION => array(
+ 'title' => __( 'Offer Paypal Express Checkout', 'unzer-payments' ),
+ 'label' => __( ' ', 'unzer-payments' ),
+ 'type' => 'select',
+ 'description' => '',
+ 'default' => 'no',
+ 'options' => array(
+ 'no' => __( 'No', 'unzer-payments' ),
+ 'yes' => __( 'Yes', 'unzer-payments' ),
+ ),
+ ),
)
);
}
public function process_payment( $order_id ) {
+
+ if (WC()->session->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID)) {
+ try {
+ return $this->payExpress(
+ WC()->session->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID),
+ $order_id
+ );
+ } catch (\Throwable $e) {
+ $this->logger->error('Express CO failed: ' . $e->getMessage());
+ WC()->session->set(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID, false);
+ // continue with default paypal flow
+ }
+ }
$return = array(
'result' => 'success',
);
@@ -121,4 +150,55 @@ public function process_payment( $order_id ) {
}
return $return;
}
+
+ public function payExpress( $unzer_payment_id, $order_id)
+ {
+ $this->logger->debug('Paypal::payExpress()');
+ ( new PaymentService() )->removeTransactionMetaData( $order_id );
+ $order = wc_get_order( $order_id );
+ $unzer = ( new PaymentService() )->getUnzerManager();
+ $payment = $unzer->fetchPayment($unzer_payment_id);
+ $basket = ( new OrderService())->getBasket($order_id);
+ $basket->setId($payment->getBasket()->getId());
+ $basket->setOrderId($order_id);
+ $this->logger->debug('Update Basket with order ID');
+ $unzer->updateBasket($basket);
+
+ ( new CustomerService() )->updatePaypalExpressCustomer( $order, $payment->getCustomer() );
+
+ if (empty($payment->getCharges())) {
+ $authorization = new Authorization(
+ $basket->getTotalValueGross(),
+ $basket->getCurrencyCode(),
+ $this->get_confirm_url( $order_id )
+ );
+ $authorization->setOrderId($order_id);
+ $this->logger->debug('Update Authorization');
+ $transactionObject = $unzer->updateAuthorization($payment->getId(), $authorization);
+ $order->update_meta_data( Main::ORDER_META_KEY_AUTHORIZATION_ID, $transactionObject->getId() );
+ } else {
+ $charge = new Charge(
+ $basket->getTotalValueGross(),
+ $basket->getCurrencyCode(),
+ $this->get_confirm_url( $order_id )
+ );
+ $charge->setOrderId($order_id);
+ $this->logger->debug('Update Charge');
+ $transactionObject = $unzer->updateCharge($payment->getId(), $charge);
+ $order->update_meta_data( Main::ORDER_META_KEY_CHARGE_ID, $transactionObject->getId() );
+ }
+ $order->update_meta_data( Main::ORDER_META_KEY_PAYMENT_ID, $transactionObject->getPayment()->getId() );
+ $order->update_meta_data( Main::ORDER_META_KEY_PAYMENT_SHORT_ID, $transactionObject->getShortId() );
+ $this->logger->debug('Save Meta Data');
+ $order->save_meta_data();
+ $this->logger->debug('Add order note');
+ $order->add_order_note( __( 'Unzer Payment ID: ', 'unzer-payments' ) . $transactionObject->getPayment()->getId() );
+ $this->before_payment_redirect( $order_id );
+ $this->logger->debug('Return confirmation URL');
+ $return = array(
+ 'result' => 'success',
+ 'redirect' => $this->get_confirm_url( $order_id )
+ );
+ return $return;
+ }
}
diff --git a/includes/services/CustomerService.php b/includes/services/CustomerService.php
index f25eaef..49755ae 100644
--- a/includes/services/CustomerService.php
+++ b/includes/services/CustomerService.php
@@ -47,6 +47,7 @@ public function calculateCustomerNumber( ?int $customerId = null ) {
}
public function getCustomerFromData( AbstractGateway $paymentGateway, array $data ) {
+ $this->logger->debug( 'CustomerService::getCustomerFromData', array( ) );
$customer = null;
$paymentService = new PaymentService();
$unzer = $paymentService->getUnzerManager( $paymentGateway, ! empty( $data['billing_company'] ), get_woocommerce_currency() );
@@ -62,6 +63,8 @@ public function getCustomerFromData( AbstractGateway $paymentGateway, array $dat
$customer->setCustomerId( $customerNumber );
}
+ $customer->setLanguage(substr(determine_locale(), 0, 2) ?: 'en');
+
$billingAddress = $customer->getBillingAddress();
if ( ! empty( $data['billing_first_name'] ) && ! empty( $data['billing_last_name'] ) ) {
@@ -112,17 +115,20 @@ public function getCustomerFromData( AbstractGateway $paymentGateway, array $dat
if ( isset( $data['billing_country'] ) ) {
$billingAddress->setCountry( $data['billing_country'] );
}
+ $this->logger->debug( 'customer data', array( $customer->expose() ) );
if ( ! empty( $customer->getFirstname() ) && ! empty( $customer->getLastname() ) ) {
if ( empty( $customer->getId() ) ) {
try {
$customer = $unzer->createCustomer( $customer );
+ $this->logger->debug( 'created customer', array( ) );
} catch ( Exception $e ) {
$this->logger->error( 'create customer failed: ' . $e->getMessage() );
}
} else {
try {
$customer = $unzer->updateCustomer( $customer );
+ $this->logger->debug( 'updated customer', array( ) );
} catch ( Exception $e ) {
$this->logger->error( 'update customer failed: ' . $e->getMessage() );
}
@@ -147,6 +153,7 @@ public function getCustomerFromSession( AbstractGateway $paymentGateway, ?int $o
* @return Customer
*/
public function getCustomerFromOrder( $order ): Customer {
+ $this->logger->debug( 'CustomerService::getCustomerFromOrder', array( ) );
$order = is_object( $order ) ? $order : wc_get_order( $order );
$paymentService = new PaymentService();
$unzer = $paymentService->getUnzerManagerForOrder( $order );
@@ -163,6 +170,7 @@ public function getCustomerFromOrder( $order ): Customer {
}
$customer
+ ->setLanguage(substr(determine_locale(), 0, 2) ?: 'en')
->setFirstname( $order->get_billing_first_name() ?: '' )
->setLastname( $order->get_billing_last_name() ?: '' )
->setPhone( $order->get_billing_phone() ?: '' )
@@ -175,12 +183,14 @@ public function getCustomerFromOrder( $order ): Customer {
if ( $customer->getId() ) {
try {
$unzer->updateCustomer( $customer );
+ $this->logger->debug( 'updated customer', array( ) );
} catch ( Exception $e ) {
$this->logger->warning( 'update customer failed: ' . $e->getMessage(), array( $customer->expose() ) );
}
} else {
try {
$customer = $unzer->createCustomer( $customer );
+ $this->logger->debug( 'created customer', array( ) );
} catch ( Exception $e ) {
$this->logger->warning( 'update customer failed: ' . $e->getMessage(), array( $customer->expose() ) );
}
@@ -189,6 +199,47 @@ public function getCustomerFromOrder( $order ): Customer {
return $customer;
}
+ /**
+ * @param int|WC_Order $order
+ * @param Customer $customer
+ * @return Customer
+ */
+ public function updatePaypalExpressCustomer($order, Customer $customer): Customer {
+ $this->logger->debug( 'CustomerService::updatePaypalExpressCustomer', array( ) );
+ $order = is_object( $order ) ? $order : wc_get_order( $order );
+ $paymentService = new PaymentService();
+ $unzer = $paymentService->getUnzerManagerForOrder( $order );
+
+ $customer
+ ->setLanguage(substr(determine_locale(), 0, 2) ?: 'en')
+ ->setFirstname( $order->get_billing_first_name() ?: '' )
+ ->setLastname( $order->get_billing_last_name() ?: '' )
+ ->setPhone( $order->get_billing_phone() ?: '' )
+ ->setCompany( $order->get_billing_company() ?: '' )
+ ->setEmail( $order->get_billing_email() ?: '' );
+
+ $this->setAddresses( $customer, $order );
+ $this->logger->debug( 'customer data', array( $customer->expose() ) );
+
+ if ( $customer->getId() ) {
+ try {
+ $unzer->updateCustomer( $customer );
+ $this->logger->debug( 'updated customer', array( ) );
+ } catch ( Exception $e ) {
+ $this->logger->warning( 'update customer failed: ' . $e->getMessage(), array( $customer->expose() ) );
+ }
+ } else {
+ try {
+ $customer = $unzer->createCustomer( $customer );
+ $this->logger->debug( 'created customer', array( ) );
+ } catch ( Exception $e ) {
+ $this->logger->warning( 'update customer failed: ' . $e->getMessage(), array( $customer->expose() ) );
+ }
+ }
+
+ return $customer;
+ }
+
protected function setAddresses( Customer $customer, WC_Abstract_Order $order ) {
$shippingType = ShippingTypes::EQUALS_BILLING;
diff --git a/includes/services/ExpressCheckoutService.php b/includes/services/ExpressCheckoutService.php
new file mode 100644
index 0000000..a1e3e73
--- /dev/null
+++ b/includes/services/ExpressCheckoutService.php
@@ -0,0 +1,411 @@
+getRegisterDataFromUnzerPayment($payment);
+ $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId);
+ }
+
+ public function startCheckoutSessionFromGooglePayData(array $paymentData, ?string $paymentMethodId): void
+ {
+ $data = $this->getRegisterDataFromGooglePayData($paymentData);
+ $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId);
+ }
+
+ public function startCheckoutSessionFromApplePayData(array $paymentData, array $shippingContact, array $billingContact, ?string $paymentMethodId): void
+ {
+ $data = $this->getRegisterDataFromApplePayData($shippingContact, $billingContact);
+ $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId);
+ }
+
+ private function setPaymentMethod(string $paymentMethodId): void
+ {
+ WC()->session->set('chosen_payment_method', $paymentMethodId);
+ }
+
+ private function createOrGetGuestCustomer(array $data): int
+ {
+ $email = sanitize_email($data['email'] ?? '');
+
+ if (!$email) {
+ return 0;
+ }
+
+ $existingUser = get_user_by('email', $email);
+
+ if ($existingUser) {
+ return (int) $existingUser->ID;
+ }
+
+ if (!empty($data['guest'])) {
+ return 0;
+ }
+
+ $password = $data['password'] ?? wp_generate_password();
+
+ $userId = wc_create_new_customer(
+ $email,
+ $email,
+ $password,
+ [
+ 'first_name' => sanitize_text_field($data['firstName'] ?? ''),
+ 'last_name' => sanitize_text_field($data['lastName'] ?? ''),
+ ]
+ );
+
+ if (is_wp_error($userId)) {
+ throw new \Exception($userId->get_error_message());
+ }
+
+ return (int) $userId;
+ }
+
+ private function setGuestCheckoutData(array $data): void
+ {
+ $billing = $data['billingAddress'] ?? [];
+ $shipping = $data['shippingAddress'] ?? $billing;
+
+ WC()->customer->set_billing_first_name($data['firstName'] ?? '');
+ WC()->customer->set_billing_last_name($data['lastName'] ?? '');
+ WC()->customer->set_billing_email($data['email'] ?? '');
+
+ $this->applyAddressToCustomer($billing, 'billing');
+ $this->applyAddressToCustomer($shipping, 'shipping');
+
+ WC()->customer->save();
+ }
+
+ private function updateCustomerAddresses(array $data, int $customerId): void
+ {
+ $customer = new \WC_Customer($customerId);
+
+ $billing = $data['billingAddress'] ?? [];
+ $shipping = $data['shippingAddress'] ?? $billing;
+
+ $this->applyAddressToCustomer($billing, 'billing', $customer);
+ $this->applyAddressToCustomer($shipping, 'shipping', $customer);
+
+ $customer->set_billing_first_name($data['firstName'] ?? '');
+ $customer->set_billing_last_name($data['lastName'] ?? '');
+ $customer->set_billing_email($data['email'] ?? '');
+
+ $customer->save();
+ }
+
+ private function applyAddressToCustomer(array $address, string $type, ?\WC_Customer $customer = null): void
+ {
+ $customer = $customer ?: WC()->customer;
+
+ $prefix = $type . '_';
+
+ $customer->{"set_{$prefix}first_name"}($address['firstName'] ?? '');
+ $customer->{"set_{$prefix}last_name"}($address['lastName'] ?? '');
+ $customer->{"set_{$prefix}company"}($address['company'] ?? '');
+ $customer->{"set_{$prefix}address_1"}($address['street'] ?? '');
+ $customer->{"set_{$prefix}address_2"}($address['additionalAddressLine1'] ?? '');
+ $customer->{"set_{$prefix}postcode"}($address['zipcode'] ?? '');
+ $customer->{"set_{$prefix}city"}($address['city'] ?? '');
+ $customer->{"set_{$prefix}country"}($address['country'] ?? '');
+ $customer->{"set_{$prefix}phone"}($address['phoneNumber'] ?? '');
+ }
+
+ private function startCheckoutDataWithNormalizedCustomerData(?array $data, ?string $paymentMethodId): void
+ {
+ if ($data === null) {
+ throw new \Exception('no customer data');
+ }
+
+ if (!WC()->session) {
+ throw new \Exception('WooCommerce session not available');
+ }
+
+ $chosen_shipping_methods = WC()->session->get('chosen_shipping_methods') ?: [];
+
+ if (!WC()->customer) {
+ wc_load_cart();
+ }
+
+ if (is_user_logged_in()) {
+ $customerId = get_current_user_id();
+
+ $this->updateCustomerAddresses($data, $customerId);
+
+ if ($paymentMethodId) {
+ $this->setPaymentMethod($paymentMethodId);
+ }
+
+ $this->setShippingMethod($chosen_shipping_methods);
+ $this->setFluidCheckoutSession($data);
+
+ return;
+ }
+
+ $customerId = $this->createOrGetGuestCustomer($data);
+
+ if ($customerId > 0) {
+ $this->updateCustomerAddresses($data, $customerId);
+ wp_set_current_user($customerId);
+ wp_set_auth_cookie($customerId);
+
+ WC()->customer = new \WC_Customer($customerId, true);
+ $existingUser = get_user_by('ID', $customerId);
+ do_action('wp_login', $existingUser->user_login, $existingUser);
+ if (WC()->session) {
+ WC()->session->set_customer_session_cookie(true);
+ WC()->session->set('customer_id', $existingUser->ID);
+ }
+ } else {
+ $this->setGuestCheckoutData($data);
+ }
+
+ if ($paymentMethodId) {
+ $this->setPaymentMethod($paymentMethodId);
+ }
+
+ $this->setShippingMethod($chosen_shipping_methods);
+ $this->setFluidCheckoutSession($data);
+ }
+
+ private function setFluidCheckoutSession($data): void
+ {
+ $fields = [
+ 'billing_first_name' => $data['firstName'],
+ 'billing_last_name' => $data['lastName'],
+ 'billing_company' => $data['company'],
+ 'billing_email' => $data['email'],
+ 'billing_country' => $data['country'],
+ 'billing_address_1' => $data['street'],
+ 'billing_address_2' => $data['additionalAddressLine1'],
+ 'billing_postcode' => $data['zipcode'],
+ 'billing_city' => $data['city'],
+
+ 'shipping_first_name' => $data['firstName'],
+ 'shipping_last_name' => $data['lastName'],
+ 'shipping_company' => $data['company'],
+ 'shipping_email' => $data['email'],
+ 'shipping_country' => $data['country'],
+ 'shipping_address_1' => $data['street'],
+ 'shipping_address_2' => $data['additionalAddressLine1'],
+ 'shipping_postcode' => $data['zipcode'],
+ 'shipping_city' => $data['city'],
+ ];
+
+ foreach ($fields as $key => $value) {
+ WC()->session->set('fc_' . $key, $value);
+ }
+ }
+
+ private function setShippingMethod($chosen_shipping_methods): void
+ {
+ $packages = WC()->shipping()->get_packages();
+ $valid_chosen_methods = [];
+ foreach ($packages as $package_index => $package) {
+ $old_method = $chosen_shipping_methods[$package_index] ?? null;
+ if ($old_method && isset($package['rates'][$old_method])) {
+ $valid_chosen_methods[$package_index] = $old_method;
+ }
+ }
+
+ if (!empty($valid_chosen_methods)) {
+ WC()->session->set('chosen_shipping_methods', $valid_chosen_methods);
+ }
+ }
+
+ private function getRegisterDataFromGooglePayData(array $paymentData): ?array
+ {
+ $data = [];
+ $password = bin2hex(random_bytes(16));
+ $name = $paymentData['paymentMethodData']['info']['billingAddress']['name'] ?? self::ADDRESS_PART_PLACEHOLDER . ' ' . self::ADDRESS_PART_PLACEHOLDER;
+ $names = $this->separateName($name);
+
+ $shippingAddress = $this->getAddressFromGoogleData($paymentData['shippingAddress'] ?? []);
+ $billingAddress = $this->getAddressFromGoogleData($paymentData['paymentMethodData']['info']['billingAddress'] ?? []);
+ if (!$this->isAddressDataBagComplete($billingAddress)) {
+ $billingAddress = $shippingAddress;
+ }
+
+ $data = array_merge($data, [
+ 'firstName' => $names['firstName'],
+ 'lastName' => $names['lastName'],
+ 'guest' => true,
+ 'email' => $paymentData['email'] ?? '',
+ 'acceptedDataProtection' => true,
+ 'password' => $password,
+ 'passwordConfirmation' => $password,
+ 'billingAddress' => $billingAddress,
+ 'shippingAddress' => $shippingAddress,
+ ]);
+
+ return $data;
+ }
+
+ private function getRegisterDataFromApplePayData(array $shippingContact, array $billingContact): ?array
+ {
+ $data = [];
+ $password = bin2hex(random_bytes(16));
+ $firstName = $shippingContact['givenName'] === '' ? '----' : $shippingContact['givenName'];
+ $lastName = $shippingContact['familyName'] === '' ? '----' : $shippingContact['familyName'];
+
+ $shippingAddress = $this->getAddressFromApplePayData($shippingContact ?? []);
+ $billingAddress = $this->getAddressFromApplePayData($billingContact ?? []);
+ if (!$this->isAddressDataBagComplete($billingAddress)) {
+ $billingAddress = $shippingAddress;
+ }
+
+ $data = array_merge($data, [
+ 'firstName' => $firstName,
+ 'lastName' => $lastName,
+ 'guest' => true,
+ 'email' => $shippingContact['emailAddress'] ?? '',
+ 'acceptedDataProtection' => true,
+ 'password' => $password,
+ 'passwordConfirmation' => $password,
+ 'billingAddress' => $billingAddress,
+ 'shippingAddress' => $shippingAddress,
+ ]);
+
+ return $data;
+ }
+
+ private function getRegisterDataFromUnzerPayment(Payment $payment): ?array
+ {
+ $data = [];
+
+ $customer = $payment->getCustomer();
+
+ if ($customer === null) {
+ return null;
+ }
+
+ $shippingAddress = $this->getAddressFromUnzerCustomer($customer, 'shipping');
+ $billingAddress = $this->getAddressFromUnzerCustomer($customer);
+ if (!$this->isAddressDataBagComplete($billingAddress)) {
+ $billingAddress = $shippingAddress;
+ }
+
+ $password = bin2hex(random_bytes(16));
+ $data = array_merge($data, [
+ 'firstName' => $customer->getFirstname(),
+ 'lastName' => $customer->getLastname(),
+ 'guest' => true,
+ 'email' => $customer->getEmail(),
+ 'acceptedDataProtection' => true,
+ 'password' => $password,
+ 'passwordConfirmation' => $password,
+ 'billingAddress' => $billingAddress,
+ 'shippingAddress' => $shippingAddress,
+ ]);
+
+ return $data;
+ }
+
+ private function isAddressDataBagComplete(array $dataBag): bool
+ {
+ foreach ($dataBag as $key => $value) {
+ if ($value === self::ADDRESS_PART_PLACEHOLDER) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private function getAddressFromGoogleData(array $address): ?array
+ {
+ $nameParts = $this->separateName($address['name'] ?? self::ADDRESS_PART_PLACEHOLDER . ' ' . self::ADDRESS_PART_PLACEHOLDER);
+
+ return [
+ 'firstName' => $nameParts['firstName'],
+ 'lastName' => $nameParts['lastName'],
+ 'city' => $address['locality'] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'country' => $address['countryCode'],
+ 'street' => $address['address1'] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'additionalAddressLine1' => $address['address2'] ?? null,
+ 'additionalAddressLine2' => $address['address3'] ?? null,
+ 'zipcode' => $address['postalCode'] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'phoneNumber' => '',
+ 'company' => '',
+ ];
+ }
+
+ private function getAddressFromApplePayData(array $address): ?array
+ {
+ return [
+ 'firstName' => $address['givenName'] === '' ? '----' : $address['givenName'],
+ 'lastName' => $address['familyName'] === '' ? '----' : $address['familyName'],
+ 'city' => $address['locality'] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'country' => $address['countryCode'],
+ 'street' => $address['addressLines'][0] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'additionalAddressLine1' => $address['addressLines'][1] ?? null,
+ 'additionalAddressLine2' => $address['addressLines'][2] ?? null,
+ 'zipcode' => $address['postalCode'] ?? self::ADDRESS_PART_PLACEHOLDER,
+ 'phoneNumber' => $address['phoneNumber'] ?? '',
+ 'company' => '',
+ ];
+ }
+
+ private function getAddressFromUnzerCustomer(Customer $customer, string $addressType = 'billing'): ?array
+ {
+ $address = ($addressType === 'billing' ? $customer->getBillingAddress() : $customer->getShippingAddress());
+
+ if (empty($address->getZip())) {
+ $address->setZip(self::ADDRESS_PART_PLACEHOLDER);
+ }
+ if (empty($address->getCity())) {
+ $address->setCity(self::ADDRESS_PART_PLACEHOLDER);
+ }
+ if (empty($address->getStreet())) {
+ $address->setStreet(self::ADDRESS_PART_PLACEHOLDER);
+ }
+ if (empty($address->getName())) {
+ $address->setName(self::ADDRESS_PART_PLACEHOLDER . ' ' . self::ADDRESS_PART_PLACEHOLDER);
+ }
+
+ $nameParts = $this->separateName($address->getName());
+
+ return [
+ 'firstName' => $nameParts['firstName'],
+ 'lastName' => $nameParts['lastName'],
+ 'city' => $address->getCity(),
+ 'country' => $address->getCountry(),
+ 'street' => $address->getStreet(),
+ 'zipcode' => $address->getZip(),
+ 'phoneNumber' => '',
+ 'company' => '',
+ ];
+ }
+
+ private function separateName(string $name): array
+ {
+ $exploded = explode(' ', $name);
+ if (\count($exploded) > 1) {
+ $lastName = array_pop($exploded);
+
+ return [
+ 'firstName' => implode(' ', $exploded),
+ 'lastName' => $lastName,
+ ];
+ }
+
+ return [
+ 'firstName' => $exploded[0],
+ 'lastName' => '.',
+ ];
+ }
+
+}
\ No newline at end of file
diff --git a/includes/services/PaymentService.php b/includes/services/PaymentService.php
index 86c8bcf..f22144e 100644
--- a/includes/services/PaymentService.php
+++ b/includes/services/PaymentService.php
@@ -185,6 +185,13 @@ protected function performChargeOrAuthorizationForOrder(
if ( $transactionEditor !== null ) {
$transactionEditor( $authorization );
}
+ $this->logger->debug(
+ 'calling authorization #' . $orderId . ' with ' . $order->get_payment_method(),
+ array(
+ 'authorization' => $authorization,
+ 'metadata' => ( new ShopService() )->getMetadata()
+ )
+ );
$transactionObject = $unzer->performAuthorization( $authorization, $paymentType, $customer, ( new ShopService() )->getMetadata(), $basket );
$order->update_meta_data( Main::ORDER_META_KEY_AUTHORIZATION_ID, $transactionObject->getId() );
} else {
@@ -193,6 +200,13 @@ protected function performChargeOrAuthorizationForOrder(
if ( $transactionEditor !== null ) {
$transactionEditor( $charge );
}
+ $this->logger->debug(
+ 'calling charge #' . $orderId . ' with ' . $order->get_payment_method(),
+ array(
+ 'charge' => $charge,
+ 'metadata' => ( new ShopService() )->getMetadata()
+ )
+ );
$transactionObject = $unzer->performCharge( $charge, $paymentType, $customer, ( new ShopService() )->getMetadata(), $basket );
$order->update_meta_data( Main::ORDER_META_KEY_CHARGE_ID, $transactionObject->getId() );
}
diff --git a/includes/services/ShopService.php b/includes/services/ShopService.php
index df5a232..29995aa 100644
--- a/includes/services/ShopService.php
+++ b/includes/services/ShopService.php
@@ -8,10 +8,21 @@ class ShopService {
public function getMetadata(): Metadata {
- return ( new Metadata() )
- ->addMetadata( 'pluginType', 'unzerdev/woocommerce' )
- ->addMetadata( 'pluginVersion', UNZER_VERSION )
- ->setShopType( 'WooCommerce' )
- ->setShopVersion( WC()->version );
+ $unzerMetadata = new Metadata();
+ $unzerMetadata->addMetadata( 'pluginType', 'unzerdev/woocommerce' )
+ ->addMetadata( 'pluginVersion', UNZER_VERSION )
+ ->setShopType( 'WooCommerce' )
+ ->setShopVersion( WC()->version );
+ if ((string)WC()->session->get(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD) !== '') {
+ $unzerMetadata->addMetadata('isExpress', '1');
+ } else {
+ $unzerMetadata->addMetadata('isExpress', '0');
+ }
+ return $unzerMetadata;
}
+
+ public function setIsExpress(Metadata $unzerMetadata, bool $isExpress): void
+ {
+ $unzerMetadata->addMetadata('isExpress', $isExpress ? '1' : '0');
+ }
}
diff --git a/includes/traits/SavePaymentInstrumentTrait.php b/includes/traits/SavePaymentInstrumentTrait.php
index df8f0e4..00e5dd3 100644
--- a/includes/traits/SavePaymentInstrumentTrait.php
+++ b/includes/traits/SavePaymentInstrumentTrait.php
@@ -4,6 +4,7 @@
use UnzerPayments\Gateways\AbstractGateway;
use UnzerPayments\Main;
+use UnzerPayments\Services\ExpressCheckoutService;
use UnzerPayments\Services\PaymentService;
use UnzerSDK\Resources\PaymentTypes\BasePaymentType;
use UnzerSDK\Resources\PaymentTypes\Card;
@@ -13,6 +14,9 @@
trait SavePaymentInstrumentTrait {
public function isSaveInstruments(): bool {
+ if (WC()->session->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID)) {
+ return false;
+ }
return $this->get_option( AbstractGateway::SETTINGS_KEY_SAVE_INSTRUMENTS ) === 'yes' && is_user_logged_in();
}
diff --git a/languages/unzer-payments-de_DE.l10n.php b/languages/unzer-payments-de_DE.l10n.php
index 336451c..995ae8f 100644
--- a/languages/unzer-payments-de_DE.l10n.php
+++ b/languages/unzer-payments-de_DE.l10n.php
@@ -52,6 +52,7 @@
'Select another Unzer payment method' => 'Wählen Sie eine andere Unzer-Zahlungsmethode',
'Return to payments' => 'Zurück zu den Zahlungen',
'An error occurred while processing your payment. Please try another payment method.' => 'Bei der Bearbeitung Ihrer Zahlung ist ein Fehler aufgetreten. Bitte versuchen Sie eine andere Zahlungsmethode.',
+ 'An error occurred while processing your request. Please try again.' => 'Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.',
'Please enter your date of birth' => 'Bitte geben Sie Ihr Geburtsdatum ein',
'Please enter your company type' => 'Bitte geben Sie Ihren Unternehmenstyp ein',
'Please accept the SEPA mandate' => 'Bitte akzeptieren Sie das SEPA Mandat',
@@ -151,6 +152,9 @@
'Enable Unzer PayPal' => 'Aktivieren Sie Unzer PayPal',
'PayPal' => 'PayPal',
'Save PayPal account for registered customers' => 'PayPal-Konto für registrierte Kunden speichern',
+ 'Offer Paypal Express Checkout' => 'Paypal Express Checkout anbieten',
+ 'Offer Apple Pay Express Checkout' => 'Apple Pay Express Checkout anbieten',
+ 'Offer Google Pay Express Checkout' => 'Google Pay Express Checkout anbieten',
'Enable Unzer Bank Transfer' => 'Unzer Banküberweisung aktivieren',
'Bank Transfer' => 'Banküberweisung',
'Enable Unzer Post Finance Card' => 'Unzer Post Finance Card aktivieren',
diff --git a/package.json b/package.json
index b114362..05269d5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "unzer-payments",
- "version": "2.1.4",
+ "version": "2.2.0",
"description": "",
"main": "index.js",
"scripts": {
diff --git a/readme.txt b/readme.txt
index 9fa68c4..3031664 100755
--- a/readme.txt
+++ b/readme.txt
@@ -3,7 +3,7 @@ Contributors: Unzer
Tags: payments, woocommerce
Requires at least: 4.5
Tested up to: 7.0
-Stable tag: 2.1.4
+Stable tag: 2.2.0
License: Apache-2.0
License URI: http://www.apache.org/licenses/LICENSE-2.0
Author URI: https://unzer.com
@@ -59,6 +59,10 @@ Unzer is one of the leading payment companies in Europe. Over 70,000 retailers t
## Changelog ##
+# 2.2.0 #
+* New: Added Express Checkout for PayPal, Google Pay, and Apple Pay
+* Fix: Automatic capture now triggers correctly when order status changes to the configured capture status
+
# 2.1.4 #
* Direct Bank Transfer order status now correctly derived from combined payment + transaction state, with webhook handling for delayed settlements
* Fixed UPL 2FA payments bypassing the redirect URL — users are now routed through the 2FA flow before landing on the order success page
diff --git a/unzer-payments.php b/unzer-payments.php
index a79f051..ce092d2 100755
--- a/unzer-payments.php
+++ b/unzer-payments.php
@@ -5,12 +5,12 @@
* Description: Official Unzer Plugin
* Author: Unzer
* Author URI: https://www.unzer.com
- * Version: 2.1.4
+ * Version: 2.2.0
* License: Apache-2.0
* Requires at least: 4.5
* Tested up to: 7.0
* WC requires at least: 6.0
- * WC tested up to: 10.8
+ * WC tested up to: 10.9
* Text Domain: unzer-payments
*/
@@ -21,7 +21,7 @@
/**
* Required minimums and constants
*/
-define( 'UNZER_VERSION', '2.1.4' );
+define( 'UNZER_VERSION', '2.2.0' );
define( 'UNZER_PLUGIN_TYPE_STRING', 'Unzer Payments' );
define( 'UNZER_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
define( 'UNZER_PLUGIN_PATH', __DIR__ . '/' );