From bf556e602975d601f789a6022572ddfce55247c2 Mon Sep 17 00:00:00 2001 From: Reinder Date: Wed, 8 Jul 2020 10:25:22 +0200 Subject: [PATCH] Credit card components are now a javascript plugin, live mode is fixed. A test button for the API configuration is available. --- composer.json | 4 +- src/Factory/MollieApiFactory.php | 2 +- .../api/mollie-payments-config.service.js | 25 ++ .../src/init/api-service.init.js | 9 + .../component/sw-plugin-config/index.js | 55 +++ .../sw-plugin-config.html.twig | 23 ++ .../extension/sw-plugin/index.js | 1 + .../src/module/mollie-payments/index.js | 1 + .../module/mollie-payments/snippet/de-DE.json | 9 + .../module/mollie-payments/snippet/en-GB.json | 9 + .../dist/storefront/js/mollie-payments.js | 2 +- src/Resources/app/storefront/src/main.js | 2 +- .../plugins/creditcard-components.plugin.js | 342 ++++++++++-------- src/Resources/config/services.xml | 7 + .../administration/js/mollie-payments.js | 2 +- .../payment/payment-fields.html.twig | 16 +- .../page/checkout/confirm/index.html.twig | 5 +- .../Controller/ConfigController.php | 84 +++++ .../CheckoutConfirmPageSubscriber.php | 4 +- 19 files changed, 441 insertions(+), 161 deletions(-) create mode 100644 src/Resources/app/administration/src/core/service/api/mollie-payments-config.service.js create mode 100644 src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/index.js create mode 100644 src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig create mode 100644 src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/index.js create mode 100644 src/Storefront/Controller/ConfigController.php diff --git a/composer.json b/composer.json index 571ef16b8..34e54ae29 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "kiener/mollie-payments-plugin", "description": "Mollie Payments", - "version": "v1.0.15", + "version": "v1.0.16", "type": "shopware-platform-plugin", "license": "MIT", "authors": [ @@ -11,6 +11,8 @@ ], "require": { "shopware/core": "*", + "shopware/administration": "*", + "shopware/storefront": "*", "mollie/mollie-api-php": "^2.0" }, "extra": { diff --git a/src/Factory/MollieApiFactory.php b/src/Factory/MollieApiFactory.php index 76ad6a447..f781be97d 100644 --- a/src/Factory/MollieApiFactory.php +++ b/src/Factory/MollieApiFactory.php @@ -86,7 +86,7 @@ public function getClient(?string $salesChannelId = null, ?Context $context = nu // @todo Add plugin version variable $this->apiClient->addVersionString( - 'MollieShopware6/1.0.15' + 'MollieShopware6/1.0.16' ); } catch (Exception $e) { $this->logger->error($e->getMessage(), [$e]); diff --git a/src/Resources/app/administration/src/core/service/api/mollie-payments-config.service.js b/src/Resources/app/administration/src/core/service/api/mollie-payments-config.service.js new file mode 100644 index 000000000..4d9256648 --- /dev/null +++ b/src/Resources/app/administration/src/core/service/api/mollie-payments-config.service.js @@ -0,0 +1,25 @@ +const ApiService = Shopware.Classes.ApiService; + +class MolliePaymentsConfigService extends ApiService { + constructor(httpClient, loginService, apiEndpoint = 'mollie') { + super(httpClient, loginService, apiEndpoint); + } + + testApiKeys(data = {liveApiKey: null, testApiKey: null}) { + const headers = this.getBasicHeaders(); + + return this.httpClient + .post( + `_action/${this.getApiBasePath()}/config/test-api-keys`, + JSON.stringify(data), + { + headers: headers + } + ) + .then((response) => { + return ApiService.handleResponse(response); + }); + } +} + +export default MolliePaymentsConfigService; \ No newline at end of file diff --git a/src/Resources/app/administration/src/init/api-service.init.js b/src/Resources/app/administration/src/init/api-service.init.js index 71cf64e18..9726d1e19 100644 --- a/src/Resources/app/administration/src/init/api-service.init.js +++ b/src/Resources/app/administration/src/init/api-service.init.js @@ -1,3 +1,6 @@ +import MolliePaymentsConfigService + from '../core/service/api/mollie-payments-config.service'; + import MolliePaymentsRefundService from '../core/service/api/mollie-payments-refund.service'; @@ -6,6 +9,12 @@ import MolliePaymentsShippingService const { Application } = Shopware; +Application.addServiceProvider('MolliePaymentsConfigService', (container) => { + const initContainer = Application.getContainer('init'); + + return new MolliePaymentsConfigService(initContainer.httpClient, container.loginService); +}); + Application.addServiceProvider('MolliePaymentsRefundService', (container) => { const initContainer = Application.getContainer('init'); diff --git a/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/index.js b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/index.js new file mode 100644 index 000000000..6b8a650b9 --- /dev/null +++ b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/index.js @@ -0,0 +1,55 @@ +import template from './sw-plugin-config.html.twig'; + +const { Component, Mixin } = Shopware; + +Component.override('sw-plugin-config', { + template, + + inject: [ + 'MolliePaymentsConfigService', + ], + + mixins: [ + Mixin.getByName('notification') + ], + + methods: { + onTestButtonClicked() { + let me = this; + + const liveApiKeyInput = document.querySelector('input[name="MolliePayments.config.liveApiKey"]'); + const testApiKeyInput = document.querySelector('input[name="MolliePayments.config.testApiKey"]'); + + const liveApiKey = !!liveApiKeyInput ? liveApiKeyInput.value : null; + const testApiKey = !!testApiKeyInput ? testApiKeyInput.value : null; + + this.MolliePaymentsConfigService.testApiKeys({liveApiKey, testApiKey}) + .then((response) => { + if (typeof response.results) { + response.results.forEach(function (result) { + let messageData = { + title: me.$tc('sw-payment.testApiKeys.title'), + message: `${me.$tc('sw-payment.testApiKeys.apiKey')} "${result.key}" (${result.mode}) ${(result.valid === true ? me.$tc('sw-payment.testApiKeys.isValid') : me.$tc('sw-payment.testApiKeys.isInvalid'))}.` + }; + + let input = result.mode === 'live' ? liveApiKeyInput : testApiKeyInput; + + if (!!input) { + input.parentNode.parentNode.classList.remove('has--error'); + } + + if (result.valid === true) { + me.createNotificationSuccess(messageData); + } else { + me.createNotificationError(messageData); + + if (!!input) { + input.parentNode.parentNode.classList.add('has--error'); + } + } + }); + } + }); + } + } +}); \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig new file mode 100644 index 000000000..157e8bd92 --- /dev/null +++ b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig @@ -0,0 +1,23 @@ +{#{% sw_extends 'administration/module/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig' %}#} + +{% block sw_plugin_config_actions %} + +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/index.js b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/index.js new file mode 100644 index 000000000..e91ec5b74 --- /dev/null +++ b/src/Resources/app/administration/src/module/mollie-payments/extension/sw-plugin/index.js @@ -0,0 +1 @@ +import './component/sw-plugin-config'; \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/mollie-payments/index.js b/src/Resources/app/administration/src/module/mollie-payments/index.js index bc1aff914..cd97e662f 100644 --- a/src/Resources/app/administration/src/module/mollie-payments/index.js +++ b/src/Resources/app/administration/src/module/mollie-payments/index.js @@ -1,4 +1,5 @@ import './extension/sw-order'; +import './extension/sw-plugin'; import deDE from './snippet/de-DE.json'; import enGB from './snippet/en-GB.json'; diff --git a/src/Resources/app/administration/src/module/mollie-payments/snippet/de-DE.json b/src/Resources/app/administration/src/module/mollie-payments/snippet/de-DE.json index ea759dad4..4147f19ac 100644 --- a/src/Resources/app/administration/src/module/mollie-payments/snippet/de-DE.json +++ b/src/Resources/app/administration/src/module/mollie-payments/snippet/de-DE.json @@ -31,5 +31,14 @@ "totalRefunds": "Refunded amount ({quantity} items)", "totalShipments": "Shipped amount ({quantity} items)" } + }, + "sw-payment": { + "testButton": "Prüfung", + "testApiKeys": { + "title": "Mollie Payments", + "apiKey": "API Schlüssel", + "isValid": "ist gültig", + "isInvalid": "ist gültig" + } } } \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/mollie-payments/snippet/en-GB.json b/src/Resources/app/administration/src/module/mollie-payments/snippet/en-GB.json index ea759dad4..a5dcedb7b 100644 --- a/src/Resources/app/administration/src/module/mollie-payments/snippet/en-GB.json +++ b/src/Resources/app/administration/src/module/mollie-payments/snippet/en-GB.json @@ -31,5 +31,14 @@ "totalRefunds": "Refunded amount ({quantity} items)", "totalShipments": "Shipped amount ({quantity} items)" } + }, + "sw-payment": { + "testButton": "Test", + "testApiKeys": { + "title": "Mollie Payments", + "apiKey": "API key", + "isValid": "is valid", + "isInvalid": "is invalid" + } } } \ No newline at end of file diff --git a/src/Resources/app/storefront/dist/storefront/js/mollie-payments.js b/src/Resources/app/storefront/dist/storefront/js/mollie-payments.js index 69e73f34d..c65bbb58c 100644 --- a/src/Resources/app/storefront/dist/storefront/js/mollie-payments.js +++ b/src/Resources/app/storefront/dist/storefront/js/mollie-payments.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([["mollie-payments"],{tHPJ:function(e,t,n){"use strict";n.r(t);n("wcNg");var r=n("FGIj");function o(e){return(o="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)}function i(e,t,n,r,o,i,c){try{var u=e[i](c),a=u.value}catch(e){return void n(e)}u.done?t(a):Promise.resolve(a).then(r,o)}function c(e,t){for(var n=0;n=0&&t.item(n)!==this;);return n>-1});e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null}},{key:"init",value:function(){var e=document.querySelector(".payment-method-input.applepay"),t=this.getClosest(e,".payment-method");t&&t.classList&&(window.ApplePaySession&&window.ApplePaySession.canMakePayments()||t.classList.add("d-none"))}}])&&g(n.prototype,r),o&&g(n,o),t}(r.a),O=window.PluginManager;O.register("MollieCreditCardComponents",s),O.register("MollieIDealIssuer",h),O.register("MollieApplePayPaymentMethod",E)}},[["tHPJ","runtime","vendor-node","vendor-shared"]]]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([["mollie-payments"],{tHPJ:function(e,t,o){"use strict";o.r(t);o("wcNg");var n=o("FGIj");function r(e){return(r="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)}function i(e,t,o,n,r,i,c){try{var u=e[i](c),a=u.value}catch(e){return void o(e)}u.done?t(a):Promise.resolve(a).then(n,r)}function c(e,t){for(var o=0;o=0&&t.item(o)!==this;);return o>-1});e&&e!==document;e=e.parentNode)if(e.matches(t))return e;return null}},{key:"init",value:function(){var e=document.querySelector(".payment-method-input.applepay"),t=this.getClosest(e,".payment-method");t&&t.classList&&(window.ApplePaySession&&window.ApplePaySession.canMakePayments()||t.classList.add("d-none"))}}])&&P(o.prototype,n),r&&P(o,r),t}(n.a),_=window.PluginManager;_.register("MollieCreditCardComponents",d,"#mollie_components_credit_card"),_.register("MollieIDealIssuer",S),_.register("MollieApplePayPaymentMethod",O)}},[["tHPJ","runtime","vendor-node","vendor-shared"]]]); \ No newline at end of file diff --git a/src/Resources/app/storefront/src/main.js b/src/Resources/app/storefront/src/main.js index 556e2a4d3..28457dc6b 100644 --- a/src/Resources/app/storefront/src/main.js +++ b/src/Resources/app/storefront/src/main.js @@ -12,6 +12,6 @@ import MollieApplePayPaymentMethod // Register them via the existing PluginManager const PluginManager = window.PluginManager; -PluginManager.register('MollieCreditCardComponents', MollieCreditCardComponents); +PluginManager.register('MollieCreditCardComponents', MollieCreditCardComponents, '#mollie_components_credit_card'); PluginManager.register('MollieIDealIssuer', MollieIDealIssuer); PluginManager.register('MollieApplePayPaymentMethod', MollieApplePayPaymentMethod); \ No newline at end of file diff --git a/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-components.plugin.js b/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-components.plugin.js index 09f4fcaba..7d257f32b 100644 --- a/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-components.plugin.js +++ b/src/Resources/app/storefront/src/mollie-payments/plugins/creditcard-components.plugin.js @@ -1,197 +1,245 @@ import Plugin from 'src/plugin-system/plugin.class'; export default class MollieCreditCardComponents extends Plugin { + static options = { + customerId: null, + locale: null, + profileId: null, + shopUrl: null, + testMode: true, + }; + init() { - // get controller - const mollieController = document.querySelector('div.mollie-components-controller'); + let me = this; + let componentsObject = null; + + // Get an existing Mollie controller element + const mollieController = document.querySelector(this.getSelectors().mollieController); - // remove existing mollie controller - if (mollieController !== undefined && mollieController !== null) { + // Remove the existing Mollie controller element + if (!!mollieController) { mollieController.remove(); } - // get container - const container = document.querySelector('div.mollie-components-credit-card'); - const cardToken = document.querySelector('#cardHolder'); + // Fix the trailing slash in the shop URL + if (this.options.shopUrl.substr(-1) === '/') { + this.options.shopUrl = this.options.shopUrl.substr(0, this.options.shopUrl.length - 1); + } + + // Get the elements from the DOM + const cardHolder = document.querySelector(this.getSelectors().cardHolder); + const componentsContainer = document.querySelector(this.getSelectors().componentsContainer); + const paymentForm = document.querySelector(this.getSelectors().paymentForm); + const radioInputs = document.querySelectorAll(this.getSelectors().radioInputs); + const submitButton = document.querySelector(this.getSelectors().submitButton); + // Initialize Mollie Components instance if ( - container !== undefined - && container !== null - && cardToken !== undefined - && cardToken !== null + !!componentsContainer + && !!cardHolder ) { - const locale = container.getAttribute('data-locale'); - const profileId = container.getAttribute('data-profile-id'); - let shopUrl = container.getAttribute('data-shop-url'); - const testMode = container.getAttribute('data-test-mode'); - - if (shopUrl.substr(-1) === '/') { - shopUrl = shopUrl.substr(0, shopUrl.length - 1); - } - - // Initialize Mollie Components instance // eslint-disable-next-line no-undef - const mollie = Mollie(profileId, { - locale: locale, - testmode: testMode + componentsObject = Mollie(this.options.profileId, { + locale: this.options.locale, + testmode: this.options.testMode, }); + } + + // Create components inputs + this.createComponentsInputs( + componentsObject, + [ + this.getInputFields().cardHolder, + this.getInputFields().cardNumber, + this.getInputFields().expiryDate, + this.getInputFields().verificationCode + ] + ); + + // Show/hide the components form based on the selected radio input + radioInputs.forEach((element) => { + element.addEventListener('change', () => { + me.showComponents(); + }); + }); + + // Submit handler + submitButton.addEventListener('click', (event) => { + event.preventDefault(); + me.submitForm(event, componentsObject, paymentForm); + }); + } + + getSelectors() { + return { + cardHolder: '#cardHolder', + componentsContainer: 'div.mollie-components-credit-card', + creditCardRadioInput: '#confirmPaymentForm input[type="radio"].creditcard', + mollieController: 'div.mollie-components-controller', + paymentForm: '#confirmPaymentForm', + radioInputs: '#confirmPaymentForm input[type="radio"]', + submitButton: '#confirmPaymentForm button[type="submit"]', + }; + } - // Default properties - const properties = { - styles: { - base: { - backgroundColor: '#fff', - fontSize: '14px', - padding: '10px 10px', - '::placeholder': { - color: 'rgba(68, 68, 68, 0.2)' - } - }, - valid: { - color: '#090' - }, - invalid: { - backgroundColor: '#fff1f3' + getDefaultProperties() { + return { + styles: { + base: { + backgroundColor: '#fff', + fontSize: '14px', + padding: '10px 10px', + '::placeholder': { + color: 'rgba(68, 68, 68, 0.2)' } + }, + valid: { + color: '#090' + }, + invalid: { + backgroundColor: '#fff1f3' } - }; + } + }; + } - const cardHolder = { + getInputFields() { + return { + cardHolder: { name: 'cardHolder', id: '#cardHolder', errors: 'cardHolderError' - }; - const cardNumber = { + }, + cardNumber: { name: 'cardNumber', id: '#cardNumber', errors: 'cardNumberError' - }; - const expiryDate = { + }, + expiryDate: { name: 'expiryDate', id: '#expiryDate', errors: 'expiryDateError' - }; - const verificationCode = { + }, + verificationCode: { name: 'verificationCode', id: '#verificationCode', errors: 'verificationCodeError' - }; + } + }; + } + + showComponents() { + const creditCardRadioInput = document.querySelector(this.getSelectors().creditCardRadioInput); + const componentsContainer = document.querySelector(this.getSelectors().componentsContainer); + + if (!!componentsContainer) { + if ( + creditCardRadioInput === undefined + || creditCardRadioInput.checked === false + ) { + componentsContainer.classList.add('d-none'); + } else { + componentsContainer.classList.remove('d-none'); + } + } + } - const inputs = [cardHolder, cardNumber, expiryDate, verificationCode]; + createComponentsInputs(componentsObject, inputs) { + let me = this; - // Elements - const customerId = container.getAttribute('data-customer-id'); - const paymentForm = document.querySelector('#confirmPaymentForm'); - const submitButton = document.querySelector('#confirmPaymentForm button[type="submit"]'); - const radioInputs = document.querySelectorAll('#confirmPaymentForm input[type="radio"]'); - const creditCardRadioInput = document.querySelector('#confirmPaymentForm input[type="radio"].creditcard'); + inputs.forEach((element, index, arr) => { + const component = componentsObject.createComponent(element.name, me.getDefaultProperties()); + component.mount(element.id); + arr[index][element.name] = component; - // Event helpers - const showComponents = () => { - if ( - creditCardRadioInput === undefined - || creditCardRadioInput.checked === false - ) { - container.classList.add('d-none'); + // Handle errors + component.addEventListener('change', event => { + const componentContainer = document.getElementById(`${element.name}`); + const componentError = document.getElementById(`${element.errors}`); + + if (event.error && event.touched) { + componentContainer.classList.add('error'); + componentError.textContent = event.error; } else { - container.classList.remove('d-none'); + componentContainer.classList.remove('error'); + componentError.textContent = ''; } - }; + }); - const setFocus = (componentName, isFocused) => { - const element = document.querySelector(componentName); - element.classList.toggle('is-focused', isFocused); - }; + // Handle labels + component.addEventListener('focus', () => { + me.setFocus(`${element.id}`, true); + }); + component.addEventListener('blur', () => { + me.setFocus(`${element.id}`, false); + }); + }); + } - const disableForm = () => { - if (submitButton !== null) { - submitButton.disabled = true; - } - }; + setFocus(componentName, isFocused) { + const element = document.querySelector(componentName); + element.classList.toggle('is-focused', isFocused); + } - const enableForm = () => { - if (submitButton !== null) { - submitButton.disabled = false; - } - }; + disableForm() { + const submitButton = document.querySelector(this.getSelectors().submitButton); - showComponents(); + if (!!submitButton) { + submitButton.disabled = true; + } + } - radioInputs.forEach((element) => { - element.addEventListener('change', () => { - showComponents(); - }); - }); + enableForm() { + const submitButton = document.querySelector(this.getSelectors().submitButton); - // Create inputs - inputs.forEach((element, index, arr) => { - const component = mollie.createComponent(element.name, properties); - component.mount(element.id); - arr[index][element.name] = component; - - // Handle errors - component.addEventListener('change', event => { - const componentContainer = document.getElementById(`${element.name}`); - const componentError = document.getElementById(`${element.errors}`); - - if (event.error && event.touched) { - componentContainer.classList.add('error'); - componentError.textContent = event.error; - } else { - componentContainer.classList.remove('error'); - componentError.textContent = ''; - } - }); - - // Handle labels - component.addEventListener('focus', () => { - setFocus(`${element.id}`, true); - }); - component.addEventListener('blur', () => { - setFocus(`${element.id}`, false); - }); - }); + if (!!submitButton) { + submitButton.disabled = false; + } + } - // Submit handler - paymentForm.addEventListener('submit', async event => { - event.preventDefault(); - disableForm(); + async submitForm(event, componentsObject, paymentForm) { + event.preventDefault(); + this.disableForm(); - // Fallback for submitting the form - setTimeout(function () { - paymentForm.submit(); - }, 2000); + const creditCardRadioInput = document.querySelector(this.getSelectors().creditCardRadioInput); - if ( - creditCardRadioInput === undefined - || creditCardRadioInput === null - || creditCardRadioInput.checked === false - ) { - paymentForm.submit(); - } + if ( + ( + creditCardRadioInput === undefined + || creditCardRadioInput === null + || creditCardRadioInput.checked === false + ) + && !!paymentForm + ) { + paymentForm.submit(); + } - if ( - creditCardRadioInput !== undefined - && creditCardRadioInput !== null - && creditCardRadioInput.checked === true - ) { - // Reset possible form errors - const verificationErrors = document.getElementById(`${verificationCode.errors}`); - verificationErrors.textContent = ''; + if ( + !!creditCardRadioInput + && creditCardRadioInput.checked === true + ) { + // Reset possible form errors + const verificationErrors = document.getElementById(`${this.getInputFields().verificationCode.errors}`); + verificationErrors.textContent = ''; - // Get a payment token - const {token, error} = await mollie.createToken(); + // Get a payment token + const {token, error} = await componentsObject.createToken(); - if (error) { - enableForm(); - verificationErrors.textContent = error.message; - return; - } + if (error) { + this.enableForm(); + verificationErrors.textContent = error.message; + return; + } - const fetchUrl = shopUrl + '/mollie/components/store-card-token/' + customerId + '/' + token; + if (!error) { + const fetchUrl = this.options.shopUrl + '/mollie/components/store-card-token/' + this.options.customerId + '/' + token; - // Store the token on the customer - fetch(fetchUrl, { headers: { 'Content-Type': 'application/json; charset=utf-8' } }) + // Store the token on the customer + if ( + !!fetchUrl + && !!paymentForm + ) { + fetch(fetchUrl, {headers: {'Content-Type': 'application/json; charset=utf-8'}}) .then(() => { // Add token to the form const tokenInput = document.getElementById('cardToken'); @@ -200,7 +248,7 @@ export default class MollieCreditCardComponents extends Plugin { }) .catch(paymentForm.submit()); } - }); + } } } } \ No newline at end of file diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 9736c34f0..841787205 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -86,6 +86,13 @@ + + + + + + + diff --git a/src/Resources/public/administration/js/mollie-payments.js b/src/Resources/public/administration/js/mollie-payments.js index 5bae52067..d6e75c33b 100644 --- a/src/Resources/public/administration/js/mollie-payments.js +++ b/src/Resources/public/administration/js/mollie-payments.js @@ -1 +1 @@ -(this.webpackJsonp=this.webpackJsonp||[]).push([["mollie-payments"],{OlTK:function(e,t){e.exports='{% block sw_order_detail_base_line_items_summary_entries %}\n {% parent %}\n
{{ $tc(\'sw-order.detailExtended.totalRefunds\', 0, { quantity: refundedItems }) }}
\n
{{ refundedAmount | currency(order.currency.shortName) }}
\n
{{ $tc(\'sw-order.detailExtended.totalShipments\', 0, { quantity: shippedItems }) }}
\n
{{ shippedAmount | currency(order.currency.shortName) }}
\n{% endblock %}'},eOFF:function(e,t){e.exports='{% block sw_order_line_items_grid_grid_actions %}\n {% parent %}\n\n\n{% endblock %}\n\n{% block sw_order_line_items_grid_grid_actions_show %}\n {% parent %}\n\n\n {{ $tc(\'mollie-payments.general.shipThroughMollie\') }}\n\n\n\n {{ $tc(\'mollie-payments.general.refundThroughMollie\') }}\n\n{% endblock %}'},kmOT:function(e){e.exports=JSON.parse('{"mollie-payments":{"general":{"mainMenuItemGeneral":"Mollie Payments","descriptionTextModule":"Mollie Payments","refundThroughMollie":"Refund through Mollie","shipThroughMollie":"Ship through Mollie"},"modals":{"refund":{"title":"Refund an order line item through Mollie","content":"Fill out the quantity of this item ({refundableQuantity} out of {quantity} left to refund) to be refunded to the customer.","quantityPlaceholder":"The quantity to refund...","createCreditText":"Create a credit item for this refund.","confirmButton":"Refund","cancelButton":"Do not refund"},"shipping":{"title":"Ship an order line item through Mollie","content":"Fill out the quantity of this item ({shippableQuantity} out of {quantity} left to ship) to be shipped to the customer.","quantityPlaceholder":"The quantity to ship...","confirmButton":"Ship","cancelButton":"Do not ship"}}},"sw-order":{"detailExtended":{"columnRefunded":"Refunded","columnShipped":"Shipped","totalRefunds":"Refunded amount ({quantity} items)","totalShipments":"Shipped amount ({quantity} items)"}}}')},mhZ0:function(e){e.exports=JSON.parse('{"mollie-payments":{"general":{"mainMenuItemGeneral":"Mollie Payments","descriptionTextModule":"Mollie Payments","refundThroughMollie":"Refund through Mollie","shipThroughMollie":"Ship through Mollie"},"modals":{"refund":{"title":"Refund an order line item through Mollie","content":"Fill out the quantity of this item ({refundableQuantity} out of {quantity} left to refund) to be refunded to the customer.","quantityPlaceholder":"The quantity to refund...","createCreditText":"Create a credit item for this refund.","confirmButton":"Refund","cancelButton":"Do not refund"},"shipping":{"title":"Ship an order line item through Mollie","content":"Fill out the quantity of this item ({shippableQuantity} out of {quantity} left to ship) to be shipped to the customer.","quantityPlaceholder":"The quantity to ship...","confirmButton":"Ship","cancelButton":"Do not ship"}}},"sw-order":{"detailExtended":{"columnRefunded":"Refunded","columnShipped":"Shipped","totalRefunds":"Refunded amount ({quantity} items)","totalShipments":"Shipped amount ({quantity} items)"}}}')},ugmU:function(e,t,n){"use strict";n.r(t);const i=Shopware.Classes.ApiService;var o=class extends i{constructor(e,t,n="mollie"){super(e,t,n)}refund(e={itemId:null,versionId:null,quantity:null,createCredit:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/refund`,JSON.stringify(e),{headers:t}).then(e=>i.handleResponse(e))}total(e={orderId:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/refund/total`,JSON.stringify(e),{headers:t}).then(e=>i.handleResponse(e))}};const s=Shopware.Classes.ApiService;var l=class extends s{constructor(e,t,n="mollie"){super(e,t,n)}ship(e={itemId:null,versionId:null,quantity:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/ship`,JSON.stringify(e),{headers:t}).then(e=>s.handleResponse(e))}total(e={orderId:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/ship/total`,JSON.stringify(e),{headers:t}).then(e=>s.handleResponse(e))}};const{Application:d}=Shopware;d.addServiceProvider("MolliePaymentsRefundService",e=>{const t=d.getContainer("init");return new o(t.httpClient,e.loginService)}),d.addServiceProvider("MolliePaymentsShippingService",e=>{const t=d.getContainer("init");return new l(t.httpClient,e.loginService)});var a=n("eOFF"),r=n.n(a);const{Component:u,Service:m}=Shopware;u.override("sw-order-line-items-grid",{template:r.a,inject:["MolliePaymentsRefundService","MolliePaymentsShippingService"],data:()=>({isLoading:!1,selectedItems:{},showRefundModal:!1,showShippingModal:!1,createCredit:!1,quantityToRefund:1,quantityToShip:1,refundQuantity:0,shippingQuantity:0}),computed:{getLineItemColumns(){const e=this.$super("getLineItemColumns");return e.push({property:"customFields.refundedQuantity",label:this.$tc("sw-order.detailExtended.columnRefunded"),allowResize:!1,align:"right",inlineEdit:!1,width:"100px"}),e.push({property:"customFields.shippedQuantity",label:this.$tc("sw-order.detailExtended.columnShipped"),allowResize:!1,align:"right",inlineEdit:!1,width:"100px"}),e}},methods:{onRefundItem(e){this.showRefundModal=e.id},onCloseRefundModal(){this.showRefundModal=!1},onConfirmRefund(e){this.showRefundModal=!1,this.quantityToRefund>0&&this.MolliePaymentsRefundService.refund({itemId:e.id,versionId:e.versionId,quantity:this.quantityToRefund,createCredit:this.createCredit}).then(document.location.reload()),this.quantityToRefund=0},onShipItem(e){this.showShippingModal=e.id},onCloseShippingModal(){this.showShippingModal=!1},onConfirmShipping(e){this.showShippingModal=!1,this.quantityToShip>0&&this.MolliePaymentsShippingService.ship({itemId:e.id,versionId:e.versionId,quantity:this.quantityToShip}).then(document.location.reload()),this.quantityToShip=0},isRefundable(e){let t=!1;return"product"===e.type&&void 0!==e.customFields&&null!==e.customFields&&void 0!==e.customFields.mollie_payments&&null!==e.customFields.mollie_payments&&void 0!==e.customFields.mollie_payments.order_line_id&&null!==e.customFields.mollie_payments.order_line_id&&(void 0===e.customFields.refundedQuantity||parseInt(e.customFields.refundedQuantity,10)void 0!==e.customFields&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.refundedQuantity,10):e.quantity,shippableQuantity:e=>void 0!==e.customFields&&void 0!==e.customFields.shippedQuantity&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.shippedQuantity,10)-parseInt(e.customFields.refundedQuantity,10):void 0!==e.customFields&&void 0===e.customFields.shippedQuantity&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.refundedQuantity,10):e.quantity}});var p=n("OlTK"),h=n.n(p);const{Component:c}=Shopware;c.override("sw-order-detail-base",{template:h.a,props:{orderId:{type:String,required:!0}},data:()=>({refundedAmount:0,refundedItems:0,shippedAmount:0,shippedItems:0}),inject:["MolliePaymentsRefundService","MolliePaymentsShippingService"],mounted(){""!==this.orderId&&(this.MolliePaymentsRefundService.total({orderId:this.orderId}).then(e=>{this.refundedAmount=e.amount,this.refundedItems=e.items}),this.MolliePaymentsShippingService.total({orderId:this.orderId}).then(e=>{this.shippedAmount=e.amount,this.shippedItems=e.items}))}});var y=n("mhZ0"),f=n("kmOT");const{Module:g}=Shopware;g.register("mollie-payments",{type:"plugin",name:"MolliePayments",title:"mollie-payments.general.mainMenuItemGeneral",description:"mollie-payments.general.descriptionTextModule",version:"1.0.0",targetVersion:"1.0.0",color:"#333",icon:"default-action-settings",snippets:{"de-DE":y,"en-GB":f}})}},[["ugmU","runtime"]]]); \ No newline at end of file +(this.webpackJsonp=this.webpackJsonp||[]).push([["mollie-payments"],{OlTK:function(e,t){e.exports='{% block sw_order_detail_base_line_items_summary_entries %}\n {% parent %}\n
{{ $tc(\'sw-order.detailExtended.totalRefunds\', 0, { quantity: refundedItems }) }}
\n
{{ refundedAmount | currency(order.currency.shortName) }}
\n
{{ $tc(\'sw-order.detailExtended.totalShipments\', 0, { quantity: shippedItems }) }}
\n
{{ shippedAmount | currency(order.currency.shortName) }}
\n{% endblock %}'},dut0:function(e,t){e.exports="{#{% sw_extends 'administration/module/sw-plugin/component/sw-plugin-config/sw-plugin-config.html.twig' %}#}\n\n{% block sw_plugin_config_actions %}\n\n{% endblock %}"},eOFF:function(e,t){e.exports='{% block sw_order_line_items_grid_grid_actions %}\n {% parent %}\n\n\n{% endblock %}\n\n{% block sw_order_line_items_grid_grid_actions_show %}\n {% parent %}\n\n\n {{ $tc(\'mollie-payments.general.shipThroughMollie\') }}\n\n\n\n {{ $tc(\'mollie-payments.general.refundThroughMollie\') }}\n\n{% endblock %}'},kmOT:function(e){e.exports=JSON.parse('{"mollie-payments":{"general":{"mainMenuItemGeneral":"Mollie Payments","descriptionTextModule":"Mollie Payments","refundThroughMollie":"Refund through Mollie","shipThroughMollie":"Ship through Mollie"},"modals":{"refund":{"title":"Refund an order line item through Mollie","content":"Fill out the quantity of this item ({refundableQuantity} out of {quantity} left to refund) to be refunded to the customer.","quantityPlaceholder":"The quantity to refund...","createCreditText":"Create a credit item for this refund.","confirmButton":"Refund","cancelButton":"Do not refund"},"shipping":{"title":"Ship an order line item through Mollie","content":"Fill out the quantity of this item ({shippableQuantity} out of {quantity} left to ship) to be shipped to the customer.","quantityPlaceholder":"The quantity to ship...","confirmButton":"Ship","cancelButton":"Do not ship"}}},"sw-order":{"detailExtended":{"columnRefunded":"Refunded","columnShipped":"Shipped","totalRefunds":"Refunded amount ({quantity} items)","totalShipments":"Shipped amount ({quantity} items)"}},"sw-payment":{"testButton":"Test","testApiKeys":{"title":"Mollie Payments","apiKey":"API key","isValid":"is valid","isInvalid":"is invalid"}}}')},mhZ0:function(e){e.exports=JSON.parse('{"mollie-payments":{"general":{"mainMenuItemGeneral":"Mollie Payments","descriptionTextModule":"Mollie Payments","refundThroughMollie":"Refund through Mollie","shipThroughMollie":"Ship through Mollie"},"modals":{"refund":{"title":"Refund an order line item through Mollie","content":"Fill out the quantity of this item ({refundableQuantity} out of {quantity} left to refund) to be refunded to the customer.","quantityPlaceholder":"The quantity to refund...","createCreditText":"Create a credit item for this refund.","confirmButton":"Refund","cancelButton":"Do not refund"},"shipping":{"title":"Ship an order line item through Mollie","content":"Fill out the quantity of this item ({shippableQuantity} out of {quantity} left to ship) to be shipped to the customer.","quantityPlaceholder":"The quantity to ship...","confirmButton":"Ship","cancelButton":"Do not ship"}}},"sw-order":{"detailExtended":{"columnRefunded":"Refunded","columnShipped":"Shipped","totalRefunds":"Refunded amount ({quantity} items)","totalShipments":"Shipped amount ({quantity} items)"}},"sw-payment":{"testButton":"Prüfung","testApiKeys":{"title":"Mollie Payments","apiKey":"API Schlüssel","isValid":"ist gültig","isInvalid":"ist gültig"}}}')},ugmU:function(e,t,n){"use strict";n.r(t);const i=Shopware.Classes.ApiService;var o=class extends i{constructor(e,t,n="mollie"){super(e,t,n)}testApiKeys(e={liveApiKey:null,testApiKey:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/config/test-api-keys`,JSON.stringify(e),{headers:t}).then(e=>i.handleResponse(e))}};const s=Shopware.Classes.ApiService;var l=class extends s{constructor(e,t,n="mollie"){super(e,t,n)}refund(e={itemId:null,versionId:null,quantity:null,createCredit:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/refund`,JSON.stringify(e),{headers:t}).then(e=>s.handleResponse(e))}total(e={orderId:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/refund/total`,JSON.stringify(e),{headers:t}).then(e=>s.handleResponse(e))}};const a=Shopware.Classes.ApiService;var d=class extends a{constructor(e,t,n="mollie"){super(e,t,n)}ship(e={itemId:null,versionId:null,quantity:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/ship`,JSON.stringify(e),{headers:t}).then(e=>a.handleResponse(e))}total(e={orderId:null}){const t=this.getBasicHeaders();return this.httpClient.post(`_action/${this.getApiBasePath()}/ship/total`,JSON.stringify(e),{headers:t}).then(e=>a.handleResponse(e))}};const{Application:r}=Shopware;r.addServiceProvider("MolliePaymentsConfigService",e=>{const t=r.getContainer("init");return new o(t.httpClient,e.loginService)}),r.addServiceProvider("MolliePaymentsRefundService",e=>{const t=r.getContainer("init");return new l(t.httpClient,e.loginService)}),r.addServiceProvider("MolliePaymentsShippingService",e=>{const t=r.getContainer("init");return new d(t.httpClient,e.loginService)});var u=n("eOFF"),p=n.n(u);const{Component:m,Service:c}=Shopware;m.override("sw-order-line-items-grid",{template:p.a,inject:["MolliePaymentsRefundService","MolliePaymentsShippingService"],data:()=>({isLoading:!1,selectedItems:{},showRefundModal:!1,showShippingModal:!1,createCredit:!1,quantityToRefund:1,quantityToShip:1,refundQuantity:0,shippingQuantity:0}),computed:{getLineItemColumns(){const e=this.$super("getLineItemColumns");return e.push({property:"customFields.refundedQuantity",label:this.$tc("sw-order.detailExtended.columnRefunded"),allowResize:!1,align:"right",inlineEdit:!1,width:"100px"}),e.push({property:"customFields.shippedQuantity",label:this.$tc("sw-order.detailExtended.columnShipped"),allowResize:!1,align:"right",inlineEdit:!1,width:"100px"}),e}},methods:{onRefundItem(e){this.showRefundModal=e.id},onCloseRefundModal(){this.showRefundModal=!1},onConfirmRefund(e){this.showRefundModal=!1,this.quantityToRefund>0&&this.MolliePaymentsRefundService.refund({itemId:e.id,versionId:e.versionId,quantity:this.quantityToRefund,createCredit:this.createCredit}).then(document.location.reload()),this.quantityToRefund=0},onShipItem(e){this.showShippingModal=e.id},onCloseShippingModal(){this.showShippingModal=!1},onConfirmShipping(e){this.showShippingModal=!1,this.quantityToShip>0&&this.MolliePaymentsShippingService.ship({itemId:e.id,versionId:e.versionId,quantity:this.quantityToShip}).then(document.location.reload()),this.quantityToShip=0},isRefundable(e){let t=!1;return"product"===e.type&&void 0!==e.customFields&&null!==e.customFields&&void 0!==e.customFields.mollie_payments&&null!==e.customFields.mollie_payments&&void 0!==e.customFields.mollie_payments.order_line_id&&null!==e.customFields.mollie_payments.order_line_id&&(void 0===e.customFields.refundedQuantity||parseInt(e.customFields.refundedQuantity,10)void 0!==e.customFields&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.refundedQuantity,10):e.quantity,shippableQuantity:e=>void 0!==e.customFields&&void 0!==e.customFields.shippedQuantity&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.shippedQuantity,10)-parseInt(e.customFields.refundedQuantity,10):void 0!==e.customFields&&void 0===e.customFields.shippedQuantity&&void 0!==e.customFields.refundedQuantity?e.quantity-parseInt(e.customFields.refundedQuantity,10):e.quantity}});var h=n("OlTK"),y=n.n(h);const{Component:f}=Shopware;f.override("sw-order-detail-base",{template:y.a,props:{orderId:{type:String,required:!0}},data:()=>({refundedAmount:0,refundedItems:0,shippedAmount:0,shippedItems:0}),inject:["MolliePaymentsRefundService","MolliePaymentsShippingService"],mounted(){""!==this.orderId&&(this.MolliePaymentsRefundService.total({orderId:this.orderId}).then(e=>{this.refundedAmount=e.amount,this.refundedItems=e.items}),this.MolliePaymentsShippingService.total({orderId:this.orderId}).then(e=>{this.shippedAmount=e.amount,this.shippedItems=e.items}))}});var g=n("dut0"),v=n.n(g);const{Component:w,Mixin:S}=Shopware;w.override("sw-plugin-config",{template:v.a,inject:["MolliePaymentsConfigService"],mixins:[S.getByName("notification")],methods:{onTestButtonClicked(){let e=this;const t=document.querySelector('input[name="MolliePayments.config.liveApiKey"]'),n=document.querySelector('input[name="MolliePayments.config.testApiKey"]'),i=t?t.value:null,o=n?n.value:null;this.MolliePaymentsConfigService.testApiKeys({liveApiKey:i,testApiKey:o}).then(i=>{i.results,i.results.forEach((function(i){let o={title:e.$tc("sw-payment.testApiKeys.title"),message:`${e.$tc("sw-payment.testApiKeys.apiKey")} "${i.key}" (${i.mode}) ${!0===i.valid?e.$tc("sw-payment.testApiKeys.isValid"):e.$tc("sw-payment.testApiKeys.isInvalid")}.`},s="live"===i.mode?t:n;s&&s.parentNode.parentNode.classList.remove("has--error"),!0===i.valid?e.createNotificationSuccess(o):(e.createNotificationError(o),s&&s.parentNode.parentNode.classList.add("has--error"))}))})}}});var b=n("mhZ0"),M=n("kmOT");const{Module:_}=Shopware;_.register("mollie-payments",{type:"plugin",name:"MolliePayments",title:"mollie-payments.general.mainMenuItemGeneral",description:"mollie-payments.general.descriptionTextModule",version:"1.0.0",targetVersion:"1.0.0",color:"#333",icon:"default-action-settings",snippets:{"de-DE":b,"en-GB":M}})}},[["ugmU","runtime"]]]); \ No newline at end of file diff --git a/src/Resources/views/storefront/component/payment/payment-fields.html.twig b/src/Resources/views/storefront/component/payment/payment-fields.html.twig index 05d3c1e9d..9f719eac2 100644 --- a/src/Resources/views/storefront/component/payment/payment-fields.html.twig +++ b/src/Resources/views/storefront/component/payment/payment-fields.html.twig @@ -15,12 +15,16 @@ {# load mollie creditcard components #} {% if payment.translated.customFields.mollie_payment_method_name == 'creditcard' %}
+ class="mollie-components-credit-card d-none" + {% if page.enable_credit_card_components == true %} + data-mollie-credit-card-components-options='{ + "customerId": "{{ context.customer.id }}", + "locale": "{{ page.mollie_locale }}", + "profileId": "{{ page.mollie_profile_id }}", + "shopUrl": "{{ seoUrl('frontend.home.page') }}", + "testMode": {{ page.mollie_test_mode }} + }' + {% endif %}> {% if page.enable_credit_card_components == true %} diff --git a/src/Resources/views/storefront/page/checkout/confirm/index.html.twig b/src/Resources/views/storefront/page/checkout/confirm/index.html.twig index 748e44f4a..5a0e87a27 100644 --- a/src/Resources/views/storefront/page/checkout/confirm/index.html.twig +++ b/src/Resources/views/storefront/page/checkout/confirm/index.html.twig @@ -1,6 +1,9 @@ {% sw_extends '@Storefront/storefront/page/checkout/confirm/index.html.twig' %} {% block page_checkout_main_content %} - + {% if page.enable_credit_card_components == true %} + + {% endif %} + {{ parent() }} {% endblock %} \ No newline at end of file diff --git a/src/Storefront/Controller/ConfigController.php b/src/Storefront/Controller/ConfigController.php new file mode 100644 index 000000000..35c14c978 --- /dev/null +++ b/src/Storefront/Controller/ConfigController.php @@ -0,0 +1,84 @@ +get('liveApiKey'); + + // Get the test API key + $testApiKey = $request->get('testApiKey'); + + /** @var array $keys */ + $keys = [ + [ + 'key' => $liveApiKey, + 'mode' => 'live', + ], + [ + 'key' => $testApiKey, + 'mode' => 'test', + ] + ]; + + /** @var array $results */ + $results = []; + + foreach ($keys as $key) { + $result = [ + 'key' => $key['key'], + 'mode' => $key['mode'], + 'valid' => false, + ]; + + try { + /** @var MollieApiClient $apiClient */ + $apiClient = new MollieApiClient(); + + // Set the current API key + $apiClient->setApiKey($key['key']); + + /** @var Profile $profile */ + $profile = $apiClient->profiles->getCurrent(); + + // Check if the profile exists + if (isset($profile->id)) { + $result['valid'] = true; + } + } catch (Exception $e) { + // No need to handle this exception + } + + $results[] = $result; + } + + return new JsonResponse([ + 'results' => $results + ]); + } +} \ No newline at end of file diff --git a/src/Subscriber/CheckoutConfirmPageSubscriber.php b/src/Subscriber/CheckoutConfirmPageSubscriber.php index 4989ac366..0c821c55a 100644 --- a/src/Subscriber/CheckoutConfirmPageSubscriber.php +++ b/src/Subscriber/CheckoutConfirmPageSubscriber.php @@ -1,6 +1,7 @@ apiClient->methods->get(PaymentMethod::IDEAL, $parameters); } catch (Exception $e) { - file_put_contents(__DIR__ . '/errors.txt', $e->getMessage()); + // } // Assign issuers to storefront if ($ideal !== null) { - file_put_contents(__DIR__ . '/issuers.txt', print_r($ideal->issuers, true)); $args->getPage()->assign([ 'ideal_issuers' => $ideal->issuers, 'preferred_issuer' => $preferredIssuer,