@tonder.io/ionic-full-sdk 0.0.48-beta.DEV-1610.1 → 0.0.48-beta.DEV-1610.2

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.
@@ -2,7 +2,7 @@ import { apmItemsTemplate, Card, cardItemsTemplate, cardTemplate, CollectorIds }
2
2
  import {
3
3
  showError,
4
4
  showMessage,
5
- mapCards, generateRandomString,
5
+ mapCards,
6
6
  } from '../helpers/utils';
7
7
  import {initSkyflow, initUpdateSkyflow} from '../helpers/skyflow'
8
8
  import { ErrorResponse } from '@tonder.io/ionic-lite-sdk/dist/classes/errorResponse';
@@ -13,6 +13,7 @@ import {ISaveCardSkyflowRequest} from "@tonder.io/ionic-lite-sdk/dist/types/card
13
13
  import {ITonderPaymentMethod} from "@tonder.io/ionic-lite-sdk/dist/types/paymentMethod";
14
14
  import {IInlineCheckoutOptions, IInlineCustomizationOptions, InCollectorContainer} from "../types/commons";
15
15
  import {IInlineCheckout} from "../types/inlineCheckout";
16
+ import get from "lodash.get";
16
17
  // @ts-ignore
17
18
  import Accordion from "accordion-js";
18
19
 
@@ -25,11 +26,13 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
25
26
  renderSaveCardButton?: boolean
26
27
  customStyles: any
27
28
  injectInterval: any
29
+ #cardsData: any[] = [];
28
30
  abortRefreshCardsController: AbortController;
29
31
  containerId: string
30
- accordionC: {open: any; closeAll: any} | null = null;
31
32
  injected: boolean
32
33
  cardsInjected: boolean
34
+ accordionCards: any = null;
35
+ accordionPaymentMethods: any = null;
33
36
  apmsInjected = false
34
37
  apmsData: ITonderPaymentMethod[] = []
35
38
  collectorIds: CollectorIds
@@ -39,7 +42,7 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
39
42
  #customizationBase: IInlineCustomizationOptions = {
40
43
  paymentButton: {
41
44
  show: false,
42
- text: "Pagar",
45
+ text: "Pagars",
43
46
  showAmount: true,
44
47
  },
45
48
  }
@@ -83,9 +86,11 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
83
86
  cvv: "collectCvv",
84
87
  tonderPayButton: "tonderPayButton",
85
88
  msgError: "msgError",
89
+ msgErrorText: "msgErrorText",
86
90
  msgNotification: "msgNotification",
87
91
  tonderSaveCardButton: "tonderSaveCardButton",
88
92
  apmsListContainer: "apmsListContainer",
93
+ msgNotificationText: "msgNotificationText",
89
94
  tdsIframe: "tdsIframe"
90
95
  }
91
96
  this.collectorIds = collectorIds ? {...this.collectorIds, ...collectorIds} :this.collectorIds
@@ -125,7 +130,14 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
125
130
  const containerTonderCheckout = document.querySelector(`#${this.containerId}`);
126
131
 
