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__ . '/' );