tonder-web-sdk 1.15.2 → 1.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +4 -0
- package/.prettierignore +8 -0
- package/.prettierrc +10 -0
- package/README.md +189 -35
- package/eslint.config.mjs +15 -0
- package/package.json +21 -4
- package/src/classes/3dsHandler.js +58 -62
- package/src/classes/BaseInlineCheckout.js +21 -36
- package/src/classes/LiteInlineCheckout.js +8 -8
- package/src/classes/checkout.js +75 -71
- package/src/classes/globalLoader.js +9 -7
- package/src/classes/inlineCheckout.js +528 -250
- package/src/data/apmApi.js +8 -14
- package/src/data/businessApi.js +5 -8
- package/src/data/cardApi.js +5 -14
- package/src/data/checkoutApi.js +54 -54
- package/src/data/customerApi.js +1 -6
- package/src/data/index.js +15 -15
- package/src/data/openPayApi.js +7 -7
- package/src/data/skyflowApi.js +14 -16
- package/src/helpers/skyflow.js +210 -119
- package/src/helpers/styles.js +56 -27
- package/src/helpers/template-skeleton.js +1 -1
- package/src/helpers/template.js +984 -541
- package/src/helpers/utils.js +152 -58
- package/src/helpers/validations.js +34 -35
- package/src/index-dev.js +38 -11
- package/src/index.html +20 -12
- package/src/index.js +19 -13
- package/src/shared/catalog/commonLogosCatalog.js +7 -0
- package/src/shared/catalog/paymentMethodsCatalog.js +242 -243
- package/src/shared/constants/colors.js +15 -0
- package/src/shared/constants/displayMode.js +4 -0
- package/src/shared/constants/fieldPathNames.js +4 -0
- package/src/shared/constants/htmlTonderIds.js +18 -0
- package/src/shared/constants/messages.js +10 -9
- package/types/card.d.ts +17 -17
- package/types/checkout.d.ts +85 -87
- package/types/common.d.ts +4 -1
- package/types/customer.d.ts +10 -10
- package/types/index.d.ts +9 -11
- package/types/inlineCheckout.d.ts +81 -61
- package/types/liteInlineCheckout.d.ts +78 -83
- package/types/paymentMethod.d.ts +17 -17
- package/types/transaction.d.ts +94 -94
- package/v1/bundle.min.js +3 -3
|
@@ -1,66 +1,98 @@
|
|
|
1
|
-
import { apmItemsTemplate, cardItemsTemplate, cardTemplate } from '../helpers/template.js'
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from
|
|
9
|
-
import { initSkyflow } from
|
|
10
|
-
import { globalLoader } from
|
|
2
|
+
apmItemsTemplate,
|
|
3
|
+
cardItemsTemplate,
|
|
4
|
+
cardTemplate,
|
|
5
|
+
containerCheckoutTemplate,
|
|
6
|
+
} from "../helpers/template.js";
|
|
7
|
+
import { clearSpace, executeCallback, mapCards, showError, showMessage } from "../helpers/utils";
|
|
8
|
+
import { initSkyflow, initUpdateSkyflow } from "../helpers/skyflow";
|
|
9
|
+
import { globalLoader } from "./globalLoader.js";
|
|
11
10
|
import { BaseInlineCheckout } from "./BaseInlineCheckout";
|
|
12
11
|
import {
|
|
12
|
+
fetchCustomerAPMs,
|
|
13
13
|
fetchCustomerCards,
|
|
14
14
|
removeCustomerCard,
|
|
15
15
|
saveCustomerCard,
|
|
16
|
-
fetchCustomerAPMs
|
|
17
16
|
} from "../data";
|
|
18
17
|
import { MESSAGES } from "../shared/constants/messages";
|
|
18
|
+
import Accordion from "accordion-js";
|
|
19
|
+
import get from "lodash.get";
|
|
20
|
+
import { HTML_IDS } from "../shared/constants/htmlTonderIds";
|
|
21
|
+
import { DISPLAY_MODE } from "../shared/constants/displayMode";
|
|
19
22
|
|
|
20
23
|
export class InlineCheckout extends BaseInlineCheckout {
|
|
21
24
|
static injected = false;
|
|
22
|
-
static cardsInjected = false
|
|
23
|
-
static apmsInjected = false
|
|
24
|
-
|
|
25
|
+
static cardsInjected = false;
|
|
26
|
+
static apmsInjected = false;
|
|
27
|
+
#cardsData = [];
|
|
28
|
+
#paymentMethodsData = [];
|
|
29
|
+
#customerData = {};
|
|
30
|
+
accordionCards = null;
|
|
31
|
+
accordionPaymentMethods = null;
|
|
32
|
+
|
|
25
33
|
deletingCards = [];
|
|
26
|
-
customer = {}
|
|
27
|
-
items = []
|
|
28
|
-
collectContainer = null
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
customer = {};
|
|
35
|
+
items = [];
|
|
36
|
+
collectContainer = null;
|
|
37
|
+
updateCollectContainer = null;
|
|
38
|
+
merchantData = {};
|
|
39
|
+
cartTotal = null;
|
|
40
|
+
metadata = {};
|
|
41
|
+
card = {};
|
|
33
42
|
collectorIds = {
|
|
34
|
-
cardsListContainer:
|
|
35
|
-
holderName:
|
|
36
|
-
cardNumber:
|
|
37
|
-
expirationMonth:
|
|
38
|
-
expirationYear:
|
|
39
|
-
cvv:
|
|
40
|
-
tonderPayButton:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
cardsListContainer: HTML_IDS.cardsListContainer,
|
|
44
|
+
holderName: HTML_IDS.collectCardholderName,
|
|
45
|
+
cardNumber: HTML_IDS.collectCardNumber,
|
|
46
|
+
expirationMonth: HTML_IDS.collectExpirationMonth,
|
|
47
|
+
expirationYear: HTML_IDS.collectExpirationYear,
|
|
48
|
+
cvv: HTML_IDS.collectCvv,
|
|
49
|
+
tonderPayButton: HTML_IDS.tonderPayButton,
|
|
50
|
+
tonderCancelButton: HTML_IDS.tonderCancelButton,
|
|
51
|
+
msgError: HTML_IDS.msgError,
|
|
52
|
+
msgNotification: HTML_IDS.msgNotification,
|
|
53
|
+
msgErrorText: HTML_IDS.msgErrorText,
|
|
54
|
+
msgNotificationText: HTML_IDS.msgNotificationText,
|
|
55
|
+
apmsListContainer: HTML_IDS.apmsListContainer,
|
|
56
|
+
};
|
|
45
57
|
customization = {
|
|
58
|
+
displayMode: DISPLAY_MODE.light,
|
|
46
59
|
saveCards: {
|
|
47
60
|
showSaveCardOption: false,
|
|
48
61
|
showSaved: false,
|
|
49
|
-
autoSave: false
|
|
50
|
-
}
|
|
51
|
-
|
|
62
|
+
autoSave: false,
|
|
63
|
+
},
|
|
64
|
+
paymentButton: {
|
|
65
|
+
show: false,
|
|
66
|
+
text: "Pagar",
|
|
67
|
+
showAmount: true,
|
|
68
|
+
},
|
|
69
|
+
cancelButton: {
|
|
70
|
+
show: false,
|
|
71
|
+
text: "Cancelar",
|
|
72
|
+
},
|
|
73
|
+
paymentMethods: {
|
|
74
|
+
show: true,
|
|
75
|
+
},
|
|
76
|
+
cardForm: {
|
|
77
|
+
show: true,
|
|
78
|
+
},
|
|
79
|
+
showMessages: true,
|
|
80
|
+
};
|
|
81
|
+
callbacks = {
|
|
82
|
+
onCancel: () => {},
|
|
83
|
+
};
|
|
52
84
|
constructor({
|
|
53
85
|
mode = "stage",
|
|
54
86
|
apiKey,
|
|
55
87
|
returnUrl,
|
|
56
|
-
|
|
57
|
-
callBack = () => { },
|
|
88
|
+
callBack = () => {},
|
|
58
89
|
styles,
|
|
59
90
|
customization,
|
|
91
|
+
callbacks,
|
|
60
92
|
}) {
|
|
61
93
|
super({ mode, apiKey, returnUrl, callBack });
|
|
62
|
-
this.
|
|
63
|
-
this.
|
|
94
|
+
this.customStyles = styles;
|
|
95
|
+
this.callbacks = { ...this.callbacks, ...(callbacks ? { ...callbacks } : {}) };
|
|
64
96
|
this.abortRefreshCardsController = new AbortController();
|
|
65
97
|
// TODO: Wait until SaveCards is ready (server token).
|
|
66
98
|
this.customization = {
|
|
@@ -70,54 +102,21 @@ export class InlineCheckout extends BaseInlineCheckout {
|
|
|
70
102
|
...this.customization.saveCards,
|
|
71
103
|
...(customization?.saveCards || {}),
|
|
72
104
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
payButton.style.display = "block";
|
|
86
|
-
payButton.textContent = `Pagar $${this.cartTotal}`;
|
|
87
|
-
payButton.onclick = async (event) => {
|
|
88
|
-
event.preventDefault();
|
|
89
|
-
await this.#handlePaymentClick(payButton);
|
|
105
|
+
paymentButton: {
|
|
106
|
+
...this.customization.paymentButton,
|
|
107
|
+
...(customization?.paymentButton || {}),
|
|
108
|
+
},
|
|
109
|
+
paymentMethods: {
|
|
110
|
+
...this.customization.paymentMethods,
|
|
111
|
+
...(customization?.paymentMethods || {}),
|
|
112
|
+
},
|
|
113
|
+
cardForm: {
|
|
114
|
+
...this.customization.cardForm,
|
|
115
|
+
...(customization?.cardForm || {}),
|
|
116
|
+
},
|
|
90
117
|
};
|
|
91
118
|
}
|
|
92
119
|
|
|
93
|
-
async #handlePaymentClick(payButton) {
|
|
94
|
-
const prevButtonContent = payButton.innerHTML;
|
|
95
|
-
payButton.innerHTML = `<div class="lds-dual-ring"></div>`;
|
|
96
|
-
try {
|
|
97
|
-
const response = await this.payment(this.customer);
|
|
98
|
-
this.callBack(response);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error("Payment error:", error);
|
|
101
|
-
} finally {
|
|
102
|
-
payButton.innerHTML = prevButtonContent;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
_setCartTotal(total) {
|
|
107
|
-
this.cartTotal = total
|
|
108
|
-
this.#updatePayButton()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
#updatePayButton() {
|
|
112
|
-
const payButton = document.querySelector("#tonderPayButton");
|
|
113
|
-
if (!payButton) return
|
|
114
|
-
payButton.textContent = `Pagar $${this.cartTotal}`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
setCallback(cb) {
|
|
118
|
-
this.cb = cb
|
|
119
|
-
}
|
|
120
|
-
|
|
121
120
|
/**
|
|
122
121
|
* Injects the checkout into the DOM and initializes it.
|
|
123
122
|
* Checks for an existing container and sets up an observer if needed.
|
|
@@ -125,81 +124,28 @@ export class InlineCheckout extends BaseInlineCheckout {
|
|
|
125
124
|
* @public
|
|
126
125
|
*/
|
|
127
126
|
async injectCheckout() {
|
|
128
|
-
if (InlineCheckout.injected) return
|
|
129
|
-
const containerTonderCheckout = document.querySelector(
|
|
127
|
+
if (InlineCheckout.injected) return;
|
|
128
|
+
const containerTonderCheckout = document.querySelector(`#${HTML_IDS.tonderCheckout}`);
|
|
130
129
|
if (containerTonderCheckout) {
|
|
131
|
-
await this.#mount(containerTonderCheckout)
|
|
130
|
+
await this.#mount(containerTonderCheckout);
|
|
132
131
|
return;
|
|
133
132
|
}
|
|
134
133
|
const observer = new MutationObserver(async (mutations, obs) => {
|
|
135
|
-
const containerTonderCheckout = document.querySelector(
|
|
134
|
+
const containerTonderCheckout = document.querySelector(`#${HTML_IDS.tonderCheckout}`);
|
|
136
135
|
if (containerTonderCheckout) {
|
|
137
|
-
await this.#mount(containerTonderCheckout)
|
|
136
|
+
await this.#mount(containerTonderCheckout);
|
|
138
137
|
obs.disconnect();
|
|
139
138
|
}
|
|
140
139
|
});
|
|
141
140
|
observer.observe(document.body, {
|
|
142
141
|
childList: true,
|
|
143
142
|
subtree: true,
|
|
144
|
-
attributeFilter: [
|
|
143
|
+
attributeFilter: ["id"],
|
|
145
144
|
});
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
globalLoader.show()
|
|
151
|
-
await this.#mountTonder();
|
|
152
|
-
InlineCheckout.injected = true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async #mountAPMs() {
|
|
156
|
-
try {
|
|
157
|
-
const apms = await fetchCustomerAPMs(this.baseUrl, this.apiKeyTonder);
|
|
158
|
-
if (apms && apms['results'] && apms['results'].length > 0) {
|
|
159
|
-
this.apmsData = apms['results']
|
|
160
|
-
this.#loadAPMList(apms['results'])
|
|
161
|
-
}
|
|
162
|
-
} catch (e) {
|
|
163
|
-
console.warn("Error getting APMS")
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async #mountTonder(getCards = true) {
|
|
168
|
-
this.#mountPayButton()
|
|
169
|
-
await this._initializeCheckout()
|
|
170
|
-
try {
|
|
171
|
-
const {
|
|
172
|
-
vault_id,
|
|
173
|
-
vault_url
|
|
174
|
-
} = this.merchantData;
|
|
175
|
-
if (this.email && getCards) {
|
|
176
|
-
const customerResponse = await this._getCustomer({ email: this.email });
|
|
177
|
-
if ("auth_token" in customerResponse) {
|
|
178
|
-
const { auth_token } = customerResponse
|
|
179
|
-
await this.#loadCardsList(auth_token)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
await this.#mountAPMs();
|
|
184
|
-
|
|
185
|
-
this.collectContainer = await initSkyflow(
|
|
186
|
-
vault_id,
|
|
187
|
-
vault_url,
|
|
188
|
-
this.baseUrl,
|
|
189
|
-
this.apiKeyTonder,
|
|
190
|
-
this.abortController.signal,
|
|
191
|
-
this.customStyles,
|
|
192
|
-
this.collectorIds
|
|
193
|
-
);
|
|
194
|
-
setTimeout(() => {
|
|
195
|
-
globalLoader.remove()
|
|
196
|
-
}, 800)
|
|
197
|
-
} catch (e) {
|
|
198
|
-
if (e && e.name !== 'AbortError') {
|
|
199
|
-
globalLoader.remove()
|
|
200
|
-
showError("No se pudieron cargar los datos del comercio.")
|
|
201
|
-
}
|
|
202
|
-
}
|
|
147
|
+
setCallback(cb) {
|
|
148
|
+
this.cb = cb;
|
|
203
149
|
}
|
|
204
150
|
|
|
205
151
|
/**
|
|
@@ -218,9 +164,9 @@ export class InlineCheckout extends BaseInlineCheckout {
|
|
|
218
164
|
* @public
|
|
219
165
|
*/
|
|
220
166
|
removeCheckout() {
|
|
221
|
-
InlineCheckout.injected = false
|
|
222
|
-
InlineCheckout.cardsInjected = false
|
|
223
|
-
InlineCheckout.apmsInjected = false
|
|
167
|
+
InlineCheckout.injected = false;
|
|
168
|
+
InlineCheckout.cardsInjected = false;
|
|
169
|
+
InlineCheckout.apmsInjected = false;
|
|
224
170
|
// Cancel all requests
|
|
225
171
|
this.abortController.abort();
|
|
226
172
|
this.abortController = new AbortController();
|
|
@@ -229,174 +175,399 @@ export class InlineCheckout extends BaseInlineCheckout {
|
|
|
229
175
|
console.log("InlineCheckout removed from DOM and cleaned up.");
|
|
230
176
|
}
|
|
231
177
|
|
|
232
|
-
async #
|
|
233
|
-
|
|
178
|
+
async #mount(containerTonderCheckout) {
|
|
179
|
+
containerTonderCheckout.innerHTML = containerCheckoutTemplate({
|
|
180
|
+
customStyles: this.customStyles,
|
|
181
|
+
customization: this.customization,
|
|
182
|
+
});
|
|
183
|
+
globalLoader.show();
|
|
184
|
+
await this._initializeCheckout();
|
|
185
|
+
await this.#loadInitialData();
|
|
186
|
+
const currentContent = document.querySelector(`#${HTML_IDS.tonderContainer}`);
|
|
187
|
+
currentContent.innerHTML = `
|
|
188
|
+
${currentContent.innerHTML}
|
|
189
|
+
${cardTemplate({
|
|
190
|
+
customStyles: this.customStyles,
|
|
191
|
+
customization: this.customization,
|
|
192
|
+
cardsData: this.#cardsData,
|
|
193
|
+
paymentMethodsData: this.#paymentMethodsData,
|
|
194
|
+
collectorIds: this.collectorIds,
|
|
195
|
+
})}
|
|
196
|
+
`;
|
|
197
|
+
await this.#mountTonder();
|
|
198
|
+
InlineCheckout.injected = true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async #mountTonder() {
|
|
234
202
|
try {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
203
|
+
const { vault_id, vault_url } = this.merchantData;
|
|
204
|
+
|
|
205
|
+
if (this.email && this.#cardsData.length > 0) {
|
|
206
|
+
await this.#loadCardsList();
|
|
207
|
+
}
|
|
208
|
+
this.#mountButtons();
|
|
209
|
+
await this.#loadAPMList();
|
|
210
|
+
|
|
211
|
+
this.collectContainer = await initSkyflow(
|
|
212
|
+
vault_id,
|
|
213
|
+
vault_url,
|
|
214
|
+
this.baseUrl,
|
|
215
|
+
this.apiKeyTonder,
|
|
216
|
+
this.abortController.signal,
|
|
217
|
+
this.customStyles,
|
|
218
|
+
this.collectorIds,
|
|
219
|
+
this.customization.displayMode,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
globalLoader.remove();
|
|
224
|
+
}, 800);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
if (e && e.name !== "AbortError") {
|
|
227
|
+
globalLoader.remove();
|
|
228
|
+
showError("No se pudieron cargar los datos del comercio.", this.radioChecked);
|
|
229
|
+
}
|
|
241
230
|
}
|
|
242
231
|
}
|
|
243
232
|
|
|
244
|
-
|
|
233
|
+
#mountButtons(cardId = "") {
|
|
234
|
+
if (this.customization.paymentButton.show) {
|
|
235
|
+
this.#mountButton(this.collectorIds.tonderPayButton, cardId, this.#handlePaymentClick);
|
|
236
|
+
}
|
|
237
|
+
if (this.customization.cancelButton.show) {
|
|
238
|
+
this.#mountButton(this.collectorIds.tonderCancelButton, cardId, async () => {
|
|
239
|
+
await executeCallback({
|
|
240
|
+
callbacks: this.callbacks,
|
|
241
|
+
callback: "onCancel",
|
|
242
|
+
throwError: true,
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const containerID = `#acContainer${cardId}`;
|
|
247
|
+
const container = document.querySelector(containerID);
|
|
248
|
+
document.querySelectorAll(".ac-option-panel-container").forEach(cont => {
|
|
249
|
+
cont.classList.remove("show");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (container) {
|
|
253
|
+
container.classList.add("show");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#mountButton(buttonId = "", cardId = "", fn = () => {}) {
|
|
258
|
+
if (!this.customization.paymentButton.show) return;
|
|
259
|
+
|
|
260
|
+
const btnID = `#${buttonId}${cardId}`;
|
|
261
|
+
const findButton = document.querySelector(btnID);
|
|
262
|
+
|
|
263
|
+
if (!findButton) {
|
|
264
|
+
console.error(`${buttonId} not found`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.#updateButton({
|
|
269
|
+
style: { display: "block" },
|
|
270
|
+
buttonId,
|
|
271
|
+
...(cardId ? { cardId } : {}),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
findButton.onclick = async event => {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
await fn();
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
#handlePaymentClick = async () => {
|
|
245
281
|
try {
|
|
246
|
-
|
|
282
|
+
const response = await this.payment();
|
|
283
|
+
this.callBack(response);
|
|
247
284
|
} catch (error) {
|
|
285
|
+
console.error("Payment error:", error);
|
|
248
286
|
}
|
|
249
|
-
|
|
250
|
-
let cardTokens;
|
|
287
|
+
};
|
|
251
288
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
289
|
+
_setCartTotal(total) {
|
|
290
|
+
this.cartTotal = total;
|
|
291
|
+
this.#updateButton();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
#updateButton(data) {
|
|
295
|
+
try {
|
|
296
|
+
const buttonDataId = data?.buttonId || this.collectorIds.tonderPayButton;
|
|
297
|
+
const btnID =
|
|
298
|
+
data?.cardId && data?.cardId !== "new"
|
|
299
|
+
? `#${buttonDataId}${data.cardId}`
|
|
300
|
+
: `#${buttonDataId}`;
|
|
301
|
+
const textButton =
|
|
302
|
+
buttonDataId === this.collectorIds.tonderPayButton
|
|
303
|
+
? `<div class="pay-button-text">${this.customization.paymentButton.text}${this.customization.paymentButton.showAmount ? ` $${this.cartTotal}` : ""}</div>`
|
|
304
|
+
: `<div class="cancel-button-text">${this.customization.cancelButton.text}</div>`;
|
|
305
|
+
|
|
306
|
+
const btnTextContent = data?.textContent || textButton;
|
|
307
|
+
const disabledBtn = data?.disabled;
|
|
308
|
+
const loadingHtml = data?.loading ? `<div class="spinner-tndr"></div>` : "";
|
|
309
|
+
const btnStyle = data?.style || {};
|
|
310
|
+
const payButton = document.querySelector(btnID);
|
|
311
|
+
if (!payButton) return;
|
|
312
|
+
if (loadingHtml !== "") {
|
|
313
|
+
payButton.innerHTML = loadingHtml;
|
|
314
|
+
} else {
|
|
315
|
+
payButton.innerHTML = btnTextContent;
|
|
316
|
+
}
|
|
317
|
+
if (btnStyle) {
|
|
318
|
+
Object.keys(btnStyle).forEach(btn => {
|
|
319
|
+
payButton.style[btn] = btnStyle[btn];
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
if (disabledBtn !== undefined && "disabled" in payButton) {
|
|
323
|
+
payButton.disabled = disabledBtn;
|
|
257
324
|
}
|
|
325
|
+
} catch (e) {
|
|
326
|
+
console.error("Pay button not found due to update", e);
|
|
258
327
|
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async #getCardTokens(cardSelected) {
|
|
331
|
+
if (this.card?.skyflow_id) return this.card;
|
|
259
332
|
try {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
333
|
+
const collectResponse =
|
|
334
|
+
cardSelected && cardSelected !== "new"
|
|
335
|
+
? await this.updateCollectContainer.container.collect()
|
|
336
|
+
: await this.collectContainer.container.collect();
|
|
337
|
+
return await collectResponse["records"][0]["fields"];
|
|
338
|
+
} catch (error) {
|
|
339
|
+
showError("Por favor, verifica todos los campos de tu tarjeta", this.radioChecked);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async _checkout() {
|
|
345
|
+
this.#updateButton({
|
|
346
|
+
cardId: this.radioChecked,
|
|
347
|
+
loading: true,
|
|
348
|
+
disabled: true,
|
|
349
|
+
});
|
|
350
|
+
try {
|
|
351
|
+
const { business } = this.merchantData;
|
|
352
|
+
let cardTokens;
|
|
353
|
+
const selected_apm = this.#paymentMethodsData
|
|
354
|
+
? this.#paymentMethodsData.find(iapm => iapm.pk === this.radioChecked)
|
|
355
|
+
: {};
|
|
356
|
+
|
|
357
|
+
if (this.radioChecked === "new" || this.radioChecked === undefined) {
|
|
358
|
+
cardTokens = await this.#getCardTokens(this.radioChecked);
|
|
359
|
+
} else {
|
|
360
|
+
if (!selected_apm) {
|
|
361
|
+
await this.#getCardTokens(this.radioChecked);
|
|
362
|
+
}
|
|
363
|
+
cardTokens = {
|
|
364
|
+
skyflow_id: this.radioChecked,
|
|
365
|
+
};
|
|
267
366
|
}
|
|
268
367
|
|
|
269
|
-
|
|
368
|
+
this.#customerData = await this._getCustomer(this.customer, this.abortController.signal);
|
|
369
|
+
if (this.email) {
|
|
370
|
+
await this.#handleSaveCard(business.pk, cardTokens);
|
|
371
|
+
}
|
|
270
372
|
|
|
271
373
|
const jsonResponseRouter = await this._handleCheckout({
|
|
272
374
|
...(selected_apm && Object.keys(selected_apm).length > 0
|
|
273
375
|
? { payment_method: selected_apm.payment_method }
|
|
274
376
|
: { card: cardTokens }),
|
|
275
|
-
customer: customerData
|
|
377
|
+
customer: this.#customerData,
|
|
276
378
|
});
|
|
277
379
|
|
|
278
380
|
if (jsonResponseRouter) {
|
|
279
|
-
try {
|
|
280
|
-
document.querySelector("#tonderPayButton").disabled = false;
|
|
281
|
-
} catch { }
|
|
282
381
|
return jsonResponseRouter;
|
|
283
382
|
} else {
|
|
284
|
-
showError("No se ha podido procesar el pago")
|
|
383
|
+
showError("No se ha podido procesar el pago", this.radioChecked);
|
|
285
384
|
return false;
|
|
286
385
|
}
|
|
287
386
|
} catch (error) {
|
|
288
|
-
console.log(error);
|
|
289
|
-
|
|
387
|
+
console.log("Error payment", error);
|
|
388
|
+
|
|
389
|
+
showError("Ha ocurrido un error", this.radioChecked);
|
|
290
390
|
throw error;
|
|
391
|
+
} finally {
|
|
392
|
+
this.#updateButton({ cardId: this.radioChecked, disabled: false });
|
|
291
393
|
}
|
|
292
|
-
}
|
|
394
|
+
}
|
|
293
395
|
|
|
294
|
-
async #handleSaveCard(
|
|
396
|
+
async #handleSaveCard(businessId, cardTokens) {
|
|
397
|
+
if (!this.#customerData.auth_token) return;
|
|
295
398
|
const saveCard = document.getElementById("save-checkout-card");
|
|
296
|
-
if (
|
|
399
|
+
if (
|
|
400
|
+
(saveCard && "checked" in saveCard && saveCard.checked) ||
|
|
401
|
+
!!this.customization.saveCards?.autoSave
|
|
402
|
+
) {
|
|
297
403
|
try {
|
|
298
404
|
await saveCustomerCard(
|
|
299
405
|
this.baseUrl,
|
|
300
|
-
auth_token,
|
|
406
|
+
this.#customerData.auth_token,
|
|
301
407
|
this.secureToken,
|
|
302
408
|
businessId,
|
|
303
|
-
{
|
|
409
|
+
{
|
|
410
|
+
skyflow_id: cardTokens.skyflow_id,
|
|
411
|
+
},
|
|
304
412
|
);
|
|
305
|
-
showMessage(MESSAGES.cardSaved, this.
|
|
413
|
+
showMessage(MESSAGES.cardSaved, this.radioChecked);
|
|
306
414
|
} catch (error) {
|
|
307
415
|
if (error?.message) {
|
|
308
|
-
showError(error.message)
|
|
416
|
+
showError(error.message, this.radioChecked);
|
|
309
417
|
}
|
|
310
418
|
}
|
|
311
419
|
|
|
312
|
-
await this.#loadCardsList(
|
|
420
|
+
await this.#loadCardsList();
|
|
313
421
|
}
|
|
314
422
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this.merchantData.business.pk,
|
|
323
|
-
);
|
|
324
|
-
let cards = []
|
|
325
|
-
if("cards" in cardsResponse) {
|
|
326
|
-
cards = cardsResponse.cards.map(mapCards)
|
|
423
|
+
|
|
424
|
+
async #loadCardsList() {
|
|
425
|
+
try {
|
|
426
|
+
if (this.cardsInjected || !this.customization.saveCards?.showSaved) return;
|
|
427
|
+
this.cardsInjected = false;
|
|
428
|
+
let cards = [];
|
|
429
|
+
cards = this.#cardsData.map(mapCards);
|
|
327
430
|
const injectInterval = setInterval(() => {
|
|
328
431
|
const queryElement = document.querySelector(`#${this.collectorIds.cardsListContainer}`);
|
|
329
432
|
if (queryElement && InlineCheckout.injected) {
|
|
330
|
-
queryElement.innerHTML = cardItemsTemplate(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
433
|
+
queryElement.innerHTML = cardItemsTemplate({
|
|
434
|
+
cards: cards,
|
|
435
|
+
customization: this.customization,
|
|
436
|
+
collectorIds: this.collectorIds,
|
|
437
|
+
customStyles: this.customStyles,
|
|
438
|
+
});
|
|
439
|
+
clearInterval(injectInterval);
|
|
440
|
+
this.#generateAccordion();
|
|
441
|
+
this.#mountRadioButtons();
|
|
442
|
+
this.cardsInjected = true;
|
|
334
443
|
}
|
|
335
444
|
}, 500);
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.warn("Error mount Customer Cards", error);
|
|
336
447
|
}
|
|
337
448
|
}
|
|
338
449
|
|
|
339
|
-
#loadAPMList(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
450
|
+
#loadAPMList() {
|
|
451
|
+
try {
|
|
452
|
+
if (this.apmsInjected || !this.customization.paymentMethods?.show) return;
|
|
453
|
+
const injectInterval = setInterval(() => {
|
|
454
|
+
const queryElement = document.querySelector(`#${this.collectorIds.apmsListContainer}`);
|
|
455
|
+
if (queryElement && InlineCheckout.injected) {
|
|
456
|
+
const filteredAndSortedApms = this.#paymentMethodsData
|
|
457
|
+
.filter(
|
|
458
|
+
apm =>
|
|
459
|
+
clearSpace(apm.category.toLowerCase()) !== "cards" &&
|
|
460
|
+
apm.status.toLowerCase() === "active",
|
|
461
|
+
)
|
|
462
|
+
.sort((a, b) => a.priority - b.priority);
|
|
463
|
+
queryElement.innerHTML = apmItemsTemplate({
|
|
464
|
+
paymentMethods: filteredAndSortedApms,
|
|
465
|
+
customization: this.customization,
|
|
466
|
+
collectorIds: this.collectorIds,
|
|
467
|
+
});
|
|
468
|
+
clearInterval(injectInterval);
|
|
469
|
+
this.#generateAccordion("paymentMethods");
|
|
470
|
+
this.#mountRadioButtons();
|
|
471
|
+
this.apmsInjected = true;
|
|
472
|
+
}
|
|
473
|
+
}, 500);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.warn("Error mount Payment Methods", error);
|
|
476
|
+
}
|
|
355
477
|
}
|
|
356
478
|
|
|
357
|
-
#
|
|
479
|
+
async #loadInitialData() {
|
|
480
|
+
try {
|
|
481
|
+
const canGetCards = this.email && this.customization.saveCards?.showSaved;
|
|
482
|
+
const pmResponsePromise = this.customization.paymentMethods?.show
|
|
483
|
+
? fetchCustomerAPMs(this.baseUrl, this.apiKeyTonder)
|
|
484
|
+
: Promise.resolve(null);
|
|
485
|
+
const customerDataPromise = canGetCards
|
|
486
|
+
? this._getCustomer({ email: this.email })
|
|
487
|
+
: Promise.resolve(null);
|
|
488
|
+
|
|
489
|
+
const [pmResponse, customerData] = await Promise.all([
|
|
490
|
+
pmResponsePromise,
|
|
491
|
+
customerDataPromise,
|
|
492
|
+
]);
|
|
493
|
+
|
|
494
|
+
this.#paymentMethodsData = get(pmResponse, "results", []);
|
|
495
|
+
this.#customerData = customerData;
|
|
496
|
+
|
|
497
|
+
if (canGetCards && customerData && "auth_token" in customerData) {
|
|
498
|
+
const { auth_token } = customerData;
|
|
499
|
+
const cardsResponse = await fetchCustomerCards(
|
|
500
|
+
this.baseUrl,
|
|
501
|
+
auth_token,
|
|
502
|
+
this.secureToken,
|
|
503
|
+
this.merchantData.business.pk,
|
|
504
|
+
);
|
|
505
|
+
this.#cardsData = get(cardsResponse, "cards", []);
|
|
506
|
+
}
|
|
507
|
+
} catch (e) {
|
|
508
|
+
console.warn("Error loading initial data", e);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
#mountRadioButtons() {
|
|
358
512
|
const radioButtons = document.getElementsByName(`card_selected`);
|
|
359
513
|
for (const radio of radioButtons) {
|
|
360
514
|
radio.style.display = "block";
|
|
361
|
-
radio.onclick = async
|
|
362
|
-
|
|
515
|
+
radio.onclick = async event => {
|
|
516
|
+
const position = Array.from(radioButtons).indexOf(radio);
|
|
517
|
+
const classType = radio.classList[0];
|
|
518
|
+
await this.#handleRadioButtonClick(radio, position, classType);
|
|
363
519
|
};
|
|
364
520
|
}
|
|
365
521
|
const cardsButtons = document.getElementsByClassName("card-delete-button");
|
|
366
522
|
for (const cardButton of cardsButtons) {
|
|
367
|
-
cardButton.addEventListener(
|
|
368
|
-
|
|
369
|
-
event
|
|
370
|
-
|
|
371
|
-
|
|
523
|
+
cardButton.addEventListener(
|
|
524
|
+
"click",
|
|
525
|
+
event => {
|
|
526
|
+
event.preventDefault();
|
|
527
|
+
event.stopImmediatePropagation();
|
|
528
|
+
this.#handleDeleteCardButtonClick(cardButton);
|
|
529
|
+
},
|
|
530
|
+
false,
|
|
531
|
+
);
|
|
372
532
|
}
|
|
373
533
|
}
|
|
374
534
|
|
|
375
|
-
async #handleRadioButtonClick(radio) {
|
|
376
|
-
if (radio.id === this.radioChecked || (radio.id === "new" && this.radioChecked === undefined))
|
|
535
|
+
async #handleRadioButtonClick(radio, position = null, type = "") {
|
|
536
|
+
if (radio.id === this.radioChecked || (radio.id === "new" && this.radioChecked === undefined))
|
|
537
|
+
return;
|
|
377
538
|
const containerForm = document.querySelector(".container-form");
|
|
378
539
|
if (containerForm) {
|
|
379
540
|
containerForm.style.display = radio.id === "new" ? "block" : "none";
|
|
380
541
|
}
|
|
542
|
+
|
|
381
543
|
if (radio.id === "new") {
|
|
544
|
+
this.#removeClass(["cvvContainer", "cvvContainerCard"]);
|
|
545
|
+
this.#handleOpenCloseAccordion("", null, true);
|
|
382
546
|
if (this.radioChecked !== radio.id) {
|
|
383
|
-
globalLoader.show()
|
|
384
|
-
this.#mountTonder(
|
|
547
|
+
globalLoader.show();
|
|
548
|
+
await this.#mountTonder();
|
|
385
549
|
InlineCheckout.injected = true;
|
|
386
550
|
}
|
|
387
551
|
} else {
|
|
552
|
+
this.#handleOpenCloseAccordion(type, null, false, true);
|
|
553
|
+
if (position !== null) {
|
|
554
|
+
this.#handleOpenCloseAccordion(type, position, true);
|
|
555
|
+
}
|
|
388
556
|
this.#unmountForm();
|
|
389
557
|
}
|
|
390
558
|
this.radioChecked = radio.id;
|
|
391
559
|
}
|
|
392
560
|
|
|
393
|
-
async #handleDeleteCardButtonClick(
|
|
394
|
-
|
|
395
|
-
|
|
561
|
+
async #handleDeleteCardButtonClick(button) {
|
|
562
|
+
if (!this.#customerData.auth_token) return;
|
|
563
|
+
|
|
564
|
+
const id = button.attributes.getNamedItem("id");
|
|
565
|
+
const skyflow_id = id?.value?.split("_")?.[2];
|
|
566
|
+
|
|
396
567
|
if (skyflow_id) {
|
|
397
|
-
const cardClicked = document.querySelector(`#
|
|
568
|
+
const cardClicked = document.querySelector(`#option_container-${skyflow_id}`);
|
|
398
569
|
if (cardClicked) {
|
|
399
|
-
cardClicked.style.display = "none"
|
|
570
|
+
cardClicked.style.display = "none";
|
|
400
571
|
}
|
|
401
572
|
try {
|
|
402
573
|
this.deletingCards.push(skyflow_id);
|
|
@@ -404,32 +575,139 @@ export class InlineCheckout extends BaseInlineCheckout {
|
|
|
404
575
|
this.abortRefreshCardsController.abort();
|
|
405
576
|
this.abortRefreshCardsController = new AbortController();
|
|
406
577
|
}
|
|
407
|
-
const businessId = this.merchantData.business.pk
|
|
578
|
+
const businessId = this.merchantData.business.pk;
|
|
408
579
|
await removeCustomerCard(
|
|
409
580
|
this.baseUrl,
|
|
410
|
-
|
|
581
|
+
this.#customerData.auth_token,
|
|
411
582
|
this.secureToken,
|
|
412
583
|
skyflow_id,
|
|
413
|
-
businessId
|
|
414
|
-
)
|
|
415
|
-
} catch (error) {
|
|
584
|
+
businessId,
|
|
585
|
+
);
|
|
586
|
+
} catch (error) {
|
|
587
|
+
} finally {
|
|
416
588
|
this.deletingCards = this.deletingCards.filter(id => id !== skyflow_id);
|
|
417
|
-
this.#refreshCardOnDelete(
|
|
589
|
+
this.#refreshCardOnDelete();
|
|
418
590
|
}
|
|
419
591
|
}
|
|
420
592
|
}
|
|
421
|
-
|
|
593
|
+
|
|
594
|
+
async #refreshCardOnDelete() {
|
|
422
595
|
if (this.deletingCards.length > 0) return;
|
|
423
|
-
await this.#loadCardsList(
|
|
596
|
+
await this.#loadCardsList();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
#generateAccordion(type = "cards") {
|
|
600
|
+
const accordionByType = {
|
|
601
|
+
cards: {
|
|
602
|
+
accClass: "accordion-container",
|
|
603
|
+
triggerClass: "card-item-label",
|
|
604
|
+
},
|
|
605
|
+
paymentMethods: {
|
|
606
|
+
accClass: "accordion-container-apm",
|
|
607
|
+
triggerClass: "apm-item-label",
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
const accordion = new Accordion("." + accordionByType[type].accClass, {
|
|
611
|
+
triggerClass: accordionByType[type].triggerClass,
|
|
612
|
+
duration: 300,
|
|
613
|
+
collapse: true,
|
|
614
|
+
showMultiple: false,
|
|
615
|
+
onOpen: async currentElement => {
|
|
616
|
+
await this.#handleOpenCardAccordion(currentElement, type);
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
if (type === "cards") {
|
|
621
|
+
this.accordionCards = accordion;
|
|
622
|
+
} else if (type === "paymentMethods") {
|
|
623
|
+
this.accordionPaymentMethods = accordion;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
#removeClass(selectors = [], className = "show") {
|
|
628
|
+
selectors.forEach(slcItem => {
|
|
629
|
+
document.querySelectorAll("." + slcItem).forEach(container => {
|
|
630
|
+
container.classList.remove(className);
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async #handleOpenCardAccordion(currentElement, type = "cards") {
|
|
636
|
+
const { vault_id, vault_url } = this.merchantData;
|
|
637
|
+
const container_radio_id = currentElement.id.replace("option_container-", "");
|
|
638
|
+
|
|
639
|
+
if (
|
|
640
|
+
this.updateCollectContainer &&
|
|
641
|
+
"unmount" in this.updateCollectContainer?.elements?.cvvElement
|
|
642
|
+
) {
|
|
643
|
+
await this.updateCollectContainer.elements.cvvElement.unmount();
|
|
644
|
+
}
|
|
645
|
+
this.#removeClass(["cvvContainer", "cvvContainerCard"]);
|
|
646
|
+
|
|
647
|
+
const radio_card = document.getElementById(container_radio_id);
|
|
648
|
+
radio_card.checked = true;
|
|
649
|
+
|
|
650
|
+
try {
|
|
651
|
+
if (type === "cards") {
|
|
652
|
+
this.updateCollectContainer = await initUpdateSkyflow(
|
|
653
|
+
container_radio_id,
|
|
654
|
+
vault_id,
|
|
655
|
+
vault_url,
|
|
656
|
+
this.baseUrl,
|
|
657
|
+
this.apiKeyTonder,
|
|
658
|
+
this.abortController.signal,
|
|
659
|
+
this.customStyles,
|
|
660
|
+
this.customization.displayMode,
|
|
661
|
+
);
|
|
662
|
+
setTimeout(() => {
|
|
663
|
+
document.querySelector(`#cvvContainer${container_radio_id}`).classList.add("show");
|
|
664
|
+
}, 5);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
this.#mountButtons(container_radio_id);
|
|
668
|
+
} catch (e) {
|
|
669
|
+
console.error("Ha ocurrido un error", e);
|
|
670
|
+
}
|
|
671
|
+
await this.#handleRadioButtonClick(radio_card, null, type);
|
|
424
672
|
}
|
|
673
|
+
|
|
674
|
+
#handleOpenCloseAccordion(type = "", position = null, closeAll = false, closeOthers = false) {
|
|
675
|
+
const accordions = [
|
|
676
|
+
{ type: "cards", accordion: this.accordionCards },
|
|
677
|
+
{ type: "paymentMethods", accordion: this.accordionPaymentMethods },
|
|
678
|
+
];
|
|
679
|
+
accordions.forEach(({ accordion, type: currentType }) => {
|
|
680
|
+
if (!accordion) return;
|
|
681
|
+
|
|
682
|
+
if (closeAll && accordion.closeAll) {
|
|
683
|
+
accordion.closeAll();
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (closeOthers && currentType !== type && accordion.closeAll) {
|
|
687
|
+
accordion.closeAll();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (position !== null && currentType === type && accordion.open) {
|
|
691
|
+
accordion.open(
|
|
692
|
+
currentType !== "paymentMethods" ? position : position - (this.#cardsData.length + 1),
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
425
698
|
#unmountForm() {
|
|
426
|
-
InlineCheckout.injected = false
|
|
699
|
+
InlineCheckout.injected = false;
|
|
427
700
|
if (this.collectContainer) {
|
|
428
|
-
if ("unmount" in this.collectContainer.elements.cardHolderNameElement)
|
|
429
|
-
|
|
430
|
-
if ("unmount" in this.collectContainer.elements.
|
|
431
|
-
|
|
432
|
-
if ("unmount" in this.collectContainer.elements.
|
|
701
|
+
if ("unmount" in this.collectContainer.elements.cardHolderNameElement)
|
|
702
|
+
this.collectContainer.elements.cardHolderNameElement.unmount();
|
|
703
|
+
if ("unmount" in this.collectContainer.elements.cardNumberElement)
|
|
704
|
+
this.collectContainer.elements.cardNumberElement.unmount();
|
|
705
|
+
if ("unmount" in this.collectContainer.elements.expiryYearElement)
|
|
706
|
+
this.collectContainer.elements.expiryYearElement.unmount();
|
|
707
|
+
if ("unmount" in this.collectContainer.elements.expiryMonthElement)
|
|
708
|
+
this.collectContainer.elements.expiryMonthElement.unmount();
|
|
709
|
+
if ("unmount" in this.collectContainer.elements.cvvElement)
|
|
710
|
+
this.collectContainer.elements.cvvElement.unmount();
|
|
433
711
|
}
|
|
434
712
|
}
|
|
435
713
|
}
|