127
132
  if (containerTonderCheckout) {
128
- containerTonderCheckout.innerHTML = cardTemplate({ collectorIds: this.collectorIds, customStyles: this.customStyles, isEnrollmentCard: this.isEnrollmentCard, renderPaymentButton: this.renderPaymentButton, customization: this.customization })
133
+ containerTonderCheckout.innerHTML = cardTemplate({
134
+ collectorIds: this.collectorIds,
135
+ customStyles: this.customStyles,
136
+ isEnrollmentCard: this.isEnrollmentCard,
137
+ renderPaymentButton: this.renderPaymentButton,
138
+ customization: this.customization,
139
+ renderSaveCardButton: this.renderSaveCardButton
140
+ })
129
141
  await this.#mountTonder();
130
142
  this.injected = true
131
143
  return;
@@ -133,7 +145,13 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
133
145
  const observer = new MutationObserver(async (mutations, obs) => {
134
146
  const containerTonderCheckout = document.querySelector(`#${this.containerId}`);
135
147
  if (containerTonderCheckout) {
136
- containerTonderCheckout.innerHTML = cardTemplate({collectorIds: this.collectorIds, customStyles: this.customStyles, isEnrollmentCard: this.isEnrollmentCard, renderPaymentButton: this.renderPaymentButton, customization: this.customization })
148
+ containerTonderCheckout.innerHTML = cardTemplate({
149
+ collectorIds: this.collectorIds,
150
+ customStyles: this.customStyles,
151
+ isEnrollmentCard: this.isEnrollmentCard,
152
+ renderSaveCardButton: this.renderSaveCardButton,
153
+ renderPaymentButton: this.renderPaymentButton,
154
+ customization: this.customization })
137
155
  await this.#mountTonder();
138
156
  // clearInterval(injectInterval);
139
157
  this.injected = true
@@ -174,10 +192,11 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
174
192
  try{
175
193
  await this.#updateSaveCardButton(true, "Guardando...")
176
194
  await this.#validateAndSaveCard()
177
- await this.#updateSaveCardButton(false)
178
195
  resolve("Tarjeta registrada con éxito");
179
196
  }catch(error){
180
197
  reject(error);
198
+ }finally {
199
+ await this.#updateSaveCardButton()
181
200
  }
182
201
  })
183
202
  }
@@ -195,26 +214,26 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
195
214
 
196
215
 
197
216
  async #loadCardsList (customerToken: string) {
198
- if(this.cardsInjected || !this.customization.saveCards?.showSaved) return;
217
+ if(this.cardsInjected || !this.customization.saveCards?.showSaved || this.isEnrollmentCard) return;
199
218
  this.cardsInjected = false
200
219
  const cardsResponse = await this._getCustomerCards(customerToken, this.merchantData!.business.pk)
220
+ this.#cardsData = get(cardsResponse, "cards", []);
221
+
201
222
  let cards: Card[] = []
202
223
  if("cards" in cardsResponse) {
203
224
  cards = cardsResponse.cards.map(mapCards)
204
225
  const injectInterval = setInterval(() => {
205
226
  const queryElement = document.querySelector(`#${this.collectorIds.cardsListContainer}`);
206
227
  if (queryElement && this.injected) {
207
- const acId = generateRandomString(10)
208
228
  queryElement.innerHTML = cardItemsTemplate({
209
229
  cards,
210
- acId: acId,
211
230
  collectorIds: this.collectorIds,
212
231
  renderPaymentButton: this.renderPaymentButton,
213
232
  customStyles: this.customStyles,
214
233
  customization: this.customization,
215
234
  })
216
235
  clearInterval(injectInterval)
217
- this.#generateCardsAccordion(acId)
236
+ this.#generateAccordion()
218
237
  this.#mountRadioButtons(customerToken)
219
238
  this.cardsInjected = true
220
239
  }
@@ -222,21 +241,68 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
222
241
  }
223
242
  }
224
243
 
225
- #generateCardsAccordion(acId: string){
226
- this.accordionC = new Accordion(`.accordion-container${acId}`, {
227
- triggerClass: "card-item-label",
244
+ #generateAccordion(type:AccordionType = "cards"){
245
+ const accordionByType:Record<AccordionType, { accClass: string; triggerClass: string }> = {
246
+ cards: {
247
+ accClass: "accordion-container",
248
+ triggerClass: "card-item-label",
249
+ },
250
+ paymentMethods: {
251
+ accClass: "accordion-container-apm",
252
+ triggerClass: "apm-item-label",
253
+ },
254
+ };
255
+ const accordion = new Accordion("." + accordionByType[type].accClass, {
256
+ triggerClass: accordionByType[type].triggerClass,
228
257
  duration: 300,
229
258
  collapse: true,
230
259
  showMultiple: false,
231
260
  onOpen: async (currentElement: any) => {
232
- await this.#handleOpenCardAccordion(currentElement)
233
- }
261
+ await this.#handleOpenCardAccordion(currentElement, type);
262
+ },
234
263
  });
264
+
265
+ if (type === "cards") {
266
+ this.accordionCards = accordion;
267
+ } else if (type === "paymentMethods") {
268
+ this.accordionPaymentMethods = accordion;
269
+ }
235
270
  }
236
271
 
237
- async #handleOpenCardAccordion(currentElement: any){
272
+ #handleOpenCloseAccordion(type = "", position: number | null = null, closeAll = false, closeOthers = false) {
273
+ const accordions = [
274
+ { type: "cards", accordion: this.accordionCards },
275
+ { type: "paymentMethods", accordion: this.accordionPaymentMethods },
276
+ ];
277
+ accordions.forEach(({ accordion, type: currentType }) => {
278
+ if (!accordion) return;
279
+
280
+ if (closeAll && "closeAll" in accordion) {
281
+ accordion.closeAll();
282
+ }
283
+
284
+ if (closeOthers && currentType !== type && accordion.closeAll) {
285
+ accordion.closeAll();
286
+ }
287
+
288
+ if (position !== null && currentType === type && accordion.open) {
289
+ accordion.open(
290
+ currentType !== "paymentMethods" ? position : position - (this.#cardsData.length + 1),
291
+ );
292
+ }
293
+ });
294
+ }
295
+ #removeClass(selectors: string[] = [], className = "show") {
296
+ selectors.forEach(slcItem => {
297
+ document.querySelectorAll("." + slcItem).forEach(container => {
298
+ container.classList.remove(className);
299
+ });
300
+ });
301
+ }
302
+ async #handleOpenCardAccordion(currentElement: any, type = "cards"){
303
+ console.log("Handling open card accordion for type:", type);
238
304
  const { vault_id, vault_url } = this.merchantData!;
239
- const container_radio_id = currentElement.id.replace("card_container-", "");
305
+ const container_radio_id = currentElement.id.replace("option_container-", "");
240
306
 
241
307
  if (this.updateCollectContainer && "unmount" in this.updateCollectContainer?.elements?.cvvElement) {
242
308
  this.updateCollectContainer.elements.cvvElement.unmount()
@@ -245,39 +311,45 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
245
311
  document.querySelectorAll(".cvvContainer").forEach((container) => {
246
312
  container.classList.remove("show");
247
313
  });
248
-
314
+ this.#removeClass(["cvvContainer"]);
249
315
  const radio_card = document.getElementById(container_radio_id) as HTMLInputElement;
250
316
  if (radio_card) radio_card.checked = true;
251
317
 
252
318
  try{
253
- this.updateCollectContainer = await initUpdateSkyflow(
254
- container_radio_id,
255
- vault_id,
256
- vault_url,
257
- this.baseUrl,
258
- this.abortController.signal,
259
- this.customStyles,
260
- this.collectorIds,
261
- this.apiKeyTonder
319
+ if(type === "cards"){
320
+ this.updateCollectContainer = await initUpdateSkyflow(
321
+ container_radio_id,
322
+ vault_id,
323
+ vault_url,
324
+ this.baseUrl,
325
+ this.abortController.signal,
326
+ this.customStyles,
327
+ this.collectorIds,
328
+ this.apiKeyTonder
329
+ );
330
+ setTimeout(() => {
331
+ const containerCvv = document.querySelector(`#cvvContainer${container_radio_id}`)
332
+ if(containerCvv) containerCvv.classList.add("show");
333
+ }, 5)
334
+ }
262
335
 
263
- );
264
- setTimeout(() => {
265
- const containerCvv = document.querySelector(`#cvvContainer${container_radio_id}`)
266
- if(containerCvv) containerCvv.classList.add("show");
267
- }, 5)
268
336
  this.#mountPayButton(container_radio_id)
269
337
  }catch (e){
270
338
  console.error("Ha ocurrido un error", e);
271
339
  }
272
-
273
- if (radio_card) await this.#handleRadioButtonClick(radio_card)
340
+ await this.#handleRadioButtonClick(radio_card, null, type);
274
341
  }
275
342
 
276
343
 
277
344
  #mountPayButton(cardId="") {
345
+ console.log("Mounting pay button with cardId:", cardId);
346
+
278
347
  if (!this.renderPaymentButton) return;
279
348
  const btnID = `#${this.collectorIds.tonderPayButton}${cardId}`;
280
- const payButton: HTMLElement | null = document.querySelector(btnID);
349
+ let payButton: HTMLElement | null = document.querySelector(btnID);
350
+ if (!payButton) {
351
+ payButton = document.querySelector(`#${this.collectorIds.tonderPayButton}`);
352
+ }
281
353
  const containerID = `#acContainer${cardId}`;
282
354
  const container = document.querySelector(containerID);
283
355
 
@@ -301,7 +373,7 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
301
373
 
302
374
  payButton.textContent = `${this.customization?.paymentButton?.text} ${this.customization.paymentButton?.showAmount ? `$${inCartTotal}`:''}`;
303
375
 
304
- document.querySelectorAll(".ac-card-panel-container").forEach((cont) => {
376
+ document.querySelectorAll(".ac-option-panel-container").forEach((cont) => {
305
377
  cont.classList.remove("show");
306
378
  });
307
379
  if (container) {
@@ -310,12 +382,12 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
310
382
 
311
383
  payButton.onclick = async (event) => {
312
384
  event.preventDefault();
313
- await this.#handlePaymentClick(payButton);
385
+ await this.#handlePaymentClick();
314
386
  };
315
387
 
316
388
  }
317
389
 
318
- async #handlePaymentClick(payButton: any) {
390
+ async #handlePaymentClick() {
319
391
  try {
320
392
  await this.payment({} as IProcessPaymentRequest);
321
393
  } catch (error) {
@@ -383,8 +455,9 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
383
455
  radio.style.display = "block";
384
456
  radio.onclick = async (event) => {
385
457
  //event.preventDefault();
458
+ const classType = radio.classList[0];
386
459
  const position = Array.from(radioButtons).indexOf(radio);
387
- await this.#handleRadioButtonClick(radio, position);
460
+ await this.#handleRadioButtonClick(radio, position, classType);
388
461
  };
389
462
  }
390
463
  const cardsButtons: HTMLCollectionOf<Element> = document.getElementsByClassName("card-delete-button");
@@ -428,30 +501,32 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
428
501
  this.cardsInjected = false;
429
502
  await this.#loadCardsList(customerToken)
430
503
  }
431
- async #handleRadioButtonClick (radio: HTMLElement, position: number | null = null) {
504
+ async #handleRadioButtonClick (radio: HTMLElement, position: number | null = null, type = "") {
505
+ if (radio.id === this.radioChecked || (radio.id === "new" && this.radioChecked === undefined))
506
+ return;
507
+
432
508
  const containerForm: HTMLElement | null = document.querySelector(".container-form");
433
509
  if(containerForm) {
434
510
  containerForm.style.display = radio.id === "new" ? "block" : "none";
435
511
  }
512
+ console.log("radio.id", radio.id)
436
513
  if(radio.id === "new") {
437
- this.#handleOpenCloseCardAccordion(null, true)
514
+ this.#removeClass(["cvvContainer"]);
515
+ this.#handleOpenCloseAccordion("", null, true);
438
516
  if(this.radioChecked !== radio.id) {
439
517
  await this.#mountForm();
440
518
  this.#mountPayButton()
441
519
  }
442
520
  } else {
443
- this.#handleOpenCloseCardAccordion(position, position !== null)
521
+ this.#handleOpenCloseAccordion(type, null, false, true);
522
+ if (position !== null) {
523
+ this.#handleOpenCloseAccordion(type, position, true);
524
+ }
444
525
  this.#unmountForm();
445
526
  }
446
527
  this.radioChecked = radio.id;
447
528
  }
448
529
 
449
- #handleOpenCloseCardAccordion(position: number | null = null, closeAll = false){
450
- if(closeAll && this.accordionC && 'closeAll' in this.accordionC) this.accordionC.closeAll();
451
- if(position !== null && this.accordionC && 'open' in this.accordionC) {
452
- this.accordionC.open(position)
453
- }
454
- }
455
530
  async #mountAPMs(){
