diff --git a/mollie-payments-for-woocommerce.php b/mollie-payments-for-woocommerce.php index b62a5af2..bffaa36c 100644 --- a/mollie-payments-for-woocommerce.php +++ b/mollie-payments-for-woocommerce.php @@ -3,7 +3,7 @@ * Plugin Name: Mollie Payments for WooCommerce * Plugin URI: https://www.mollie.com * Description: Accept payments in WooCommerce with the official Mollie plugin - * Version: 7.5.1 + * Version: 7.5.2-beta * Author: Mollie * Author URI: https://www.mollie.com * Requires at least: 5.0 diff --git a/public/images/bancomatpay.svg b/public/images/bancomatpay.svg new file mode 100644 index 00000000..6d0b5e84 --- /dev/null +++ b/public/images/bancomatpay.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/resources/js/blocks/molliePaymentMethod.js b/resources/js/blocks/molliePaymentMethod.js index 39303c61..83de427e 100644 --- a/resources/js/blocks/molliePaymentMethod.js +++ b/resources/js/blocks/molliePaymentMethod.js @@ -191,6 +191,36 @@ const MollieComponent = (props) => { }, [activePaymentMethod, onCheckoutValidation, billing.billingData, shippingData.shippingAddress, item, phoneString, inputBirthdate, inputPhone]); + useEffect(() => { + let phoneLabel = getPhoneField()?.labels?.[0] ?? null; + if (!phoneLabel || phoneLabel.length === 0) { + return + } + if (activePaymentMethod === 'mollie_wc_gateway_bancomatpay') { + phoneLabel.innerText = item.phonePlaceholder + } else { + if (phoneString !== false) { + phoneLabel.innerText = phoneString + } + } + let isPhoneEmpty = (billing.billingData.phone === '' && shippingData.shippingAddress.phone === '') && inputPhone === ''; + const unsubscribeProcessing = onCheckoutValidation( + + () => { + if (activePaymentMethod === 'mollie_wc_gateway_bancomatpay' && isPhoneEmpty) { + return { + errorMessage: item.errorMessage, + }; + } + } + ); + return () => { + unsubscribeProcessing() + }; + + }, [activePaymentMethod, onCheckoutValidation, billing.billingData, shippingData.shippingAddress, item, phoneString, inputPhone]); + + onSubmitLocal = onSubmit const updateIssuer = ( changeEvent ) => { selectIssuer( changeEvent.target.value ) @@ -214,7 +244,9 @@ const MollieComponent = (props) => { } function fieldMarkup(id, fieldType, label, action, value) { - return
+ const className = "wc-block-components-text-input wc-block-components-address-form__" + id; + return
+
} if (item.name === "mollie_wc_gateway_billie"){ @@ -231,7 +263,17 @@ const MollieComponent = (props) => { fields.push(fieldMarkup("billing-birthdate", "date", birthdateField, updateBirthdate, inputBirthdate)); if (!isPhoneFieldVisible) { const phoneField = item.phonePlaceholder ? item.phonePlaceholder : "Phone"; - fields.push(fieldMarkup("billing-phone", "tel", phoneField, updatePhone, inputPhone)); + fields.push(fieldMarkup("billing-phone-in3", "tel", phoneField, updatePhone, inputPhone)); + } + + return <>{fields}; + } + + if (item.name === "mollie_wc_gateway_bancomatpay"){ + let fields = []; + if (!isPhoneFieldVisible) { + const phoneField = item.phonePlaceholder ? item.phonePlaceholder : "Phone"; + fields.push(fieldMarkup("billing-phone-bancomatpay", "tel", phoneField, updatePhone, inputPhone)); } return <>{fields}; diff --git a/resources/js/mollieBillie.js b/resources/js/mollieBillie.js deleted file mode 100644 index ad31361e..00000000 --- a/resources/js/mollieBillie.js +++ /dev/null @@ -1,32 +0,0 @@ -import {maybeRequireField, saveOriginalField} from "./wooCheckoutFieldsUtility"; - -( - function ({jQuery}) { - let gateway = 'mollie_wc_gateway_billie'; - let inputCompanyName = 'billing_company'; - let originalBillingCompanyField = saveOriginalField(inputCompanyName, {}); - let companyFieldId = 'billing_company_field'; - let companyField = jQuery('form[name="checkout"] p#billing_company_field'); - let positionCompanyField = 'li.wc_payment_method.payment_method_mollie_wc_gateway_billie'; - let companyMarkup = '

' - + '' - + '' - + '' - + '' - + '

' - - jQuery(function () { - jQuery('body') - .on('updated_checkout payment_method_selected', function () { - companyField = maybeRequireField(companyField, positionCompanyField, companyMarkup, inputCompanyName, companyFieldId, originalBillingCompanyField, gateway); - }); - }); - } -)( - window -) - - - diff --git a/resources/js/mollieBlockIndex.js b/resources/js/mollieBlockIndex.js index 641511e5..8f4f9e3c 100644 --- a/resources/js/mollieBlockIndex.js +++ b/resources/js/mollieBlockIndex.js @@ -7,33 +7,30 @@ import molliePaymentMethod from './blocks/molliePaymentMethod' } window.onload = (event) => { const { registerPaymentMethod } = wc.wcBlocksRegistry; + const { checkoutData, defaultFields } = wc.wcSettings.allSettings; + const { billing_address, shipping_address } = checkoutData; const { ajaxUrl, filters, gatewayData, availableGateways } = mollieBlockData.gatewayData; const {useEffect} = wp.element; const isAppleSession = typeof window.ApplePaySession === "function" const isBlockEditor = !!wp?.blockEditor; function getCompanyField() { - let shippingCompany = document.getElementById('shipping-company'); - let billingCompany = document.getElementById('billing-company'); + let shippingCompany = shipping_address.company ?? false; + let billingCompany = billing_address.company ?? false; return shippingCompany ? shippingCompany : billingCompany; } function getPhoneField() { - const shippingPhone = document.getElementById('shipping-phone'); - const billingPhone = document.getElementById('billing-phone'); + const shippingPhone = shipping_address.phone ?? false; + const billingPhone = billing_address.phone ?? false return billingPhone || shippingPhone; } - function isFieldVisible(field) - { - return field && field.style.display !== 'none'; - } - let companyField = getCompanyField(); - const isCompanyFieldVisible = companyField && isFieldVisible(companyField); - const companyNameString = companyField && companyField.parentNode.querySelector('label') ? companyField.parentNode.querySelector('label').innerHTML : false; - let phoneField = getPhoneField(); - const isPhoneFieldVisible = phoneField && isFieldVisible(phoneField); - const phoneString = phoneField && phoneField.parentNode.querySelector('label') ? phoneField.parentNode.querySelector('label').innerHTML : false; + + const isCompanyFieldVisible = getCompanyField(); + const companyNameString = defaultFields.company.label + const isPhoneFieldVisible = getPhoneField(); + const phoneString = defaultFields.phone.label let requiredFields = { 'companyNameString': companyNameString, 'phoneString': phoneString, diff --git a/src/Assets/AssetsModule.php b/src/Assets/AssetsModule.php index 5a97e4e7..da8c7a6e 100644 --- a/src/Assets/AssetsModule.php +++ b/src/Assets/AssetsModule.php @@ -276,13 +276,6 @@ protected function registerFrontendScripts(string $pluginUrl, string $pluginPath (string) filemtime($this->getPluginPath($pluginPath, '/public/js/gatewaySurcharge.min.js')), true ); - wp_register_script( - 'mollie-billie-classic-handles', - $this->getPluginUrl($pluginUrl, '/public/js/mollieBillie.min.js'), - ['underscore', 'jquery'], - (string) filemtime($this->getPluginPath($pluginPath, '/public/js/mollieBillie.min.js')), - true - ); } public function registerBlockScripts(string $pluginUrl, string $pluginPath): void diff --git a/src/Gateway/GatewayModule.php b/src/Gateway/GatewayModule.php index e06c151e..cc6fba01 100644 --- a/src/Gateway/GatewayModule.php +++ b/src/Gateway/GatewayModule.php @@ -7,6 +7,7 @@ namespace Mollie\WooCommerce\Gateway; use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController; +use Automattic\WooCommerce\StoreApi\Exceptions\RouteException; use Mollie\WooCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule; use Mollie\WooCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait; use Mollie\WooCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule; @@ -102,7 +103,7 @@ public function services(): array } return $availableMethods; }, - 'gateway.getKlarnaPaymentMethodsAfterFeatureFlag' => static function (ContainerInterface $container): array { + 'gateway.getPaymentMethodsAfterFeatureFlag' => static function (ContainerInterface $container): array { $availablePaymentMethods = $container->get('gateway.listAllMethodsAvailable'); $klarnaOneFlag = apply_filters('inpsyde.feature-flags.mollie-woocommerce.klarna_one_enabled', true); if (!$klarnaOneFlag) { @@ -110,6 +111,12 @@ public function services(): array return $method['id'] !== Constants::KLARNA; }); } + $bancomatpayFlag = apply_filters('inpsyde.feature-flags.mollie-woocommerce.bancomatpay_enabled', false); + if (!$bancomatpayFlag) { + return array_filter($availablePaymentMethods, static function ($method) { + return $method['id'] !== Constants::BANCOMATPAY; + }); + } return $availablePaymentMethods; }, 'gateway.isSDDGatewayEnabled' => static function (ContainerInterface $container): bool { @@ -263,6 +270,32 @@ static function () { [$this, 'in3FieldsMandatoryPayForOrder'], 11 ); + add_action( + 'woocommerce_checkout_posted_data', + [$this, 'switchFields'], + 11 + ); + add_action('woocommerce_rest_checkout_process_payment_with_context', [$this, 'addPhoneWhenRest'], 11); + } + $isBancomatPayEnabled = mollieWooCommerceIsGatewayEnabled('mollie_wc_gateway_bancomatpay_settings', 'enabled'); + if ($isBancomatPayEnabled) { + add_filter( + 'woocommerce_after_checkout_validation', + [$this, 'bancomatpayFieldsMandatory'], + 11, + 2 + ); + add_action( + 'woocommerce_before_pay_action', + [$this, 'bancomatpayFieldsMandatoryPayForOrder'], + 11 + ); + add_action( + 'woocommerce_checkout_posted_data', + [$this, 'switchFields'], + 11 + ); + add_action('woocommerce_rest_checkout_process_payment_with_context', [$this, 'addPhoneWhenRest'], 11); } // Set order to paid and processed when eventually completed without Mollie add_action('woocommerce_payment_complete', [$this, 'setOrderPaidByOtherGateway'], 10, 1); @@ -313,6 +346,17 @@ static function ($paymentContext) { } ); add_action('add_meta_boxes_woocommerce_page_wc-orders', [$this, 'addShopOrderMetabox'], 10); + add_filter('woocommerce_form_field_args', static function ($args, $key, $value) use ($container) { + if ($key !== 'billing_phone') { + return $args; + } + if ($args['required'] === true) { + update_option('mollie_wc_is_phone_required_flag', true); + } else { + update_option('mollie_wc_is_phone_required_flag', false); + } + return $args; + }, 10, 3); return true; } @@ -582,7 +626,7 @@ public function instantiatePaymentMethodGateways(ContainerInterface $container): protected function instantiatePaymentMethods($container): array { $paymentMethods = []; - $listAllAvailablePaymentMethods = $container->get('gateway.getKlarnaPaymentMethodsAfterFeatureFlag'); + $listAllAvailablePaymentMethods = $container->get('gateway.getPaymentMethodsAfterFeatureFlag'); $iconFactory = $container->get(IconFactory::class); assert($iconFactory instanceof IconFactory); $settingsHelper = $container->get('settings.settings_helper'); @@ -629,14 +673,23 @@ public function BillieFieldsMandatory($fields, $errors) public function in3FieldsMandatory($fields, $errors) { $gatewayName = "mollie_wc_gateway_in3"; - $phoneField = 'billing_phone'; + $phoneField = 'billing_phone_in3'; $birthdateField = self::FIELD_IN3_BIRTHDATE; $phoneLabel = __('Phone', 'mollie-payments-for-woocommerce'); $birthDateLabel = __('Birthdate', 'mollie-payments-for-woocommerce'); - $fields = $this->addPaymentMethodMandatoryFields($fields, $gatewayName, $phoneField, $phoneLabel, $errors); + $fields = $this->addPaymentMethodMandatoryFieldsPhoneVerification($fields, $gatewayName, $phoneField, $phoneLabel, $errors); return $this->addPaymentMethodMandatoryFields($fields, $gatewayName, $birthdateField, $birthDateLabel, $errors); } + public function bancomatpayFieldsMandatory($fields, $errors) + { + $gatewayName = "mollie_wc_gateway_bancomatpay"; + $phoneField = 'billing_phone_bancomatpay'; + $phoneLabel = __('Phone', 'mollie-payments-for-woocommerce'); + return $this->addPaymentMethodMandatoryFieldsPhoneVerification($fields, $gatewayName, $phoneField, $phoneLabel, $errors); + } + + /** * @param $order */ @@ -660,6 +713,49 @@ public function in3FieldsMandatoryPayForOrder($order) 'error' ); } + $phoneValue = filter_input(INPUT_POST, 'billing_phone_in3', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + $phoneValue = $phoneValue && $this->isPhoneValid($phoneValue) ? $phoneValue : false; + $phoneLabel = __('Phone', 'mollie-payments-for-woocommerce'); + + if (!$phoneValue) { + wc_add_notice( + sprintf( + __('%s is a required field. Valid phone format +000000000', 'mollie-payments-for-woocommerce'), + "$phoneLabel" + ), + 'error' + ); + } else { + $order->set_billing_phone($phoneValue); + } + } + + /** + * @param $order + */ + public function bancomatpayFieldsMandatoryPayForOrder($order) + { + $paymentMethod = filter_input(INPUT_POST, 'payment_method', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + + if ($paymentMethod !== 'mollie_wc_gateway_bancomatpay') { + return; + } + + $phoneValue = filter_input(INPUT_POST, 'billing_phone_bancomatpay', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + $phoneValue = $phoneValue && $this->isPhoneValid($phoneValue) ? $phoneValue : false; + $phoneLabel = __('Phone', 'mollie-payments-for-woocommerce'); + + if (!$phoneValue) { + wc_add_notice( + sprintf( + __('%s is a required field. Valid phone format +00000000000', 'mollie-payments-for-woocommerce'), + "$phoneLabel" + ), + 'error' + ); + } else { + $order->set_billing_phone($phoneValue); + } } /** @@ -722,4 +818,97 @@ public function addPaymentMethodMandatoryFields($fields, string $gatewayName, st return $fields; } + + public function addPaymentMethodMandatoryFieldsPhoneVerification( + $fields, + string $gatewayName, + string $field, + string $fieldLabel, + $errors + ) { + if ($fields['payment_method'] !== $gatewayName) { + return $fields; + } + if (isset($fields['billing_phone']) && $this->isPhoneValid($fields['billing_phone'])) { + return $fields; + } + $fieldPosted = filter_input(INPUT_POST, $field, FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + if (!$fieldPosted) { + $errors->add( + 'validation', + sprintf( + __('%s is a required field.', 'woocommerce'), + "$fieldLabel" + ) + ); + return $fields; + } + + if (!$this->isPhoneValid($fieldPosted)) { + $errors->add( + 'validation', + sprintf( + __('%s is not a valid phone number. Valid phone format +00000000000', 'woocommerce'), + "$fieldLabel" + ) + ); + return $fields; + } else { + $fields['billing_phone'] = $fieldPosted; + } + return $fields; + } + + public function switchFields($data) + { + if (isset($data['payment_method']) && $data['payment_method'] === 'mollie_wc_gateway_in3') { + $fieldPosted = filter_input(INPUT_POST, 'billing_phone_in3', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + if ($fieldPosted) { + $data['billing_phone'] = !empty($fieldPosted) ? $fieldPosted : $data['billing_phone']; + } + } + if (isset($data['payment_method']) && $data['payment_method'] === 'mollie_wc_gateway_bancomatpay') { + $fieldPosted = filter_input(INPUT_POST, 'billing_phone_bancomatpay', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + if ($fieldPosted) { + $data['billing_phone'] = !empty($fieldPosted) ? $fieldPosted : $data['billing_phone']; + } + } + if (isset($data['payment_method']) && $data['payment_method'] === 'mollie_wc_gateway_billie') { + $fieldPosted = filter_input(INPUT_POST, 'billing_company_billie', FILTER_SANITIZE_SPECIAL_CHARS) ?? false; + if ($fieldPosted) { + $data['billing_company'] = !empty($fieldPosted) ? $fieldPosted : $data['billing_company']; + } + } + return $data; + } + + private function isPhoneValid($billing_phone) + { + return preg_match('/^\+[1-9]\d{10,13}$/', $billing_phone); + } + + public function addPhoneWhenRest($arrayContext) + { + $context = $arrayContext; + $phoneMandatoryGateways = ['mollie_wc_gateway_in3', 'mollie_wc_gateway_bancomatpay']; + $paymentMethod = $context->payment_data['payment_method']; + if (in_array($paymentMethod, $phoneMandatoryGateways)) { + $billingPhone = $context->order->get_billing_phone(); + if (!empty($billingPhone) && $this->isPhoneValid($billingPhone)) { + return; + } + $billingPhone = $context->payment_data['billing_phone']; + if ($billingPhone) { + $context->order->set_billing_phone($billingPhone); + $context->order->save(); + } else { + $message = __('Please introduce a valid phone number. +00000000000', 'mollie-payments-for-woocommerce'); + throw new RouteException( + 'woocommerce_rest_checkout_process_payment_error', + $message, + 402 + ); + } + } + } } diff --git a/src/Payment/PaymentService.php b/src/Payment/PaymentService.php index 50127fdb..f866de5f 100644 --- a/src/Payment/PaymentService.php +++ b/src/Payment/PaymentService.php @@ -491,7 +491,7 @@ protected function processAsMolliePayment( : '', ]; - $this->logger->debug($apiCallLog); + $this->logger->debug(json_encode($apiCallLog, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); // Try as simple payment $paymentObject = $this->apiHelper->getApiClient( diff --git a/src/PaymentMethods/Bancomatpay.php b/src/PaymentMethods/Bancomatpay.php new file mode 100644 index 00000000..510aa922 --- /dev/null +++ b/src/PaymentMethods/Bancomatpay.php @@ -0,0 +1,56 @@ + 'bancomatpay', + 'defaultTitle' => __('Bancomat Pay', 'mollie-payments-for-woocommerce'), + 'settingsDescription' => '', + 'defaultDescription' => '', + 'paymentFields' => true, + 'instructions' => false, + 'supports' => [ + 'products', + 'refunds', + ], + 'filtersOnBuild' => true, + 'confirmationDelayed' => false, + 'errorMessage' => __( + 'Required field is empty. Phone field is required.', + 'mollie-payments-for-woocommerce' + ), + 'phonePlaceholder' => __('Please enter your phone here. +00..', 'mollie-payments-for-woocommerce'), + ]; + } + + public function getFormFields($generalFormFields): array + { + return $generalFormFields; + } + + public function filtersOnBuild() + { + add_filter('woocommerce_mollie_wc_gateway_' . $this->getProperty('id') . 'payment_args', function (array $args, \WC_Order $order): array { + return $this->addPaymentArguments($args, $order); + }, 10, 2); + } + /** + * @param WC_Order $order + * @return array + */ + public function addPaymentArguments(array $args, $order) + { + $phone = $order->get_billing_phone(); + if (!empty($phone)) { + $args['billingAddress']['phone'] = $phone; + } + + return $args; + } +} diff --git a/src/PaymentMethods/Billie.php b/src/PaymentMethods/Billie.php index 720aed88..369f4952 100644 --- a/src/PaymentMethods/Billie.php +++ b/src/PaymentMethods/Billie.php @@ -16,7 +16,7 @@ protected function getConfig(): array 'mollie-payments-for-woocommerce' ), 'defaultDescription' => '', - 'paymentFields' => false, + 'paymentFields' => true, 'instructions' => false, 'supports' => [ 'products', diff --git a/src/PaymentMethods/Constants.php b/src/PaymentMethods/Constants.php index 9ff73d94..6f62e6b2 100644 --- a/src/PaymentMethods/Constants.php +++ b/src/PaymentMethods/Constants.php @@ -10,4 +10,6 @@ class Constants public const KLARNA = 'klarna'; public const DIRECTDEBIT = 'directdebit'; public const BANKTRANSFER = 'banktransfer'; + + public const BANCOMATPAY = 'bancomatpay'; } diff --git a/src/PaymentMethods/PaymentFieldsStrategies/BancomatpayFieldsStrategy.php b/src/PaymentMethods/PaymentFieldsStrategies/BancomatpayFieldsStrategy.php new file mode 100644 index 00000000..0952e392 --- /dev/null +++ b/src/PaymentMethods/PaymentFieldsStrategies/BancomatpayFieldsStrategy.php @@ -0,0 +1,60 @@ +getOrderIdOnPayForOrderPage(); + $phoneValue = $order->get_billing_phone(); + $showPhoneField = true; + } + + if (is_checkout() && !is_checkout_pay_page() && !$isPhoneRequired) { + $showPhoneField = true; + } + + if ($showPhoneField) { + $this->phoneNumber($phoneValue); + } + } + + protected function getOrderIdOnPayForOrderPage() + { + global $wp; + $orderId = absint($wp->query_vars['order-pay']); + return wc_get_order($orderId); + } + + protected function phoneNumber($phoneValue) + { + $phoneValue = $phoneValue ?: ''; + ?> +

+ + + + +

+ getOrderIdOnPayForOrderPage(); + $showCompanyField = empty($order->get_billing_company()); + } + + if (is_checkout() && !is_checkout_pay_page()) { + $showCompanyField = true; + } + + if ($showCompanyField) { + $this->company(); + } + } + + protected function getOrderIdOnPayForOrderPage() + { + global $wp; + $orderId = absint($wp->query_vars['order-pay']); + return wc_get_order($orderId); + } + + protected function company() + { + ?> +

+ + + + +

+ getOrderIdOnPayForOrderPage(); - $showPhoneField = empty($order->get_billing_phone()); $showBirthdateField = true; + $showPhoneField = true; + $order = $this->getOrderIdOnPayForOrderPage(); + $phoneValue = $order->get_billing_phone(); } + if (is_checkout() && !is_checkout_pay_page() && !$isPhoneRequired) { + $showPhoneField = true; + } if (is_checkout() && !is_checkout_pay_page()) { - $checkoutFields = WC()->checkout()->get_checkout_fields(); - - if (!isset($checkoutFields["billing"][self::FIELD_PHONE])) { - $showPhoneField = true; - } - - if (!isset($checkoutFields["billing"][self::FIELD_BIRTHDATE])) { - $showBirthdateField = true; - } + $showBirthdateField = true; } if ($showPhoneField) { - $this->phoneNumber(); + $this->phoneNumber($phoneValue); } if ($showBirthdateField) { @@ -63,8 +61,9 @@ protected function dateOfBirth()