From be1f8851eb4f774f02883685cd28b40ca535e8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Mon, 21 Nov 2022 11:03:02 +0100 Subject: [PATCH 01/29] Add Mollie Components field to credit card payment method. --- js/dist/components.js | 76 ++++++++++++++++ js/dist/components.js.map | 1 + js/src/components.js | 87 +++++++++++++++++++ package.json | 7 ++ src/ComponentsField.php | 177 ++++++++++++++++++++++++++++++++++++++ src/Gateway.php | 24 ++++++ 6 files changed, 372 insertions(+) create mode 100644 js/dist/components.js create mode 100644 js/dist/components.js.map create mode 100644 js/src/components.js create mode 100644 src/ComponentsField.php diff --git a/js/dist/components.js b/js/dist/components.js new file mode 100644 index 0000000..c383f86 --- /dev/null +++ b/js/dist/components.js @@ -0,0 +1,76 @@ +/* global Mollie */ + +'use strict'; + +/** + * Mollie Components. + * + * @link https://docs.mollie.com/components/overview + */ +(() => { + function initMollieComponents() { + const $elements = document.querySelectorAll('.pronamic_pay_mollie_components'); + $elements.forEach($element => { + const data = $element.dataset; + if (!("mollie-profile-id" in data)) { + throw new Error('No Mollie profile ID in element dataset. Unable to load Mollie Components.'); + return; + } + + // Initialize Mollie object. + const mollie = Mollie(data['mollie-profile-id'], { + locale: data['mollie-locale'] ?? null, + testmode: "mollie-testmode" in data + }); + + // Create components. + const components = [{ + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, { + id: 'expiry-date', + label: 'Expiry Date', + component: 'expiryDate' + }, { + id: 'verification-code', + label: 'Verification Code', + component: 'verificationCode' + }]; + components.forEach(element => { + let fieldElement = document.createElement('div'); + fieldElement.setAttribute('id', element.id); + let errorElement = document.createElement('div'); + errorElement.setAttribute('id', element.id + '-error'); + $element.append(element.label); + $element.append(fieldElement); + $element.append(errorElement); + + // Mount component. + if (document.querySelectorAll('#' + element.id).length > 0) { + let component = mollie.createComponent(element.component); + component.mount('#' + element.id); + } + }); + + // Handling errors. + /* + var cardNumberError = document.querySelector('#card-number-error'); + cardNumber.addEventListener('change', event => { + if (event.error && event.touched) { + cardNumberError.textContent = event.error; + } else { + cardNumberError.textContent = ''; + } + }); + */ + }); + } + + jQuery(document.body).on('updated_checkout', initMollieComponents); +})(); +//# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/js/dist/components.js.map b/js/dist/components.js.map new file mode 100644 index 0000000..762ac23 --- /dev/null +++ b/js/dist/components.js.map @@ -0,0 +1 @@ +{"version":3,"file":"components.js","names":["initMollieComponents","$elements","document","querySelectorAll","forEach","$element","data","dataset","Error","mollie","Mollie","locale","testmode","components","id","label","component","element","fieldElement","createElement","setAttribute","errorElement","append","length","createComponent","mount","jQuery","body","on"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( () => {\n\tfunction initMollieComponents() {\n\t\tconst $elements = document.querySelectorAll( '.pronamic_pay_mollie_components' );\n\n\t\t$elements.forEach( ( $element ) => {\n\t\t\tconst data = $element.dataset;\n\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\t// Create components.\n\t\t\tconst components = [\n\t\t\t\t{\n\t\t\t\t\tid: 'card-number',\n\t\t\t\t\tlabel: 'Card Number',\n\t\t\t\t\tcomponent: 'cardNumber'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'card-holder',\n\t\t\t\t\tlabel: 'Card Holder',\n\t\t\t\t\tcomponent: 'cardHolder'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'expiry-date',\n\t\t\t\t\tlabel: 'Expiry Date',\n\t\t\t\t\tcomponent: 'expiryDate'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'verification-code',\n\t\t\t\t\tlabel: 'Verification Code',\n\t\t\t\t\tcomponent: 'verificationCode'\n\t\t\t\t}\n\t\t\t];\n\n\t\t\tcomponents.forEach( ( element ) => {\n\t\t\t\tlet fieldElement = document.createElement( 'div' );\n\t\t\t\tfieldElement.setAttribute( 'id', element.id );\n\n\t\t\t\tlet errorElement = document.createElement( 'div' );\n\t\t\t\terrorElement.setAttribute( 'id', element.id + '-error' );\n\n\t\t\t\t$element.append( element.label );\n\t\t\t\t$element.append( fieldElement );\n\t\t\t\t$element.append( errorElement );\n\n\t\t\t\t// Mount component.\n\t\t\t\tif ( document.querySelectorAll('#' + element.id ).length > 0 ) {\n\t\t\t\t\tlet component = mollie.createComponent( element.component );\n\n\t\t\t\t\tcomponent.mount( '#' + element.id );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Handling errors.\n\t\t\t/*\n var cardNumberError = document.querySelector('#card-number-error');\n\n cardNumber.addEventListener('change', event => {\n if (event.error && event.touched) {\n cardNumberError.textContent = event.error;\n } else {\n cardNumberError.textContent = '';\n }\n });\n\t\t\t */\n\t\t});\n\t}\n\n\tjQuery( document.body ).on( 'updated_checkout', initMollieComponents );\n} )();\n"],"mappings":"AAAA;;AAEA,YAAY;;AAEZ;AACA;AACA;AACA;AACA;AACA,CAAE,MAAM;EACP,SAASA,oBAAoB,GAAG;IAC/B,MAAMC,SAAS,GAAGC,QAAQ,CAACC,gBAAgB,CAAE,iCAAiC,CAAE;IAEhFF,SAAS,CAACG,OAAO,CAAIC,QAAQ,IAAM;MAClC,MAAMC,IAAI,GAAGD,QAAQ,CAACE,OAAO;MAE7B,IAAK,EAAI,mBAAmB,IAAID,IAAI,CAAE,EAAG;QACxC,MAAM,IAAIE,KAAK,CAAE,4EAA4E,CAAE;QAE/F;MACD;;MAEA;MACA,MAAMC,MAAM,GAAGC,MAAM,CACpBJ,IAAI,CAAC,mBAAmB,CAAC,EACzB;QACCK,MAAM,EAAEL,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI;QACrCM,QAAQ,EAAI,iBAAiB,IAAIN;MAClC,CAAC,CACD;;MAED;MACA,MAAMO,UAAU,GAAG,CAClB;QACCC,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,mBAAmB;QACvBC,KAAK,EAAE,mBAAmB;QAC1BC,SAAS,EAAE;MACZ,CAAC,CACD;MAEDH,UAAU,CAACT,OAAO,CAAIa,OAAO,IAAM;QAClC,IAAIC,YAAY,GAAGhB,QAAQ,CAACiB,aAAa,CAAE,KAAK,CAAE;QAClDD,YAAY,CAACE,YAAY,CAAE,IAAI,EAAEH,OAAO,CAACH,EAAE,CAAE;QAE7C,IAAIO,YAAY,GAAGnB,QAAQ,CAACiB,aAAa,CAAE,KAAK,CAAE;QAClDE,YAAY,CAACD,YAAY,CAAE,IAAI,EAAEH,OAAO,CAACH,EAAE,GAAG,QAAQ,CAAE;QAExDT,QAAQ,CAACiB,MAAM,CAAEL,OAAO,CAACF,KAAK,CAAE;QAChCV,QAAQ,CAACiB,MAAM,CAAEJ,YAAY,CAAE;QAC/Bb,QAAQ,CAACiB,MAAM,CAAED,YAAY,CAAE;;QAE/B;QACA,IAAKnB,QAAQ,CAACC,gBAAgB,CAAC,GAAG,GAAGc,OAAO,CAACH,EAAE,CAAE,CAACS,MAAM,GAAG,CAAC,EAAG;UAC9D,IAAIP,SAAS,GAAGP,MAAM,CAACe,eAAe,CAAEP,OAAO,CAACD,SAAS,CAAE;UAE3DA,SAAS,CAACS,KAAK,CAAE,GAAG,GAAGR,OAAO,CAACH,EAAE,CAAE;QACpC;MACD,CAAC,CAAE;;MAEH;MACA;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IAEE,CAAC,CAAC;EACH;;EAEAY,MAAM,CAAExB,QAAQ,CAACyB,IAAI,CAAE,CAACC,EAAE,CAAE,kBAAkB,EAAE5B,oBAAoB,CAAE;AACvE,CAAC,GAAI"} \ No newline at end of file diff --git a/js/src/components.js b/js/src/components.js new file mode 100644 index 0000000..ab61f50 --- /dev/null +++ b/js/src/components.js @@ -0,0 +1,87 @@ +/* global Mollie */ + +'use strict'; + +/** + * Mollie Components. + * + * @link https://docs.mollie.com/components/overview + */ +( () => { + function initMollieComponents() { + const $elements = document.querySelectorAll( '.pronamic_pay_mollie_components' ); + + $elements.forEach( ( $element ) => { + const data = $element.dataset; + + if ( ! ( "mollie-profile-id" in data ) ) { + throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); + + return; + } + + // Initialize Mollie object. + const mollie = Mollie( + data['mollie-profile-id'], + { + locale: data['mollie-locale'] ?? null, + testmode: ( "mollie-testmode" in data ), + } + ); + + // Create components. + const components = [ + { + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, + { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, + { + id: 'expiry-date', + label: 'Expiry Date', + component: 'expiryDate' + }, + { + id: 'verification-code', + label: 'Verification Code', + component: 'verificationCode' + } + ]; + + components.forEach( ( element ) => { + let fieldElement = document.createElement( 'div' ); + fieldElement.setAttribute( 'id', element.id ); + + let errorElement = document.createElement( 'div' ); + errorElement.setAttribute( 'id', element.id + '-error' ); + + $element.append( element.label ); + $element.append( fieldElement ); + $element.append( errorElement ); + + // Mount component. + if ( document.querySelectorAll('#' + element.id ).length > 0 ) { + let component = mollie.createComponent( element.component ); + + component.mount( '#' + element.id ); + } + + // Handling errors. + /* + var cardNumberError = document.querySelector( '#card-number-error' ); + + cardNumber.addEventListener( 'change', event => { + cardNumberError.textContent = event.error && event.touched ? event.error : ''; + } ); + */ + } ); + } ); + } + + jQuery( document.body ).on( 'updated_checkout', initMollieComponents ); +} )(); diff --git a/package.json b/package.json index 296a465..0c6ae72 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,17 @@ }, "homepage": "http://www.wp-pay.org/gateways/mollie/", "devDependencies": { + "@babel/cli": "^7.15.4", + "@babel/core": "^7.15.5", + "@babel/preset-env": "^7.15.6", "@wordpress/env": "^5.4.0", + "eslint": "^7.32.0", + "eslint-plugin-json": "^3.1.0", "npm-run-all": "^4.1.5" }, "scripts": { + "babel": "babel ./js/src -d ./js/dist --source-maps", + "eslint": "eslint . --ext .json --ext .js", "start": "wp-env start --xdebug && npm run setup && npm run login", "setup": "npm-run-all setup-*", "setup-mollie-api-key": "wp-env run cli wp config set MOLLIE_API_KEY $MOLLIE_API_KEY", diff --git a/src/ComponentsField.php b/src/ComponentsField.php new file mode 100644 index 0000000..d9498cb --- /dev/null +++ b/src/ComponentsField.php @@ -0,0 +1,177 @@ + + * @copyright 2005-2022 Pronamic + * @license GPL-3.0-or-later + * @package Pronamic\WordPress\Pay\Mollie + */ + +namespace Pronamic\WordPress\Pay\Gateways\Mollie; + +use Pronamic\WordPress\Html\Element; +use Pronamic\WordPress\Pay\Fields\Field; + +/** + * HTML field class + */ +class ComponentsField extends Field { + public function setup() { + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. + \wp_register_script( + 'pronamic-pay-mollie', + 'https://js.mollie.com/v1/mollie.js', + [], + null, + false + ); + + $file = '../js/dist/components.js'; + + \wp_register_script( + 'pronamic-pay-mollie-components', + \plugins_url( $file, __FILE__ ), + [ 'pronamic-pay-mollie' ], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + true + ); + } + + /** + * Mollie profile ID. + */ + private ?string $profile_id; + + public function set_profile_id( $profile_id ) { + $this->profile_id = $profile_id; + } + + /** + * Get HTML attributes. + * + * @return array + */ + protected function get_html_attributes() : array { + $attributes = parent::get_html_attributes(); + + $attributes['class'] = $this->get_id(); + $attributes['data-mollie-profile-id'] = $this->profile_id; + + return $attributes; + } + + /** + * Render field. + * + * @return string + */ + public function render() : string { + \wp_enqueue_script( 'pronamic-pay-mollie-components' ); + + $element = new Element( 'div', $this->get_html_attributes() ); + + return '' . $element->render(); + } + + /** + * Serialize to JSON. + */ + public function jsonSerialize() : array { + $data = parent::jsonSerialize(); + + $data['type'] = 'html'; + + return $data; + } +} diff --git a/src/Gateway.php b/src/Gateway.php index 8fe3800..677bfe2 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -122,6 +122,29 @@ function() { $field_consumer_iban->set_label( __( 'Account number (IBAN)', 'pronamic_ideal' ) ); $field_consumer_iban->meta_key = 'consumer_bank_details_iban'; + $field_mollie_components = new ComponentsField( 'pronamic_pay_mollie_components' ); + $field_mollie_components->meta_key = 'mollie_card_token'; + + $cache_key = 'pronamic_pay_mollie_profile_id_' . \md5( (string) \wp_json_encode( $config ) ); + + $profile_id = \get_transient( $cache_key ); + + if ( false === $profile_id ) { + try { + $current_profile = $this->client->get_current_profile(); + + $profile = Profile::from_object( $current_profile ); + + $profile_id = $profile->get_id(); + } catch ( \Exception $e ) { + // No problem. + } + + \set_transient( $cache_key, $profile_id, \DAY_IN_SECONDS ); + } + + $field_mollie_components->set_profile_id( (string) $profile_id ); + // Apple Pay. $payment_method_apple_pay = new PaymentMethod( PaymentMethods::APPLE_PAY ); $payment_method_apple_pay->add_support( 'recurring' ); @@ -136,6 +159,7 @@ function() { // Payment method credit card. $payment_method_credit_card = new PaymentMethod( PaymentMethods::CREDIT_CARD ); $payment_method_credit_card->add_support( 'recurring' ); + $payment_method_credit_card->add_field( $field_mollie_components ); $this->register_payment_method( $payment_method_credit_card ); From c94f8992aa700d603be20c6bdde9d68dc67e4810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Tue, 22 Nov 2022 23:33:28 +0100 Subject: [PATCH 02/29] Make Mollie Components work with WooCommerce and test meta box. --- css/components.css | 30 +++++++ js/dist/components.js | 164 ++++++++++++++++++++++++----------- js/dist/components.js.map | 2 +- js/src/components.js | 177 +++++++++++++++++++++++++++----------- src/ComponentsField.php | 127 +++++++-------------------- src/Gateway.php | 11 ++- 6 files changed, 314 insertions(+), 197 deletions(-) create mode 100644 css/components.css diff --git a/css/components.css b/css/components.css new file mode 100644 index 0000000..61ec1a9 --- /dev/null +++ b/css/components.css @@ -0,0 +1,30 @@ +body .pronamic_pay_mollie_card_token label { + display: block; +} + +.mollie-component { + margin-top: .5rem; + padding: 10px; + font-weight: 500; + border: 1px solid transparent; + border-radius: 4px; + background: #fff; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + transition: 0.3s border-color cubic-bezier(0.4, 0, 0.2, 1); + max-width: 500px; +} + +.mollie-component.has-focus { + border-color: #0077ff; +} + +.mollie-component.is-invalid { + border-color: #ff1717; +} + +.field-error { + margin: 4px 0 1rem 0; + font-size: .8em; + font-weight: 400; + color: #ff1717; +} diff --git a/js/dist/components.js b/js/dist/components.js index c383f86..227a6c9 100644 --- a/js/dist/components.js +++ b/js/dist/components.js @@ -7,11 +7,36 @@ * * @link https://docs.mollie.com/components/overview */ -(() => { - function initMollieComponents() { - const $elements = document.querySelectorAll('.pronamic_pay_mollie_components'); - $elements.forEach($element => { - const data = $element.dataset; +($ => { + const components = [{ + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, { + id: 'expiry-date', + label: 'Expiry Date', + component: 'expiryDate' + }, { + id: 'verification-code', + label: 'Verification Code', + component: 'verificationCode' + }]; + function initMollieComponents(forms) { + let $cardTokenElements; + if (typeof forms === 'string') { + $cardTokenElements = document.querySelectorAll(forms + ' .pronamic_pay_mollie_card_token'); + } else { + $cardTokenElements = forms.querySelectorAll('.pronamic_pay_mollie_card_token'); + } + $cardTokenElements.forEach($cardTokenElement => { + // Create components. + const data = $cardTokenElement.dataset; + + // Check required Mollie profile ID. if (!("mollie-profile-id" in data)) { throw new Error('No Mollie profile ID in element dataset. Unable to load Mollie Components.'); return; @@ -22,55 +47,98 @@ locale: data['mollie-locale'] ?? null, testmode: "mollie-testmode" in data }); + components.forEach(component => { + // Label. + let label = document.createElement('label'); + label.setAttribute('for', component.id); + label.innerText = component.label; - // Create components. - const components = [{ - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, { - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, { - id: 'expiry-date', - label: 'Expiry Date', - component: 'expiryDate' - }, { - id: 'verification-code', - label: 'Verification Code', - component: 'verificationCode' - }]; - components.forEach(element => { - let fieldElement = document.createElement('div'); - fieldElement.setAttribute('id', element.id); - let errorElement = document.createElement('div'); - errorElement.setAttribute('id', element.id + '-error'); - $element.append(element.label); - $element.append(fieldElement); - $element.append(errorElement); + // Component container. + let field = document.createElement('div'); + field.setAttribute('id', component.id); + + // Error. + let error = document.createElement('div'); + error.setAttribute('id', component.id + '-error'); + error.setAttribute('role', 'alert'); + error.classList.add('field-error'); + $cardTokenElement.append(label, field, error); + + // Create and mount component. + let mollieComponent = mollie.createComponent(component.component); + mollieComponent.mount('#' + component.id); + + // Handle errors. + mollieComponent.addEventListener('change', event => { + error.textContent = event.error && event.touched ? event.error : ''; + }); + }); + + // Create Mollie token on checkout submit. + const form = $cardTokenElement.closest('form'); + form.addEventListener('submit', async e => { + // Check existing card token input. + let cardTokenInput = form.querySelector('input[name="pronamic_pay_mollie_card_token"]'); + if (cardTokenInput) { + return; + } + e.preventDefault(); - // Mount component. - if (document.querySelectorAll('#' + element.id).length > 0) { - let component = mollie.createComponent(element.component); - component.mount('#' + element.id); + // Create token. + const { + token, + error + } = await mollie.createToken(); + if (error) { + throw new Error(error.message || ''); + } + + // Add token to form. + const tokenInput = document.createElement('input'); + tokenInput.setAttribute('type', 'text'); + tokenInput.setAttribute('name', 'pronamic_pay_mollie_card_token'); + tokenInput.setAttribute('value', token); + form.append(tokenInput); + if (false !== form.dispatchEvent(new Event('pronamic_pay_mollie_components_card_token_added', { + cancelable: true + }))) { + // Submit form, now containing the hidden card token field. + // form.submit(); — not working with test meta box + form.querySelector('input[name="' + e.submitter.name + '"]').click(); } }); + }); + } + function setupWooCommerce() { + const checkoutForm = document.querySelector('form.woocommerce-checkout'); + if (!checkoutForm) { + return; + } - // Handling errors. - /* - var cardNumberError = document.querySelector('#card-number-error'); - cardNumber.addEventListener('change', event => { - if (event.error && event.touched) { - cardNumberError.textContent = event.error; - } else { - cardNumberError.textContent = ''; - } - }); - */ + // Init components on updated checkout. + $(document.body).on('updated_checkout', function (e) { + initMollieComponents(checkoutForm); }); + const $form = $(checkoutForm); + + // Prevent placing order, need to create token first. + $form.on('checkout_place_order_pronamic_pay_credit_card', returnFalse); + + // Re-enable placing order if card token has been added. + checkoutForm.addEventListener('pronamic_pay_mollie_components_card_token_added', function (e) { + e.preventDefault(); + $form.off('checkout_place_order_pronamic_pay_credit_card', returnFalse); + $form.submit(); + }); + } + function returnFalse() { + return false; } - jQuery(document.body).on('updated_checkout', initMollieComponents); -})(); + // Init Mollie Components. + document.addEventListener('DOMContentLoaded', function (e) { + initMollieComponents('form:not(.woocommerce-checkout)'); + }); + setupWooCommerce(); +})(jQuery); //# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/js/dist/components.js.map b/js/dist/components.js.map index 762ac23..fe6747a 100644 --- a/js/dist/components.js.map +++ b/js/dist/components.js.map @@ -1 +1 @@ -{"version":3,"file":"components.js","names":["initMollieComponents","$elements","document","querySelectorAll","forEach","$element","data","dataset","Error","mollie","Mollie","locale","testmode","components","id","label","component","element","fieldElement","createElement","setAttribute","errorElement","append","length","createComponent","mount","jQuery","body","on"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( () => {\n\tfunction initMollieComponents() {\n\t\tconst $elements = document.querySelectorAll( '.pronamic_pay_mollie_components' );\n\n\t\t$elements.forEach( ( $element ) => {\n\t\t\tconst data = $element.dataset;\n\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\t// Create components.\n\t\t\tconst components = [\n\t\t\t\t{\n\t\t\t\t\tid: 'card-number',\n\t\t\t\t\tlabel: 'Card Number',\n\t\t\t\t\tcomponent: 'cardNumber'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'card-holder',\n\t\t\t\t\tlabel: 'Card Holder',\n\t\t\t\t\tcomponent: 'cardHolder'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'expiry-date',\n\t\t\t\t\tlabel: 'Expiry Date',\n\t\t\t\t\tcomponent: 'expiryDate'\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: 'verification-code',\n\t\t\t\t\tlabel: 'Verification Code',\n\t\t\t\t\tcomponent: 'verificationCode'\n\t\t\t\t}\n\t\t\t];\n\n\t\t\tcomponents.forEach( ( element ) => {\n\t\t\t\tlet fieldElement = document.createElement( 'div' );\n\t\t\t\tfieldElement.setAttribute( 'id', element.id );\n\n\t\t\t\tlet errorElement = document.createElement( 'div' );\n\t\t\t\terrorElement.setAttribute( 'id', element.id + '-error' );\n\n\t\t\t\t$element.append( element.label );\n\t\t\t\t$element.append( fieldElement );\n\t\t\t\t$element.append( errorElement );\n\n\t\t\t\t// Mount component.\n\t\t\t\tif ( document.querySelectorAll('#' + element.id ).length > 0 ) {\n\t\t\t\t\tlet component = mollie.createComponent( element.component );\n\n\t\t\t\t\tcomponent.mount( '#' + element.id );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Handling errors.\n\t\t\t/*\n var cardNumberError = document.querySelector('#card-number-error');\n\n cardNumber.addEventListener('change', event => {\n if (event.error && event.touched) {\n cardNumberError.textContent = event.error;\n } else {\n cardNumberError.textContent = '';\n }\n });\n\t\t\t */\n\t\t});\n\t}\n\n\tjQuery( document.body ).on( 'updated_checkout', initMollieComponents );\n} )();\n"],"mappings":"AAAA;;AAEA,YAAY;;AAEZ;AACA;AACA;AACA;AACA;AACA,CAAE,MAAM;EACP,SAASA,oBAAoB,GAAG;IAC/B,MAAMC,SAAS,GAAGC,QAAQ,CAACC,gBAAgB,CAAE,iCAAiC,CAAE;IAEhFF,SAAS,CAACG,OAAO,CAAIC,QAAQ,IAAM;MAClC,MAAMC,IAAI,GAAGD,QAAQ,CAACE,OAAO;MAE7B,IAAK,EAAI,mBAAmB,IAAID,IAAI,CAAE,EAAG;QACxC,MAAM,IAAIE,KAAK,CAAE,4EAA4E,CAAE;QAE/F;MACD;;MAEA;MACA,MAAMC,MAAM,GAAGC,MAAM,CACpBJ,IAAI,CAAC,mBAAmB,CAAC,EACzB;QACCK,MAAM,EAAEL,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI;QACrCM,QAAQ,EAAI,iBAAiB,IAAIN;MAClC,CAAC,CACD;;MAED;MACA,MAAMO,UAAU,GAAG,CAClB;QACCC,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,aAAa;QACjBC,KAAK,EAAE,aAAa;QACpBC,SAAS,EAAE;MACZ,CAAC,EACD;QACCF,EAAE,EAAE,mBAAmB;QACvBC,KAAK,EAAE,mBAAmB;QAC1BC,SAAS,EAAE;MACZ,CAAC,CACD;MAEDH,UAAU,CAACT,OAAO,CAAIa,OAAO,IAAM;QAClC,IAAIC,YAAY,GAAGhB,QAAQ,CAACiB,aAAa,CAAE,KAAK,CAAE;QAClDD,YAAY,CAACE,YAAY,CAAE,IAAI,EAAEH,OAAO,CAACH,EAAE,CAAE;QAE7C,IAAIO,YAAY,GAAGnB,QAAQ,CAACiB,aAAa,CAAE,KAAK,CAAE;QAClDE,YAAY,CAACD,YAAY,CAAE,IAAI,EAAEH,OAAO,CAACH,EAAE,GAAG,QAAQ,CAAE;QAExDT,QAAQ,CAACiB,MAAM,CAAEL,OAAO,CAACF,KAAK,CAAE;QAChCV,QAAQ,CAACiB,MAAM,CAAEJ,YAAY,CAAE;QAC/Bb,QAAQ,CAACiB,MAAM,CAAED,YAAY,CAAE;;QAE/B;QACA,IAAKnB,QAAQ,CAACC,gBAAgB,CAAC,GAAG,GAAGc,OAAO,CAACH,EAAE,CAAE,CAACS,MAAM,GAAG,CAAC,EAAG;UAC9D,IAAIP,SAAS,GAAGP,MAAM,CAACe,eAAe,CAAEP,OAAO,CAACD,SAAS,CAAE;UAE3DA,SAAS,CAACS,KAAK,CAAE,GAAG,GAAGR,OAAO,CAACH,EAAE,CAAE;QACpC;MACD,CAAC,CAAE;;MAEH;MACA;AACH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IAEE,CAAC,CAAC;EACH;;EAEAY,MAAM,CAAExB,QAAQ,CAACyB,IAAI,CAAE,CAACC,EAAE,CAAE,kBAAkB,EAAE5B,oBAAoB,CAAE;AACvE,CAAC,GAAI"} \ No newline at end of file +{"version":3,"file":"components.js","names":["$","components","id","label","component","initMollieComponents","forms","$cardTokenElements","document","querySelectorAll","forEach","$cardTokenElement","data","dataset","Error","mollie","Mollie","locale","testmode","createElement","setAttribute","innerText","field","error","classList","add","append","mollieComponent","createComponent","mount","addEventListener","event","textContent","touched","form","closest","e","cardTokenInput","querySelector","preventDefault","token","createToken","message","tokenInput","dispatchEvent","Event","cancelable","submitter","name","click","setupWooCommerce","checkoutForm","body","on","$form","returnFalse","off","submit","jQuery"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( ( $ ) => {\n\tconst components = [\n\t\t{\n\t\t\tid: 'card-number',\n\t\t\tlabel: 'Card Number',\n\t\t\tcomponent: 'cardNumber'\n\t\t},\n\t\t{\n\t\t\tid: 'card-holder',\n\t\t\tlabel: 'Card Holder',\n\t\t\tcomponent: 'cardHolder'\n\t\t},\n\t\t{\n\t\t\tid: 'expiry-date',\n\t\t\tlabel: 'Expiry Date',\n\t\t\tcomponent: 'expiryDate'\n\t\t},\n\t\t{\n\t\t\tid: 'verification-code',\n\t\t\tlabel: 'Verification Code',\n\t\t\tcomponent: 'verificationCode'\n\t\t}\n\t];\n\n\tfunction initMollieComponents( forms ) {\n\t\tlet $cardTokenElements;\n\n\t\tif ( typeof forms === 'string' ) {\n\t\t\t$cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' );\n\t\t} else {\n\t\t\t$cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' );\n\t\t}\n\n\t\t$cardTokenElements.forEach( ( $cardTokenElement ) => {\n\t\t\t// Create components.\n\t\t\tconst data = $cardTokenElement.dataset;\n\n\t\t\t// Check required Mollie profile ID.\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tcomponents.forEach( ( component ) => {\n\t\t\t\t// Label.\n\t\t\t\tlet label = document.createElement( 'label' );\n\t\t\t\tlabel.setAttribute( 'for', component.id );\n\t\t\t\tlabel.innerText = component.label;\n\n\t\t\t\t// Component container.\n\t\t\t\tlet field = document.createElement( 'div' );\n\t\t\t\tfield.setAttribute( 'id', component.id );\n\n\t\t\t\t// Error.\n\t\t\t\tlet error = document.createElement( 'div' );\n\t\t\t\terror.setAttribute( 'id', component.id + '-error' );\n\t\t\t\terror.setAttribute( 'role', 'alert' );\n\t\t\t\terror.classList.add( 'field-error' );\n\n\t\t\t\t$cardTokenElement.append( label, field, error );\n\n\t\t\t\t// Create and mount component.\n\t\t\t\tlet mollieComponent = mollie.createComponent( component.component );\n\n\t\t\t\tmollieComponent.mount( '#' + component.id );\n\n\t\t\t\t// Handle errors.\n\t\t\t\tmollieComponent.addEventListener( 'change', ( event ) => {\n\t\t\t\t\terror.textContent = event.error && event.touched ? event.error : '';\n\t\t\t\t} );\n\t\t\t} );\n\n\t\t\t// Create Mollie token on checkout submit.\n\t\t\tconst form = $cardTokenElement.closest( 'form' );\n\n\t\t\tform.addEventListener( 'submit', async ( e ) => {\n\t\t\t\t// Check existing card token input.\n\t\t\t\tlet cardTokenInput = form.querySelector( 'input[name=\"pronamic_pay_mollie_card_token\"]' );\n\n\t\t\t\tif ( cardTokenInput ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Create token.\n\t\t\t\tconst { token, error } = await mollie.createToken();\n\n\t\t\t\tif ( error ) {\n\t\t\t\t\tthrow new Error( error.message || '' );\n\t\t\t\t}\n\n\t\t\t\t// Add token to form.\n\t\t\t\tconst tokenInput = document.createElement( 'input' );\n\t\t\t\ttokenInput.setAttribute( 'type', 'text' );\n\t\t\t\ttokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' );\n\t\t\t\ttokenInput.setAttribute( 'value', token );\n\n\t\t\t\tform.append( tokenInput );\n\n\t\t\t\tif ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) {\n\t\t\t\t\t// Submit form, now containing the hidden card token field.\n\t\t\t\t\t// form.submit(); — not working with test meta box\n\t\t\t\t\tform.querySelector( 'input[name=\"' + e.submitter.name + '\"]' ).click();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\tfunction setupWooCommerce() {\n\t\tconst checkoutForm = document.querySelector( 'form.woocommerce-checkout' );\n\n\t\tif ( ! checkoutForm ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Init components on updated checkout.\n\t\t$( document.body ).on( 'updated_checkout', function( e ) {\n\t\t\tinitMollieComponents( checkoutForm );\n\t\t} );\n\n\t\tconst $form = $( checkoutForm );\n\n\t\t// Prevent placing order, need to create token first.\n\t\t$form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t// Re-enable placing order if card token has been added.\n\t\tcheckoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) {\n\t\t\te.preventDefault();\n\n\t\t\t$form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t\t$form.submit();\n\t\t} );\n\t}\n\n\tfunction returnFalse() {\n\t\treturn false;\n\t}\n\n\t// Init Mollie Components.\n\tdocument.addEventListener( 'DOMContentLoaded', function( e ) {\n\t\tinitMollieComponents( 'form:not(.woocommerce-checkout)' );\n\t} );\n\n\tsetupWooCommerce();\n} )( jQuery );\n"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/components.js b/js/src/components.js index ab61f50..0f1cbed 100644 --- a/js/src/components.js +++ b/js/src/components.js @@ -7,13 +7,44 @@ * * @link https://docs.mollie.com/components/overview */ -( () => { - function initMollieComponents() { - const $elements = document.querySelectorAll( '.pronamic_pay_mollie_components' ); +( ( $ ) => { + const components = [ + { + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, + { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, + { + id: 'expiry-date', + label: 'Expiry Date', + component: 'expiryDate' + }, + { + id: 'verification-code', + label: 'Verification Code', + component: 'verificationCode' + } + ]; - $elements.forEach( ( $element ) => { - const data = $element.dataset; + function initMollieComponents( forms ) { + let $cardTokenElements; + if ( typeof forms === 'string' ) { + $cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' ); + } else { + $cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' ); + } + + $cardTokenElements.forEach( ( $cardTokenElement ) => { + // Create components. + const data = $cardTokenElement.dataset; + + // Check required Mollie profile ID. if ( ! ( "mollie-profile-id" in data ) ) { throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); @@ -29,59 +60,107 @@ } ); - // Create components. - const components = [ - { - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, - { - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, - { - id: 'expiry-date', - label: 'Expiry Date', - component: 'expiryDate' - }, - { - id: 'verification-code', - label: 'Verification Code', - component: 'verificationCode' - } - ]; + components.forEach( ( component ) => { + // Label. + let label = document.createElement( 'label' ); + label.setAttribute( 'for', component.id ); + label.innerText = component.label; - components.forEach( ( element ) => { - let fieldElement = document.createElement( 'div' ); - fieldElement.setAttribute( 'id', element.id ); + // Component container. + let field = document.createElement( 'div' ); + field.setAttribute( 'id', component.id ); - let errorElement = document.createElement( 'div' ); - errorElement.setAttribute( 'id', element.id + '-error' ); + // Error. + let error = document.createElement( 'div' ); + error.setAttribute( 'id', component.id + '-error' ); + error.setAttribute( 'role', 'alert' ); + error.classList.add( 'field-error' ); - $element.append( element.label ); - $element.append( fieldElement ); - $element.append( errorElement ); + $cardTokenElement.append( label, field, error ); - // Mount component. - if ( document.querySelectorAll('#' + element.id ).length > 0 ) { - let component = mollie.createComponent( element.component ); + // Create and mount component. + let mollieComponent = mollie.createComponent( component.component ); - component.mount( '#' + element.id ); + mollieComponent.mount( '#' + component.id ); + + // Handle errors. + mollieComponent.addEventListener( 'change', ( event ) => { + error.textContent = event.error && event.touched ? event.error : ''; + } ); + } ); + + // Create Mollie token on checkout submit. + const form = $cardTokenElement.closest( 'form' ); + + form.addEventListener( 'submit', async ( e ) => { + // Check existing card token input. + let cardTokenInput = form.querySelector( 'input[name="pronamic_pay_mollie_card_token"]' ); + + if ( cardTokenInput ) { + return; } - // Handling errors. - /* - var cardNumberError = document.querySelector( '#card-number-error' ); + e.preventDefault(); - cardNumber.addEventListener( 'change', event => { - cardNumberError.textContent = event.error && event.touched ? event.error : ''; - } ); - */ + // Create token. + const { token, error } = await mollie.createToken(); + + if ( error ) { + throw new Error( error.message || '' ); + } + + // Add token to form. + const tokenInput = document.createElement( 'input' ); + tokenInput.setAttribute( 'type', 'text' ); + tokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' ); + tokenInput.setAttribute( 'value', token ); + + form.append( tokenInput ); + + if ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) { + // Submit form, now containing the hidden card token field. + // form.submit(); — not working with test meta box + form.querySelector( 'input[name="' + e.submitter.name + '"]' ).click(); + } } ); } ); } - jQuery( document.body ).on( 'updated_checkout', initMollieComponents ); -} )(); + function setupWooCommerce() { + const checkoutForm = document.querySelector( 'form.woocommerce-checkout' ); + + if ( ! checkoutForm ) { + return; + } + + // Init components on updated checkout. + $( document.body ).on( 'updated_checkout', function( e ) { + initMollieComponents( checkoutForm ); + } ); + + const $form = $( checkoutForm ); + + // Prevent placing order, need to create token first. + $form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); + + // Re-enable placing order if card token has been added. + checkoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) { + e.preventDefault(); + + $form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); + + $form.submit(); + } ); + } + + function returnFalse() { + return false; + } + + // Init Mollie Components. + document.addEventListener( 'DOMContentLoaded', function( e ) { + initMollieComponents( 'form:not(.woocommerce-checkout)' ); + } ); + + setupWooCommerce(); +} )( jQuery ); diff --git a/src/ComponentsField.php b/src/ComponentsField.php index d9498cb..2030f8c 100644 --- a/src/ComponentsField.php +++ b/src/ComponentsField.php @@ -17,7 +17,15 @@ * HTML field class */ class ComponentsField extends Field { - public function setup() { + /** + * Mollie profile ID. + */ + private ?string $profile_id; + + /** + * Setup field. + */ + public function setup(): void { // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. \wp_register_script( 'pronamic-pay-mollie', @@ -27,6 +35,15 @@ public function setup() { false ); + $file = '../css/components.css'; + + \wp_register_style( + 'pronamic-pay-mollie-components', + \plugins_url( $file, __FILE__ ), + [], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + ); + $file = '../js/dist/components.js'; \wp_register_script( @@ -39,11 +56,11 @@ public function setup() { } /** - * Mollie profile ID. + * Set Mollie profile ID. + * + * @param string $profile_id Mollie profile ID. */ - private ?string $profile_id; - - public function set_profile_id( $profile_id ) { + public function set_profile_id( string $profile_id ): void { $this->profile_id = $profile_id; } @@ -55,113 +72,27 @@ public function set_profile_id( $profile_id ) { protected function get_html_attributes() : array { $attributes = parent::get_html_attributes(); + $locale_transformer = new LocaleTransformer(); + $attributes['class'] = $this->get_id(); $attributes['data-mollie-profile-id'] = $this->profile_id; + $attributes['data-mollie-locale'] = $locale_transformer->transform_wp_to_mollie( \get_locale() ); + $attributes['data-mollie-testmode'] = true; return $attributes; } /** * Render field. - * - * @return string */ public function render() : string { \wp_enqueue_script( 'pronamic-pay-mollie-components' ); + \wp_enqueue_style( 'pronamic-pay-mollie-components' ); + $element = new Element( 'div', $this->get_html_attributes() ); - return '' . $element->render(); + return $element->render(); } /** diff --git a/src/Gateway.php b/src/Gateway.php index 677bfe2..2ef3c58 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -122,7 +122,7 @@ function() { $field_consumer_iban->set_label( __( 'Account number (IBAN)', 'pronamic_ideal' ) ); $field_consumer_iban->meta_key = 'consumer_bank_details_iban'; - $field_mollie_components = new ComponentsField( 'pronamic_pay_mollie_components' ); + $field_mollie_components = new ComponentsField( 'pronamic_pay_mollie_card_token' ); $field_mollie_components->meta_key = 'mollie_card_token'; $cache_key = 'pronamic_pay_mollie_profile_id_' . \md5( (string) \wp_json_encode( $config ) ); @@ -668,6 +668,15 @@ private function get_payment_request( Payment $payment ) { $request->issuer = $payment->get_meta( 'issuer' ); } + // Card token. + if ( Methods::CREDITCARD === $request->method ) { + $card_token = $payment->get_meta( 'mollie_card_token' ); + + if ( ! empty( $card_token ) ) { + $request->card_token = $card_token; + } + } + // Billing email. $billing_email = ( null === $customer ) ? null : $customer->get_email(); From d704351a88d2dbcada1210a260a194f46e082bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Mon, 28 Nov 2022 12:19:01 +0100 Subject: [PATCH 03/29] Only render components field for WooCommerce and test meta box. --- src/ComponentsField.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/ComponentsField.php b/src/ComponentsField.php index 2030f8c..6ecc08c 100644 --- a/src/ComponentsField.php +++ b/src/ComponentsField.php @@ -86,6 +86,10 @@ protected function get_html_attributes() : array { * Render field. */ public function render() : string { + if ( ! $this->should_render() ) { + return ''; + } + \wp_enqueue_script( 'pronamic-pay-mollie-components' ); \wp_enqueue_style( 'pronamic-pay-mollie-components' ); @@ -95,6 +99,25 @@ public function render() : string { return $element->render(); } + /** + * Should render component. + * + * @return bool + */ + private function should_render(): bool { + $post_id = \get_the_ID(); + + $should_render = [ + // Payment gateway test meta box. + 'pronamic_gateway' === \get_post_type( $post_id ), + + // WooCommerce. + \did_action( 'woocommerce_checkout_order_review' ) || \did_action( 'woocommerce_checkout_update_order_review' ), + ]; + + return \in_array( true, $should_render, true ); + } + /** * Serialize to JSON. */ From 59bd4739904335f6083ba32bae5d6221c178dae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Mon, 28 Nov 2022 12:20:04 +0100 Subject: [PATCH 04/29] Update components field script and style. --- .sass-lint.yml | 71 ++++++++++++++++++++++ css/components.css | 87 +++++++++++++++++++-------- css/components.css.map | 1 + js/dist/components.js | 34 +++++++---- js/dist/components.js.bak | 122 ++++++++++++++++++++++++++++++++++++++ js/dist/components.js.map | 2 +- js/src/components.js | 42 +++++++++---- package.json | 19 ++++-- postcss.config.js | 7 +++ scss/components.scss | 84 ++++++++++++++++++++++++++ 10 files changed, 415 insertions(+), 54 deletions(-) create mode 100644 .sass-lint.yml create mode 100644 css/components.css.map create mode 100644 js/dist/components.js.bak create mode 100644 postcss.config.js create mode 100644 scss/components.scss diff --git a/.sass-lint.yml b/.sass-lint.yml new file mode 100644 index 0000000..a1260cc --- /dev/null +++ b/.sass-lint.yml @@ -0,0 +1,71 @@ +files: + include: 'scss/**/*.scss' +rules: + indentation: + - 1 + - size: 'tab' + class-name-format: + - 1 + - ignore: + - column-pronamic_payment_status + - column-pronamic_payment_subscription + - column-pronamic_subscription_status + - post-type-pronamic_payment + - post-type-pronamic_pay_subscr + nesting-depth: + - 1 + - max-depth: 3 + no-ids: + - 0 + property-sort-order: + - 1 + - order: + - background + - background-color + - background-image + - background-repeat + - border + - border-top + - border-top-width + - border-right + - border-right-color + - border-right-width + - border-bottom + - border-bottom-width + - border-left + - border-left-width + - border-radius + - border-spacing + - box-shadow + - box-sizing + - clear + - clip + - color + - content + - cursor + - display + - float + - font-family + - font-size + - font-style + - font-weight + - line-height + - list-style + - margin + - margin-top + - margin-right + - margin-bottom + - margin-left + - max-width + - padding + - outline + - overflow + - position + - top + - right + - bottom + - left + - speak + - text-indent + - width + - height diff --git a/css/components.css b/css/components.css index 61ec1a9..aa2230a 100644 --- a/css/components.css +++ b/css/components.css @@ -1,30 +1,67 @@ -body .pronamic_pay_mollie_card_token label { - display: block; +.pronamic_pay_mollie_card_token .mollie-components { + display: grid; + grid-auto-flow: row; + grid-template-columns: 2fr 1fr 1fr; + gap: 10px; + align-items: end; + max-width: 500px; } - -.mollie-component { - margin-top: .5rem; - padding: 10px; - font-weight: 500; - border: 1px solid transparent; - border-radius: 4px; - background: #fff; - box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); - transition: 0.3s border-color cubic-bezier(0.4, 0, 0.2, 1); - max-width: 500px; +.pronamic_pay_mollie_card_token .mollie-components label { + display: block; } - -.mollie-component.has-focus { - border-color: #0077ff; +.pronamic_pay_mollie_card_token .mollie-components label:first-child { + grid-column: 1/4; } - -.mollie-component.is-invalid { - border-color: #ff1717; +.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(2), .pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(3), .pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(4) { + grid-row: 3; } - -.field-error { - margin: 4px 0 1rem 0; - font-size: .8em; - font-weight: 400; - color: #ff1717; +.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(2) { + grid-column: 1; + min-width: 250px; +} +.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(3) { + grid-column: 2; + min-width: 70px; +} +.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(4) { + grid-column: 3; + min-width: 70px; +} +.pronamic_pay_mollie_card_token .mollie-components label.is-invalid + .field-error { + display: block; +} +.pronamic_pay_mollie_card_token .mollie-components .mollie-component { + padding: 5px; + font-weight: 500; + background: #ffffff; + border-bottom: 2px solid #aaaaaa; + transition: 0.3s border-color ease; } +.pronamic_pay_mollie_card_token .mollie-components .mollie-component.has-focus { + border-bottom-color: #0077ff; +} +.pronamic_pay_mollie_card_token .mollie-components .mollie-component.is-invalid { + border-bottom-color: #ff1717; +} +.pronamic_pay_mollie_card_token .mollie-components .field-error { + display: none; + grid-column: 1/4; + grid-row: 4; + font-size: 0.8em; + font-weight: 400; + color: #ff1717; +} +.pronamic_pay_mollie_card_token .mollie-components .field-error:first-of-type { + grid-row: 2; +} +.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(2) { + grid-row: 4; +} +.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(3) { + grid-row: 5; +} +.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(4) { + grid-row: 6; +} + +/*# sourceMappingURL=components.css.map */ diff --git a/css/components.css.map b/css/components.css.map new file mode 100644 index 0000000..8c1c362 --- /dev/null +++ b/css/components.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../scss/components.scss"],"names":[],"mappings":"AACC;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAEC;EACA;;AAGD;EAGC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC","file":"components.css"} \ No newline at end of file diff --git a/js/dist/components.js b/js/dist/components.js index 227a6c9..c506ef3 100644 --- a/js/dist/components.js +++ b/js/dist/components.js @@ -1,5 +1,4 @@ /* global Mollie */ - 'use strict'; /** @@ -9,20 +8,20 @@ */ ($ => { const components = [{ - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, { id: 'card-holder', label: 'Card Holder', component: 'cardHolder' + }, { + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' }, { id: 'expiry-date', - label: 'Expiry Date', + label: 'Expiry', component: 'expiryDate' }, { id: 'verification-code', - label: 'Verification Code', + label: 'CVC', component: 'verificationCode' }]; function initMollieComponents(forms) { @@ -47,6 +46,9 @@ locale: data['mollie-locale'] ?? null, testmode: "mollie-testmode" in data }); + let componentsContainer = document.createElement('div'); + componentsContainer.classList.add('mollie-components'); + $cardTokenElement.append(componentsContainer); components.forEach(component => { // Label. let label = document.createElement('label'); @@ -56,13 +58,14 @@ // Component container. let field = document.createElement('div'); field.setAttribute('id', component.id); + label.append(field); // Error. let error = document.createElement('div'); error.setAttribute('id', component.id + '-error'); error.setAttribute('role', 'alert'); error.classList.add('field-error'); - $cardTokenElement.append(label, field, error); + componentsContainer.append(label, error); // Create and mount component. let mollieComponent = mollie.createComponent(component.component); @@ -70,7 +73,16 @@ // Handle errors. mollieComponent.addEventListener('change', event => { - error.textContent = event.error && event.touched ? event.error : ''; + // Add error. + if (event.error && event.touched) { + error.textContent = event.error; + label.classList.add('is-invalid'); + return; + } + + // Remove error. + error.textContent = ''; + label.classList.remove('is-invalid'); }); }); @@ -95,7 +107,7 @@ // Add token to form. const tokenInput = document.createElement('input'); - tokenInput.setAttribute('type', 'text'); + tokenInput.setAttribute('type', 'hidden'); tokenInput.setAttribute('name', 'pronamic_pay_mollie_card_token'); tokenInput.setAttribute('value', token); form.append(tokenInput); @@ -104,7 +116,7 @@ }))) { // Submit form, now containing the hidden card token field. // form.submit(); — not working with test meta box - form.querySelector('input[name="' + e.submitter.name + '"]').click(); + form.querySelector('[name="' + e.submitter.name + '"]').click(); } }); }); diff --git a/js/dist/components.js.bak b/js/dist/components.js.bak new file mode 100644 index 0000000..ad29dcf --- /dev/null +++ b/js/dist/components.js.bak @@ -0,0 +1,122 @@ +/* global Mollie */ + +'use strict'; + +/** + * Mollie Components. + * + * @link https://docs.mollie.com/components/overview + */ +($ => { + const componentItems = [{ + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, { + id: 'expiry-date', + label: 'Expiry Date', + component: 'expiryDate' + }, { + id: 'verification-code', + label: 'Verification Code', + component: 'verificationCode' + }]; + function returnFalse() { + return false; + } + function initMollieComponents() { + const $elements = document.querySelectorAll('.pronamic_pay_mollie_card_token'); + $elements.forEach($element => { + const data = $element.dataset; + if (!("mollie-profile-id" in data)) { + throw new Error('No Mollie profile ID in element dataset. Unable to load Mollie Components.'); + return; + } + + // Initialize Mollie object. + const mollie = Mollie(data['mollie-profile-id'], { + locale: data['mollie-locale'] ?? null, + testmode: "mollie-testmode" in data + }); + + // Create components. + componentItems.forEach(item => { + // Field label. + let label = document.createElement('label'); + label.setAttribute('for', item.id); + label.innerText = item.label; + + // Field container. + let field = document.createElement('div'); + field.setAttribute('id', item.id); + + // Field error. + let error = document.createElement('div'); + error.setAttribute('id', item.id + '-error'); + error.setAttribute('role', 'alert'); + error.classList.add('field-error'); + $element.append(label, field, error); + + // Create and mount component. + let component = mollie.createComponent(item.component); + component.mount('#' + item.id); + + // Handling errors. + component.addEventListener('change', event => { + error.textContent = event.error && event.touched ? event.error : ''; + }); + }); + + // Get token on form submit. + let form = $element.closest('form'), + $form = $(form); + + // Prevent placing order, need to create token first. + $form.on('checkout_place_order_pronamic_pay_credit_card', returnFalse); + + // Create Mollie token on checkout submit. + $form.on('submit', async e => { + e.preventDefault(); + + // Check card token input. + let cardTokenInput = form.querySelector('input[name="pronamic_pay_mollie_card_token"]'); + if (cardTokenInput) { + return; + } + + // Create Mollie token. + const { + token, + error + } = await mollie.createToken(); + if (error) { + throw new Error(error.message || ''); + } + + // Add token to the form. + const tokenInput = document.createElement('input'); + tokenInput.setAttribute('type', 'hidden'); + tokenInput.setAttribute('name', 'pronamic_pay_mollie_card_token'); + tokenInput.setAttribute('value', token); + form.append(tokenInput); + + // Re-enable placing order again. + $form.off('checkout_place_order_pronamic_pay_credit_card', returnFalse); + + // Submit form, now containing the hidden card token field. + $form.submit(); + }); + }); + } + + // Init Mollie Components. + document.addEventListener('DOMContentLoaded', initMollieComponents); + + // WooCommerce support. + $(document.body).on('updated_checkout', initMollieComponents); +})(jQuery); +//# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/js/dist/components.js.map b/js/dist/components.js.map index fe6747a..0ff570e 100644 --- a/js/dist/components.js.map +++ b/js/dist/components.js.map @@ -1 +1 @@ -{"version":3,"file":"components.js","names":["$","components","id","label","component","initMollieComponents","forms","$cardTokenElements","document","querySelectorAll","forEach","$cardTokenElement","data","dataset","Error","mollie","Mollie","locale","testmode","createElement","setAttribute","innerText","field","error","classList","add","append","mollieComponent","createComponent","mount","addEventListener","event","textContent","touched","form","closest","e","cardTokenInput","querySelector","preventDefault","token","createToken","message","tokenInput","dispatchEvent","Event","cancelable","submitter","name","click","setupWooCommerce","checkoutForm","body","on","$form","returnFalse","off","submit","jQuery"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( ( $ ) => {\n\tconst components = [\n\t\t{\n\t\t\tid: 'card-number',\n\t\t\tlabel: 'Card Number',\n\t\t\tcomponent: 'cardNumber'\n\t\t},\n\t\t{\n\t\t\tid: 'card-holder',\n\t\t\tlabel: 'Card Holder',\n\t\t\tcomponent: 'cardHolder'\n\t\t},\n\t\t{\n\t\t\tid: 'expiry-date',\n\t\t\tlabel: 'Expiry Date',\n\t\t\tcomponent: 'expiryDate'\n\t\t},\n\t\t{\n\t\t\tid: 'verification-code',\n\t\t\tlabel: 'Verification Code',\n\t\t\tcomponent: 'verificationCode'\n\t\t}\n\t];\n\n\tfunction initMollieComponents( forms ) {\n\t\tlet $cardTokenElements;\n\n\t\tif ( typeof forms === 'string' ) {\n\t\t\t$cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' );\n\t\t} else {\n\t\t\t$cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' );\n\t\t}\n\n\t\t$cardTokenElements.forEach( ( $cardTokenElement ) => {\n\t\t\t// Create components.\n\t\t\tconst data = $cardTokenElement.dataset;\n\n\t\t\t// Check required Mollie profile ID.\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tcomponents.forEach( ( component ) => {\n\t\t\t\t// Label.\n\t\t\t\tlet label = document.createElement( 'label' );\n\t\t\t\tlabel.setAttribute( 'for', component.id );\n\t\t\t\tlabel.innerText = component.label;\n\n\t\t\t\t// Component container.\n\t\t\t\tlet field = document.createElement( 'div' );\n\t\t\t\tfield.setAttribute( 'id', component.id );\n\n\t\t\t\t// Error.\n\t\t\t\tlet error = document.createElement( 'div' );\n\t\t\t\terror.setAttribute( 'id', component.id + '-error' );\n\t\t\t\terror.setAttribute( 'role', 'alert' );\n\t\t\t\terror.classList.add( 'field-error' );\n\n\t\t\t\t$cardTokenElement.append( label, field, error );\n\n\t\t\t\t// Create and mount component.\n\t\t\t\tlet mollieComponent = mollie.createComponent( component.component );\n\n\t\t\t\tmollieComponent.mount( '#' + component.id );\n\n\t\t\t\t// Handle errors.\n\t\t\t\tmollieComponent.addEventListener( 'change', ( event ) => {\n\t\t\t\t\terror.textContent = event.error && event.touched ? event.error : '';\n\t\t\t\t} );\n\t\t\t} );\n\n\t\t\t// Create Mollie token on checkout submit.\n\t\t\tconst form = $cardTokenElement.closest( 'form' );\n\n\t\t\tform.addEventListener( 'submit', async ( e ) => {\n\t\t\t\t// Check existing card token input.\n\t\t\t\tlet cardTokenInput = form.querySelector( 'input[name=\"pronamic_pay_mollie_card_token\"]' );\n\n\t\t\t\tif ( cardTokenInput ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Create token.\n\t\t\t\tconst { token, error } = await mollie.createToken();\n\n\t\t\t\tif ( error ) {\n\t\t\t\t\tthrow new Error( error.message || '' );\n\t\t\t\t}\n\n\t\t\t\t// Add token to form.\n\t\t\t\tconst tokenInput = document.createElement( 'input' );\n\t\t\t\ttokenInput.setAttribute( 'type', 'text' );\n\t\t\t\ttokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' );\n\t\t\t\ttokenInput.setAttribute( 'value', token );\n\n\t\t\t\tform.append( tokenInput );\n\n\t\t\t\tif ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) {\n\t\t\t\t\t// Submit form, now containing the hidden card token field.\n\t\t\t\t\t// form.submit(); — not working with test meta box\n\t\t\t\t\tform.querySelector( 'input[name=\"' + e.submitter.name + '\"]' ).click();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\tfunction setupWooCommerce() {\n\t\tconst checkoutForm = document.querySelector( 'form.woocommerce-checkout' );\n\n\t\tif ( ! checkoutForm ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Init components on updated checkout.\n\t\t$( document.body ).on( 'updated_checkout', function( e ) {\n\t\t\tinitMollieComponents( checkoutForm );\n\t\t} );\n\n\t\tconst $form = $( checkoutForm );\n\n\t\t// Prevent placing order, need to create token first.\n\t\t$form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t// Re-enable placing order if card token has been added.\n\t\tcheckoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) {\n\t\t\te.preventDefault();\n\n\t\t\t$form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t\t$form.submit();\n\t\t} );\n\t}\n\n\tfunction returnFalse() {\n\t\treturn false;\n\t}\n\n\t// Init Mollie Components.\n\tdocument.addEventListener( 'DOMContentLoaded', function( e ) {\n\t\tinitMollieComponents( 'form:not(.woocommerce-checkout)' );\n\t} );\n\n\tsetupWooCommerce();\n} )( jQuery );\n"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"components.js","names":["$","components","id","label","component","initMollieComponents","forms","$cardTokenElements","document","querySelectorAll","forEach","$cardTokenElement","data","dataset","Error","mollie","Mollie","locale","testmode","componentsContainer","createElement","classList","add","append","setAttribute","innerText","field","error","mollieComponent","createComponent","mount","addEventListener","event","touched","textContent","remove","form","closest","e","cardTokenInput","querySelector","preventDefault","token","createToken","message","tokenInput","dispatchEvent","Event","cancelable","submitter","name","click","setupWooCommerce","checkoutForm","body","on","$form","returnFalse","off","submit","jQuery"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( ( $ ) => {\n\tconst components = [\n\t\t{\n\t\t\tid: 'card-holder',\n\t\t\tlabel: 'Card Holder',\n\t\t\tcomponent: 'cardHolder'\n\t\t},\n\t\t{\n\t\t\tid: 'card-number',\n\t\t\tlabel: 'Card Number',\n\t\t\tcomponent: 'cardNumber'\n\t\t},\n\t\t{\n\t\t\tid: 'expiry-date',\n\t\t\tlabel: 'Expiry',\n\t\t\tcomponent: 'expiryDate'\n\t\t},\n\t\t{\n\t\t\tid: 'verification-code',\n\t\t\tlabel: 'CVC',\n\t\t\tcomponent: 'verificationCode'\n\t\t}\n\t];\n\n\tfunction initMollieComponents( forms ) {\n\t\tlet $cardTokenElements;\n\n\t\tif ( typeof forms === 'string' ) {\n\t\t\t$cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' );\n\t\t} else {\n\t\t\t$cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' );\n\t\t}\n\n\t\t$cardTokenElements.forEach( ( $cardTokenElement ) => {\n\t\t\t// Create components.\n\t\t\tconst data = $cardTokenElement.dataset;\n\n\t\t\t// Check required Mollie profile ID.\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tlet componentsContainer = document.createElement( 'div' );\n\t\t\tcomponentsContainer.classList.add( 'mollie-components' );\n\n\t\t\t$cardTokenElement.append( componentsContainer );\n\n\t\t\tcomponents.forEach( ( component ) => {\n\t\t\t\t// Label.\n\t\t\t\tlet label = document.createElement( 'label' );\n\t\t\t\tlabel.setAttribute( 'for', component.id );\n\t\t\t\tlabel.innerText = component.label;\n\n\t\t\t\t// Component container.\n\t\t\t\tlet field = document.createElement( 'div' );\n\t\t\t\tfield.setAttribute( 'id', component.id );\n\n\t\t\t\tlabel.append( field );\n\n\t\t\t\t// Error.\n\t\t\t\tlet error = document.createElement( 'div' );\n\t\t\t\terror.setAttribute( 'id', component.id + '-error' );\n\t\t\t\terror.setAttribute( 'role', 'alert' );\n\t\t\t\terror.classList.add( 'field-error' );\n\n\t\t\t\tcomponentsContainer.append( label, error );\n\n\t\t\t\t// Create and mount component.\n\t\t\t\tlet mollieComponent = mollie.createComponent( component.component );\n\n\t\t\t\tmollieComponent.mount( '#' + component.id );\n\n\t\t\t\t// Handle errors.\n\t\t\t\tmollieComponent.addEventListener( 'change', ( event ) => {\n\t\t\t\t\t// Add error.\n\t\t\t\t\tif ( event.error && event.touched ) {\n\t\t\t\t\t\terror.textContent = event.error;\n\n\t\t\t\t\t\tlabel.classList.add( 'is-invalid' );\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Remove error.\n\t\t\t\t\terror.textContent = '';\n\n\t\t\t\t\tlabel.classList.remove( 'is-invalid' );\n\t\t\t\t} );\n\t\t\t} );\n\n\t\t\t// Create Mollie token on checkout submit.\n\t\t\tconst form = $cardTokenElement.closest( 'form' );\n\n\t\t\tform.addEventListener( 'submit', async ( e ) => {\n\t\t\t\t// Check existing card token input.\n\t\t\t\tlet cardTokenInput = form.querySelector( 'input[name=\"pronamic_pay_mollie_card_token\"]' );\n\n\t\t\t\tif ( cardTokenInput ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Create token.\n\t\t\t\tconst { token, error } = await mollie.createToken();\n\n\t\t\t\tif ( error ) {\n\t\t\t\t\tthrow new Error( error.message || '' );\n\t\t\t\t}\n\n\t\t\t\t// Add token to form.\n\t\t\t\tconst tokenInput = document.createElement( 'input' );\n\t\t\t\ttokenInput.setAttribute( 'type', 'hidden' );\n\t\t\t\ttokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' );\n\t\t\t\ttokenInput.setAttribute( 'value', token );\n\n\t\t\t\tform.append( tokenInput );\n\n\t\t\t\tif ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) {\n\t\t\t\t\t// Submit form, now containing the hidden card token field.\n\t\t\t\t\t// form.submit(); — not working with test meta box\n\t\t\t\t\tform.querySelector( '[name=\"' + e.submitter.name + '\"]' ).click();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\tfunction setupWooCommerce() {\n\t\tconst checkoutForm = document.querySelector( 'form.woocommerce-checkout' );\n\n\t\tif ( ! checkoutForm ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Init components on updated checkout.\n\t\t$( document.body ).on( 'updated_checkout', function( e ) {\n\t\t\tinitMollieComponents( checkoutForm );\n\t\t} );\n\n\t\tconst $form = $( checkoutForm );\n\n\t\t// Prevent placing order, need to create token first.\n\t\t$form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t// Re-enable placing order if card token has been added.\n\t\tcheckoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) {\n\t\t\te.preventDefault();\n\n\t\t\t$form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t\t$form.submit();\n\t\t} );\n\t}\n\n\tfunction returnFalse() {\n\t\treturn false;\n\t}\n\n\t// Init Mollie Components.\n\tdocument.addEventListener( 'DOMContentLoaded', function( e ) {\n\t\tinitMollieComponents( 'form:not(.woocommerce-checkout)' );\n\t} );\n\n\tsetupWooCommerce();\n} )( jQuery );\n"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/components.js b/js/src/components.js index 0f1cbed..af451b5 100644 --- a/js/src/components.js +++ b/js/src/components.js @@ -1,5 +1,4 @@ /* global Mollie */ - 'use strict'; /** @@ -9,24 +8,24 @@ */ ( ( $ ) => { const components = [ - { - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, { id: 'card-holder', label: 'Card Holder', component: 'cardHolder' }, + { + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, { id: 'expiry-date', - label: 'Expiry Date', + label: 'Expiry', component: 'expiryDate' }, { id: 'verification-code', - label: 'Verification Code', + label: 'CVC', component: 'verificationCode' } ]; @@ -60,6 +59,11 @@ } ); + let componentsContainer = document.createElement( 'div' ); + componentsContainer.classList.add( 'mollie-components' ); + + $cardTokenElement.append( componentsContainer ); + components.forEach( ( component ) => { // Label. let label = document.createElement( 'label' ); @@ -70,13 +74,15 @@ let field = document.createElement( 'div' ); field.setAttribute( 'id', component.id ); + label.append( field ); + // Error. let error = document.createElement( 'div' ); error.setAttribute( 'id', component.id + '-error' ); error.setAttribute( 'role', 'alert' ); error.classList.add( 'field-error' ); - $cardTokenElement.append( label, field, error ); + componentsContainer.append( label, error ); // Create and mount component. let mollieComponent = mollie.createComponent( component.component ); @@ -85,7 +91,19 @@ // Handle errors. mollieComponent.addEventListener( 'change', ( event ) => { - error.textContent = event.error && event.touched ? event.error : ''; + // Add error. + if ( event.error && event.touched ) { + error.textContent = event.error; + + label.classList.add( 'is-invalid' ); + + return; + } + + // Remove error. + error.textContent = ''; + + label.classList.remove( 'is-invalid' ); } ); } ); @@ -111,7 +129,7 @@ // Add token to form. const tokenInput = document.createElement( 'input' ); - tokenInput.setAttribute( 'type', 'text' ); + tokenInput.setAttribute( 'type', 'hidden' ); tokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' ); tokenInput.setAttribute( 'value', token ); @@ -120,7 +138,7 @@ if ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) { // Submit form, now containing the hidden card token field. // form.submit(); — not working with test meta box - form.querySelector( 'input[name="' + e.submitter.name + '"]' ).click(); + form.querySelector( '[name="' + e.submitter.name + '"]' ).click(); } } ); } ); diff --git a/package.json b/package.json index 0c6ae72..2ea6618 100644 --- a/package.json +++ b/package.json @@ -32,17 +32,26 @@ }, "homepage": "http://www.wp-pay.org/gateways/mollie/", "devDependencies": { - "@babel/cli": "^7.15.4", - "@babel/core": "^7.15.5", - "@babel/preset-env": "^7.15.6", - "@wordpress/env": "^5.4.0", + "@babel/cli": "^7.19.3", + "@babel/core": "^7.20.2", + "@babel/preset-env": "^7.20.2", + "@wordpress/env": "^5.7.0", + "autoprefixer": "^10.4.13", + "cssnano": "^5.1.14", "eslint": "^7.32.0", "eslint-plugin-json": "^3.1.0", - "npm-run-all": "^4.1.5" + "npm-run-all": "^4.1.5", + "postcss": "^8.4.19", + "postcss-cli": "^10.0.0", + "postcss-eol": "0.0.8", + "sass": "^1.56.1", + "sass-lint": "^1.13.1" }, "scripts": { "babel": "babel ./js/src -d ./js/dist --source-maps", "eslint": "eslint . --ext .json --ext .js", + "sass": "sass scss:css", + "sass-lint": "sass-lint --verbose", "start": "wp-env start --xdebug && npm run setup && npm run login", "setup": "npm-run-all setup-*", "setup-mollie-api-key": "wp-env run cli wp config set MOLLIE_API_KEY $MOLLIE_API_KEY", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..72d2d27 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require( 'cssnano' )( { + preset: 'default', + } ), + ], +}; \ No newline at end of file diff --git a/scss/components.scss b/scss/components.scss new file mode 100644 index 0000000..4ca7726 --- /dev/null +++ b/scss/components.scss @@ -0,0 +1,84 @@ +.pronamic_pay_mollie_card_token { + .mollie-components { + display: grid; + grid-auto-flow: row; + grid-template-columns: 2fr 1fr 1fr; + gap: 10px; + align-items: end; + max-width: 500px; + + label { + display: block; + + &:first-child { + grid-column: 1/4; + } + + &:nth-of-type(2), + &:nth-of-type(3), + &:nth-of-type(4) { + grid-row: 3; + } + + &:nth-of-type(2) { + grid-column: 1; + min-width: 250px; + } + + &:nth-of-type(3) { + grid-column: 2; + min-width: 70px; + } + + &:nth-of-type(4) { + grid-column: 3; + min-width: 70px; + } + + &.is-invalid + .field-error { + display: block; + } + } + + .mollie-component { + padding: 5px; + font-weight: 500; + background: #ffffff; + border-bottom: 2px solid #aaaaaa; + transition: 0.3s border-color ease; + + &.has-focus { + border-bottom-color: #0077ff; + } + + &.is-invalid { + border-bottom-color: #ff1717; + } + } + + .field-error { + display: none; + grid-column: 1/4; + grid-row: 4; + font-size: .8em; + font-weight: 400; + color: #ff1717; + + &:first-of-type { + grid-row: 2; + } + + &:nth-of-type(2) { + grid-row: 4; + } + + &:nth-of-type(3) { + grid-row: 5; + } + + &:nth-of-type(4) { + grid-row: 6; + } + } + } +} From bf47a2efcc6859b6092470e9a7c3cafe6fb20dd5 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:37:43 +0100 Subject: [PATCH 05/29] Update ComponentsField.php --- src/ComponentsField.php | 59 +++++++++++------------------------------ 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/src/ComponentsField.php b/src/ComponentsField.php index 6ecc08c..e30bd57 100644 --- a/src/ComponentsField.php +++ b/src/ComponentsField.php @@ -65,57 +65,28 @@ public function set_profile_id( string $profile_id ): void { } /** - * Get HTML attributes. - * - * @return array - */ - protected function get_html_attributes() : array { - $attributes = parent::get_html_attributes(); - - $locale_transformer = new LocaleTransformer(); - - $attributes['class'] = $this->get_id(); - $attributes['data-mollie-profile-id'] = $this->profile_id; - $attributes['data-mollie-locale'] = $locale_transformer->transform_wp_to_mollie( \get_locale() ); - $attributes['data-mollie-testmode'] = true; - - return $attributes; - } - - /** - * Render field. + * Get element. + * + * @return Element|null */ - public function render() : string { - if ( ! $this->should_render() ) { - return ''; - } - + protected function get_element() { \wp_enqueue_script( 'pronamic-pay-mollie-components' ); \wp_enqueue_style( 'pronamic-pay-mollie-components' ); - $element = new Element( 'div', $this->get_html_attributes() ); - - return $element->render(); - } - - /** - * Should render component. - * - * @return bool - */ - private function should_render(): bool { - $post_id = \get_the_ID(); - - $should_render = [ - // Payment gateway test meta box. - 'pronamic_gateway' === \get_post_type( $post_id ), + $locale_transformer = new LocaleTransformer(); - // WooCommerce. - \did_action( 'woocommerce_checkout_order_review' ) || \did_action( 'woocommerce_checkout_update_order_review' ), - ]; + $element = new Element( + 'div', + [ + 'class' => $this->get_id(), + 'data-mollie-profile-id' => $this->profile_id, + 'data-mollie-locale' => $locale_transformer->transform_wp_to_mollie( \get_locale() ), + 'data-mollie-testmode' => true, + ] + ); - return \in_array( true, $should_render, true ); + return $element; } /** From 27e08e99ade039b0209d735ea4c992201c3f5d39 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:52:45 +0100 Subject: [PATCH 06/29] Script didn't work for me, fix it. --- js/dist/components.js | 202 ++++++++++++++++++++++++------------------ js/src/components.js | 8 +- 2 files changed, 119 insertions(+), 91 deletions(-) diff --git a/js/dist/components.js b/js/dist/components.js index c506ef3..e15f6f3 100644 --- a/js/dist/components.js +++ b/js/dist/components.js @@ -6,151 +6,179 @@ * * @link https://docs.mollie.com/components/overview */ -($ => { - const components = [{ - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, { - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, { - id: 'expiry-date', - label: 'Expiry', - component: 'expiryDate' - }, { - id: 'verification-code', - label: 'CVC', - component: 'verificationCode' - }]; - function initMollieComponents(forms) { +( ( $ ) => { + const components = [ + { + id: 'card-holder', + label: 'Card Holder', + component: 'cardHolder' + }, + { + id: 'card-number', + label: 'Card Number', + component: 'cardNumber' + }, + { + id: 'expiry-date', + label: 'Expiry', + component: 'expiryDate' + }, + { + id: 'verification-code', + label: 'CVC', + component: 'verificationCode' + } + ]; + + function initMollieComponents( forms ) { let $cardTokenElements; - if (typeof forms === 'string') { - $cardTokenElements = document.querySelectorAll(forms + ' .pronamic_pay_mollie_card_token'); + + if ( typeof forms === 'string' ) { + $cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' ); } else { - $cardTokenElements = forms.querySelectorAll('.pronamic_pay_mollie_card_token'); + $cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' ); } - $cardTokenElements.forEach($cardTokenElement => { + + $cardTokenElements.forEach( ( $cardTokenElement ) => { // Create components. const data = $cardTokenElement.dataset; // Check required Mollie profile ID. - if (!("mollie-profile-id" in data)) { - throw new Error('No Mollie profile ID in element dataset. Unable to load Mollie Components.'); + if ( ! $cardTokenElement.dataset.mollieProfileId ) { + throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); + return; } // Initialize Mollie object. - const mollie = Mollie(data['mollie-profile-id'], { - locale: data['mollie-locale'] ?? null, - testmode: "mollie-testmode" in data - }); - let componentsContainer = document.createElement('div'); - componentsContainer.classList.add('mollie-components'); - $cardTokenElement.append(componentsContainer); - components.forEach(component => { + const mollie = Mollie( + $cardTokenElement.dataset.mollieProfileId, + { + locale: $cardTokenElement.dataset.mollieLocale ?? null, + testmode: '1' === $cardTokenElement.dataset.mollieTestmode, + } + ); + + let componentsContainer = document.createElement( 'div' ); + componentsContainer.classList.add( 'mollie-components' ); + + $cardTokenElement.append( componentsContainer ); + + components.forEach( ( component ) => { // Label. - let label = document.createElement('label'); - label.setAttribute('for', component.id); + let label = document.createElement( 'label' ); + label.setAttribute( 'for', component.id ); label.innerText = component.label; // Component container. - let field = document.createElement('div'); - field.setAttribute('id', component.id); - label.append(field); + let field = document.createElement( 'div' ); + field.setAttribute( 'id', component.id ); + + label.append( field ); // Error. - let error = document.createElement('div'); - error.setAttribute('id', component.id + '-error'); - error.setAttribute('role', 'alert'); - error.classList.add('field-error'); - componentsContainer.append(label, error); + let error = document.createElement( 'div' ); + error.setAttribute( 'id', component.id + '-error' ); + error.setAttribute( 'role', 'alert' ); + error.classList.add( 'field-error' ); + + componentsContainer.append( label, error ); // Create and mount component. - let mollieComponent = mollie.createComponent(component.component); - mollieComponent.mount('#' + component.id); + let mollieComponent = mollie.createComponent( component.component ); + + mollieComponent.mount( '#' + component.id ); // Handle errors. - mollieComponent.addEventListener('change', event => { + mollieComponent.addEventListener( 'change', ( event ) => { // Add error. - if (event.error && event.touched) { + if ( event.error && event.touched ) { error.textContent = event.error; - label.classList.add('is-invalid'); + + label.classList.add( 'is-invalid' ); + return; } // Remove error. error.textContent = ''; - label.classList.remove('is-invalid'); - }); - }); + + label.classList.remove( 'is-invalid' ); + } ); + } ); // Create Mollie token on checkout submit. - const form = $cardTokenElement.closest('form'); - form.addEventListener('submit', async e => { + const form = $cardTokenElement.closest( 'form' ); + + form.addEventListener( 'submit', async ( e ) => { // Check existing card token input. - let cardTokenInput = form.querySelector('input[name="pronamic_pay_mollie_card_token"]'); - if (cardTokenInput) { + let cardTokenInput = form.querySelector( 'input[name="pronamic_pay_mollie_card_token"]' ); + + if ( cardTokenInput ) { return; } + e.preventDefault(); // Create token. - const { - token, - error - } = await mollie.createToken(); - if (error) { - throw new Error(error.message || ''); + const { token, error } = await mollie.createToken(); + + if ( error ) { + throw new Error( error.message || '' ); } // Add token to form. - const tokenInput = document.createElement('input'); - tokenInput.setAttribute('type', 'hidden'); - tokenInput.setAttribute('name', 'pronamic_pay_mollie_card_token'); - tokenInput.setAttribute('value', token); - form.append(tokenInput); - if (false !== form.dispatchEvent(new Event('pronamic_pay_mollie_components_card_token_added', { - cancelable: true - }))) { + const tokenInput = document.createElement( 'input' ); + tokenInput.setAttribute( 'type', 'hidden' ); + tokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' ); + tokenInput.setAttribute( 'value', token ); + + form.append( tokenInput ); + + if ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) { // Submit form, now containing the hidden card token field. // form.submit(); — not working with test meta box - form.querySelector('[name="' + e.submitter.name + '"]').click(); + form.querySelector( '[name="' + e.submitter.name + '"]' ).click(); } - }); - }); + } ); + } ); } + function setupWooCommerce() { - const checkoutForm = document.querySelector('form.woocommerce-checkout'); - if (!checkoutForm) { + const checkoutForm = document.querySelector( 'form.woocommerce-checkout' ); + + if ( ! checkoutForm ) { return; } // Init components on updated checkout. - $(document.body).on('updated_checkout', function (e) { - initMollieComponents(checkoutForm); - }); - const $form = $(checkoutForm); + $( document.body ).on( 'updated_checkout', function( e ) { + initMollieComponents( checkoutForm ); + } ); + + const $form = $( checkoutForm ); // Prevent placing order, need to create token first. - $form.on('checkout_place_order_pronamic_pay_credit_card', returnFalse); + $form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); // Re-enable placing order if card token has been added. - checkoutForm.addEventListener('pronamic_pay_mollie_components_card_token_added', function (e) { + checkoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) { e.preventDefault(); - $form.off('checkout_place_order_pronamic_pay_credit_card', returnFalse); + + $form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); + $form.submit(); - }); + } ); } + function returnFalse() { return false; } // Init Mollie Components. - document.addEventListener('DOMContentLoaded', function (e) { - initMollieComponents('form:not(.woocommerce-checkout)'); - }); + document.addEventListener( 'DOMContentLoaded', function( e ) { + initMollieComponents( 'form:not(.woocommerce-checkout)' ); + } ); + setupWooCommerce(); -})(jQuery); -//# sourceMappingURL=components.js.map \ No newline at end of file +} )( jQuery ); diff --git a/js/src/components.js b/js/src/components.js index af451b5..0021892 100644 --- a/js/src/components.js +++ b/js/src/components.js @@ -44,7 +44,7 @@ const data = $cardTokenElement.dataset; // Check required Mollie profile ID. - if ( ! ( "mollie-profile-id" in data ) ) { + if ( ! $cardTokenElement.dataset.mollieProfileId ) { throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); return; @@ -52,10 +52,10 @@ // Initialize Mollie object. const mollie = Mollie( - data['mollie-profile-id'], + $cardTokenElement.dataset.mollieProfileId, { - locale: data['mollie-locale'] ?? null, - testmode: ( "mollie-testmode" in data ), + locale: $cardTokenElement.dataset.mollieLocale ?? null, + testmode: '1' === $cardTokenElement.dataset.mollieTestmode, } ); From 79861d538a6477be75f920152aa24be219ef504e Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:17:31 +0100 Subject: [PATCH 07/29] Simplify Mollie card component. --- .wp-env.json | 1 + js/dist/components.js | 184 -------------------------------------- js/dist/components.js.bak | 122 ------------------------- js/dist/components.js.map | 1 - js/dist/mollie.min.js | 1 + js/src/components.js | 184 -------------------------------------- js/src/mollie.js | 56 ++++++++++++ package.json | 9 +- src/CardField.php | 148 ++++++++++++++++++++++++++++++ src/ComponentsField.php | 102 --------------------- src/Gateway.php | 49 +++++----- 11 files changed, 240 insertions(+), 617 deletions(-) delete mode 100644 js/dist/components.js delete mode 100644 js/dist/components.js.bak delete mode 100644 js/dist/components.js.map create mode 100644 js/dist/mollie.min.js delete mode 100644 js/src/components.js create mode 100644 js/src/mollie.js create mode 100644 src/CardField.php delete mode 100644 src/ComponentsField.php diff --git a/.wp-env.json b/.wp-env.json index e1b7d3c..47d8240 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -7,6 +7,7 @@ "https://downloads.wordpress.org/plugin/query-monitor.zip", "https://downloads.wordpress.org/plugin/log-http-requests.zip", "https://downloads.wordpress.org/plugin/one-time-login.zip", + "https://downloads.wordpress.org/plugin/woocommerce.zip" "https://downloads.wordpress.org/plugin/wp-plugin-dependencies.zip" ], "mappings": { diff --git a/js/dist/components.js b/js/dist/components.js deleted file mode 100644 index e15f6f3..0000000 --- a/js/dist/components.js +++ /dev/null @@ -1,184 +0,0 @@ -/* global Mollie */ -'use strict'; - -/** - * Mollie Components. - * - * @link https://docs.mollie.com/components/overview - */ -( ( $ ) => { - const components = [ - { - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, - { - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, - { - id: 'expiry-date', - label: 'Expiry', - component: 'expiryDate' - }, - { - id: 'verification-code', - label: 'CVC', - component: 'verificationCode' - } - ]; - - function initMollieComponents( forms ) { - let $cardTokenElements; - - if ( typeof forms === 'string' ) { - $cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' ); - } else { - $cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' ); - } - - $cardTokenElements.forEach( ( $cardTokenElement ) => { - // Create components. - const data = $cardTokenElement.dataset; - - // Check required Mollie profile ID. - if ( ! $cardTokenElement.dataset.mollieProfileId ) { - throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); - - return; - } - - // Initialize Mollie object. - const mollie = Mollie( - $cardTokenElement.dataset.mollieProfileId, - { - locale: $cardTokenElement.dataset.mollieLocale ?? null, - testmode: '1' === $cardTokenElement.dataset.mollieTestmode, - } - ); - - let componentsContainer = document.createElement( 'div' ); - componentsContainer.classList.add( 'mollie-components' ); - - $cardTokenElement.append( componentsContainer ); - - components.forEach( ( component ) => { - // Label. - let label = document.createElement( 'label' ); - label.setAttribute( 'for', component.id ); - label.innerText = component.label; - - // Component container. - let field = document.createElement( 'div' ); - field.setAttribute( 'id', component.id ); - - label.append( field ); - - // Error. - let error = document.createElement( 'div' ); - error.setAttribute( 'id', component.id + '-error' ); - error.setAttribute( 'role', 'alert' ); - error.classList.add( 'field-error' ); - - componentsContainer.append( label, error ); - - // Create and mount component. - let mollieComponent = mollie.createComponent( component.component ); - - mollieComponent.mount( '#' + component.id ); - - // Handle errors. - mollieComponent.addEventListener( 'change', ( event ) => { - // Add error. - if ( event.error && event.touched ) { - error.textContent = event.error; - - label.classList.add( 'is-invalid' ); - - return; - } - - // Remove error. - error.textContent = ''; - - label.classList.remove( 'is-invalid' ); - } ); - } ); - - // Create Mollie token on checkout submit. - const form = $cardTokenElement.closest( 'form' ); - - form.addEventListener( 'submit', async ( e ) => { - // Check existing card token input. - let cardTokenInput = form.querySelector( 'input[name="pronamic_pay_mollie_card_token"]' ); - - if ( cardTokenInput ) { - return; - } - - e.preventDefault(); - - // Create token. - const { token, error } = await mollie.createToken(); - - if ( error ) { - throw new Error( error.message || '' ); - } - - // Add token to form. - const tokenInput = document.createElement( 'input' ); - tokenInput.setAttribute( 'type', 'hidden' ); - tokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' ); - tokenInput.setAttribute( 'value', token ); - - form.append( tokenInput ); - - if ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) { - // Submit form, now containing the hidden card token field. - // form.submit(); — not working with test meta box - form.querySelector( '[name="' + e.submitter.name + '"]' ).click(); - } - } ); - } ); - } - - function setupWooCommerce() { - const checkoutForm = document.querySelector( 'form.woocommerce-checkout' ); - - if ( ! checkoutForm ) { - return; - } - - // Init components on updated checkout. - $( document.body ).on( 'updated_checkout', function( e ) { - initMollieComponents( checkoutForm ); - } ); - - const $form = $( checkoutForm ); - - // Prevent placing order, need to create token first. - $form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); - - // Re-enable placing order if card token has been added. - checkoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) { - e.preventDefault(); - - $form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); - - $form.submit(); - } ); - } - - function returnFalse() { - return false; - } - - // Init Mollie Components. - document.addEventListener( 'DOMContentLoaded', function( e ) { - initMollieComponents( 'form:not(.woocommerce-checkout)' ); - } ); - - setupWooCommerce(); -} )( jQuery ); diff --git a/js/dist/components.js.bak b/js/dist/components.js.bak deleted file mode 100644 index ad29dcf..0000000 --- a/js/dist/components.js.bak +++ /dev/null @@ -1,122 +0,0 @@ -/* global Mollie */ - -'use strict'; - -/** - * Mollie Components. - * - * @link https://docs.mollie.com/components/overview - */ -($ => { - const componentItems = [{ - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, { - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, { - id: 'expiry-date', - label: 'Expiry Date', - component: 'expiryDate' - }, { - id: 'verification-code', - label: 'Verification Code', - component: 'verificationCode' - }]; - function returnFalse() { - return false; - } - function initMollieComponents() { - const $elements = document.querySelectorAll('.pronamic_pay_mollie_card_token'); - $elements.forEach($element => { - const data = $element.dataset; - if (!("mollie-profile-id" in data)) { - throw new Error('No Mollie profile ID in element dataset. Unable to load Mollie Components.'); - return; - } - - // Initialize Mollie object. - const mollie = Mollie(data['mollie-profile-id'], { - locale: data['mollie-locale'] ?? null, - testmode: "mollie-testmode" in data - }); - - // Create components. - componentItems.forEach(item => { - // Field label. - let label = document.createElement('label'); - label.setAttribute('for', item.id); - label.innerText = item.label; - - // Field container. - let field = document.createElement('div'); - field.setAttribute('id', item.id); - - // Field error. - let error = document.createElement('div'); - error.setAttribute('id', item.id + '-error'); - error.setAttribute('role', 'alert'); - error.classList.add('field-error'); - $element.append(label, field, error); - - // Create and mount component. - let component = mollie.createComponent(item.component); - component.mount('#' + item.id); - - // Handling errors. - component.addEventListener('change', event => { - error.textContent = event.error && event.touched ? event.error : ''; - }); - }); - - // Get token on form submit. - let form = $element.closest('form'), - $form = $(form); - - // Prevent placing order, need to create token first. - $form.on('checkout_place_order_pronamic_pay_credit_card', returnFalse); - - // Create Mollie token on checkout submit. - $form.on('submit', async e => { - e.preventDefault(); - - // Check card token input. - let cardTokenInput = form.querySelector('input[name="pronamic_pay_mollie_card_token"]'); - if (cardTokenInput) { - return; - } - - // Create Mollie token. - const { - token, - error - } = await mollie.createToken(); - if (error) { - throw new Error(error.message || ''); - } - - // Add token to the form. - const tokenInput = document.createElement('input'); - tokenInput.setAttribute('type', 'hidden'); - tokenInput.setAttribute('name', 'pronamic_pay_mollie_card_token'); - tokenInput.setAttribute('value', token); - form.append(tokenInput); - - // Re-enable placing order again. - $form.off('checkout_place_order_pronamic_pay_credit_card', returnFalse); - - // Submit form, now containing the hidden card token field. - $form.submit(); - }); - }); - } - - // Init Mollie Components. - document.addEventListener('DOMContentLoaded', initMollieComponents); - - // WooCommerce support. - $(document.body).on('updated_checkout', initMollieComponents); -})(jQuery); -//# sourceMappingURL=components.js.map \ No newline at end of file diff --git a/js/dist/components.js.map b/js/dist/components.js.map deleted file mode 100644 index 0ff570e..0000000 --- a/js/dist/components.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"components.js","names":["$","components","id","label","component","initMollieComponents","forms","$cardTokenElements","document","querySelectorAll","forEach","$cardTokenElement","data","dataset","Error","mollie","Mollie","locale","testmode","componentsContainer","createElement","classList","add","append","setAttribute","innerText","field","error","mollieComponent","createComponent","mount","addEventListener","event","touched","textContent","remove","form","closest","e","cardTokenInput","querySelector","preventDefault","token","createToken","message","tokenInput","dispatchEvent","Event","cancelable","submitter","name","click","setupWooCommerce","checkoutForm","body","on","$form","returnFalse","off","submit","jQuery"],"sources":["../src/components.js"],"sourcesContent":["/* global Mollie */\n'use strict';\n\n/**\n * Mollie Components.\n *\n * @link https://docs.mollie.com/components/overview\n */\n( ( $ ) => {\n\tconst components = [\n\t\t{\n\t\t\tid: 'card-holder',\n\t\t\tlabel: 'Card Holder',\n\t\t\tcomponent: 'cardHolder'\n\t\t},\n\t\t{\n\t\t\tid: 'card-number',\n\t\t\tlabel: 'Card Number',\n\t\t\tcomponent: 'cardNumber'\n\t\t},\n\t\t{\n\t\t\tid: 'expiry-date',\n\t\t\tlabel: 'Expiry',\n\t\t\tcomponent: 'expiryDate'\n\t\t},\n\t\t{\n\t\t\tid: 'verification-code',\n\t\t\tlabel: 'CVC',\n\t\t\tcomponent: 'verificationCode'\n\t\t}\n\t];\n\n\tfunction initMollieComponents( forms ) {\n\t\tlet $cardTokenElements;\n\n\t\tif ( typeof forms === 'string' ) {\n\t\t\t$cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' );\n\t\t} else {\n\t\t\t$cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' );\n\t\t}\n\n\t\t$cardTokenElements.forEach( ( $cardTokenElement ) => {\n\t\t\t// Create components.\n\t\t\tconst data = $cardTokenElement.dataset;\n\n\t\t\t// Check required Mollie profile ID.\n\t\t\tif ( ! ( \"mollie-profile-id\" in data ) ) {\n\t\t\t\tthrow new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' );\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Initialize Mollie object.\n\t\t\tconst mollie = Mollie(\n\t\t\t\tdata['mollie-profile-id'],\n\t\t\t\t{\n\t\t\t\t\tlocale: data['mollie-locale'] ?? null,\n\t\t\t\t\ttestmode: ( \"mollie-testmode\" in data ),\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tlet componentsContainer = document.createElement( 'div' );\n\t\t\tcomponentsContainer.classList.add( 'mollie-components' );\n\n\t\t\t$cardTokenElement.append( componentsContainer );\n\n\t\t\tcomponents.forEach( ( component ) => {\n\t\t\t\t// Label.\n\t\t\t\tlet label = document.createElement( 'label' );\n\t\t\t\tlabel.setAttribute( 'for', component.id );\n\t\t\t\tlabel.innerText = component.label;\n\n\t\t\t\t// Component container.\n\t\t\t\tlet field = document.createElement( 'div' );\n\t\t\t\tfield.setAttribute( 'id', component.id );\n\n\t\t\t\tlabel.append( field );\n\n\t\t\t\t// Error.\n\t\t\t\tlet error = document.createElement( 'div' );\n\t\t\t\terror.setAttribute( 'id', component.id + '-error' );\n\t\t\t\terror.setAttribute( 'role', 'alert' );\n\t\t\t\terror.classList.add( 'field-error' );\n\n\t\t\t\tcomponentsContainer.append( label, error );\n\n\t\t\t\t// Create and mount component.\n\t\t\t\tlet mollieComponent = mollie.createComponent( component.component );\n\n\t\t\t\tmollieComponent.mount( '#' + component.id );\n\n\t\t\t\t// Handle errors.\n\t\t\t\tmollieComponent.addEventListener( 'change', ( event ) => {\n\t\t\t\t\t// Add error.\n\t\t\t\t\tif ( event.error && event.touched ) {\n\t\t\t\t\t\terror.textContent = event.error;\n\n\t\t\t\t\t\tlabel.classList.add( 'is-invalid' );\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Remove error.\n\t\t\t\t\terror.textContent = '';\n\n\t\t\t\t\tlabel.classList.remove( 'is-invalid' );\n\t\t\t\t} );\n\t\t\t} );\n\n\t\t\t// Create Mollie token on checkout submit.\n\t\t\tconst form = $cardTokenElement.closest( 'form' );\n\n\t\t\tform.addEventListener( 'submit', async ( e ) => {\n\t\t\t\t// Check existing card token input.\n\t\t\t\tlet cardTokenInput = form.querySelector( 'input[name=\"pronamic_pay_mollie_card_token\"]' );\n\n\t\t\t\tif ( cardTokenInput ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Create token.\n\t\t\t\tconst { token, error } = await mollie.createToken();\n\n\t\t\t\tif ( error ) {\n\t\t\t\t\tthrow new Error( error.message || '' );\n\t\t\t\t}\n\n\t\t\t\t// Add token to form.\n\t\t\t\tconst tokenInput = document.createElement( 'input' );\n\t\t\t\ttokenInput.setAttribute( 'type', 'hidden' );\n\t\t\t\ttokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' );\n\t\t\t\ttokenInput.setAttribute( 'value', token );\n\n\t\t\t\tform.append( tokenInput );\n\n\t\t\t\tif ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) {\n\t\t\t\t\t// Submit form, now containing the hidden card token field.\n\t\t\t\t\t// form.submit(); — not working with test meta box\n\t\t\t\t\tform.querySelector( '[name=\"' + e.submitter.name + '\"]' ).click();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\tfunction setupWooCommerce() {\n\t\tconst checkoutForm = document.querySelector( 'form.woocommerce-checkout' );\n\n\t\tif ( ! checkoutForm ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Init components on updated checkout.\n\t\t$( document.body ).on( 'updated_checkout', function( e ) {\n\t\t\tinitMollieComponents( checkoutForm );\n\t\t} );\n\n\t\tconst $form = $( checkoutForm );\n\n\t\t// Prevent placing order, need to create token first.\n\t\t$form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t// Re-enable placing order if card token has been added.\n\t\tcheckoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) {\n\t\t\te.preventDefault();\n\n\t\t\t$form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse );\n\n\t\t\t$form.submit();\n\t\t} );\n\t}\n\n\tfunction returnFalse() {\n\t\treturn false;\n\t}\n\n\t// Init Mollie Components.\n\tdocument.addEventListener( 'DOMContentLoaded', function( e ) {\n\t\tinitMollieComponents( 'form:not(.woocommerce-checkout)' );\n\t} );\n\n\tsetupWooCommerce();\n} )( jQuery );\n"],"mappings":"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"} \ No newline at end of file diff --git a/js/dist/mollie.min.js b/js/dist/mollie.min.js new file mode 100644 index 0000000..5d75b3d --- /dev/null +++ b/js/dist/mollie.min.js @@ -0,0 +1 @@ +!function(){function e(e){const n=Mollie(e.profileId,e.options),t=document.getElementById(e.elementId);if(!t)throw new Error("No data token element.");if(!t.form)throw new Error("Data token element not in form.");const o=t.form;o.addEventListener("submit",(async function e(i){i.preventDefault();const{token:r}=await n.createToken();r&&(t.value=r),o.removeEventListener("submit",e),o.requestSubmit(i.submitter),o.addEventListener("submit",e)}));n.createComponent("card").mount(e.mount)}window.pronamicPayMollieFields=window.pronamicPayMollieFields||[],window.pronamicPayMollieFields.push=function(){Array.from(arguments).forEach((function(n){e(n)}))},window.pronamicPayMollieFields.filter((function(n){return e(n),!1}))}(); \ No newline at end of file diff --git a/js/src/components.js b/js/src/components.js deleted file mode 100644 index 0021892..0000000 --- a/js/src/components.js +++ /dev/null @@ -1,184 +0,0 @@ -/* global Mollie */ -'use strict'; - -/** - * Mollie Components. - * - * @link https://docs.mollie.com/components/overview - */ -( ( $ ) => { - const components = [ - { - id: 'card-holder', - label: 'Card Holder', - component: 'cardHolder' - }, - { - id: 'card-number', - label: 'Card Number', - component: 'cardNumber' - }, - { - id: 'expiry-date', - label: 'Expiry', - component: 'expiryDate' - }, - { - id: 'verification-code', - label: 'CVC', - component: 'verificationCode' - } - ]; - - function initMollieComponents( forms ) { - let $cardTokenElements; - - if ( typeof forms === 'string' ) { - $cardTokenElements = document.querySelectorAll( forms + ' .pronamic_pay_mollie_card_token' ); - } else { - $cardTokenElements = forms.querySelectorAll( '.pronamic_pay_mollie_card_token' ); - } - - $cardTokenElements.forEach( ( $cardTokenElement ) => { - // Create components. - const data = $cardTokenElement.dataset; - - // Check required Mollie profile ID. - if ( ! $cardTokenElement.dataset.mollieProfileId ) { - throw new Error( 'No Mollie profile ID in element dataset. Unable to load Mollie Components.' ); - - return; - } - - // Initialize Mollie object. - const mollie = Mollie( - $cardTokenElement.dataset.mollieProfileId, - { - locale: $cardTokenElement.dataset.mollieLocale ?? null, - testmode: '1' === $cardTokenElement.dataset.mollieTestmode, - } - ); - - let componentsContainer = document.createElement( 'div' ); - componentsContainer.classList.add( 'mollie-components' ); - - $cardTokenElement.append( componentsContainer ); - - components.forEach( ( component ) => { - // Label. - let label = document.createElement( 'label' ); - label.setAttribute( 'for', component.id ); - label.innerText = component.label; - - // Component container. - let field = document.createElement( 'div' ); - field.setAttribute( 'id', component.id ); - - label.append( field ); - - // Error. - let error = document.createElement( 'div' ); - error.setAttribute( 'id', component.id + '-error' ); - error.setAttribute( 'role', 'alert' ); - error.classList.add( 'field-error' ); - - componentsContainer.append( label, error ); - - // Create and mount component. - let mollieComponent = mollie.createComponent( component.component ); - - mollieComponent.mount( '#' + component.id ); - - // Handle errors. - mollieComponent.addEventListener( 'change', ( event ) => { - // Add error. - if ( event.error && event.touched ) { - error.textContent = event.error; - - label.classList.add( 'is-invalid' ); - - return; - } - - // Remove error. - error.textContent = ''; - - label.classList.remove( 'is-invalid' ); - } ); - } ); - - // Create Mollie token on checkout submit. - const form = $cardTokenElement.closest( 'form' ); - - form.addEventListener( 'submit', async ( e ) => { - // Check existing card token input. - let cardTokenInput = form.querySelector( 'input[name="pronamic_pay_mollie_card_token"]' ); - - if ( cardTokenInput ) { - return; - } - - e.preventDefault(); - - // Create token. - const { token, error } = await mollie.createToken(); - - if ( error ) { - throw new Error( error.message || '' ); - } - - // Add token to form. - const tokenInput = document.createElement( 'input' ); - tokenInput.setAttribute( 'type', 'hidden' ); - tokenInput.setAttribute( 'name', 'pronamic_pay_mollie_card_token' ); - tokenInput.setAttribute( 'value', token ); - - form.append( tokenInput ); - - if ( false !== form.dispatchEvent( new Event( 'pronamic_pay_mollie_components_card_token_added', { cancelable: true } ) ) ) { - // Submit form, now containing the hidden card token field. - // form.submit(); — not working with test meta box - form.querySelector( '[name="' + e.submitter.name + '"]' ).click(); - } - } ); - } ); - } - - function setupWooCommerce() { - const checkoutForm = document.querySelector( 'form.woocommerce-checkout' ); - - if ( ! checkoutForm ) { - return; - } - - // Init components on updated checkout. - $( document.body ).on( 'updated_checkout', function( e ) { - initMollieComponents( checkoutForm ); - } ); - - const $form = $( checkoutForm ); - - // Prevent placing order, need to create token first. - $form.on( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); - - // Re-enable placing order if card token has been added. - checkoutForm.addEventListener( 'pronamic_pay_mollie_components_card_token_added', function( e ) { - e.preventDefault(); - - $form.off( 'checkout_place_order_pronamic_pay_credit_card', returnFalse ); - - $form.submit(); - } ); - } - - function returnFalse() { - return false; - } - - // Init Mollie Components. - document.addEventListener( 'DOMContentLoaded', function( e ) { - initMollieComponents( 'form:not(.woocommerce-checkout)' ); - } ); - - setupWooCommerce(); -} )( jQuery ); diff --git a/js/src/mollie.js b/js/src/mollie.js new file mode 100644 index 0000000..d214fa5 --- /dev/null +++ b/js/src/mollie.js @@ -0,0 +1,56 @@ +/* global Mollie */ +( function () { + function init( data ) { + const mollie = Mollie( data.profileId, data.options ); + + const tokenElement = document.getElementById( data.elementId ); + + if ( ! tokenElement ) { + throw new Error( 'No data token element.' ); + } + + if ( ! tokenElement.form ) { + throw new Error( 'Data token element not in form.' ); + } + + const form = tokenElement.form; + + async function createToken( e ) { + e.preventDefault(); + + const { token } = await mollie.createToken(); + + if ( token ) { + tokenElement.value = token; + } + + form.removeEventListener( 'submit', createToken ); + + form.requestSubmit( e.submitter ); + + form.addEventListener( 'submit', createToken ); + } + + form.addEventListener( 'submit', createToken ); + + const cardComponent = mollie.createComponent( 'card' ); + + cardComponent.mount( data.mount ); + } + + window.pronamicPayMollieFields = window.pronamicPayMollieFields || []; + + window.pronamicPayMollieFields.push = function () { + const args = Array.from( arguments ); + + args.forEach( function ( data ) { + init( data ); + } ); + }; + + window.pronamicPayMollieFields.filter( function ( item ) { + init( item ); + + return false; + } ); +} )(); diff --git a/package.json b/package.json index ceb721e..ebbfc7c 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,16 @@ "homepage": "http://www.wp-pay.org/gateways/mollie/", "devDependencies": { "@wordpress/env": "^8.13.0", - "npm-run-all": "^4.1.5" + "@wordpress/prettier-config": "^3.4.0", + "@wordpress/scripts": "^26.18.0", + "npm-run-all": "^4.1.5", + "prettier": "npm:wp-prettier@^3.0.3", + "terser": "^5.24.0" }, + "prettier": "@wordpress/prettier-config", "scripts": { + "lint-js": "wp-scripts lint-js ./js/src", + "js-minify": "terser js/src/mollie.js --compress --mangle --output js/dist/mollie.min.js", "wp-env-setup": "npm-run-all wp-env-setup-*", "wp-env-setup-mollie": "wp-env run cli wp config set MOLLIE_API_KEY $MOLLIE_API_KEY", "wp-env-setup-buckaroo-website-key": "wp-env run cli wp config set BUCKAROO_WEBSITE_KEY $BUCKAROO_WEBSITE_KEY", diff --git a/src/CardField.php b/src/CardField.php new file mode 100644 index 0000000..649b2a1 --- /dev/null +++ b/src/CardField.php @@ -0,0 +1,148 @@ + + * @copyright 2005-2022 Pronamic + * @license GPL-3.0-or-later + * @package Pronamic\WordPress\Pay\Mollie + */ + +namespace Pronamic\WordPress\Pay\Gateways\Mollie; + +use Pronamic\WordPress\Html\Element; +use Pronamic\WordPress\Pay\Fields\Field; + +/** + * Mollie card field class + */ +class CardField extends Field { + /** + * Gatweay. + * + * @var Gateway + */ + private $gateway; + + /** + * Construct card field. + * + * @param string $id ID. + * @param Gateway $gateway Gateway. + */ + public function __construct( $id, Gateway $gateway ) { + parent::__construct( $id ); + + $this->gateway = $gateway; + } + + /** + * Setup field. + */ + public function setup(): void { + \wp_register_script( + 'mollie.js', + 'https://js.mollie.com/v1/mollie.js', + [], + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. + null, + false + ); + + $file = '../css/components.css'; + + \wp_register_style( + 'pronamic-pay-mollie-components', + \plugins_url( $file, __FILE__ ), + [], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + ); + + $file = '../js/dist/mollie.min.js'; + + \wp_register_script( + 'pronamic-pay-mollie', + \plugins_url( $file, __FILE__ ), + [ 'mollie.js' ], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + true + ); + } + + /** + * Get element. + * + * @return Element|null + */ + protected function get_element() { + \wp_enqueue_script( 'pronamic-pay-mollie' ); + + \wp_enqueue_style( 'pronamic-pay-mollie' ); + + $element = new Element( 'div' ); + + $element->children[] = new Element( + 'div', + [ + 'id' => 'card', + ] + ); + + $element->children[] = new Element( + 'input', + [ + 'id' => $this->get_id(), + 'name' => $this->get_id(), + 'type' => 'hidden', + ] + ); + + return $element; + } + + /** + * Ouput. + * + * @return void + */ + public function output() { + parent::output(); + + try { + $profile_id = $this->gateway->get_profile_id(); + } catch ( \Exception $e ) { + return; + } + + $locale_transformer = new LocaleTransformer(); + + $data = [ + 'elementId' => $this->get_id(), + 'profileId' => $profile_id, + 'options' => [ + 'locale' => $locale_transformer->transform_wp_to_mollie( \get_locale() ), + 'testmode' => 'test' === $this->gateway->get_mode(), + ], + 'mount' => '#card', + ]; + + ?> + + - * @copyright 2005-2022 Pronamic - * @license GPL-3.0-or-later - * @package Pronamic\WordPress\Pay\Mollie - */ - -namespace Pronamic\WordPress\Pay\Gateways\Mollie; - -use Pronamic\WordPress\Html\Element; -use Pronamic\WordPress\Pay\Fields\Field; - -/** - * HTML field class - */ -class ComponentsField extends Field { - /** - * Mollie profile ID. - */ - private ?string $profile_id; - - /** - * Setup field. - */ - public function setup(): void { - // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. - \wp_register_script( - 'pronamic-pay-mollie', - 'https://js.mollie.com/v1/mollie.js', - [], - null, - false - ); - - $file = '../css/components.css'; - - \wp_register_style( - 'pronamic-pay-mollie-components', - \plugins_url( $file, __FILE__ ), - [], - \hash_file( 'crc32b', __DIR__ . '/' . $file ), - ); - - $file = '../js/dist/components.js'; - - \wp_register_script( - 'pronamic-pay-mollie-components', - \plugins_url( $file, __FILE__ ), - [ 'pronamic-pay-mollie' ], - \hash_file( 'crc32b', __DIR__ . '/' . $file ), - true - ); - } - - /** - * Set Mollie profile ID. - * - * @param string $profile_id Mollie profile ID. - */ - public function set_profile_id( string $profile_id ): void { - $this->profile_id = $profile_id; - } - - /** - * Get element. - * - * @return Element|null - */ - protected function get_element() { - \wp_enqueue_script( 'pronamic-pay-mollie-components' ); - - \wp_enqueue_style( 'pronamic-pay-mollie-components' ); - - $locale_transformer = new LocaleTransformer(); - - $element = new Element( - 'div', - [ - 'class' => $this->get_id(), - 'data-mollie-profile-id' => $this->profile_id, - 'data-mollie-locale' => $locale_transformer->transform_wp_to_mollie( \get_locale() ), - 'data-mollie-testmode' => true, - ] - ); - - return $element; - } - - /** - * Serialize to JSON. - */ - public function jsonSerialize() : array { - $data = parent::jsonSerialize(); - - $data['type'] = 'html'; - - return $data; - } -} diff --git a/src/Gateway.php b/src/Gateway.php index 8390bc7..6fae26a 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -127,28 +127,8 @@ function () { $field_consumer_iban->set_required( true ); $field_consumer_iban->meta_key = 'consumer_bank_details_iban'; - $field_mollie_components = new ComponentsField( 'pronamic_pay_mollie_card_token' ); - $field_mollie_components->meta_key = 'mollie_card_token'; - - $cache_key = 'pronamic_pay_mollie_profile_id_' . \md5( (string) \wp_json_encode( $config ) ); - - $profile_id = \get_transient( $cache_key ); - - if ( false === $profile_id ) { - try { - $current_profile = $this->client->get_current_profile(); - - $profile = Profile::from_object( $current_profile ); - - $profile_id = $profile->get_id(); - } catch ( \Exception $e ) { - // No problem. - } - - \set_transient( $cache_key, $profile_id, \DAY_IN_SECONDS ); - } - - $field_mollie_components->set_profile_id( (string) $profile_id ); + $field_mollie_card = new CardField( 'pronamic_pay_mollie_card_token', $this ); + $field_mollie_card->meta_key = 'mollie_card_token'; // Apple Pay. $payment_method_apple_pay = new PaymentMethod( PaymentMethods::APPLE_PAY ); @@ -177,7 +157,7 @@ function () { // Payment method credit card. $payment_method_credit_card = new PaymentMethod( PaymentMethods::CREDIT_CARD ); $payment_method_credit_card->add_support( 'recurring' ); - $payment_method_credit_card->add_field( $field_mollie_components ); + $payment_method_credit_card->add_field( $field_mollie_card ); $this->register_payment_method( $payment_method_credit_card ); @@ -265,6 +245,29 @@ function () { $this->register_payment_method( $payment_method_sofort ); } + /** + * Get profile ID. + * + * @return string + */ + public function get_profile_id() { + $cache_key = 'pronamic_pay_mollie_profile_id_' . \md5( (string) \wp_json_encode( $this->config ) ); + + $profile_id = \get_transient( $cache_key ); + + if ( false === $profile_id ) { + $current_profile = $this->client->get_current_profile(); + + $profile = Profile::from_object( $current_profile ); + + $profile_id = $profile->get_id(); + + \set_transient( $cache_key, $profile_id, \DAY_IN_SECONDS ); + } + + return $profile_id; + } + /** * Get payment methods. * From 506d8ed7cc127c7c13e017a76994e924947e1c35 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:01:04 +0100 Subject: [PATCH 08/29] wip --- js/src/mollie.js | 56 ++++++++++++++++++++++++++++++++--------------- src/CardField.php | 2 +- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/js/src/mollie.js b/js/src/mollie.js index d214fa5..b176fd5 100644 --- a/js/src/mollie.js +++ b/js/src/mollie.js @@ -1,41 +1,61 @@ /* global Mollie */ ( function () { function init( data ) { - const mollie = Mollie( data.profileId, data.options ); + const element = document.getElementById( data.elementId ); - const tokenElement = document.getElementById( data.elementId ); - - if ( ! tokenElement ) { + if ( ! element ) { throw new Error( 'No data token element.' ); } - if ( ! tokenElement.form ) { + if ( ! element.form ) { throw new Error( 'Data token element not in form.' ); } - const form = tokenElement.form; + const form = element.form; - async function createToken( e ) { - e.preventDefault(); + if ( ! form.mollie ) { + form.mollie = Mollie( data.profileId, data.options ); - const { token } = await mollie.createToken(); + async function createToken( e ) { + const tokenElement = document.getElementById( data.elementId ); - if ( token ) { - tokenElement.value = token; - } + if ( ! tokenElement ) { + return; + } + + e.preventDefault(); + + const { token, error } = await form.mollie.createToken(); + + if ( error ) { + console.log( error ); + } - form.removeEventListener( 'submit', createToken ); + if ( token ) { + tokenElement.value = token; + } - form.requestSubmit( e.submitter ); + form.requestSubmit( e.submitter ); - form.addEventListener( 'submit', createToken ); + form.addEventListener( 'submit', createToken, { + once: true, + } ); + } + + form.addEventListener( 'submit', createToken, { + once: true, + } ); } - form.addEventListener( 'submit', createToken ); + if ( form.mollieCardComponent ) { + form.mollieCardComponent.unmount(); + } - const cardComponent = mollie.createComponent( 'card' ); + if ( ! form.mollieCardComponent ) { + form.mollieCardComponent = form.mollie.createComponent( 'card' ); + } - cardComponent.mount( data.mount ); + form.mollieCardComponent.mount( data.mount ); } window.pronamicPayMollieFields = window.pronamicPayMollieFields || []; diff --git a/src/CardField.php b/src/CardField.php index 649b2a1..24a323a 100644 --- a/src/CardField.php +++ b/src/CardField.php @@ -58,7 +58,7 @@ public function setup(): void { \hash_file( 'crc32b', __DIR__ . '/' . $file ), ); - $file = '../js/dist/mollie.min.js'; + $file = '../js/src/mollie.js'; \wp_register_script( 'pronamic-pay-mollie', From 830bfc9baff1e414b8cd58bb3bc26a111435fdcf Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:41:56 +0100 Subject: [PATCH 09/29] Update mollie.js --- js/src/mollie.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/js/src/mollie.js b/js/src/mollie.js index b176fd5..d1d0f77 100644 --- a/js/src/mollie.js +++ b/js/src/mollie.js @@ -16,7 +16,7 @@ if ( ! form.mollie ) { form.mollie = Mollie( data.profileId, data.options ); - async function createToken( e ) { + function createToken( e ) { const tokenElement = document.getElementById( data.elementId ); if ( ! tokenElement ) { @@ -24,21 +24,22 @@ } e.preventDefault(); + e.stopImmediatePropagation(); - const { token, error } = await form.mollie.createToken(); + form.mollie.createToken().then( function( result ) { + if ( result.error ) { + console.log( result.error ); + } - if ( error ) { - console.log( error ); - } - - if ( token ) { - tokenElement.value = token; - } + if ( result.token ) { + tokenElement.value = result.token; + } - form.requestSubmit( e.submitter ); + form.requestSubmit( e.submitter ); - form.addEventListener( 'submit', createToken, { - once: true, + form.addEventListener( 'submit', createToken, { + once: true, + } ); } ); } From 3e46792d362097e391315bebc1f5ab972ad63e82 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:42:27 +0100 Subject: [PATCH 10/29] Update mollie.js --- js/src/mollie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/mollie.js b/js/src/mollie.js index d1d0f77..96113fa 100644 --- a/js/src/mollie.js +++ b/js/src/mollie.js @@ -26,7 +26,7 @@ e.preventDefault(); e.stopImmediatePropagation(); - form.mollie.createToken().then( function( result ) { + form.mollie.createToken().then( function ( result ) { if ( result.error ) { console.log( result.error ); } From c5d1fc57bf989137d6efdfc838b2f1aaeb23fbf6 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:39:21 +0100 Subject: [PATCH 11/29] Update mollie.js --- js/src/mollie.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/js/src/mollie.js b/js/src/mollie.js index 96113fa..c58a42e 100644 --- a/js/src/mollie.js +++ b/js/src/mollie.js @@ -75,3 +75,13 @@ return false; } ); } )(); + +console.log( jQuery ); + +if ( jQuery ) { + jQuery( 'form.woocommerce-checkout' ).on( 'checkout_place_order', function() { + console.log( this ); + + return false; + } ); +} From 31c29a47d36db89198e2c0c0a869d30d3dd63940 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:39:26 +0100 Subject: [PATCH 12/29] Update mollie.js --- js/src/mollie.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/src/mollie.js b/js/src/mollie.js index c58a42e..e11fbe4 100644 --- a/js/src/mollie.js +++ b/js/src/mollie.js @@ -76,8 +76,6 @@ } ); } )(); -console.log( jQuery ); - if ( jQuery ) { jQuery( 'form.woocommerce-checkout' ).on( 'checkout_place_order', function() { console.log( this ); From b11b29a66eb91bbd3a3f3ea035e6ebe3184f0153 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:53:51 +0100 Subject: [PATCH 13/29] Update .wp-env.json --- .wp-env.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.wp-env.json b/.wp-env.json index 47d8240..92b9861 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -7,7 +7,7 @@ "https://downloads.wordpress.org/plugin/query-monitor.zip", "https://downloads.wordpress.org/plugin/log-http-requests.zip", "https://downloads.wordpress.org/plugin/one-time-login.zip", - "https://downloads.wordpress.org/plugin/woocommerce.zip" + "https://downloads.wordpress.org/plugin/woocommerce.zip", "https://downloads.wordpress.org/plugin/wp-plugin-dependencies.zip" ], "mappings": { From e4f6c5130aba050441dd453cffe4a67a87586aad Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:59:11 +0100 Subject: [PATCH 14/29] Simplify Mollie components, for now only WooCommerce support. --- js/dist/mollie.min.js | 1 - js/dist/wc-legacy-checkout.min.js | 1 + js/src/mollie.js | 85 ----------------- js/src/wc-legacy-checkout.js | 147 ++++++++++++++++++++++++++++++ package.json | 2 +- src/CardField.php | 85 ++++------------- src/Integration.php | 3 + src/ScriptsController.php | 97 ++++++++++++++++++++ 8 files changed, 265 insertions(+), 156 deletions(-) delete mode 100644 js/dist/mollie.min.js create mode 100644 js/dist/wc-legacy-checkout.min.js delete mode 100644 js/src/mollie.js create mode 100644 js/src/wc-legacy-checkout.js create mode 100644 src/ScriptsController.php diff --git a/js/dist/mollie.min.js b/js/dist/mollie.min.js deleted file mode 100644 index 5d75b3d..0000000 --- a/js/dist/mollie.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function e(e){const n=Mollie(e.profileId,e.options),t=document.getElementById(e.elementId);if(!t)throw new Error("No data token element.");if(!t.form)throw new Error("Data token element not in form.");const o=t.form;o.addEventListener("submit",(async function e(i){i.preventDefault();const{token:r}=await n.createToken();r&&(t.value=r),o.removeEventListener("submit",e),o.requestSubmit(i.submitter),o.addEventListener("submit",e)}));n.createComponent("card").mount(e.mount)}window.pronamicPayMollieFields=window.pronamicPayMollieFields||[],window.pronamicPayMollieFields.push=function(){Array.from(arguments).forEach((function(n){e(n)}))},window.pronamicPayMollieFields.filter((function(n){return e(n),!1}))}(); \ No newline at end of file diff --git a/js/dist/wc-legacy-checkout.min.js b/js/dist/wc-legacy-checkout.min.js new file mode 100644 index 0000000..21a152b --- /dev/null +++ b/js/dist/wc-legacy-checkout.min.js @@ -0,0 +1 @@ +class PronamicPayMollieWooCommerceLegacyCheckoutFormController{constructor(e,t,o){this.jQuery=e,this.body=t,this.form=o}setup(){this.jQuery(this.body).on("init_checkout",(()=>this.initCheckout()))}initCheckout(){const e=this.form.querySelector(".pronamic-pay-mollie-card-field");if(null===e)return;const t=e.dataset.mollieProfileId,o=JSON.parse(e.dataset.mollieOptions);this.mollie=Mollie(t,o),this.checkoutPlaceOrderListener=()=>this.checkoutPlaceOrder(),this.jQuery(this.form).on("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.body).on("updated_checkout",(()=>this.updatedCheckout())),this.mollieCardComponent=this.mollie.createComponent("card")}updatedCheckout(){this.cardElement&&this.mollieCardComponent.unmount(),this.cardElement=this.form.querySelector(".pronamic-pay-mollie-card-field"),null!==this.cardElement&&this.mollieCardComponent.mount(this.cardElement)}checkoutPlaceOrder(){return this.mollie.createToken().then((e=>this.processTokenResponse(e))),!1}processTokenResponse(e){if(e.error)return;const t=document.getElementById("pronamic_pay_mollie_card_token");t&&(t.value=e.token),this.jQuery(this.form).off("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.form).submit()}}!function(){if(!jQuery)return;if(!document.forms.checkout)return;new PronamicPayMollieWooCommerceLegacyCheckoutFormController(jQuery,document.body,document.forms.checkout).setup()}(); \ No newline at end of file diff --git a/js/src/mollie.js b/js/src/mollie.js deleted file mode 100644 index e11fbe4..0000000 --- a/js/src/mollie.js +++ /dev/null @@ -1,85 +0,0 @@ -/* global Mollie */ -( function () { - function init( data ) { - const element = document.getElementById( data.elementId ); - - if ( ! element ) { - throw new Error( 'No data token element.' ); - } - - if ( ! element.form ) { - throw new Error( 'Data token element not in form.' ); - } - - const form = element.form; - - if ( ! form.mollie ) { - form.mollie = Mollie( data.profileId, data.options ); - - function createToken( e ) { - const tokenElement = document.getElementById( data.elementId ); - - if ( ! tokenElement ) { - return; - } - - e.preventDefault(); - e.stopImmediatePropagation(); - - form.mollie.createToken().then( function ( result ) { - if ( result.error ) { - console.log( result.error ); - } - - if ( result.token ) { - tokenElement.value = result.token; - } - - form.requestSubmit( e.submitter ); - - form.addEventListener( 'submit', createToken, { - once: true, - } ); - } ); - } - - form.addEventListener( 'submit', createToken, { - once: true, - } ); - } - - if ( form.mollieCardComponent ) { - form.mollieCardComponent.unmount(); - } - - if ( ! form.mollieCardComponent ) { - form.mollieCardComponent = form.mollie.createComponent( 'card' ); - } - - form.mollieCardComponent.mount( data.mount ); - } - - window.pronamicPayMollieFields = window.pronamicPayMollieFields || []; - - window.pronamicPayMollieFields.push = function () { - const args = Array.from( arguments ); - - args.forEach( function ( data ) { - init( data ); - } ); - }; - - window.pronamicPayMollieFields.filter( function ( item ) { - init( item ); - - return false; - } ); -} )(); - -if ( jQuery ) { - jQuery( 'form.woocommerce-checkout' ).on( 'checkout_place_order', function() { - console.log( this ); - - return false; - } ); -} diff --git a/js/src/wc-legacy-checkout.js b/js/src/wc-legacy-checkout.js new file mode 100644 index 0000000..9301902 --- /dev/null +++ b/js/src/wc-legacy-checkout.js @@ -0,0 +1,147 @@ +/* global Mollie */ +/* eslint-env jquery */ + +/** + * Pronamic Pay Mollie WooCommerce legacy checkout form controller class + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes + */ +class PronamicPayMollieWooCommerceLegacyCheckoutFormController { + /** + * Construct Pronamic Pay Mollie WooCommerce lgeacy checkout form controller. + * + * @param {jQuery} jQuery The jQuery library. + * @param {HTMLElement} body Body element. + * @param {HTMLElement} form WooCommerce legacy checkout form element. + */ + constructor( jQuery, body, form ) { + this.jQuery = jQuery; + this.body = body; + this.form = form; + } + + /** + * Setup. + */ + setup() { + this.jQuery( this.body ).on( 'init_checkout', () => + this.initCheckout() + ); + } + + /** + * Init checkout. + * + * @see https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js#L56-L59 + */ + initCheckout() { + const cardElement = this.form.querySelector( + '.pronamic-pay-mollie-card-field' + ); + + if ( null === cardElement ) { + return; + } + + const mollieProfileId = cardElement.dataset.mollieProfileId; + const mollieOptions = JSON.parse( cardElement.dataset.mollieOptions ); + + this.mollie = Mollie( mollieProfileId, mollieOptions ); + + this.checkoutPlaceOrderListener = () => this.checkoutPlaceOrder(); + + this.jQuery( this.form ).on( + 'checkout_place_order', + this.checkoutPlaceOrderListener + ); + + this.jQuery( this.body ).on( 'updated_checkout', () => + this.updatedCheckout() + ); + + this.mollieCardComponent = this.mollie.createComponent( 'card' ); + } + + /** + * Updated checkout. + * + * @see https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js#L428-L429 + */ + updatedCheckout() { + if ( this.cardElement ) { + this.mollieCardComponent.unmount(); + } + + this.cardElement = this.form.querySelector( + '.pronamic-pay-mollie-card-field' + ); + + if ( null === this.cardElement ) { + return; + } + + this.mollieCardComponent.mount( this.cardElement ); + } + + /** + * Checkout place order. + * + * @see https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js#L478-L480 + */ + checkoutPlaceOrder() { + this.mollie + .createToken() + .then( ( result ) => this.processTokenResponse( result ) ); + + return false; + } + + /** + * Process token response. + * + * @param {Object} result Mollie create token repsonse object. + */ + processTokenResponse( result ) { + if ( result.error ) { + return; + } + + const tokenElement = document.getElementById( + 'pronamic_pay_mollie_card_token' + ); + + if ( tokenElement ) { + tokenElement.value = result.token; + } + + this.jQuery( this.form ).off( + 'checkout_place_order', + this.checkoutPlaceOrderListener + ); + + this.jQuery( this.form ).submit(); + } +} + +/** + * Initialization. + */ +( function () { + if ( ! jQuery ) { + return; + } + + if ( ! document.forms.checkout ) { + return; + } + + const controller = + new PronamicPayMollieWooCommerceLegacyCheckoutFormController( + jQuery, + document.body, + document.forms.checkout + ); + + controller.setup(); +} )(); diff --git a/package.json b/package.json index ebbfc7c..b25508d 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "prettier": "@wordpress/prettier-config", "scripts": { "lint-js": "wp-scripts lint-js ./js/src", - "js-minify": "terser js/src/mollie.js --compress --mangle --output js/dist/mollie.min.js", + "js-minify": "terser js/src/wc-legacy-checkout.js --compress --mangle --output js/dist/wc-legacy-checkout.min.js", "wp-env-setup": "npm-run-all wp-env-setup-*", "wp-env-setup-mollie": "wp-env run cli wp config set MOLLIE_API_KEY $MOLLIE_API_KEY", "wp-env-setup-buckaroo-website-key": "wp-env run cli wp config set BUCKAROO_WEBSITE_KEY $BUCKAROO_WEBSITE_KEY", diff --git a/src/CardField.php b/src/CardField.php index 24a323a..d6918d5 100644 --- a/src/CardField.php +++ b/src/CardField.php @@ -36,45 +36,20 @@ public function __construct( $id, Gateway $gateway ) { $this->gateway = $gateway; } - /** - * Setup field. - */ - public function setup(): void { - \wp_register_script( - 'mollie.js', - 'https://js.mollie.com/v1/mollie.js', - [], - // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. - null, - false - ); - - $file = '../css/components.css'; - - \wp_register_style( - 'pronamic-pay-mollie-components', - \plugins_url( $file, __FILE__ ), - [], - \hash_file( 'crc32b', __DIR__ . '/' . $file ), - ); - - $file = '../js/src/mollie.js'; - - \wp_register_script( - 'pronamic-pay-mollie', - \plugins_url( $file, __FILE__ ), - [ 'mollie.js' ], - \hash_file( 'crc32b', __DIR__ . '/' . $file ), - true - ); - } - /** * Get element. * * @return Element|null */ protected function get_element() { + try { + $profile_id = $this->gateway->get_profile_id(); + } catch ( \Exception $e ) { + return null; + } + + $locale_transformer = new LocaleTransformer(); + \wp_enqueue_script( 'pronamic-pay-mollie' ); \wp_enqueue_style( 'pronamic-pay-mollie' ); @@ -84,7 +59,14 @@ protected function get_element() { $element->children[] = new Element( 'div', [ - 'id' => 'card', + 'class' => 'pronamic-pay-mollie-card-field', + 'data-mollie-profile-id' => $profile_id, + 'data-mollie-options' => \wp_json_encode( + [ + 'locale' => $locale_transformer->transform_wp_to_mollie( \get_locale() ), + 'testmode' => ( 'test' === $this->gateway->get_mode() ), + ] + ), ] ); @@ -100,41 +82,6 @@ protected function get_element() { return $element; } - /** - * Ouput. - * - * @return void - */ - public function output() { - parent::output(); - - try { - $profile_id = $this->gateway->get_profile_id(); - } catch ( \Exception $e ) { - return; - } - - $locale_transformer = new LocaleTransformer(); - - $data = [ - 'elementId' => $this->get_id(), - 'profileId' => $profile_id, - 'options' => [ - 'locale' => $locale_transformer->transform_wp_to_mollie( \get_locale() ), - 'testmode' => 'test' === $this->gateway->get_mode(), - ], - 'mount' => '#card', - ]; - - ?> - - add( new Install( null === $version ? '1.0.0' : $version ) ); + // Scripts. + ScriptsController::instance()->setup(); + /** * Admin */ diff --git a/src/ScriptsController.php b/src/ScriptsController.php new file mode 100644 index 0000000..d2fa311 --- /dev/null +++ b/src/ScriptsController.php @@ -0,0 +1,97 @@ + + * @copyright 2005-2022 Pronamic + * @license GPL-3.0-or-later + * @package Pronamic\WordPress\Pay\Mollie + */ + +namespace Pronamic\WordPress\Pay\Gateways\Mollie; + +/** + * Scripts controller class + */ +class ScriptsController { + /** + * Instance of this class. + * + * @var self + */ + protected static $instance = null; + + /** + * Return an instance of this class. + * + * @return self A single instance of this class. + */ + public static function instance() { + if ( null === self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Setup. + * + * @return void + */ + public function setup() { + if ( \has_action( 'wp_print_scripts', [ $this, 'print_scripts' ] ) ) { + return; + } + + \add_action( 'wp_print_scripts', [ $this, 'print_scripts' ] ); + + /** + * Mollie.js. + * + * @link https://docs.mollie.com/reference/mollie-js/overview + */ + \wp_register_script( + 'mollie.js', + 'https://js.mollie.com/v1/mollie.js', + [], + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version is part of URL. + null, + false + ); + + /** + * WooCommerce legacy checkout script. + */ + $file = '../js/dist/wc-legacy-checkout.min.js'; + + \wp_register_script( + 'pronamic-pay-mollie-wc-legacy-checkout', + \plugins_url( $file, __FILE__ ), + [ + 'jquery', + 'mollie.js', + 'wc-checkout', + ], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + true + ); + } + + /** + * Print scripts. + * + * @link https://developer.wordpress.org/reference/functions/wp_print_scripts/ + */ + public function print_scripts() { + /** + * WooCommerce legacy checkout. + * + * @link https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/includes/class-wc-frontend-scripts.php#L392-L394 + * @link https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js + */ + if ( \wp_script_is( 'wc-checkout' ) ) { + \wp_enqueue_script( 'pronamic-pay-mollie-wc-legacy-checkout' ); + } + } +} From aa3e3d3561c17efe73d8b2d318a9b68bfaeec6fd Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:00:59 +0100 Subject: [PATCH 15/29] Remove styling for now. --- .sass-lint.yml | 71 ----------------------------------- css/components.css | 67 --------------------------------- css/components.css.map | 1 - scss/components.scss | 84 ------------------------------------------ 4 files changed, 223 deletions(-) delete mode 100644 .sass-lint.yml delete mode 100644 css/components.css delete mode 100644 css/components.css.map delete mode 100644 scss/components.scss diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index a1260cc..0000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,71 +0,0 @@ -files: - include: 'scss/**/*.scss' -rules: - indentation: - - 1 - - size: 'tab' - class-name-format: - - 1 - - ignore: - - column-pronamic_payment_status - - column-pronamic_payment_subscription - - column-pronamic_subscription_status - - post-type-pronamic_payment - - post-type-pronamic_pay_subscr - nesting-depth: - - 1 - - max-depth: 3 - no-ids: - - 0 - property-sort-order: - - 1 - - order: - - background - - background-color - - background-image - - background-repeat - - border - - border-top - - border-top-width - - border-right - - border-right-color - - border-right-width - - border-bottom - - border-bottom-width - - border-left - - border-left-width - - border-radius - - border-spacing - - box-shadow - - box-sizing - - clear - - clip - - color - - content - - cursor - - display - - float - - font-family - - font-size - - font-style - - font-weight - - line-height - - list-style - - margin - - margin-top - - margin-right - - margin-bottom - - margin-left - - max-width - - padding - - outline - - overflow - - position - - top - - right - - bottom - - left - - speak - - text-indent - - width - - height diff --git a/css/components.css b/css/components.css deleted file mode 100644 index aa2230a..0000000 --- a/css/components.css +++ /dev/null @@ -1,67 +0,0 @@ -.pronamic_pay_mollie_card_token .mollie-components { - display: grid; - grid-auto-flow: row; - grid-template-columns: 2fr 1fr 1fr; - gap: 10px; - align-items: end; - max-width: 500px; -} -.pronamic_pay_mollie_card_token .mollie-components label { - display: block; -} -.pronamic_pay_mollie_card_token .mollie-components label:first-child { - grid-column: 1/4; -} -.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(2), .pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(3), .pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(4) { - grid-row: 3; -} -.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(2) { - grid-column: 1; - min-width: 250px; -} -.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(3) { - grid-column: 2; - min-width: 70px; -} -.pronamic_pay_mollie_card_token .mollie-components label:nth-of-type(4) { - grid-column: 3; - min-width: 70px; -} -.pronamic_pay_mollie_card_token .mollie-components label.is-invalid + .field-error { - display: block; -} -.pronamic_pay_mollie_card_token .mollie-components .mollie-component { - padding: 5px; - font-weight: 500; - background: #ffffff; - border-bottom: 2px solid #aaaaaa; - transition: 0.3s border-color ease; -} -.pronamic_pay_mollie_card_token .mollie-components .mollie-component.has-focus { - border-bottom-color: #0077ff; -} -.pronamic_pay_mollie_card_token .mollie-components .mollie-component.is-invalid { - border-bottom-color: #ff1717; -} -.pronamic_pay_mollie_card_token .mollie-components .field-error { - display: none; - grid-column: 1/4; - grid-row: 4; - font-size: 0.8em; - font-weight: 400; - color: #ff1717; -} -.pronamic_pay_mollie_card_token .mollie-components .field-error:first-of-type { - grid-row: 2; -} -.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(2) { - grid-row: 4; -} -.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(3) { - grid-row: 5; -} -.pronamic_pay_mollie_card_token .mollie-components .field-error:nth-of-type(4) { - grid-row: 6; -} - -/*# sourceMappingURL=components.css.map */ diff --git a/css/components.css.map b/css/components.css.map deleted file mode 100644 index 8c1c362..0000000 --- a/css/components.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":"","sources":["../scss/components.scss"],"names":[],"mappings":"AACC;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAEC;EACA;;AAGD;EAGC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;EACA;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC","file":"components.css"} \ No newline at end of file diff --git a/scss/components.scss b/scss/components.scss deleted file mode 100644 index 4ca7726..0000000 --- a/scss/components.scss +++ /dev/null @@ -1,84 +0,0 @@ -.pronamic_pay_mollie_card_token { - .mollie-components { - display: grid; - grid-auto-flow: row; - grid-template-columns: 2fr 1fr 1fr; - gap: 10px; - align-items: end; - max-width: 500px; - - label { - display: block; - - &:first-child { - grid-column: 1/4; - } - - &:nth-of-type(2), - &:nth-of-type(3), - &:nth-of-type(4) { - grid-row: 3; - } - - &:nth-of-type(2) { - grid-column: 1; - min-width: 250px; - } - - &:nth-of-type(3) { - grid-column: 2; - min-width: 70px; - } - - &:nth-of-type(4) { - grid-column: 3; - min-width: 70px; - } - - &.is-invalid + .field-error { - display: block; - } - } - - .mollie-component { - padding: 5px; - font-weight: 500; - background: #ffffff; - border-bottom: 2px solid #aaaaaa; - transition: 0.3s border-color ease; - - &.has-focus { - border-bottom-color: #0077ff; - } - - &.is-invalid { - border-bottom-color: #ff1717; - } - } - - .field-error { - display: none; - grid-column: 1/4; - grid-row: 4; - font-size: .8em; - font-weight: 400; - color: #ff1717; - - &:first-of-type { - grid-row: 2; - } - - &:nth-of-type(2) { - grid-row: 4; - } - - &:nth-of-type(3) { - grid-row: 5; - } - - &:nth-of-type(4) { - grid-row: 6; - } - } - } -} From eff6a414e50826f538f834d03c6255c15edbc66a Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:08:16 +0100 Subject: [PATCH 16/29] Delete postcss.config.js --- postcss.config.js | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 postcss.config.js diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 72d2d27..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - plugins: [ - require( 'cssnano' )( { - preset: 'default', - } ), - ], -}; \ No newline at end of file From a7a16ea0e56de484ae7f834fdb467165660b9207 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:09:49 +0100 Subject: [PATCH 17/29] PHPStan fixes. --- src/CardField.php | 2 ++ src/ScriptsController.php | 1 + 2 files changed, 3 insertions(+) diff --git a/src/CardField.php b/src/CardField.php index d6918d5..f7ad3f4 100644 --- a/src/CardField.php +++ b/src/CardField.php @@ -84,6 +84,8 @@ protected function get_element() { /** * Serialize to JSON. + * + * @return array */ public function jsonSerialize(): array { $data = parent::jsonSerialize(); diff --git a/src/ScriptsController.php b/src/ScriptsController.php index d2fa311..fded701 100644 --- a/src/ScriptsController.php +++ b/src/ScriptsController.php @@ -82,6 +82,7 @@ public function setup() { * Print scripts. * * @link https://developer.wordpress.org/reference/functions/wp_print_scripts/ + * @return void */ public function print_scripts() { /** From 803c797fbc15c9e3eca6d7b8d63fc2840e09bb52 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:51:42 +0100 Subject: [PATCH 18/29] Add styling support. --- assets/dist/card-field.asset.php | 1 + assets/dist/card-field.css | 1 + assets/dist/card-field.js | 0 assets/dist/wc-legacy-checkout.asset.php | 1 + assets/dist/wc-legacy-checkout.js | 1 + assets/src/card-field.js | 1 + assets/src/card-field.scss | 20 +++++++++++++ {js => assets}/src/wc-legacy-checkout.js | 0 js/dist/wc-legacy-checkout.min.js | 1 - package.json | 36 +++++++++++++----------- phpcs.xml.dist | 4 +++ src/ScriptsController.php | 17 ++++++++++- 12 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 assets/dist/card-field.asset.php create mode 100644 assets/dist/card-field.css create mode 100644 assets/dist/card-field.js create mode 100644 assets/dist/wc-legacy-checkout.asset.php create mode 100644 assets/dist/wc-legacy-checkout.js create mode 100644 assets/src/card-field.js create mode 100644 assets/src/card-field.scss rename {js => assets}/src/wc-legacy-checkout.js (100%) delete mode 100644 js/dist/wc-legacy-checkout.min.js diff --git a/assets/dist/card-field.asset.php b/assets/dist/card-field.asset.php new file mode 100644 index 0000000..b62c097 --- /dev/null +++ b/assets/dist/card-field.asset.php @@ -0,0 +1 @@ + array(), 'version' => '68ca97bd5add9245a662'); diff --git a/assets/dist/card-field.css b/assets/dist/card-field.css new file mode 100644 index 0000000..924d3ca --- /dev/null +++ b/assets/dist/card-field.css @@ -0,0 +1 @@ +.pronamic-pay-mollie-card-field{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;gap:1em}.pronamic-pay-mollie-card-field .mollie-card-component{-webkit-box-flex:2;-ms-flex:2 1 100%;flex:2 1 100%}.pronamic-pay-mollie-card-field .mollie-card-component--expiryDate,.pronamic-pay-mollie-card-field .mollie-card-component--verificationCode{-webkit-box-flex:1;-ms-flex:1 2 auto;flex:1 2 auto}.pronamic-pay-mollie-card-field .mollie-card-component__error{color:red} diff --git a/assets/dist/card-field.js b/assets/dist/card-field.js new file mode 100644 index 0000000..e69de29 diff --git a/assets/dist/wc-legacy-checkout.asset.php b/assets/dist/wc-legacy-checkout.asset.php new file mode 100644 index 0000000..cf84756 --- /dev/null +++ b/assets/dist/wc-legacy-checkout.asset.php @@ -0,0 +1 @@ + array(), 'version' => '302ba6457f6b9a2b8590'); diff --git a/assets/dist/wc-legacy-checkout.js b/assets/dist/wc-legacy-checkout.js new file mode 100644 index 0000000..834587b --- /dev/null +++ b/assets/dist/wc-legacy-checkout.js @@ -0,0 +1 @@ +!function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(t,o){for(var r=0;rthis.initCheckout()))}initCheckout(){const e=this.form.querySelector(".pronamic-pay-mollie-card-field");if(null===e)return;const t=e.dataset.mollieProfileId,o=JSON.parse(e.dataset.mollieOptions);this.mollie=Mollie(t,o),this.checkoutPlaceOrderListener=()=>this.checkoutPlaceOrder(),this.jQuery(this.form).on("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.body).on("updated_checkout",(()=>this.updatedCheckout())),this.mollieCardComponent=this.mollie.createComponent("card")}updatedCheckout(){this.cardElement&&this.mollieCardComponent.unmount(),this.cardElement=this.form.querySelector(".pronamic-pay-mollie-card-field"),null!==this.cardElement&&this.mollieCardComponent.mount(this.cardElement)}checkoutPlaceOrder(){return this.mollie.createToken().then((e=>this.processTokenResponse(e))),!1}processTokenResponse(e){if(e.error)return;const t=document.getElementById("pronamic_pay_mollie_card_token");t&&(t.value=e.token),this.jQuery(this.form).off("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.form).submit()}}!function(){if(!jQuery)return;if(!document.forms.checkout)return;new PronamicPayMollieWooCommerceLegacyCheckoutFormController(jQuery,document.body,document.forms.checkout).setup()}(); \ No newline at end of file diff --git a/package.json b/package.json index b25508d..7c656be 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,6 @@ "name": "mollie", "version": "4.8.1", "description": "Mollie driver for the WordPress payment processing library.", - "repository": { - "type": "git", - "url": "https://github.com/wp-pay-gateways/mollie" - }, - "keywords": [ - "wordpress", - "wp", - "pay", - "mollie", - "gateway", - "pronamic" - ], "author": { "name": "Pronamic", "email": "info@pronamic.nl", @@ -26,23 +14,37 @@ "url": "http://www.remcotolsma.nl/" } ], - "license": "GPL-3.0", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "wp", + "pay", + "mollie", + "gateway", + "pronamic" + ], + "homepage": "http://www.wp-pay.org/gateways/mollie/", + "repository": { + "type": "git", + "url": "https://github.com/wp-pay-gateways/mollie" + }, "bugs": { "url": "https://github.com/wp-pay-gateways/mollie/issues" }, - "homepage": "http://www.wp-pay.org/gateways/mollie/", "devDependencies": { "@wordpress/env": "^8.13.0", "@wordpress/prettier-config": "^3.4.0", "@wordpress/scripts": "^26.18.0", "npm-run-all": "^4.1.5", - "prettier": "npm:wp-prettier@^3.0.3", - "terser": "^5.24.0" + "prettier": "npm:wp-prettier@^3.0.3" }, "prettier": "@wordpress/prettier-config", "scripts": { + "build": "wp-scripts build assets/src/card-field.js assets/src/wc-legacy-checkout.js --output-path=assets/dist", + "start": "wp-scripts start assets/src/card-field.js assets/src/wc-legacy-checkout.js --output-path=assets/dist", "lint-js": "wp-scripts lint-js ./js/src", - "js-minify": "terser js/src/wc-legacy-checkout.js --compress --mangle --output js/dist/wc-legacy-checkout.min.js", + "lint:pkg-json": "wp-scripts lint-pkg-json", + "lint:style": "wp-scripts lint-style 'assets/src/**/*.scss'", "wp-env-setup": "npm-run-all wp-env-setup-*", "wp-env-setup-mollie": "wp-env run cli wp config set MOLLIE_API_KEY $MOLLIE_API_KEY", "wp-env-setup-buckaroo-website-key": "wp-env run cli wp config set BUCKAROO_WEBSITE_KEY $BUCKAROO_WEBSITE_KEY", diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 099c4c0..7a76f0d 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -29,4 +29,8 @@ src/Integration.php + + + assets/dist/*.asset.php + diff --git a/src/ScriptsController.php b/src/ScriptsController.php index fded701..95990cb 100644 --- a/src/ScriptsController.php +++ b/src/ScriptsController.php @@ -60,10 +60,24 @@ public function setup() { false ); + /** + * Card field style. + * + * @link https://github.com/mollie/components-examples + */ + $file = '../assets/dist/card-field.css'; + + \wp_register_style( + 'pronamic-pay-mollie-card-field', + \plugins_url( $file, __FILE__ ), + [], + \hash_file( 'crc32b', __DIR__ . '/' . $file ), + ); + /** * WooCommerce legacy checkout script. */ - $file = '../js/dist/wc-legacy-checkout.min.js'; + $file = '../assets/dist/wc-legacy-checkout.js'; \wp_register_script( 'pronamic-pay-mollie-wc-legacy-checkout', @@ -92,6 +106,7 @@ public function print_scripts() { * @link https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js */ if ( \wp_script_is( 'wc-checkout' ) ) { + \wp_enqueue_style( 'pronamic-pay-mollie-card-field' ); \wp_enqueue_script( 'pronamic-pay-mollie-wc-legacy-checkout' ); } } From 8c1fa3da92cf20f0aa173c0233714ee047277149 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:40:25 +0100 Subject: [PATCH 19/29] Improve base styling of card field. --- assets/dist/card-field.asset.php | 2 +- assets/dist/card-field.css | 2 +- assets/src/card-field.scss | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/assets/dist/card-field.asset.php b/assets/dist/card-field.asset.php index b62c097..65ef3f5 100644 --- a/assets/dist/card-field.asset.php +++ b/assets/dist/card-field.asset.php @@ -1 +1 @@ - array(), 'version' => '68ca97bd5add9245a662'); + array(), 'version' => 'c1e1308986d4d65f5f9d'); diff --git a/assets/dist/card-field.css b/assets/dist/card-field.css index 924d3ca..8e82be0 100644 --- a/assets/dist/card-field.css +++ b/assets/dist/card-field.css @@ -1 +1 @@ -.pronamic-pay-mollie-card-field{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;gap:1em}.pronamic-pay-mollie-card-field .mollie-card-component{-webkit-box-flex:2;-ms-flex:2 1 100%;flex:2 1 100%}.pronamic-pay-mollie-card-field .mollie-card-component--expiryDate,.pronamic-pay-mollie-card-field .mollie-card-component--verificationCode{-webkit-box-flex:1;-ms-flex:1 2 auto;flex:1 2 auto}.pronamic-pay-mollie-card-field .mollie-card-component__error{color:red} +.pronamic-pay-mollie-card-field{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;gap:1em}.pronamic-pay-mollie-card-field .mollie-card-component{-webkit-box-flex:2;-ms-flex:2 1 100%;flex:2 1 100%}.pronamic-pay-mollie-card-field .mollie-card-component--expiryDate,.pronamic-pay-mollie-card-field .mollie-card-component--verificationCode{-webkit-box-flex:1;-ms-flex:1 2 auto;flex:1 2 auto}.pronamic-pay-mollie-card-field .mollie-card-component__error{color:red}.pronamic-pay-mollie-card-field .mollie-component{background:#fff;border:1px solid #767676;border-radius:3px;padding:.5em} diff --git a/assets/src/card-field.scss b/assets/src/card-field.scss index 622153a..bc3d823 100644 --- a/assets/src/card-field.scss +++ b/assets/src/card-field.scss @@ -1,3 +1,8 @@ +/** + * Card field style. + * + * @link https://github.com/mollie/components-examples + */ .pronamic-pay-mollie-card-field { display: flex; @@ -17,4 +22,13 @@ color: #f00; } } + + .mollie-component { + background: #fff; + + border: 1px solid #767676; + border-radius: 3px; + + padding: .5em; + } } From 3c9cbeee6b199f2c94a0fd945939a53c03a00568 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:40:40 +0100 Subject: [PATCH 20/29] Improve error handling. --- assets/src/wc-legacy-checkout.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/assets/src/wc-legacy-checkout.js b/assets/src/wc-legacy-checkout.js index 9301902..1987db8 100644 --- a/assets/src/wc-legacy-checkout.js +++ b/assets/src/wc-legacy-checkout.js @@ -49,7 +49,8 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { this.mollie = Mollie( mollieProfileId, mollieOptions ); - this.checkoutPlaceOrderListener = () => this.checkoutPlaceOrder(); + this.checkoutPlaceOrderListener = ( event, wcCheckoutForm ) => + this.checkoutPlaceOrder( event, wcCheckoutForm ); this.jQuery( this.form ).on( 'checkout_place_order', @@ -88,11 +89,21 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { * Checkout place order. * * @see https://github.com/woocommerce/woocommerce/blob/8.3.0/plugins/woocommerce/client/legacy/js/frontend/checkout.js#L478-L480 + * @param {jQuery.Event} event A `jQuery.Event` object. + * @param {Object} wcCheckoutForm WooCommerce checkout form object. */ - checkoutPlaceOrder() { + checkoutPlaceOrder( event, wcCheckoutForm ) { + if ( + 'pronamic_pay_credit_card' !== wcCheckoutForm.get_payment_method() + ) { + return true; + } + this.mollie .createToken() - .then( ( result ) => this.processTokenResponse( result ) ); + .then( ( result ) => + this.processTokenResponse( result, wcCheckoutForm ) + ); return false; } @@ -100,10 +111,17 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { /** * Process token response. * - * @param {Object} result Mollie create token repsonse object. + * @param {Object} result Mollie create token repsonse object. + * @param {Object} wcCheckoutForm WooCommerce checkout form object. */ - processTokenResponse( result ) { + processTokenResponse( result, wcCheckoutForm ) { if ( result.error ) { + wcCheckoutForm.submit_error( + '
' + + result.error.message + + '
' + ); + return; } From 9f05c44e1c0c1de73a35ed746b476e4f9ce3b208 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:40:48 +0100 Subject: [PATCH 21/29] build --- assets/dist/wc-legacy-checkout.asset.php | 2 +- assets/dist/wc-legacy-checkout.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/dist/wc-legacy-checkout.asset.php b/assets/dist/wc-legacy-checkout.asset.php index cf84756..fdaf56d 100644 --- a/assets/dist/wc-legacy-checkout.asset.php +++ b/assets/dist/wc-legacy-checkout.asset.php @@ -1 +1 @@ - array(), 'version' => '302ba6457f6b9a2b8590'); + array(), 'version' => '049a721c86807b256be6'); diff --git a/assets/dist/wc-legacy-checkout.js b/assets/dist/wc-legacy-checkout.js index 834587b..504c0d7 100644 --- a/assets/dist/wc-legacy-checkout.js +++ b/assets/dist/wc-legacy-checkout.js @@ -1 +1 @@ -!function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(t,o){for(var r=0;r'+e.error.message+"");else{var o=document.getElementById("pronamic_pay_mollie_card_token");o&&(o.value=e.token),this.jQuery(this.form).off("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.form).submit()}}}])&&t(o.prototype,r),Object.defineProperty(o,"prototype",{writable:!1}),e}();jQuery&&document.forms.checkout&&new o(jQuery,document.body,document.forms.checkout).setup()}(); \ No newline at end of file From ce329ea03c157d8bcfc9a8d6a8a41c6d84010de1 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:40:56 +0100 Subject: [PATCH 22/29] Fix Lint JS script. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c656be..1b3e364 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "scripts": { "build": "wp-scripts build assets/src/card-field.js assets/src/wc-legacy-checkout.js --output-path=assets/dist", "start": "wp-scripts start assets/src/card-field.js assets/src/wc-legacy-checkout.js --output-path=assets/dist", - "lint-js": "wp-scripts lint-js ./js/src", + "lint-js": "wp-scripts lint-js assets/src", "lint:pkg-json": "wp-scripts lint-pkg-json", "lint:style": "wp-scripts lint-style 'assets/src/**/*.scss'", "wp-env-setup": "npm-run-all wp-env-setup-*", From 5f779e0c0525fb699ed74ca6e6953a4a197d208b Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:45:51 +0100 Subject: [PATCH 23/29] Update phpcs.xml.dist --- phpcs.xml.dist | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 7a76f0d..2a19e89 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -3,6 +3,7 @@ . + assets/dist/*.asset.php tests/bootstrap.php tests/wp-config.php @@ -29,8 +30,4 @@ src/Integration.php - - - assets/dist/*.asset.php - From 6d8eb249480a7348d60e58f9ed300f5fcabd95a6 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:48:00 +0100 Subject: [PATCH 24/29] Update composer.json --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 1224212..b6dee47 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,10 @@ } }, "config": { + "platform": { + "php": "8.0" + }, + "platform-check": false, "sort-packages": true, "allow-plugins": { "composer/installers": true, From d4a626c2c11b3c8dc37dae976fb471aa7dd6b256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Wed, 6 Dec 2023 09:55:58 +0100 Subject: [PATCH 25/29] Fix typo. --- src/CardField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CardField.php b/src/CardField.php index f7ad3f4..f564648 100644 --- a/src/CardField.php +++ b/src/CardField.php @@ -18,7 +18,7 @@ */ class CardField extends Field { /** - * Gatweay. + * Gateway. * * @var Gateway */ From b03b489cb2593472408fb18cba6d2f4db377fe8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Wed, 6 Dec 2023 10:17:58 +0100 Subject: [PATCH 26/29] Fix typos. --- assets/src/wc-legacy-checkout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/src/wc-legacy-checkout.js b/assets/src/wc-legacy-checkout.js index 1987db8..fbf0bae 100644 --- a/assets/src/wc-legacy-checkout.js +++ b/assets/src/wc-legacy-checkout.js @@ -9,7 +9,7 @@ */ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { /** - * Construct Pronamic Pay Mollie WooCommerce lgeacy checkout form controller. + * Construct Pronamic Pay Mollie WooCommerce legacy checkout form controller. * * @param {jQuery} jQuery The jQuery library. * @param {HTMLElement} body Body element. @@ -111,7 +111,7 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { /** * Process token response. * - * @param {Object} result Mollie create token repsonse object. + * @param {Object} result Mollie create token response object. * @param {Object} wcCheckoutForm WooCommerce checkout form object. */ processTokenResponse( result, wcCheckoutForm ) { From 793280081914924938903eb5a294dbc49de7366e Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:09:23 +0100 Subject: [PATCH 27/29] No longer submit Mollie error to WooCommerce checkout form. --- assets/dist/wc-legacy-checkout.asset.php | 2 +- assets/dist/wc-legacy-checkout.js | 2 +- assets/src/wc-legacy-checkout.js | 20 ++++++++------------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/assets/dist/wc-legacy-checkout.asset.php b/assets/dist/wc-legacy-checkout.asset.php index fdaf56d..d11682e 100644 --- a/assets/dist/wc-legacy-checkout.asset.php +++ b/assets/dist/wc-legacy-checkout.asset.php @@ -1 +1 @@ - array(), 'version' => '049a721c86807b256be6'); + array(), 'version' => 'f465b8f2332f2d0a9b79'); diff --git a/assets/dist/wc-legacy-checkout.js b/assets/dist/wc-legacy-checkout.js index 504c0d7..90bc1ff 100644 --- a/assets/dist/wc-legacy-checkout.js +++ b/assets/dist/wc-legacy-checkout.js @@ -1 +1 @@ -!function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(t,o){for(var r=0;r'+e.error.message+"");else{var o=document.getElementById("pronamic_pay_mollie_card_token");o&&(o.value=e.token),this.jQuery(this.form).off("checkout_place_order",this.checkoutPlaceOrderListener),this.jQuery(this.form).submit()}}}])&&t(o.prototype,r),Object.defineProperty(o,"prototype",{writable:!1}),e}();jQuery&&document.forms.checkout&&new o(jQuery,document.body,document.forms.checkout).setup()}(); \ No newline at end of file +!function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(t,o){for(var r=0;r - this.processTokenResponse( result, wcCheckoutForm ) - ); + .then( ( result ) => this.processTokenResponse( result ) ); return false; } @@ -111,17 +109,10 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { /** * Process token response. * - * @param {Object} result Mollie create token repsonse object. - * @param {Object} wcCheckoutForm WooCommerce checkout form object. + * @param {Object} result Mollie create token repsonse object. */ - processTokenResponse( result, wcCheckoutForm ) { + processTokenResponse( result ) { if ( result.error ) { - wcCheckoutForm.submit_error( - '
' + - result.error.message + - '
' - ); - return; } @@ -139,6 +130,11 @@ class PronamicPayMollieWooCommerceLegacyCheckoutFormController { ); this.jQuery( this.form ).submit(); + + this.jQuery( this.form ).on( + 'checkout_place_order', + this.checkoutPlaceOrderListener + ); } } From 494e0be05589de5771b7b3efc9ded513f5005ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reu=CC=88el=20van=20der=20Steege?= Date: Wed, 6 Dec 2023 14:35:53 +0100 Subject: [PATCH 28/29] Update Mollie profile ID in config meta instead of transient. --- src/Config.php | 7 +++++++ src/Gateway.php | 14 +++++++------- src/Integration.php | 9 +++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Config.php b/src/Config.php index 1a558d4..cee95b7 100644 --- a/src/Config.php +++ b/src/Config.php @@ -31,6 +31,13 @@ class Config extends GatewayConfig implements JsonSerializable { */ public $api_key; + /** + * Mollie profile ID. + * + * @var string|null + */ + public $profile_id; + /** * Bank transfer due date days. * diff --git a/src/Gateway.php b/src/Gateway.php index 6fae26a..a6e177c 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -247,22 +247,22 @@ function () { /** * Get profile ID. - * - * @return string + * + * @return string|null */ public function get_profile_id() { - $cache_key = 'pronamic_pay_mollie_profile_id_' . \md5( (string) \wp_json_encode( $this->config ) ); + $profile_id = $this->config->profile_id; - $profile_id = \get_transient( $cache_key ); - - if ( false === $profile_id ) { + if ( '' === $profile_id ) { $current_profile = $this->client->get_current_profile(); $profile = Profile::from_object( $current_profile ); $profile_id = $profile->get_id(); - \set_transient( $cache_key, $profile_id, \DAY_IN_SECONDS ); + \update_post_meta( $this->config->id, '_pronamic_gateway_mollie_profile_id', $profile_id ); + + $this->config->profile_id = $profile_id; } return $profile_id; diff --git a/src/Integration.php b/src/Integration.php index aa8589b..0374bb7 100644 --- a/src/Integration.php +++ b/src/Integration.php @@ -237,8 +237,16 @@ public function payment_provider_url( $url, Payment $payment ) { * @return void */ public function save_post( $post_id ) { + \delete_post_meta( $post_id, '_pronamic_gateway_mollie_profile_id' ); + $config = $this->get_config( $post_id ); + try { + $config->profile_id = $this->get_gateway( $post_id )->get_profile_id(); + } catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch -- No problem. + // No problem. + } + \delete_transient( 'pronamic_pay_mollie_payment_methods_' . \md5( (string) \wp_json_encode( $config ) ) ); \delete_transient( 'pronamic_pay_ideal_issuers_' . \md5( (string) \wp_json_encode( $config ) ) ); } @@ -254,6 +262,7 @@ public function get_config( $post_id ) { $config->id = intval( $post_id ); $config->api_key = $this->get_meta( $post_id, 'mollie_api_key' ); + $config->profile_id = $this->get_meta( $post_id, 'mollie_profile_id' ); $config->due_date_days = $this->get_meta( $post_id, 'mollie_due_date_days' ); return $config; From 80ab15651b0c094e6a4597fee6d3b9999b966375 Mon Sep 17 00:00:00 2001 From: Remco Tolsma <869674+remcotolsma@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:49:34 +0100 Subject: [PATCH 29/29] Improve error styling. --- assets/dist/card-field.asset.php | 2 +- assets/dist/card-field.css | 2 +- assets/src/card-field.scss | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/assets/dist/card-field.asset.php b/assets/dist/card-field.asset.php index 65ef3f5..98b62fd 100644 --- a/assets/dist/card-field.asset.php +++ b/assets/dist/card-field.asset.php @@ -1 +1 @@ - array(), 'version' => 'c1e1308986d4d65f5f9d'); + array(), 'version' => 'da91ceff221d2b17ebc7'); diff --git a/assets/dist/card-field.css b/assets/dist/card-field.css index 8e82be0..100cd5c 100644 --- a/assets/dist/card-field.css +++ b/assets/dist/card-field.css @@ -1 +1 @@ -.pronamic-pay-mollie-card-field{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;gap:1em}.pronamic-pay-mollie-card-field .mollie-card-component{-webkit-box-flex:2;-ms-flex:2 1 100%;flex:2 1 100%}.pronamic-pay-mollie-card-field .mollie-card-component--expiryDate,.pronamic-pay-mollie-card-field .mollie-card-component--verificationCode{-webkit-box-flex:1;-ms-flex:1 2 auto;flex:1 2 auto}.pronamic-pay-mollie-card-field .mollie-card-component__error{color:red}.pronamic-pay-mollie-card-field .mollie-component{background:#fff;border:1px solid #767676;border-radius:3px;padding:.5em} +.pronamic-pay-mollie-card-field{-webkit-box-orient:horizontal;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;gap:1em}.pronamic-pay-mollie-card-field .mollie-card-component{-webkit-box-flex:2;-ms-flex:2 1 100%;flex:2 1 100%}.pronamic-pay-mollie-card-field .mollie-card-component--expiryDate,.pronamic-pay-mollie-card-field .mollie-card-component--verificationCode{-webkit-box-flex:1;-ms-flex:1 2 auto;flex:1 2 auto}.pronamic-pay-mollie-card-field .mollie-card-component__error{color:#a00}.pronamic-pay-mollie-card-field .mollie-component{background:#fff;border:1px solid #767676;border-radius:3px;padding:.5em}.pronamic-pay-mollie-card-field .mollie-component.is-invalid{border-color:#a00} diff --git a/assets/src/card-field.scss b/assets/src/card-field.scss index bc3d823..fd91140 100644 --- a/assets/src/card-field.scss +++ b/assets/src/card-field.scss @@ -19,7 +19,7 @@ } &__error { - color: #f00; + color: #a00; } } @@ -30,5 +30,9 @@ border-radius: 3px; padding: .5em; + + &.is-invalid { + border-color: #a00; + } } }