456
531
  try{
457
532
  const apms = await this._fetchCustomerPaymentMethods();
@@ -469,8 +544,14 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
469
544
  const injectInterval = setInterval(() => {
470
545
  const queryElement = document.querySelector(`#${this.collectorIds.apmsListContainer}`);
471
546
  if (queryElement && this.injected) {
472
- queryElement.innerHTML = apmItemsTemplate(!!this.customStyles, apms);
547
+ queryElement.innerHTML = apmItemsTemplate({
548
+ apms: apms,
549
+ collectorIds: this.collectorIds,
550
+ renderPaymentButton: this.renderPaymentButton,
551
+ customization: this.customization
552
+ });
473
553
  clearInterval(injectInterval);
554
+ this.#generateAccordion("paymentMethods");
474
555
  this.#mountRadioButtons();
475
556
  this.apmsInjected = true;
476
557
  }
@@ -478,6 +559,7 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
478
559
  }
479
560
 
480
561
  async #mountTonder() {
562
+
481
563
  if(this.isEnrollmentCard){
482
564
  this.#mountSaveCardButton()
483
565
  }else{
@@ -560,10 +642,10 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
560
642
  const collectResponseSkyflowTonder: any = await containerCollect.collect();
561
643
  return await collectResponseSkyflowTonder["records"][0]["fields"];
562
644
  }else{
563
- showError("Por favor, verifica todos los campos de tu tarjeta", this.collectorIds.msgError, tonderButton)
645
+ showError("Por favor, verifica todos los campos de tu tarjeta", this.radioChecked, this.collectorIds.msgError)
564
646
  }
565
647
  } catch (error) {
566
- showError("Por favor, verifica todos los campos de tu tarjeta", this.collectorIds.msgError, tonderButton)
648
+ showError("Por favor, verifica todos los campos de tu tarjeta", this.radioChecked, this.collectorIds.msgError)
567
649
  throw error;
568
650
  }
569
651
  }
