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

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.
@@ -4,8 +4,8 @@ import Skyflow from "skyflow-js";
4
4
  import { CollectorIds } from "./template";
5
5
  import RevealElement from "skyflow-js/types/core/external/reveal/reveal-element";
6
6
  import ComposableElement from "skyflow-js/types/core/external/collect/compose-collect-element";
7
- import { InCollectorContainer } from "../types/commons";
8
- import { getVaultToken } from "@tonder.io/ionic-lite-sdk/dist/data/skyflowApi";
7
+ import {IEvents, IInputEvents, InCollectorContainer} from "../types/commons";
8
+ import get from "lodash.get";
9
9
 
10
10
  export async function initSkyflow(
11
11
  vaultId: string,
@@ -15,6 +15,7 @@ export async function initSkyflow(
15
15
  customStyles: any = {},
16
16
  collectorIds: CollectorIds,
17
17
  apiKey: string,
18
+ events?: IEvents
18
19
  ): Promise<InCollectorContainer> {
19
20
  const skyflow = await initializeSkyflow(
20
21
  vaultId,
@@ -74,6 +75,8 @@ export async function initSkyflow(
74
75
 
75
76
  if ("on" in cardHolderNameElement) {
76
77
  cardHolderNameElement.on(Skyflow.EventName.CHANGE, (state: any) => {
78
+ executeEvent( "onChange", state, events?.cardHolderEvents)
79
+
77
80
  let element = document.getElementById("errorCardHolderIdTonder");
78
81
 
79
82
  if (element && state.isValid && !state.isEmpty) {
@@ -82,6 +85,8 @@ export async function initSkyflow(
82
85
  });
83
86
 
84
87
  cardHolderNameElement.on(Skyflow.EventName.BLUR, (state: any) => {
88
+ executeEvent( "onBlur", state, events?.cardHolderEvents)
89
+
85
90
  let tonderContainer = document.getElementById(collectorIds.holderName);
86
91
 
87
92
  let element = document.getElementById("errorCardHolderIdTonder");
@@ -100,6 +105,11 @@ export async function initSkyflow(
100
105
  tonderContainer?.appendChild(errorLabel);
101
106
  }
102
107
  });
108
+
109
+ cardHolderNameElement.on(Skyflow.EventName.FOCUS, (state: any) => {
110
+ executeEvent( "onFocus", state, events?.cardHolderEvents)
111
+ });
112
+
103
113
  }
104
114
 
105
115
  // Create collect elements.
@@ -120,6 +130,7 @@ export async function initSkyflow(
120
130
 
121
131
  if ("on" in cardNumberElement) {
122
132
  cardNumberElement.on(Skyflow.EventName.CHANGE, (state: any) => {
133
+ executeEvent( "onChange", state, events?.cardNumberEvents)
123
134
  let element = document.getElementById("errorCardNumberIdTonder");
124
135
 
125
136
  if (element && state.isValid && !state.isEmpty) {
@@ -128,6 +139,8 @@ export async function initSkyflow(
128
139
  });
129
140
 
130
141
  cardNumberElement.on(Skyflow.EventName.BLUR, (state: any) => {
142
+ executeEvent( "onBlur", state, events?.cardNumberEvents)
143
+
131
144
  let tonderContainer = document.getElementById(collectorIds.cardNumber);
132
145
 
133
146
  let element = document.getElementById("errorCardNumberIdTonder");
@@ -146,6 +159,11 @@ export async function initSkyflow(
146
159
  tonderContainer?.appendChild(errorLabel);
147
160
  }
148
161
  });
162
+
163
+ cardNumberElement.on(Skyflow.EventName.FOCUS, (state: any) => {
164
+ executeEvent( "onFocus", state, events?.cardNumberEvents)
165
+ });
166
+
149
167
  }
150
168
 
151
169
  const cvvElement: CollectElement | RevealElement | ComposableElement =
@@ -161,6 +179,7 @@ export async function initSkyflow(
161
179
 
162
180
  if ("on" in cvvElement) {
163
181
  cvvElement.on(Skyflow.EventName.CHANGE, (state: any) => {
182
+ executeEvent( "onChange", state, events?.cvvEvents)
164
183
  let element = document.getElementById("errorCvvIdTonder");
165
184
 
166
185
  if (element && state.isValid && !state.isEmpty) {
@@ -169,6 +188,7 @@ export async function initSkyflow(
169
188
  });
170
189
 
171
190
  cvvElement.on(Skyflow.EventName.BLUR, (state: any) => {
191
+ executeEvent( "onBlur", state, events?.cvvEvents)
172
192
  let tonderContainer = document.getElementById(collectorIds.cvv);
173
193
 
174
194
  let element = document.getElementById("errorCvvIdTonder");
@@ -187,6 +207,10 @@ export async function initSkyflow(
187
207
  tonderContainer?.appendChild(errorLabel);
188
208
  }
189
209
  });
210
+
211
+ cvvElement.on(Skyflow.EventName.FOCUS, (state: any) => {
212
+ executeEvent( "onFocus", state, events?.cvvEvents)
213
+ });
190
214
  }
191
215
 
192
216
  const expiryMonthElement: CollectElement | RevealElement | ComposableElement = collectContainer.create({
@@ -201,6 +225,7 @@ export async function initSkyflow(
201
225
 
202
226
  if ("on" in expiryMonthElement) {
203
227
  expiryMonthElement.on(Skyflow.EventName.CHANGE, (state: any) => {
228
+ executeEvent( "onChange", state, events?.monthEvents)
204
229
  let element = document.getElementById("errorExpiryMonthIdTonder");
205
230
 
206
231
  if (element && state.isValid && !state.isEmpty) {
@@ -209,6 +234,7 @@ export async function initSkyflow(
209
234
  });
210
235
 
211
236
  expiryMonthElement.on(Skyflow.EventName.BLUR, (state: any) => {
237
+ executeEvent( "onBlur", state, events?.monthEvents)
212
238
  let tonderContainer = document.getElementById(
213
239
  collectorIds.expirationMonth,
214
240
  );
@@ -229,8 +255,11 @@ export async function initSkyflow(
229
255
  tonderContainer?.appendChild(errorLabel);
230
256
  }
231
257
  });
258
+
259
+ expiryMonthElement.on(Skyflow.EventName.FOCUS, (state: any) => {
260
+ executeEvent( "onFocus", state, events?.monthEvents)
261
+ });
232
262
  }
233
- console.log("collectStylesOptions ====", collectStylesOptions)
234
263
 
235
264
  const expiryYearElement: CollectElement | RevealElement | ComposableElement = collectContainer.create({
236
265
  table: "cards",
@@ -244,6 +273,7 @@ export async function initSkyflow(
244
273
 
245
274
  if ("on" in expiryYearElement) {
246
275
  expiryYearElement.on(Skyflow.EventName.CHANGE, (state: any) => {
276
+ executeEvent( "onChange", state, events?.yearEvents)
247
277
  let element = document.getElementById("errorExpiryYearIdTonder");
248
278
 
249
279
  if (element && state.isValid && !state.isEmpty) {
@@ -252,6 +282,7 @@ export async function initSkyflow(
252
282
  });
253
283
 
254
284
  expiryYearElement.on(Skyflow.EventName.BLUR, (state: any) => {
285
+ executeEvent( "onBlur", state, events?.yearEvents)
255
286
  let tonderContainer = document.getElementById(
256
287
  collectorIds.expirationYear,
257
288
  );
@@ -272,6 +303,10 @@ export async function initSkyflow(
272
303
  tonderContainer?.appendChild(errorLabel);
273
304
  }
274
305
  });
306
+
307
+ expiryYearElement.on(Skyflow.EventName.FOCUS, (state: any) => {
308
+ executeEvent( "onFocus", state, events?.yearEvents)
309
+ });
275
310
  }
276
311
 
277
312
  const elementsConfig = {
@@ -320,6 +355,7 @@ export async function initUpdateSkyflow(
320
355
  customStyles: any = {},
321
356
  collectorIds: CollectorIds,
322
357
  apiKey: string,
358
+ events?: IEvents
323
359
  ) {
324
360
  const skyflow = await initializeSkyflow(
325
361
  vaultId,
@@ -365,6 +401,7 @@ export async function initUpdateSkyflow(
365
401
 
366
402
  if ("on" in cvvElement) {
367
403
  cvvElement.on(Skyflow.EventName.CHANGE, (state: any) => {
404
+ executeEvent( "onChange", state, events?.cvvEvents)
368
405
  let element = document.getElementById(`errorCvvIdTonder${skyflowId}`);
369
406
 
370
407
  if (element && state.isValid && !state.isEmpty) {
@@ -373,6 +410,7 @@ export async function initUpdateSkyflow(
373
410
  });
374
411
 
375
412
  cvvElement.on(Skyflow.EventName.BLUR, (state: any) => {
413
+ executeEvent( "onBlur", state, events?.cvvEvents)
376
414
  let tonderContainer = document.getElementById(
377
415
  collectorIds.cvv + skyflowId,
378
416
  );
@@ -393,6 +431,10 @@ export async function initUpdateSkyflow(
393
431
  tonderContainer?.appendChild(errorLabel);
394
432
  }
395
433
  });
434
+
435
+ cvvElement.on(Skyflow.EventName.FOCUS, (state: any) => {
436
+ executeEvent( "onFocus", state, events?.cvvEvents)
437
+ });
396
438
  }
397
439
 
398
440
  const elementsConfig = {
@@ -473,3 +515,16 @@ const regexMatchRule = {
473
515
  error: "El campo es requerido",
474
516
  },
475
517
  };
518
+ const executeEvent = (eventName: "onChange" | "onBlur" | "onFocus", data: any, events?: IInputEvents, ) => {
519
+ if (events && eventName in events){
520
+ const eventHandler = events[eventName];
521
+ if (typeof eventHandler === "function") {
522
+ eventHandler({
523
+ elementType: get(data, "elementType",""),
524
+ isEmpty: get(data, "isEmpty",""),
525
+ isFocused: get(data, "isFocused",""),
526
+ isValid: get(data, "isValid",""),
527
+ });
528
+ }
529
+ }
530
+ }
@@ -61,7 +61,6 @@ export function showError(
61
61
  msgErrorText!.innerHTML = "";
62
62
  msgErrorText!.innerHTML = message;
63
63
  msgErrorDiv!.style.display = "flex";
64
- console.log("msgErrorDiv", msgErrorDiv)
65
64
  setTimeout(function () {
66
65
  msgErrorDiv!.style.display = "none";
67
66
  msgErrorText!.innerHTML = "";
@@ -94,11 +94,31 @@ export interface IInlineCustomizationOptions extends CustomizationOptions {
94
94
  autoSave?: boolean;
95
95
  }
96
96
  }
97
+ export interface IEventSecureInput {
98
+ elementType: string;
99
+ isEmpty: boolean;
100
+ isFocused: boolean;
101
+ isValid: boolean;
102
+ }
103
+ export interface IInputEvents {
104
+ onChange?: (event: IEventSecureInput) => void;
105
+ onFocus?: (event: IEventSecureInput) => void;
106
+ onBlur?: (event: IEventSecureInput) => void;
107
+ }
97
108
 
109
+ export interface ICardFormEvents {
110
+ cardHolderEvents?: IInputEvents;
111
+ cardNumberEvents?: IInputEvents;
112
+ cvvEvents?: IInputEvents;
113
+ monthEvents?: IInputEvents;
114
+ yearEvents?: IInputEvents;
115
+ }
116
+ export interface IEvents extends ICardFormEvents {}
98
117
  export interface IInlineCheckoutOptions extends IInlineCheckoutBaseOptions{
99
118
  styles?: Record<string, string>;
100
119
  renderPaymentButton?: boolean;
101
120
  customization?: IInlineCustomizationOptions;
121
+ events?: IEvents;
102
122
  renderSaveCardButton?: boolean,
103
123
  containerId?: string,
104
124
  collectorIds?: CollectorIds,
@@ -1,326 +0,0 @@
1
-
2
- import {CustomizationOptions} from "@tonder.io/ionic-lite-sdk/dist/types/commons";
3
-
4
- type ThreeDSHandlerContructor = {
5
- payload?: any,
6
- apiKey?: string,
7
- baseUrl?: string,
8
- customization?: CustomizationOptions,
9
- tdsIframeId?: string,
10
- callBack?: (params: any) => any
11
- }
12
-
13
- export class ThreeDSHandler {
14
-
15
- baseUrl?: string
16
- apiKey?: string
17
- payload?: any
18
- localStorageKey: string = "verify_transaction_status_url"
19
- customization?: CustomizationOptions
20
- callBack?: (params: any) => any
21
- tdsIframeId?: string
22
-
23
- constructor({
24
- payload = null,
25
- apiKey,
26
- baseUrl,
27
- customization,
28
- tdsIframeId,
29
- callBack
30
- }: ThreeDSHandlerContructor) {
31
- this.baseUrl = baseUrl,
32
- this.apiKey = apiKey,
33
- this.payload = payload
34
- this.customization = customization
35
- this.tdsIframeId = tdsIframeId
36
- this.callBack = callBack;
37
- }
38
-
39
- setStorageItem (data: any) {
40
- return localStorage.setItem(this.localStorageKey, JSON.stringify(data))
41
- }
42
-
43
- getStorageItem () {
44
- return localStorage.getItem(this.localStorageKey)
45
- }
46
-
47
- removeStorageItem () {
48
- return localStorage.removeItem(this.localStorageKey)
49
- }
50
-
51
- saveVerifyTransactionUrl() {
52
- const url = this.payload?.next_action?.redirect_to_url?.verify_transaction_status_url
53
- if (url) {
54
- this.saveUrlWithExpiration(url)
55
- } else {
56
- const url = this.payload?.next_action?.iframe_resources?.verify_transaction_status_url
57
- if (url) {
58
- this.saveUrlWithExpiration(url)
59
- } else {
60
- console.log('No verify_transaction_status_url found');
61
- }
62
- }
63
- }
64
-
65
- saveUrlWithExpiration(url: string) {
66
- try {
67
- const now = new Date()
68
- const item = {
69
- url: url,
70
- // Expires after 20 minutes
71
- expires: now.getTime() + 20 * 60 * 1000
72
- }
73
- this.setStorageItem(item)
74
- } catch (error) {
75
- console.log('error: ', error)
76
- }
77
- }
78
-
79
- getUrlWithExpiration() {
80
- const status = this.getStorageItem();
81
- if(status) {
82
- const item = JSON.parse(status)
83
- if (!item) return
84
- const now = new Date()
85
- if (now.getTime() > item.expires) {
86
- this.removeVerifyTransactionUrl()
87
- return null
88
- } else {
89
- return item.url
90
- }
91
- } else {
92
- return null
93
- }
94
- }
95
-
96
- removeVerifyTransactionUrl() {
97
- return this.removeStorageItem()
98
- }
99
-
100
- getVerifyTransactionUrl() {
101
- return this.getStorageItem()
102
- }
103
-
104
- loadIframe() {
105
- const iframe = this.payload?.next_action?.iframe_resources?.iframe
106
-
107
- if (iframe) {
108
- return new Promise((resolve, reject) => {
109
- const iframe = this.payload?.next_action?.iframe_resources?.iframe
110
-
111
- if (iframe) {
112
- this.saveVerifyTransactionUrl()
113
- const container = document.createElement('div')
114
- container.innerHTML = iframe
115
- document.body.appendChild(container)
116
-
117
- // Create and append the script tag manually
118
- const script = document.createElement('script')
119
- script.textContent = 'document.getElementById("tdsMmethodForm").submit();'
120
- container.appendChild(script)
121
-
122
- // Resolve the promise when the iframe is loaded
123
- const iframeElement = document.getElementById('tdsMmethodTgtFrame')
124
- if(iframeElement) {
125
- iframeElement.onload = () => resolve(true)
126
- } else {
127
- console.log('No redirection found');
128
- reject(false)
129
- }
130
- } else {
131
- console.log('No redirection found');
132
- reject(false)
133
- }
134
- })
135
- }
136
- }
137
-
138
- getRedirectUrl() {
139
- return this.payload?.next_action?.redirect_to_url?.url
140
- }
141
-
142
- redirectToChallenge() {
143
- const url = this.getRedirectUrl()
144
- if (url) {
145
- this.saveVerifyTransactionUrl()
146
- if(this.customization) {
147
- if(this.customization?.redirectOnComplete) {
148
- window.location = url;
149
- } else {
150
- const iframe = document.querySelector(`#${this.tdsIframeId}`)
151
- if(iframe) {
152
-
153
- iframe.setAttribute("src", url);
154
- iframe.setAttribute("style", "display: block");
155
-
156
- const self = this;
157
-
158
- const listenerHandler = async (event: any) => {
159
-
160
- const checkStatus = (result: any) => result?.transaction_status !== "Pending";
161
-
162
- const executeAction = () => {
163
- if(iframe) {
164
- iframe.setAttribute("style", "display: none");
165
- }
166
- if(self.callBack) self.callBack(self.payload);
167
- iframe.removeEventListener("load", listenerHandler);
168
- }
169
-
170
- const chainPromises = async (promise: Promise<any>) => {
171
- const result = await new Promise((resolve, reject) => resolve(promise))
172
- if(result) {
173
- if(checkStatus(result)) {
174
- return executeAction()
175
- } else {
176
- const timer = setTimeout(async () => {
177
- clearTimeout(timer);
178
- await chainPromises(self.requestTransactionStatus());
179
- }, 5000)
180
- }
181
- }
182
- }
183
-
184
- await chainPromises(self.requestTransactionStatus())
185
- }
186
-
187
- iframe.addEventListener("load", listenerHandler)
188
-
189
- } else {
190
- console.log('No iframe found');
191
- }
192
- }
193
- } else {
194
- window.location = url;
195
- }
196
- } else {
197
- console.log('No redirection found');
198
- }
199
- }
200
-
201
- // Returns an object
202
- // https://example.com/?name=John&age=30&city=NewYork
203
- // { name: "John", age: "30", city: "NewYork" }
204
- getURLParameters() {
205
- const parameters: any = {};
206
- const urlParams: any = new URLSearchParams(window.location.search);
207
-
208
- for (const [key, value] of urlParams) {
209
- parameters[key] = value;
210
- }
211
-
212
- return parameters;
213
- }
214
-
215
- handleSuccessTransaction(response: any) {
216
- this.removeVerifyTransactionUrl();
217
- console.log('Transacción autorizada.');
218
- return response;
219
- }
220
-
221
- handleDeclinedTransaction(response: any) {
222
- this.removeVerifyTransactionUrl();
223
- return response;
224
- }
225
-
226
- // TODO: the method below needs to be tested with a real 3DS challenge
227
- // since we couldn't get a test card that works with this feature
228
- async handle3dsChallenge(response_json: any) {
229
- // Create the form element:
230
- const form = document.createElement('form');
231
- form.name = 'frm';
232
- form.method = 'POST';
233
- form.action = response_json.redirect_post_url;
234
-
235
- // Add hidden fields:
236
- const creqInput = document.createElement('input');
237
- creqInput.type = 'hidden';
238
- creqInput.name = response_json.creq;
239
- creqInput.value = response_json.creq;
240
- form.appendChild(creqInput);
241
-
242
- const termUrlInput = document.createElement('input');
243
- termUrlInput.type = 'hidden';
244
- termUrlInput.name = response_json.term_url;
245
- termUrlInput.value = response_json.TermUrl;
246
- form.appendChild(termUrlInput);
247
-
248
- // Append the form to the body:
249
- document.body.appendChild(form);
250
- form.submit();
251
-
252
- await this.verifyTransactionStatus();
253
- }
254
-
255
- // TODO: This method could be removed
256
- async handleTransactionResponse(response: any) {
257
- const response_json = await response.json();
258
- if (response_json.status === "Pending" && response_json.redirect_post_url) {
259
- return await this.handle3dsChallenge(response_json);
260
- } else if (["Success", "Authorized"].includes(response_json.status)) {
261
- return this.handleSuccessTransaction(response_json);
262
- } else {
263
- this.handleDeclinedTransaction(response);
264
- return response_json
265
- }
266
- }
267
-
268
- async requestTransactionStatus() {
269
-
270
- const verifyUrl = this.getUrlWithExpiration();
271
- console.log('verifyUrls: ', verifyUrl);
272
- const url = verifyUrl.startsWith("https://") ? verifyUrl : `${this.baseUrl}${verifyUrl}`;
273
- const response = await fetch(url, {
274
- method: "GET",
275
- headers: {
276
- "Content-Type": "application/json",
277
- Authorization: `Token ${this.apiKey}`,
278
- },
279
- // body: JSON.stringify(data),
280
- });
281
-
282
- if (response.status !== 200) {
283
- console.error('La verificación de la transacción falló.');
284
- return null;
285
- } else {
286
- const response_json = await response.json();
287
- return response_json;
288
- }
289
-
290
- }
291
-
292
- async verifyTransactionStatus() {
293
- const verifyUrl = this.getUrlWithExpiration();
294
-
295
- if (verifyUrl) {
296
- const url = verifyUrl.startsWith("https://") ? verifyUrl : `${this.baseUrl}${verifyUrl}`;
297
- try {
298
- const response = await fetch(url, {
299
- method: "GET",
300
- headers: {
301
- "Content-Type": "application/json",
302
- Authorization: `Token ${this.apiKey}`,
303
- },
304
- // body: JSON.stringify(data),
305
- });
306
-
307
- if (response.status !== 200) {
308
- console.error('La verificación de la transacción falló.');
309
- this.removeVerifyTransactionUrl();
310
- return response
311
- }
312
-
313
- return await this.handleTransactionResponse(response);
314
- } catch (error) {
315
- console.error('Error al verificar la transacción:', error);
316
- this.removeVerifyTransactionUrl();
317
- }
318
- } else {
319
- console.log('No verify_transaction_status_url found');
320
- }
321
- }
322
-
323
- setPayload = (payload: any) => {
324
- this.payload = payload
325
- }
326
- }