diff --git a/Api/Admin/Builder/OrderItemHandlerInterface.php b/Api/Admin/Builder/OrderItemHandlerInterface.php new file mode 100644 index 0000000..88b5f6d --- /dev/null +++ b/Api/Admin/Builder/OrderItemHandlerInterface.php @@ -0,0 +1,22 @@ +=0) | Positive (Y>=0) | Product | X | Y | + * | null | Negative (X<0) | Negative (Y<0) | Discount | X | Y | + * | Product/Fee/Shipping | X | Y | Product/Fee/Shipping | Abs(X) | Abs(Y) | + * | Discount | X | Y | Discount | -Abs(X) | -Abs(Y) | + * +----------------------+-----------------+-----------------+----------------------+-------------------------+------------------------+ + * + * @api + */ +interface QliroOrderItemInterface extends ContainerInterface +{ + const TYPE_PRODUCT = 'Product'; + const TYPE_DISCOUNT = 'Discount'; + const TYPE_FEE = 'Fee'; + const TYPE_SHIPPING = 'Shipping'; + + /** + * @return string + */ + public function getMerchantReference(); + + /** + * Get item type. + * Can be 'Product', 'Discount', 'Fee' or 'Shipping' + * + * @return string + */ + public function getType(); + + /** + * @return int + */ + public function getQuantity(); + + /** + * @return float + */ + public function getPricePerItemIncVat(); + + /** + * @return float + */ + public function getPricePerItemExVat(); + + /** + * @return string + */ + public function getDescription(); + + /** + * @return array + */ + public function getMetaData(); + + /** + * @param string $value + * @return $this + */ + public function setMerchantReference($value); + + /** + * Set item type. + * Can be 'Product', 'Discount', 'Fee' or 'Shipping' + * + * @param string $value + * @return $this + */ + public function setType($value); + + /** + * @param int $value + * @return $this + */ + public function setQuantity($value); + + /** + * @param float $value + * @return $this + */ + public function setPricePerItemIncVat($value); + + /** + * @param float $value + * @return $this + */ + public function setPricePerItemExVat($value); + + /** + * @param string $value + * @return $this + */ + public function setDescription($value); + + /** + * Additional metadata. + * + * In OrderManagement API can be used to have two possible elements + * - HeaderLines (array) Array of strings that will be diplayed above the item on the invoice. + * Maximum number of strings is 5 and maximum length of each string is 115 characters. + * - FooterLines (array) Array of strings that will be diplayed below the item on the invoice. + * Maximum number of strings is 5 and maximum length of each string is 115 characters. + * + * @param array $value + * @return $this + */ + public function setMetaData($value); +} diff --git a/Api/Data/QliroOrderManagementStatusInterface.php b/Api/Data/QliroOrderManagementStatusInterface.php new file mode 100644 index 0000000..82692c3 --- /dev/null +++ b/Api/Data/QliroOrderManagementStatusInterface.php @@ -0,0 +1,170 @@ + element + * + * @param string $value + * @return $this + */ + public function setInputName($value) + { + return $this->setName($value); + } + + /** + * Set "id" for " class="required-entry admin__control-text" + value="getInfoData('id_number') ?>"/> + + + diff --git a/view/adminhtml/templates/info/pdf/qliroone.phtml b/view/adminhtml/templates/info/pdf/qliroone.phtml new file mode 100644 index 0000000..7051e8d --- /dev/null +++ b/view/adminhtml/templates/info/pdf/qliroone.phtml @@ -0,0 +1,11 @@ + +escapeHtml(__('Identification Number: %1', 123)) ?> + {{pdf_row_separator}} diff --git a/view/adminhtml/templates/info/qliroone.phtml b/view/adminhtml/templates/info/qliroone.phtml new file mode 100644 index 0000000..aaf2dd6 --- /dev/null +++ b/view/adminhtml/templates/info/qliroone.phtml @@ -0,0 +1,28 @@ + +
escapeHtml($block->getMethod()->getTitle()); ?> + showWarning()): ?> + getWarningText(); ?> + +
+ + + + + + + + + + + + + +
escapeHtml(__('Qliro Order Id')); ?>:getQliroOrderId(); ?>
escapeHtml(__('Qliro Reference')); ?>:getQliroReference(); ?>
escapeHtml(__('Payment Method')); ?>:getQliroMethod(); ?>
diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml new file mode 100644 index 0000000..93c4aad --- /dev/null +++ b/view/frontend/layout/checkout_cart_index.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + Qliro_QliroOne/js/view/cart/summary/fee + + Payment Fee + + + + + + + + + + + + \ No newline at end of file diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml new file mode 100644 index 0000000..c36b264 --- /dev/null +++ b/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + Qliro_QliroOne/js/view/payment/qliroone-payments + + + true + + + + + + + + + + + + + + + + + + + + Qliro_QliroOne/js/view/checkout/cart/totals/fee + 20 + + Qliro_QliroOne/checkout/cart/totals/fee + Payment Fee + + + + + + + + + + + + + + + + + diff --git a/view/frontend/layout/checkout_qliro_index.xml b/view/frontend/layout/checkout_qliro_index.xml new file mode 100644 index 0000000..7fbde53 --- /dev/null +++ b/view/frontend/layout/checkout_qliro_index.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + true + + + + + true + + + + true + + + + + true + + + + + true + + + + + true + + + + + + true + + + + + + + + + uiComponent + + + + true + + + + Magento_SalesRule/js/view/payment/discount + 0 + + + 0 + Magento_SalesRule/js/view/payment/discount-messages + messages + + + + + + + Qliro_QliroOne/js/view/qliroone-view + 10 + qlirooneCustomCheckout + + + + + + + + + + true + + + + + + + + + + + + diff --git a/view/frontend/layout/checkout_qliro_pending.xml b/view/frontend/layout/checkout_qliro_pending.xml new file mode 100644 index 0000000..02a8a6f --- /dev/null +++ b/view/frontend/layout/checkout_qliro_pending.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/view/frontend/layout/checkout_qliro_success.xml b/view/frontend/layout/checkout_qliro_success.xml new file mode 100644 index 0000000..c5b4f75 --- /dev/null +++ b/view/frontend/layout/checkout_qliro_success.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/view/frontend/layout/sales_email_order_creditmemo_items.xml b/view/frontend/layout/sales_email_order_creditmemo_items.xml new file mode 100644 index 0000000..ab0b86d --- /dev/null +++ b/view/frontend/layout/sales_email_order_creditmemo_items.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_email_order_invoice_items.xml b/view/frontend/layout/sales_email_order_invoice_items.xml new file mode 100644 index 0000000..2dad8e0 --- /dev/null +++ b/view/frontend/layout/sales_email_order_invoice_items.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_email_order_items.xml b/view/frontend/layout/sales_email_order_items.xml new file mode 100644 index 0000000..bd79d9c --- /dev/null +++ b/view/frontend/layout/sales_email_order_items.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_guest_invoice.xml b/view/frontend/layout/sales_guest_invoice.xml new file mode 100644 index 0000000..dc2c910 --- /dev/null +++ b/view/frontend/layout/sales_guest_invoice.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_guest_print.xml b/view/frontend/layout/sales_guest_print.xml new file mode 100644 index 0000000..8d89bb9 --- /dev/null +++ b/view/frontend/layout/sales_guest_print.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_guest_printinvoice.xml b/view/frontend/layout/sales_guest_printinvoice.xml new file mode 100644 index 0000000..23ff298 --- /dev/null +++ b/view/frontend/layout/sales_guest_printinvoice.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_guest_view.xml b/view/frontend/layout/sales_guest_view.xml new file mode 100644 index 0000000..c317779 --- /dev/null +++ b/view/frontend/layout/sales_guest_view.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/view/frontend/layout/sales_order_creditmemo.xml b/view/frontend/layout/sales_order_creditmemo.xml new file mode 100644 index 0000000..3e0a371 --- /dev/null +++ b/view/frontend/layout/sales_order_creditmemo.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_order_invoice.xml b/view/frontend/layout/sales_order_invoice.xml new file mode 100644 index 0000000..23ff298 --- /dev/null +++ b/view/frontend/layout/sales_order_invoice.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_order_print.xml b/view/frontend/layout/sales_order_print.xml new file mode 100644 index 0000000..71ed030 --- /dev/null +++ b/view/frontend/layout/sales_order_print.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/view/frontend/layout/sales_order_view.xml b/view/frontend/layout/sales_order_view.xml new file mode 100644 index 0000000..71ed030 --- /dev/null +++ b/view/frontend/layout/sales_order_view.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/view/frontend/page_layout/qliroone-checkout.xml b/view/frontend/page_layout/qliroone-checkout.xml new file mode 100644 index 0000000..a89bd00 --- /dev/null +++ b/view/frontend/page_layout/qliroone-checkout.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js new file mode 100644 index 0000000..d4b8c60 --- /dev/null +++ b/view/frontend/requirejs-config.js @@ -0,0 +1,14 @@ +var config = { + map: { + '*': { + pollPending: 'Qliro_QliroOne/js/poll-pending' + } + }, + config: { + mixins: { + "Magento_Checkout/js/view/shipping": { + "Qliro_QliroOne/js/mixins/shipping": true + } + } + } +}; \ No newline at end of file diff --git a/view/frontend/templates/checkout/pending.phtml b/view/frontend/templates/checkout/pending.phtml new file mode 100644 index 0000000..85b75e2 --- /dev/null +++ b/view/frontend/templates/checkout/pending.phtml @@ -0,0 +1,32 @@ + + +
+
", + "texts": { + "loaderText": "", + "imgAlt": "" + } + } + }'> +
+
+ + diff --git a/view/frontend/templates/checkout/success.phtml b/view/frontend/templates/checkout/success.phtml new file mode 100644 index 0000000..f133080 --- /dev/null +++ b/view/frontend/templates/checkout/success.phtml @@ -0,0 +1,21 @@ + + +
+
+
+ escapeHtml(__('Order has successfully been placed. Order ID: #%1', $block->getIncrementId())); ?> +
+ +
+ getHtmlSnippet() ?> +
+
+
+ diff --git a/view/frontend/templates/info/qliroone.phtml b/view/frontend/templates/info/qliroone.phtml new file mode 100644 index 0000000..9b2bbba --- /dev/null +++ b/view/frontend/templates/info/qliroone.phtml @@ -0,0 +1,29 @@ + + +
escapeHtml($block->getMethod()->getTitle()); ?> + showWarning()): ?> + getWarningText(); ?> + +
+ + + + + + + + + + + + + +
escapeHtml(__('Qliro Order Id')); ?>:getQliroOrderId(); ?>
escapeHtml(__('Qliro Reference')); ?>:getQliroReference(); ?>
escapeHtml(__('Payment Method')); ?>:getQliroMethod(); ?>
diff --git a/view/frontend/web/css/qliro.css b/view/frontend/web/css/qliro.css new file mode 100644 index 0000000..c394484 --- /dev/null +++ b/view/frontend/web/css/qliro.css @@ -0,0 +1,95 @@ +.page-layout-qliroone-checkout .opc-payment-additional { + width: calc(100% - 48px); + max-width: 452px; + margin: 0 auto 32px; + background: #f8f8f8; + padding: 24px 24px 12px; +} + +.page-layout-qliroone-checkout .actions-toolbar { + margin: 12px 0; +} + +.page-layout-qliroone-checkout .payment-option-title { + margin-bottom: 12px; + cursor: pointer; +} + +.page-layout-qliroone-checkout .action-toggle:after { + position: relative; + display: inline-block; + content: '>'; + font-size: 16px; + line-height: 23px; + vertical-align: middle; + margin-left: 4px; +} + +.page-layout-qliroone-checkout ._active .action-toggle:after { + transform: rotate(90deg); +} + +.checkout-qliro-success .minicart-wrapper { + display: none; /* Hide minicart since quote might note be cleared on success */ +} + +.qliroone-checkout-processed { + text-align: center; +} + +.qliroone-checkout-processed .loading-mask { + position: relative; +} + +.qliroone-checkout-processed .loading-mask .loader > img { + position: relative; + margin-bottom: 16px; +} + +.qliroone-checkout-processed .loading-mask .loader > p { + font-size: 2.2rem; + display: block; +} + +.qliroone-checkout-success { + text-align: center; + font-size: 1.6rem; +} + +@media only screen and (min-width: 780px) { + .page-layout-qliroone-checkout .checkout-container { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + } + + .page-layout-qliroone-checkout .checkout-container .authentication-wrapper { + width: 100%; + max-width: 100%; + } + + .page-layout-qliroone-checkout .checkout-container .opc-sidebar { + width: 100%; + float: none; + -ms-flex-order: 1; + -webkit-order: 1; + order: 1; + } + + .page-layout-qliroone-checkout .checkout-container .opc-wrapper { + width: 100%; + float: none; + -ms-flex-order: 2; + -webkit-order: 2; + order: 2; + } + + .page-layout-qliroone-checkout .checkout-container .opc-payment-additional .actions-toolbar { + margin-left: 0; + } +} diff --git a/view/frontend/web/js/mixins/shipping.js b/view/frontend/web/js/mixins/shipping.js new file mode 100644 index 0000000..320bbff --- /dev/null +++ b/view/frontend/web/js/mixins/shipping.js @@ -0,0 +1,19 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + * + * Remove template rendering for this component + */ + +define([], function () { + 'use strict'; + + return function (shippingFunction) { + var result = {}; + if (window.checkoutConfig.qliro.enabled) { + result = {defaults: {template: ''}}; + } + return shippingFunction.extend(result); + } +}); + diff --git a/view/frontend/web/js/model/config.js b/view/frontend/web/js/model/config.js new file mode 100644 index 0000000..e11df7f --- /dev/null +++ b/view/frontend/web/js/model/config.js @@ -0,0 +1,10 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define([], function () { + 'use strict'; + + return window.checkoutConfig && window.checkoutConfig.qliro || {}; +}); diff --git a/view/frontend/web/js/model/qliro.js b/view/frontend/web/js/model/qliro.js new file mode 100644 index 0000000..2ce2db8 --- /dev/null +++ b/view/frontend/web/js/model/qliro.js @@ -0,0 +1,194 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +// @codingStandardsIgnoreFile +// phpcs:ignoreFile + +define([ + 'jquery', + 'Qliro_QliroOne/js/model/config', + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/model/cart/totals-processor/default', + 'Magento_Checkout/js/model/cart/cache', + 'Magento_Customer/js/customer-data', + 'mage/translate' +], function( + $, + config, + quote, + totalsProcessor, + cartCache, + customerData, + __ +) { + function sendUpdateQuote() { + return ( + $.ajax({ + url: config.updateQuoteUrl + '?quote_id=' + quote.getQuoteId() + '&token=' + config.securityToken, + method: 'POST' + }) + ) + } + + function sendAjaxAsJson(url, data) { + qliroDebug('Calling sendAjaxAsJson', data); + return $.ajax({ + url: url + '?token=' + config.securityToken, + method: 'POST', + data: JSON.stringify(data), + processData: false, + contentType: 'application/json' + }); + } + + function showErrorMessage(message) { + qliroDebug('Calling showErrorMessage', message); + customerData.set('messages', { + messages: [{ + type: 'error', + text: message + }] + }); + } + + function qliroDebug(caption, data) { + if (config.isDebug) { + console.log(caption, data); + } + } + + function qliroSuccessDebug(caption, data) { + qliroDebug('Success: ' + caption, data); + } + + function updateTotals() { + cartCache.set('totals', null); + totalsProcessor.estimateTotals(quote.shippingAddress()); + } + + return { + updateCart: function() { + if (!config.isEagerCheckoutRefresh) { + window.q1.lock(); + } else { + qliroDebug('Skipping checkout lock.'); + } + + sendUpdateQuote() + .then( + function(data) { + var unmatchCount = 0; + + window.q1.onOrderUpdated(function(order) { + if (config.isEagerCheckoutRefresh) { + qliroDebug('Skipping checkout update polling.'); + + return true; + } + + if (Math.abs(order.totalPrice - data.order.totalPrice) < 0.005) { + unmatchCount = 0; + window.q1.unlock(); + } else { + unmatchCount++; + + if (unmatchCount > 3) { + unmatchCount = 0; + showErrorMessage(__('Store and Qliro One totals don\'t match. Refresh the page.')); + } + } + }) + }, + function(response, state, reason) { + var data = response.responseJSON || {}; + + if (!config.isEagerCheckoutRefresh) { + window.q1.unlock(); + } else { + qliroDebug('Skipping checkout unlock.'); + } + + showErrorMessage(data.error || reason); + } + ); + }, + + onCheckoutLoaded: function() { + qliroSuccessDebug('onCheckoutLoaded', q1); + }, + + onCustomerInfoChanged: function(customer) { + sendAjaxAsJson(config.updateCustomerUrl, customer).then( + function(data) { + qliroSuccessDebug('onCustomerInfoChanged', data); + }, + function(response) { + var data = response.responseJSON || {}; + var error = data.error || __('Something went wrong while updating customer.'); + showErrorMessage(error); + } + ); + }, + + onPaymentDeclined: function(declineReason) { + $(".opc-block-summary").show(); + $(".discount-code").show(); + qliroSuccessDebug('onPaymentDeclined', declineReason); + }, + + onPaymentMethodChanged: function(paymentMethod) { + sendAjaxAsJson(config.updatePaymentMethodUrl, paymentMethod).then( + function(data) { + qliroSuccessDebug('onPaymentMethodChanged', data); + updateTotals(); + }, + function(response) { + var data = response.responseJSON || {}; + var error = data.error || __('Something went wrong while updating payment method.'); + showErrorMessage(error); + } + ); + }, + + onPaymentProcess: function() { + $(".opc-block-summary").hide(); + $(".discount-code").hide(); + qliroSuccessDebug('onPaymentProcess', q1); + }, + + onSessionExpired: function() { + qliroSuccessDebug('onSessionExpired', q1); + }, + + onShippingMethodChanged: function(shipping) { + sendAjaxAsJson(config.updateShippingMethodUrl, shipping).then( + function(data) { + qliroSuccessDebug('onShippingMethodChanged', data); + updateTotals(); + }, + function(response) { + var data = response.responseJSON || {}; + var error = data.error || __('Something went wrong while updating shipping method.'); + showErrorMessage(error); + } + ); + }, + + onShippingPriceChanged: function(newShippingPrice) { + sendAjaxAsJson(config.updateShippingPriceUrl, {newShippingPrice: newShippingPrice}).then( + function(data) { + qliroSuccessDebug('onShippingPriceChanged', data); + updateTotals(); + }, + function(response) { + var data = response.responseJSON || {}; + var error = data.error || __('Something went wrong while updating shipping method options.'); + showErrorMessage(error); + } + ); + } + } +}); + diff --git a/view/frontend/web/js/poll-pending.js b/view/frontend/web/js/poll-pending.js new file mode 100644 index 0000000..2e6ae57 --- /dev/null +++ b/view/frontend/web/js/poll-pending.js @@ -0,0 +1,112 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +// @codingStandardsIgnoreFile +// phpcs:ignoreFile + +define([ + 'jquery', + 'mage/url', + 'mage/translate', + 'Magento_Ui/js/model/messageList', + 'mage/cookies' +], function ($, url, __, messageList) { + 'use strict'; + + $.widget('qliro.pollPending', { + waitingResponse: false, + counter: 0, + response: 'PENDING', + htmlSnippet: null, + errorMessage: null, + orderId: null, + + _create: function () { + this.debug('Initialize polling...'); + + $('.qliroone-checkout-processed').trigger('processStart'); + + this.poll().then(function() { + $('.qliroone-checkout-processed').hide().trigger('processStop'); + }.bind(this)); + }, + + makeAjaxRequest: function() { + if (!this.waitingResponse) { + this.waitingResponse = true; + + this.debug('Polling', this.options.pollPendingUrl); + + $.ajax({ + url: this.options.pollPendingUrl, + method: 'POST' + }).then( + function (data) { + this.debug('Polled data', data); + + this.response = data.status || 'PENDING'; + + if (data.status === 'OK') { + this.orderId = data.orderIncrementId; + this.htmlSnippet = data.htmlSnippet; + } + + this.waitingResponse = false; + }.bind(this), + function (response) { + var data = response.responseJSON || {}; + + this.debug('Poll has failed', data); + + this.response = data.status || 'FAILED'; + this.errorMessage = data.error; + + this.waitingResponse = false; + }.bind(this) + ); + } + }, + + poll: function() { + return new Promise(function(resolve) { + var polling = setInterval(function() { + switch(this.response) { + case 'OK': + this.debug('[DONE]'); + clearInterval(polling); + resolve('DONE'); + location = url.build('checkout/qliro/success'); + break; + case 'FAILED': + this.debug('[FAILED]'); + clearInterval(polling); + this.queueErrorMessage(); + $.mage.cookies.clear('QOMR'); + location = url.build('checkout/cart'); + break; + case 'PENDING': + default: + this.debug('... still pending ...'); + this.makeAjaxRequest(); + } + }.bind(this), 1000) + }.bind(this)) + }, + + debug: function(caption, data) { + if (this.options.isDebug) { + if (data) { + console.log(caption, data); + } else { + console.log(caption); + } + } + }, + + queueErrorMessage: function() { + messageList.addErrorMessage({ message: this.errorMessage }); + } + }); +}); diff --git a/view/frontend/web/js/view/cart/summary/fee.js b/view/frontend/web/js/view/cart/summary/fee.js new file mode 100644 index 0000000..dfe9823 --- /dev/null +++ b/view/frontend/web/js/view/cart/summary/fee.js @@ -0,0 +1,59 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define( + [ + 'Magento_Checkout/js/view/summary/abstract-total', + 'Magento_Checkout/js/model/quote', + 'Magento_Catalog/js/price-utils', + 'Magento_Checkout/js/model/totals' + ], + function (Component, quote, priceUtils, totals) { + "use strict"; + return Component.extend({ + defaults: { + isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, + template: 'Qliro_QliroOne/cart/summary/fee' + }, + totals: quote.getTotals(), + isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, + isDisplayed: function() { + return this.isFullMode(); + }, + getValue: function() { + var price = null; + if (this.totals()) { + price = this.getTotalsFeeValue(); + } + return (price || this.getTitle()) ? this.getFormattedPrice(price) : null; + }, + getTotalsFeeValue: function() { + var totalsPaymentFeeSegment = totals.getSegment('qliroone_fee'); + return totalsPaymentFeeSegment ? totalsPaymentFeeSegment.value : null; + }, + getBaseValue: function() { + var price = null; + if (this.totals()) { + price = this.totals().base_payment_charge; + } + return (price || this.getTitle()) ? priceUtils.formatPrice(price, quote.getBasePriceFormat()) : null; + }, + getTitle: function() { + if (this.totals()) { + var totalSegments = this.totals().total_segments; + if (totalSegments) { + for (var i = 0; i < totalSegments.length; i++) { + if (totalSegments[i].code == 'qliroone_fee' && totalSegments[i].title) { + return totalSegments[i].title; + } + } + } + } + var feeSection = window.checkoutConfig.qliro.qliroone_fee; + return feeSection.fee_setup ? feeSection.fee_setup.description : null; + }, + }); + } +); \ No newline at end of file diff --git a/view/frontend/web/js/view/cart/totals/fee.js b/view/frontend/web/js/view/cart/totals/fee.js new file mode 100644 index 0000000..fa92456 --- /dev/null +++ b/view/frontend/web/js/view/cart/totals/fee.js @@ -0,0 +1,25 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define( + [ + 'Qliro_QliroOne/js/view/cart/summary/fee' + ], + function (Component) { + 'use strict'; + + return Component.extend({ + + /** + * @override + * + * @returns {boolean} + */ + isDisplayed: function () { + return true; + } + }); + } +); diff --git a/view/frontend/web/js/view/checkout/cart/totals/fee.js b/view/frontend/web/js/view/checkout/cart/totals/fee.js new file mode 100644 index 0000000..162cda4 --- /dev/null +++ b/view/frontend/web/js/view/checkout/cart/totals/fee.js @@ -0,0 +1,39 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define( + [ + 'Qliro_QliroOne/js/view/checkout/summary/fee', + 'Magento_Checkout/js/model/quote', + 'Magento_Catalog/js/price-utils', + 'ko' + ], + function (Component, quote, priceUtils, ko) { + 'use strict'; + + return Component.extend({ + showPaymentFee: ko.observable(false), + + getValue: function() { + var price = null, + paymentFee; + + if (this.totals()) { + price = this.getTotalsFeeValue(); + } + + paymentFee = (price || this.getTitle()) ? priceUtils.formatPrice(price, quote.getBasePriceFormat()) : null; + + if (paymentFee) { + this.showPaymentFee(true); + } else { + this.showPaymentFee(false); + } + + return (price || this.getTitle()) ? this.getFormattedPrice(price) : null; + } + }); + } +); diff --git a/view/frontend/web/js/view/checkout/summary/fee.js b/view/frontend/web/js/view/checkout/summary/fee.js new file mode 100644 index 0000000..29281ba --- /dev/null +++ b/view/frontend/web/js/view/checkout/summary/fee.js @@ -0,0 +1,59 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define( + [ + 'Magento_Checkout/js/view/summary/abstract-total', + 'Magento_Checkout/js/model/quote', + 'Magento_Catalog/js/price-utils', + 'Magento_Checkout/js/model/totals' + ], + function (Component, quote, priceUtils, totals) { + "use strict"; + return Component.extend({ + defaults: { + isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, + template: 'Qliro_QliroOne/checkout/summary/fee' + }, + totals: quote.getTotals(), + isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, + isDisplayed: function() { + return this.isFullMode(); + }, + getValue: function() { + var price = null; + if (this.totals()) { + price = this.getTotalsFeeValue(); + } + return (price || this.getTitle()) ? this.getFormattedPrice(price) : null; + }, + getTotalsFeeValue: function() { + var totalsPaymentFeeSegment = totals.getSegment('qliroone_fee'); + return totalsPaymentFeeSegment ? totalsPaymentFeeSegment.value : null; + }, + getBaseValue: function() { + var price = null; + if (this.totals()) { + price = this.totals().base_payment_charge; + } + return (price || this.getTitle()) ? priceUtils.formatPrice(price, quote.getBasePriceFormat()) : null; + }, + getTitle: function() { + if (this.totals()) { + var totalSegments = this.totals().total_segments; + if (totalSegments) { + for (var i = 0; i < totalSegments.length; i++) { + if (totalSegments[i].code == 'qliroone_fee' && totalSegments[i].title) { + return totalSegments[i].title; + } + } + } + } + var feeSection = window.checkoutConfig.qliro.qliroone_fee; + return feeSection.fee_setup ? feeSection.fee_setup.default.description : null; + }, + }); + } +); \ No newline at end of file diff --git a/view/frontend/web/js/view/payment/method-renderer/qliroone-method.js b/view/frontend/web/js/view/payment/method-renderer/qliroone-method.js new file mode 100644 index 0000000..abdc925 --- /dev/null +++ b/view/frontend/web/js/view/payment/method-renderer/qliroone-method.js @@ -0,0 +1,18 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define([ + 'Magento_Checkout/js/view/payment/default' + ], + function (Component) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Qliro_QliroOne/payment/qliroone' + } + }); + } +); diff --git a/view/frontend/web/js/view/payment/qliroone-payments.js b/view/frontend/web/js/view/payment/qliroone-payments.js new file mode 100644 index 0000000..43b54d5 --- /dev/null +++ b/view/frontend/web/js/view/payment/qliroone-payments.js @@ -0,0 +1,23 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +define([ + 'uiComponent', + 'Magento_Checkout/js/model/payment/renderer-list' + ], + function (Component, rendererList) { + 'use strict'; + + rendererList.push( + { + type: 'qliroone', + component: 'Qliro_QliroOne/js/view/payment/method-renderer/qliroone-method' + } + ); + + /** Add view logic here if needed */ + return Component.extend({}); + }); + diff --git a/view/frontend/web/js/view/qliroone-view.js b/view/frontend/web/js/view/qliroone-view.js new file mode 100644 index 0000000..2409e88 --- /dev/null +++ b/view/frontend/web/js/view/qliroone-view.js @@ -0,0 +1,97 @@ +/** + * Copyright © Qliro AB. All rights reserved. + * See LICENSE.txt for license details. + */ + +// @codingStandardsIgnoreFile +// phpcs:ignoreFile + +define([ + 'ko', + 'uiComponent', + 'underscore', + 'Magento_Checkout/js/model/step-navigator', + 'Magento_Checkout/js/model/shipping-service', + 'Magento_Customer/js/customer-data', + 'Magento_Checkout/js/model/quote', + 'Qliro_QliroOne/js/model/qliro', + 'Qliro_QliroOne/js/model/config' +], function ( + ko, + Component, + _, + stepNavigator, + shippingService, + customerData, + quote, + qliro, + config +) { + 'use strict'; + return Component.extend({ + defaults: { + template: 'Qliro_QliroOne/checkout/onepage', + imports: { + discountApplied: 'checkout.steps.billing-step.discount:isApplied' + }, + }, + discountApplied: ko.observable(false), + + isVisible: ko.observable(true), + + initialize: function () { + this._super(); + + this.initializeSubscribers(); + this.initializeQliro(); + + // if (!quote.isVirtual()) { + // stepNavigator.registerStep('shipping', null, 'Shipping', false, _.bind(this.navigate, this), 0); + // } + + stepNavigator.registerStep( + 'qliroone-step', + null, + config.checkoutTitle, + this.isVisible, + _.bind(this.navigate, this), + 100 + ); + + return this; + }, + + initializeSubscribers: function() { + this.discountApplied.subscribe(function() { + qliro.updateCart(); + }); + }, + + initializeQliro: function() { + window.q1Ready = function(q1) { + console.log(q1); // Debugging + q1.onCheckoutLoaded(qliro.onCheckoutLoaded); + q1.onCustomerInfoChanged(qliro.onCustomerInfoChanged); + q1.onPaymentDeclined(qliro.onPaymentDeclined); + q1.onPaymentMethodChanged(qliro.onPaymentMethodChanged); + q1.onPaymentProcess(qliro.onPaymentProcess); + q1.onSessionExpired(qliro.onSessionExpired); + q1.onShippingMethodChanged(qliro.onShippingMethodChanged); + q1.onShippingPriceChanged(qliro.onShippingPriceChanged); + } + }, + + /** + * The navigate() method is responsible for navigation between checkout step + * during checkout. You can add custom logic, for example some conditions + * for switching to your custom step. (This method is required even though it + * is blank, don't delete) + */ + navigate: function () { + }, + + navigateToNextStep: function () { + stepNavigator.next(); + } + }); +}); diff --git a/view/frontend/web/template/cart/summary/fee.html b/view/frontend/web/template/cart/summary/fee.html new file mode 100644 index 0000000..da17195 --- /dev/null +++ b/view/frontend/web/template/cart/summary/fee.html @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/view/frontend/web/template/cart/totals/fee.html b/view/frontend/web/template/cart/totals/fee.html new file mode 100644 index 0000000..5d215a5 --- /dev/null +++ b/view/frontend/web/template/cart/totals/fee.html @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/view/frontend/web/template/checkout/cart/totals/fee.html b/view/frontend/web/template/checkout/cart/totals/fee.html new file mode 100644 index 0000000..a92f445 --- /dev/null +++ b/view/frontend/web/template/checkout/cart/totals/fee.html @@ -0,0 +1,12 @@ + + + + + + + diff --git a/view/frontend/web/template/checkout/onepage.html b/view/frontend/web/template/checkout/onepage.html new file mode 100644 index 0000000..ea7c73f --- /dev/null +++ b/view/frontend/web/template/checkout/onepage.html @@ -0,0 +1,10 @@ + + +
  • +
    +
    +
    +
  • diff --git a/view/frontend/web/template/checkout/summary/fee.html b/view/frontend/web/template/checkout/summary/fee.html new file mode 100644 index 0000000..da17195 --- /dev/null +++ b/view/frontend/web/template/checkout/summary/fee.html @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/view/frontend/web/template/payment/qliroone.html b/view/frontend/web/template/payment/qliroone.html new file mode 100644 index 0000000..0c67de9 --- /dev/null +++ b/view/frontend/web/template/payment/qliroone.html @@ -0,0 +1,39 @@ + +
    +
    + + +
    +
    + + + + +
    +
    + +
    +
    +
    +