@@ -590,16 +672,16 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
590
672
  if("auth_token" in customerResponse && this.secureToken) {
591
673
  const { auth_token } = customerResponse;
592
674
  await this._saveCustomerCard(auth_token, this.merchantData!.business.pk, { skyflow_id: cardTokensSkyflowTonder.skyflow_id });
593
- showMessage("Tarjeta registrada con éxito", this.collectorIds.msgNotification);
675
+ showMessage("Tarjeta registrada con éxito", this.radioChecked, this.collectorIds.msgNotification);
594
676
  } else {
595
- showError("No se han configurado los datos de seguridad para guardar tarjeta", this.collectorIds.msgError, this.collectorIds.tonderSaveCardButton!)
677
+ showError("No se han configurado los datos de seguridad para guardar tarjeta", this.radioChecked, this.collectorIds.msgError)
596
678
  }
597
679
  }else{
598
- showError("No se han configurado los datos del proveedor de servicio", this.collectorIds.msgError, this.collectorIds.tonderSaveCardButton!)
680
+ showError("No se han configurado los datos del proveedor de servicio", this.radioChecked, this.collectorIds.msgError)
599
681
  }
600
682
  } catch (error) {
601
683
  const exist_msg_error = typeof error === "object" && error !== null && "message" in error;
602
- showError(exist_msg_error ? error?.message as string:"Ha ocurrido un error", this.collectorIds.msgError, this.collectorIds.tonderSaveCardButton!)
684
+ showError(exist_msg_error ? error?.message as string:"Ha ocurrido un error", this.radioChecked, this.collectorIds.msgError)
603
685
  throw error;
604
686
  } finally {}
