MDL-69166 core_payment: show cost on the modal
[moodle.git] / payment / amd / src / gateways_modal.js
CommitLineData
e9de4309
SR
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * Contain the logic for the gateways modal.
18 *
19 * @module core_payment/gateways_modal
20 * @package core_payment
21 * @copyright 2019 Shamim Rezaie <shamim@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25import ModalFactory from 'core/modal_factory';
26import Templates from 'core/templates';
27import {get_string as getString} from 'core/str';
ad6df317 28import {getGatewaysSupportingCurrency} from './repository';
e9de4309 29import Selectors from './selectors';
ad6df317 30import ModalEvents from 'core/modal_events';
b23dcc37 31import PaymentEvents from 'core_payment/events';
8ef156b7 32import {add as addToast, addToastRegion} from 'core/toast';
b5507b86 33import Notification from 'core/notification';
b23dcc37 34import ModalGateways from './modal_gateways';
e9de4309
SR
35
36/**
37 * Register event listeners for the module.
38 *
39 * @param {string} nodeSelector The root to listen to.
40 */
9d773ee5
SR
41export const registerEventListenersBySelector = (nodeSelector) => {
42 document.querySelectorAll(nodeSelector).forEach((element) => {
43 registerEventListeners(element);
44 });
45};
e9de4309 46
9d773ee5
SR
47/**
48 * Register event listeners for the module.
49 *
50 * @param {HTMLElement} rootNode The root to listen to.
51 */
52export const registerEventListeners = (rootNode) => {
e9de4309
SR
53 rootNode.addEventListener('click', (e) => {
54 e.preventDefault();
55 show(rootNode, {focusOnClose: e.target});
56 });
57};
58
59/**
60 * Shows the gateway selector modal.
61 *
62 * @param {HTMLElement} rootNode
63 * @param {Object} options - Additional options
64 * @param {HTMLElement} options.focusOnClose The element to focus on when the modal is closed.
65 */
ad6df317 66const show = async(rootNode, {
e9de4309
SR
67 focusOnClose = null,
68} = {}) => {
ad6df317 69 const modal = await ModalFactory.create({
b23dcc37 70 type: ModalGateways.TYPE,
ad6df317
SR
71 title: await getString('selectpaymenttype', 'core_payment'),
72 body: await Templates.render('core_payment/gateways_modal', {}),
73 });
e9de4309 74
ad6df317 75 addToastRegion(modal.getRoot()[0]);
8ef156b7 76
ad6df317 77 modal.show();
e9de4309 78
ad6df317
SR
79 modal.getRoot().on(ModalEvents.hidden, () => {
80 // Destroy when hidden.
81 modal.destroy();
82 try {
83 focusOnClose.focus();
84 } catch (e) {
85 // eslint-disable-line
86 }
87 });
e9de4309 88
b23dcc37 89 modal.getRoot().on(PaymentEvents.proceed, (e) => {
ad6df317
SR
90 const root = modal.getRoot()[0];
91 const gateway = (root.querySelector(Selectors.values.gateway) || {value: ''}).value;
8ef156b7 92
ad6df317
SR
93 if (gateway) {
94 processPayment(
95 gateway,
96 rootNode.dataset.amount,
97 rootNode.dataset.currency,
98 rootNode.dataset.component,
99 rootNode.dataset.componentid,
100 rootNode.dataset.description,
101 ({success, message = ''}) => {
102 modal.hide();
103 if (success) {
104 Notification.addNotification({
105 message: message,
106 type: 'success',
107 });
108 location.reload();
109 } else {
110 Notification.alert('', message);
111 }
112 },
113 );
114 } else {
115 // We cannot use await in the following line.
116 // The reason is that we are preventing the default action of the save event being triggered,
117 // therefore we cannot define the event handler function asynchronous.
118 getString('nogatewayselected', 'core_payment').then(message => addToast(message));
119 }
8ef156b7 120
ad6df317 121 e.preventDefault();
8ef156b7 122 });
ad6df317
SR
123
124 const currency = rootNode.dataset.currency;
125 const gateways = await getGatewaysSupportingCurrency(currency);
126 const context = {
127 gateways
128 };
129
130 const {html, js} = await Templates.renderForPromise('core_payment/gateways', context);
11b2d9e9
SR
131 const root = modal.getRoot()[0];
132 Templates.replaceNodeContents(root.querySelector(Selectors.regions.gatewaysContainer), html, js);
133 updateCostRegion(root, parseFloat(rootNode.dataset.amount), rootNode.dataset.currency);
8ef156b7 134};
11b2d9e9
SR
135
136/**
137 * Shows the cost of the item the user is purchasing in the cost region.
138 *
139 * @param {HTMLElement} root An HTMLElement that contains the cost region
140 * @param {number} amount The amount part of cost
141 * @param {string} currency The currency part of cost in the 3-letter ISO-4217 format
142 * @returns {Promise<void>}
143 */
144const updateCostRegion = async(root, amount, currency) => {
145 const locale = await updateCostRegion.locale; // This only takes a bit the first time.
146 const localisedCost = amount.toLocaleString(locale, {style: "currency", currency: currency});
147
148 const {html, js} = await Templates.renderForPromise('core_payment/fee_breakdown', {fee: localisedCost});
149 Templates.replaceNodeContents(root.querySelector(Selectors.regions.costContainer), html, js);
150};
151updateCostRegion.locale = getString("localecldr", "langconfig");
8ef156b7
SR
152
153/**
154 * Process payment using the selected gateway.
155 *
8ef156b7
SR
156 * @param {string} gateway The gateway to be used for payment
157 * @param {number} amount Amount of payment
158 * @param {string} currency The currency in the three-character ISO-4217 format
159 * @param {string} component Name of the component that the componentid belongs to
160 * @param {number} componentid An internal identifier that is used by the component
03f20edb 161 * @param {string} description Description of the payment
b5507b86 162 * @param {processPaymentCallback} callback The callback function to call when processing is finished
8ef156b7
SR
163 * @returns {Promise<void>}
164 */
b5507b86 165const processPayment = async(gateway, amount, currency, component, componentid, description, callback) => {
8ef156b7
SR
166 const paymentMethod = await import(`pg_${gateway}/gateways_modal`);
167
b5507b86 168 paymentMethod.process(amount, currency, component, componentid, description, callback);
e9de4309 169};
b5507b86
SR
170
171/**
172 * The callback definition for processPayment.
173 *
174 * @callback processPaymentCallback
175 * @param {bool} success
176 * @param {string} message
177 */