605
687
  }
@@ -646,16 +728,16 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
646
728
  if (jsonResponseRouter) {
647
729
  return jsonResponseRouter;
648
730
  } else {
649
- showError("No se ha podido procesar el pago", this.collectorIds.msgError, this.collectorIds.tonderPayButton)
731
+ showError("No se ha podido procesar el pago", this.radioChecked, this.collectorIds.msgError,)
650
732
  return false;
651
733
  }
652
734
  }
653
735
  } else {
654
- showError("No se han configurado los datos del proveedor de servicio", this.collectorIds.msgError, this.collectorIds.tonderPayButton)
736
+ showError("No se han configurado los datos del proveedor de servicio", this.radioChecked, this.collectorIds.msgError)
655
737
  }
656
738
  } catch (error) {
657
739
  console.log(error);
658
- showError("Ha ocurrido un error", this.collectorIds.msgError, this.collectorIds.tonderPayButton)
740
+ showError("Ha ocurrido un error", this.radioChecked, this.collectorIds.msgError)
659
741
  throw error;
660
742
  } finally {
661
743
  this.#updatePayButton({cardId: this.radioChecked, disabled: false});
@@ -671,14 +753,14 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
671
753
  await this._saveCustomerCard(authToken, businessId, cardTokens);
672
754
  this.cardsInjected = false;
673
755
  await this.#loadCardsList(authToken)
674
- showMessage("Tarjeta registrada con éxito", this.collectorIds.msgNotification);
756
+ showMessage("Tarjeta registrada con éxito",this.radioChecked, this.collectorIds.msgNotification);
675
757
  } else {
676
- showError("No se han configurado los datos de seguridad para guardar tarjeta", this.collectorIds.msgError, this.collectorIds.tonderSaveCardButton!)
758
+ showError("No se han configurado los datos de seguridad para guardar tarjeta", this.radioChecked, this.collectorIds.msgError)
677
759
  }
678
760
  }catch (error){
679
761
  const exist_msg_error = typeof error === "object" && error !== null && "message" in error;
680
762
  if(exist_msg_error && error?.message !== 'Error'){
681
- showError(error?.message as string, this.collectorIds.msgError, this.collectorIds.tonderPayButton);
763
+ showError(error?.message as string, this.radioChecked, this.collectorIds.msgError);
682
764
  }
683
765
  }
684
766
  }
@@ -3,6 +3,7 @@ enum PAYMENT_METHOD {
3
3
  OXXO = "OXXO",
4
4
  SPEI= "SPEI",
5
5
  CODI= "CODI",
6
+ OXXOPAY= "OXXOPAY",
6
7
  MERCADOPAGO= "MERCADOPAGO",
7
8
  PAYPAL= "PAYPAL",
8
9
  COMERCIALMEXICANA= "COMERCIALMEXICANA",