@idonatedev/idonate-sdk 1.1.0-dev9 → 1.2.0-dev1

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.
Files changed (71) hide show
  1. package/README.md +308 -7
  2. package/dist/constants.d.ts +1 -1
  3. package/dist/constants.js +2 -6
  4. package/dist/esm/apple-pay.d.ts +12 -0
  5. package/dist/esm/apple-pay.js +74 -0
  6. package/dist/esm/cloudflare-challenge-handler.d.ts +5 -0
  7. package/dist/esm/cloudflare-challenge-handler.js +77 -0
  8. package/dist/esm/config-handler.d.ts +22 -0
  9. package/dist/esm/config-handler.js +47 -0
  10. package/dist/esm/constants.d.ts +18 -0
  11. package/dist/esm/constants.js +81 -0
  12. package/dist/esm/google-pay.d.ts +18 -0
  13. package/dist/esm/google-pay.js +140 -0
  14. package/dist/esm/idonate-client.d.ts +32 -0
  15. package/dist/esm/idonate-client.js +294 -0
  16. package/dist/esm/index.d.ts +11 -0
  17. package/dist/esm/index.js +11 -0
  18. package/dist/esm/recaptcha.d.ts +12 -0
  19. package/dist/esm/recaptcha.js +89 -0
  20. package/dist/esm/shared.d.ts +4 -0
  21. package/dist/esm/shared.js +21 -0
  22. package/dist/esm/test-utils.d.ts +81 -0
  23. package/dist/esm/test-utils.js +94 -0
  24. package/dist/esm/tokenize/CardConnectTokenizer.d.ts +51 -0
  25. package/dist/esm/tokenize/CardConnectTokenizer.js +726 -0
  26. package/dist/esm/tokenize/PayPalTokenizer.d.ts +76 -0
  27. package/dist/esm/tokenize/PayPalTokenizer.js +258 -0
  28. package/dist/esm/tokenize/SpreedlyTokenizer.d.ts +91 -0
  29. package/dist/esm/tokenize/SpreedlyTokenizer.js +1137 -0
  30. package/dist/esm/tokenize/Tokenizer.d.ts +39 -0
  31. package/dist/esm/tokenize/Tokenizer.js +164 -0
  32. package/dist/esm/tokenize/iats.d.ts +3 -0
  33. package/dist/esm/tokenize/iats.js +48 -0
  34. package/dist/esm/tokenize/index.d.ts +7 -0
  35. package/dist/esm/tokenize/index.js +7 -0
  36. package/dist/esm/tokenize/spreedly-secure.d.ts +8 -0
  37. package/dist/esm/tokenize/spreedly-secure.js +40 -0
  38. package/dist/esm/tokenize/styles.d.ts +4 -0
  39. package/dist/esm/tokenize/styles.js +46 -0
  40. package/dist/esm/tokenize/tokenizer-constants.d.ts +62 -0
  41. package/dist/esm/tokenize/tokenizer-constants.js +62 -0
  42. package/dist/esm/tokenize/tokenizer-utils.d.ts +19 -0
  43. package/dist/esm/tokenize/tokenizer-utils.js +139 -0
  44. package/dist/esm/tokenize/types.d.ts +146 -0
  45. package/dist/esm/tokenize/types.js +26 -0
  46. package/dist/esm/typeAdapters.d.ts +29 -0
  47. package/dist/esm/typeAdapters.js +189 -0
  48. package/dist/esm/types.d.ts +378 -0
  49. package/dist/esm/types.js +14 -0
  50. package/dist/esm/util.d.ts +17 -0
  51. package/dist/esm/util.js +113 -0
  52. package/dist/idonate-client.d.ts +6 -2
  53. package/dist/idonate-client.js +40 -2
  54. package/dist/shared.d.ts +2 -1
  55. package/dist/shared.js +9 -0
  56. package/dist/tokenize/CardConnectTokenizer.d.ts +1 -1
  57. package/dist/tokenize/CardConnectTokenizer.js +67 -86
  58. package/dist/tokenize/PayPalTokenizer.d.ts +76 -0
  59. package/dist/tokenize/PayPalTokenizer.js +262 -0
  60. package/dist/tokenize/SpreedlyTokenizer.d.ts +0 -1
  61. package/dist/tokenize/SpreedlyTokenizer.js +6 -9
  62. package/dist/tokenize/Tokenizer.d.ts +2 -0
  63. package/dist/tokenize/Tokenizer.js +20 -2
  64. package/dist/tokenize/index.d.ts +1 -0
  65. package/dist/tokenize/index.js +3 -1
  66. package/dist/tokenize/tokenizer-utils.js +1 -1
  67. package/dist/tokenize/types.d.ts +3 -7
  68. package/dist/typeAdapters.js +3 -2
  69. package/dist/types.d.ts +16 -5
  70. package/package.json +24 -3
  71. package/umd/idonate-sdk.js +1 -1
@@ -0,0 +1,726 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Tokenizer } from './Tokenizer';
11
+ import { TokenizationError, } from './types';
12
+ import ConfigHandler from '../config-handler';
13
+ import { DEFAULT_UNIFIED_STYLES, mergeStyles, getContainerStylesForLayout, } from './styles';
14
+ import { INIT_TIMEOUT, TOKENIZE_TIMEOUT, BANK_FIELD_FLEX, } from './tokenizer-constants';
15
+ import { createInputElement, validateRoutingNumber, validateAccountNumber, createAccountTypeSelect, } from './tokenizer-utils';
16
+ export class CardConnectTokenizer extends Tokenizer {
17
+ constructor(iframe, iframeUrl, containerId, mode = 'credit_card', enableTestMode = false, layout = 'single-line') {
18
+ super();
19
+ this.iframeUrl = iframeUrl;
20
+ this.containerId = containerId;
21
+ this.layout = layout;
22
+ this.enableTestMode = false;
23
+ this.mode = mode;
24
+ this.enableTestMode = enableTestMode;
25
+ this.iframe = iframe;
26
+ const url = new URL(iframeUrl);
27
+ this.expectedOrigin = `${url.protocol}//${url.host}`;
28
+ this.currentValidationState = {
29
+ isValid: false,
30
+ cardNumber: { isValid: false, isEmpty: true },
31
+ cvv: { isValid: false, isEmpty: true },
32
+ expiry: { isValid: false, isEmpty: true },
33
+ };
34
+ }
35
+ static create(gateway, container, config) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ var _a;
38
+ if (container.mode === 'bank_account' && container.bankCountry === 'CA') {
39
+ throw new Error('CardConnect does not support Canadian bank accounts');
40
+ }
41
+ let baseUrl = ((_a = gateway.config) === null || _a === void 0 ? void 0 : _a.base_url) || config.clientConfig.cardConnectBaseUrl;
42
+ config.cardConnectBaseUrl = baseUrl;
43
+ const mergedStyles = mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling);
44
+ const iframeUrl = CardConnectTokenizer.generateIframeUrl(baseUrl, container);
45
+ const iframe = CardConnectTokenizer.createIframe(iframeUrl, mergedStyles);
46
+ const tokenizer = new CardConnectTokenizer(iframe, iframeUrl, container.containerId, container.mode || 'credit_card', container.enableTestMode || false, container.layout || 'single-line');
47
+ tokenizer.createInternalElements(container);
48
+ yield tokenizer.init();
49
+ return tokenizer;
50
+ });
51
+ }
52
+ createInternalElements(container) {
53
+ const containerEl = document.getElementById(container.containerId);
54
+ if (!containerEl) {
55
+ throw new Error(`Container element not found: ${container.containerId}`);
56
+ }
57
+ this.containerEl = containerEl;
58
+ containerEl.innerHTML = '';
59
+ const mergedStyles = getContainerStylesForLayout(mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling), this.layout);
60
+ Object.assign(containerEl.style, mergedStyles.container);
61
+ if (this.mode === 'bank_account') {
62
+ this.createBankAccountFields(containerEl, mergedStyles);
63
+ }
64
+ else {
65
+ this.createCreditCardFields(containerEl, mergedStyles);
66
+ }
67
+ }
68
+ createCreditCardFields(containerEl, mergedStyles) {
69
+ this.iframe.style.width = '100%';
70
+ if (this.layout === 'two-line') {
71
+ const inputHeight = mergedStyles.input.height || '40px';
72
+ this.iframe.style.height = `calc((${inputHeight}) * 2 + 10px)`;
73
+ }
74
+ else {
75
+ this.iframe.style.height = mergedStyles.input.height || '40px';
76
+ }
77
+ this.iframe.style.border = 'none';
78
+ this.iframe.style.display = 'block';
79
+ containerEl.appendChild(this.iframe);
80
+ }
81
+ createBankAccountFields(containerEl, mergedStyles) {
82
+ this.iframe.style.display = 'none';
83
+ if (this.layout === 'two-line') {
84
+ this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
85
+ Object.assign(this.accountTypeEl.style, {
86
+ flex: '1',
87
+ minWidth: BANK_FIELD_FLEX.accountType.minWidth,
88
+ });
89
+ this.applyInputStyles(this.accountTypeEl, mergedStyles, 'left');
90
+ containerEl.appendChild(this.accountTypeEl);
91
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
92
+ Object.assign(this.routingNumberEl.style, {
93
+ flex: '1',
94
+ minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
95
+ });
96
+ this.applyInputStyles(this.routingNumberEl, mergedStyles, 'right');
97
+ containerEl.appendChild(this.routingNumberEl);
98
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
99
+ Object.assign(this.accountNumberEl.style, {
100
+ flex: '1',
101
+ flexBasis: '100%',
102
+ minWidth: BANK_FIELD_FLEX.accountNumber.minWidth,
103
+ });
104
+ this.applyInputStyles(this.accountNumberEl, mergedStyles);
105
+ containerEl.appendChild(this.accountNumberEl);
106
+ }
107
+ else {
108
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
109
+ Object.assign(this.routingNumberEl.style, {
110
+ flex: BANK_FIELD_FLEX.routingNumber.flex,
111
+ minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
112
+ });
113
+ this.applyInputStyles(this.routingNumberEl, mergedStyles, 'left');
114
+ containerEl.appendChild(this.routingNumberEl);
115
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
116
+ Object.assign(this.accountNumberEl.style, {
117
+ flex: BANK_FIELD_FLEX.accountNumber.flex,
118
+ minWidth: BANK_FIELD_FLEX.accountNumber.minWidth,
119
+ });
120
+ this.applyInputStyles(this.accountNumberEl, mergedStyles, 'middle');
121
+ containerEl.appendChild(this.accountNumberEl);
122
+ this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
123
+ Object.assign(this.accountTypeEl.style, {
124
+ flex: BANK_FIELD_FLEX.accountType.flex,
125
+ minWidth: BANK_FIELD_FLEX.accountType.minWidth,
126
+ });
127
+ this.applyInputStyles(this.accountTypeEl, mergedStyles, 'right');
128
+ containerEl.appendChild(this.accountTypeEl);
129
+ }
130
+ }
131
+ init() {
132
+ return __awaiter(this, void 0, void 0, function* () {
133
+ if (this.mode === 'bank_account') {
134
+ if (this.enableTestMode) {
135
+ setTimeout(() => {
136
+ if (this.routingNumberEl) {
137
+ this.routingNumberEl.value = '021000021';
138
+ this.routingNumberEl.dispatchEvent(new Event('input', { bubbles: true }));
139
+ }
140
+ if (this.accountNumberEl) {
141
+ this.accountNumberEl.value = '9876543210';
142
+ this.accountNumberEl.dispatchEvent(new Event('input', { bubbles: true }));
143
+ }
144
+ if (this.accountTypeEl) {
145
+ this.accountTypeEl.value = 'checking';
146
+ this.accountTypeEl.dispatchEvent(new Event('change', { bubbles: true }));
147
+ }
148
+ }, 100);
149
+ }
150
+ this.emit('ready');
151
+ return Promise.resolve();
152
+ }
153
+ return new Promise((resolve, reject) => {
154
+ const timeout = setTimeout(() => {
155
+ reject(new Error('CardConnect initialization timeout'));
156
+ }, INIT_TIMEOUT);
157
+ this.messageHandler = (event) => {
158
+ if (event.origin !== this.expectedOrigin) {
159
+ return;
160
+ }
161
+ this.handleMessage(event);
162
+ };
163
+ window.addEventListener('message', this.messageHandler);
164
+ this.iframe.onload = () => {
165
+ clearTimeout(timeout);
166
+ if (this.enableTestMode && this.mode === 'credit_card') {
167
+ }
168
+ this.emit('ready');
169
+ resolve();
170
+ };
171
+ this.iframe.onerror = () => {
172
+ clearTimeout(timeout);
173
+ reject(new Error('Failed to load CardConnect iframe'));
174
+ };
175
+ });
176
+ });
177
+ }
178
+ tokenize(paymentData) {
179
+ return __awaiter(this, void 0, void 0, function* () {
180
+ if (!this.isReady) {
181
+ throw new Error('Tokenizer not initialized');
182
+ }
183
+ if (this.mode === 'credit_card' && this.hasToken()) {
184
+ const cachedToken = this.getToken();
185
+ if (cachedToken) {
186
+ return cachedToken;
187
+ }
188
+ }
189
+ if (this.mode === 'bank_account' || this.isBankAccountData(paymentData)) {
190
+ return this.tokenizeBankAccountInternal(paymentData);
191
+ }
192
+ else {
193
+ return this.tokenizeCardInternal(paymentData);
194
+ }
195
+ });
196
+ }
197
+ tokenizeCardInternal(cardData) {
198
+ return __awaiter(this, void 0, void 0, function* () {
199
+ if (this.cachedTokenResult && this.cachedTokenResult.errorCode === '0') {
200
+ return {
201
+ token: this.cachedTokenResult.token,
202
+ lastFour: this.cachedTokenResult.token.slice(-4),
203
+ cardType: this.normalizeCardType('unknown'),
204
+ provider: 'cardconnect',
205
+ };
206
+ }
207
+ if (this.tokenizationPromise) {
208
+ return this.tokenizationPromise;
209
+ }
210
+ this.tokenizationPromise = new Promise((resolve, reject) => {
211
+ this.tokenizationResolve = resolve;
212
+ this.tokenizationReject = reject;
213
+ const timeout = setTimeout(() => {
214
+ this.tokenizationPromise = undefined;
215
+ this.tokenizationResolve = undefined;
216
+ this.tokenizationReject = undefined;
217
+ reject(new TokenizationError('Tokenization timeout - ensure all card fields are filled in iframe', 'TIMEOUT'));
218
+ }, TOKENIZE_TIMEOUT);
219
+ const tokenizationHandler = (data) => {
220
+ clearTimeout(timeout);
221
+ this.off('tokenization', tokenizationHandler);
222
+ };
223
+ this.on('tokenization', tokenizationHandler);
224
+ });
225
+ return this.tokenizationPromise;
226
+ });
227
+ }
228
+ tokenizeBankAccountInternal(bankData) {
229
+ return __awaiter(this, void 0, void 0, function* () {
230
+ var _a, _b, _c;
231
+ const routingNumber = (_a = this.routingNumberEl) === null || _a === void 0 ? void 0 : _a.value;
232
+ const accountNumber = (_b = this.accountNumberEl) === null || _b === void 0 ? void 0 : _b.value;
233
+ const accountType = ((_c = this.accountTypeEl) === null || _c === void 0 ? void 0 : _c.value) || 'checking';
234
+ const accountHolderType = 'personal';
235
+ if (!routingNumber || !accountNumber) {
236
+ throw new TokenizationError('Routing number and account number are required', 'VALIDATION_ERROR');
237
+ }
238
+ if (!validateRoutingNumber(routingNumber)) {
239
+ throw new TokenizationError('Invalid routing number', 'VALIDATION_ERROR');
240
+ }
241
+ if (!validateAccountNumber(accountNumber)) {
242
+ throw new TokenizationError('Invalid account number', 'VALIDATION_ERROR');
243
+ }
244
+ const baseUrl = new URL(this.iframeUrl).origin;
245
+ const config = new ConfigHandler({});
246
+ config.cardConnectBaseUrl = baseUrl;
247
+ const result = yield CardConnectTokenizer.tokenizeBankAccount(routingNumber, accountNumber, config);
248
+ return {
249
+ token: result.token,
250
+ lastFour: accountNumber.slice(-4),
251
+ accountType: accountType,
252
+ paymentMethodType: 'bank_account',
253
+ provider: 'cardconnect',
254
+ };
255
+ });
256
+ }
257
+ validate() {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ if (!this.isReady) {
260
+ throw new Error('Tokenizer not initialized');
261
+ }
262
+ if (this.mode === 'bank_account') {
263
+ return this.validateBankAccountInternal();
264
+ }
265
+ else {
266
+ return this.validateCardInternal();
267
+ }
268
+ });
269
+ }
270
+ validateCardInternal() {
271
+ return __awaiter(this, void 0, void 0, function* () {
272
+ const errors = [];
273
+ if (this.currentValidationState.cardNumber) {
274
+ if (this.currentValidationState.cardNumber.isEmpty) {
275
+ errors.push({
276
+ field: 'cardNumber',
277
+ message: 'Card number is required',
278
+ });
279
+ }
280
+ else if (!this.currentValidationState.cardNumber.isValid) {
281
+ errors.push({
282
+ field: 'cardNumber',
283
+ message: 'Invalid card number',
284
+ });
285
+ }
286
+ }
287
+ else {
288
+ errors.push({
289
+ field: 'cardNumber',
290
+ message: 'Card number is required',
291
+ });
292
+ }
293
+ if (this.currentValidationState.expiry) {
294
+ if (this.currentValidationState.expiry.isEmpty) {
295
+ errors.push({
296
+ field: 'expiry',
297
+ message: 'Expiry date is required',
298
+ });
299
+ }
300
+ else if (!this.currentValidationState.expiry.isValid) {
301
+ errors.push({
302
+ field: 'expiry',
303
+ message: 'Invalid expiry date',
304
+ });
305
+ }
306
+ }
307
+ else {
308
+ errors.push({
309
+ field: 'expiry',
310
+ message: 'Expiry date is required',
311
+ });
312
+ }
313
+ if (this.currentValidationState.cvv) {
314
+ if (this.currentValidationState.cvv.isEmpty) {
315
+ errors.push({
316
+ field: 'cvv',
317
+ message: 'CVV is required',
318
+ });
319
+ }
320
+ else if (!this.currentValidationState.cvv.isValid) {
321
+ errors.push({
322
+ field: 'cvv',
323
+ message: 'Invalid CVV',
324
+ });
325
+ }
326
+ }
327
+ else {
328
+ errors.push({
329
+ field: 'cvv',
330
+ message: 'CVV is required',
331
+ });
332
+ }
333
+ return {
334
+ isValid: this.currentValidationState.isValid,
335
+ errors,
336
+ };
337
+ });
338
+ }
339
+ validateBankAccountInternal() {
340
+ return __awaiter(this, void 0, void 0, function* () {
341
+ var _a, _b;
342
+ const errors = [];
343
+ if (!((_a = this.routingNumberEl) === null || _a === void 0 ? void 0 : _a.value)) {
344
+ errors.push({
345
+ field: 'routingNumber',
346
+ message: 'Routing number is required',
347
+ });
348
+ }
349
+ else if (!validateRoutingNumber(this.routingNumberEl.value)) {
350
+ errors.push({
351
+ field: 'routingNumber',
352
+ message: 'Invalid routing number',
353
+ });
354
+ }
355
+ if (!((_b = this.accountNumberEl) === null || _b === void 0 ? void 0 : _b.value)) {
356
+ errors.push({
357
+ field: 'accountNumber',
358
+ message: 'Account number is required',
359
+ });
360
+ }
361
+ else if (!validateAccountNumber(this.accountNumberEl.value)) {
362
+ errors.push({
363
+ field: 'accountNumber',
364
+ message: 'Invalid account number',
365
+ });
366
+ }
367
+ return {
368
+ isValid: errors.length === 0,
369
+ errors,
370
+ };
371
+ });
372
+ }
373
+ clear() {
374
+ var _a;
375
+ if (this.mode === 'bank_account') {
376
+ if (this.routingNumberEl) {
377
+ this.routingNumberEl.value = '';
378
+ }
379
+ if (this.accountNumberEl) {
380
+ this.accountNumberEl.value = '';
381
+ }
382
+ if (this.accountTypeEl) {
383
+ this.accountTypeEl.value = 'checking';
384
+ }
385
+ }
386
+ else {
387
+ (_a = this.iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage({
388
+ action: 'clear',
389
+ }, this.expectedOrigin);
390
+ }
391
+ this.currentValidationState = {
392
+ isValid: false,
393
+ cardNumber: { isValid: false, isEmpty: true },
394
+ cvv: { isValid: false, isEmpty: true },
395
+ expiry: { isValid: false, isEmpty: true },
396
+ };
397
+ this.emit('validation', this.currentValidationState);
398
+ }
399
+ focus(field) {
400
+ var _a;
401
+ if (this.mode === 'bank_account') {
402
+ if (field === 'routingNumber' && this.routingNumberEl) {
403
+ this.routingNumberEl.focus();
404
+ }
405
+ else if (field === 'accountNumber' && this.accountNumberEl) {
406
+ this.accountNumberEl.focus();
407
+ }
408
+ }
409
+ else {
410
+ const fieldMap = {
411
+ cardNumber: 'number',
412
+ cvv: 'cvv',
413
+ expiry: 'expiry',
414
+ };
415
+ const iframeField = fieldMap[field];
416
+ if (iframeField) {
417
+ (_a = this.iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage({
418
+ action: 'focus',
419
+ field: iframeField,
420
+ }, this.expectedOrigin);
421
+ }
422
+ }
423
+ }
424
+ destroy() {
425
+ if (this.messageHandler) {
426
+ window.removeEventListener('message', this.messageHandler);
427
+ }
428
+ if (this.containerEl) {
429
+ this.containerEl.innerHTML = '';
430
+ }
431
+ this.eventHandlers.clear();
432
+ }
433
+ hasToken() {
434
+ var _a, _b;
435
+ if (this.mode === 'credit_card') {
436
+ return (((_a = this.cachedTokenResult) === null || _a === void 0 ? void 0 : _a.errorCode) === '0' &&
437
+ !!((_b = this.cachedTokenResult) === null || _b === void 0 ? void 0 : _b.token));
438
+ }
439
+ return false;
440
+ }
441
+ getToken() {
442
+ var _a;
443
+ if (this.mode === 'credit_card' &&
444
+ ((_a = this.cachedTokenResult) === null || _a === void 0 ? void 0 : _a.errorCode) === '0') {
445
+ const cardType = this.cachedTokenResult.cardType
446
+ ? this.normalizeCardType(this.cachedTokenResult.cardType)
447
+ : this.normalizeCardType('unknown');
448
+ return {
449
+ token: this.cachedTokenResult.token,
450
+ lastFour: this.cachedTokenResult.last4 || this.cachedTokenResult.token.slice(-4),
451
+ cardType,
452
+ provider: 'cardconnect',
453
+ };
454
+ }
455
+ return null;
456
+ }
457
+ get tokenizationMode() {
458
+ return this.mode === 'credit_card' ? 'auto' : 'manual';
459
+ }
460
+ handleMessage(event) {
461
+ try {
462
+ const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
463
+ if (data.token && data.errorCode !== undefined) {
464
+ this.cachedTokenResult = data;
465
+ this.emit('tokenization', data);
466
+ if (data.errorCode === '0') {
467
+ const cardType = data.cardType
468
+ ? this.normalizeCardType(data.cardType)
469
+ : this.normalizeCardType('unknown');
470
+ if (data.cardType && cardType !== this.currentCardType) {
471
+ this.currentCardType = cardType;
472
+ this.emit('cardTypeChange', { cardType });
473
+ }
474
+ const token = {
475
+ token: data.token,
476
+ lastFour: data.last4 || data.token.slice(-4),
477
+ cardType,
478
+ provider: 'cardconnect',
479
+ };
480
+ if (this.tokenizationResolve) {
481
+ this.tokenizationResolve(token);
482
+ this.tokenizationPromise = undefined;
483
+ this.tokenizationResolve = undefined;
484
+ this.tokenizationReject = undefined;
485
+ }
486
+ this.currentValidationState = {
487
+ isValid: true,
488
+ cardNumber: { isValid: true, isEmpty: false },
489
+ cvv: { isValid: true, isEmpty: false },
490
+ expiry: { isValid: true, isEmpty: false },
491
+ };
492
+ this.emit('validation', { isValid: true });
493
+ this.emit('tokenReady', token);
494
+ }
495
+ else {
496
+ if (this.tokenizationReject) {
497
+ this.tokenizationReject(new TokenizationError(data.errorMessage || 'Tokenization failed', data.errorCode || 'UNKNOWN'));
498
+ this.tokenizationPromise = undefined;
499
+ this.tokenizationResolve = undefined;
500
+ this.tokenizationReject = undefined;
501
+ }
502
+ }
503
+ }
504
+ if (data.event === 'validation' || data.validationError !== undefined) {
505
+ this.handleValidationMessage(data);
506
+ }
507
+ if (data.event === 'focus' || data.event === 'blur') {
508
+ this.emit(data.event, { field: data.data });
509
+ }
510
+ if (data.event === 'input' || data.event === 'change') {
511
+ this.emit('change', { field: data.data });
512
+ if (data.cardType) {
513
+ const cardType = this.normalizeCardType(data.cardType);
514
+ if (cardType !== this.currentCardType) {
515
+ this.currentCardType = cardType;
516
+ this.emit('cardTypeChange', { cardType });
517
+ }
518
+ }
519
+ }
520
+ }
521
+ catch (error) {
522
+ }
523
+ }
524
+ handleValidationMessage(data) {
525
+ var _a, _b, _c, _d, _e, _f;
526
+ if (data.validationError) {
527
+ const errorLower = data.validationError.toLowerCase();
528
+ if (errorLower.includes('card') || errorLower.includes('number')) {
529
+ this.currentValidationState.cardNumber = {
530
+ isValid: false,
531
+ isEmpty: false,
532
+ };
533
+ }
534
+ else if (errorLower.includes('cvv') ||
535
+ errorLower.includes('security')) {
536
+ this.currentValidationState.cvv = { isValid: false, isEmpty: false };
537
+ }
538
+ else if (errorLower.includes('expir') ||
539
+ errorLower.includes('month') ||
540
+ errorLower.includes('year')) {
541
+ this.currentValidationState.expiry = { isValid: false, isEmpty: false };
542
+ }
543
+ this.emit('error', new TokenizationError(data.validationError));
544
+ }
545
+ this.currentValidationState.isValid =
546
+ ((_b = (_a = this.currentValidationState.cardNumber) === null || _a === void 0 ? void 0 : _a.isValid) !== null && _b !== void 0 ? _b : false) &&
547
+ ((_d = (_c = this.currentValidationState.cvv) === null || _c === void 0 ? void 0 : _c.isValid) !== null && _d !== void 0 ? _d : false) &&
548
+ ((_f = (_e = this.currentValidationState.expiry) === null || _e === void 0 ? void 0 : _e.isValid) !== null && _f !== void 0 ? _f : false);
549
+ }
550
+ static generateIframeUrl(baseUrl, container) {
551
+ const params = new URLSearchParams({
552
+ invalidinputevent: 'true',
553
+ enhancedresponse: 'true',
554
+ useexpiry: 'true',
555
+ usecvv: 'true',
556
+ formatinput: 'true',
557
+ unique: 'true',
558
+ norsa: 'true',
559
+ placeholder: 'Card Number *',
560
+ placeholdercvv: 'CVV',
561
+ invalidcreditcardevent: 'true',
562
+ invalidexpiry: 'true',
563
+ invalidcvv: 'true',
564
+ });
565
+ if (container.layout === 'two-line') {
566
+ params.set('useexpiryfield', 'true');
567
+ params.set('orientation', 'horizontal');
568
+ params.set('placeholdermonth', 'MM');
569
+ params.set('placeholderyear', 'YYYY');
570
+ }
571
+ else {
572
+ params.set('orientation', 'horizontal');
573
+ params.set('placeholdermonth', 'MM');
574
+ params.set('placeholderyear', 'YYYY');
575
+ }
576
+ const mergedStyles = mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling);
577
+ const cssString = CardConnectTokenizer.generateCardConnectCss(mergedStyles, container.layout || 'single-line');
578
+ const queryPairs = [];
579
+ params.forEach((value, key) => {
580
+ queryPairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
581
+ });
582
+ queryPairs.push(`css=${cssString}`);
583
+ const tokenizerPath = '/itoke/ajax-tokenizer.html';
584
+ return `${baseUrl}${tokenizerPath}?${queryPairs.join('&')}`;
585
+ }
586
+ static createIframe(url, styles) {
587
+ const iframe = document.createElement('iframe');
588
+ iframe.src = url;
589
+ iframe.style.width = '100%';
590
+ iframe.style.height = styles.input.height;
591
+ iframe.style.border = 'none';
592
+ iframe.style.overflow = 'hidden';
593
+ iframe.style.display = 'block';
594
+ iframe.style.minWidth = '0';
595
+ if (styles.container) {
596
+ Object.entries(styles.container).forEach(([key, value]) => {
597
+ if (key !== 'height' && key !== 'width') {
598
+ iframe.style[key] = value;
599
+ }
600
+ });
601
+ if (styles.container.width) {
602
+ iframe.style.width = styles.container.width;
603
+ }
604
+ }
605
+ iframe.setAttribute('scrolling', 'no');
606
+ iframe.setAttribute('title', 'Secure card input');
607
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
608
+ return iframe;
609
+ }
610
+ static generateCardConnectCss(styles, layout = 'single-line') {
611
+ var _a, _b, _c, _d;
612
+ const css = [];
613
+ if (layout === 'two-line') {
614
+ css.push('html,form,body{margin:0;padding:0;}');
615
+ css.push('label{display:none;}');
616
+ css.push('br{display:none;}');
617
+ }
618
+ else {
619
+ css.push('body{margin:0;padding:0;display:flex;align-items:center;}');
620
+ css.push('form{margin:0;padding:0;display:flex;width:100%;align-items:center;}');
621
+ css.push('label{display:none;}');
622
+ css.push('br{display:none;}');
623
+ }
624
+ const isConnected = styles.container.gap === '0';
625
+ const gap = isConnected ? '0' : styles.container.gap || '1rem';
626
+ if (styles.input) {
627
+ const commonStyles = [];
628
+ if (styles.input.height)
629
+ commonStyles.push(`height:${styles.input.height}`);
630
+ if (styles.input.padding)
631
+ commonStyles.push(`padding:${styles.input.padding}`);
632
+ if (styles.input.fontSize)
633
+ commonStyles.push(`font-size:${styles.input.fontSize}`);
634
+ if (styles.input.fontFamily)
635
+ commonStyles.push(`font-family:${styles.input.fontFamily}`);
636
+ if (styles.input.border)
637
+ commonStyles.push(`border:${styles.input.border}`);
638
+ if (styles.input.backgroundColor)
639
+ commonStyles.push(`background-color:${styles.input.backgroundColor}`);
640
+ if (styles.input.color)
641
+ commonStyles.push(`color:${styles.input.color}`);
642
+ commonStyles.push('box-sizing:border-box');
643
+ const baseStyles = commonStyles.join(';');
644
+ css.push(`input{${baseStyles};}`);
645
+ css.push(`select{${baseStyles};}`);
646
+ }
647
+ if (layout === 'two-line') {
648
+ css.push('input#ccnumfield{width:100%;display:block;margin-bottom:8px;}');
649
+ css.push('input#ccexpiryfieldmonth{width:20%;}');
650
+ css.push('input#ccexpiryfieldyear{width:24%;}');
651
+ css.push('input#cccvvfield{width:calc(56% - 20px);}');
652
+ if ((_a = styles.input) === null || _a === void 0 ? void 0 : _a.borderRadius) {
653
+ css.push(`input{border-radius:${styles.input.borderRadius};}`);
654
+ }
655
+ }
656
+ else if (isConnected) {
657
+ css.push('input#ccnumfield{width:44%;margin:0;}');
658
+ css.push('select#ccexpirymonth{width:16%;margin:0;margin-right:-12px;}');
659
+ css.push('select#ccexpiryyear{width:20%;}');
660
+ css.push('input#cccvvfield{width:20%;margin:0;margin-left:-12px;}');
661
+ if ((_b = styles.input) === null || _b === void 0 ? void 0 : _b.borderRadius) {
662
+ css.push(`input#ccnumfield{border-radius:${styles.input.borderRadius} 0 0 ${styles.input.borderRadius};border-right:none;}`);
663
+ css.push('select#ccexpirymonth{border-radius:0;border-right:none;}');
664
+ css.push('select#ccexpiryyear{border-radius:0;border-right:none;}');
665
+ css.push(`input#cccvvfield{border-radius:0 ${styles.input.borderRadius} ${styles.input.borderRadius} 0;}`);
666
+ }
667
+ else {
668
+ css.push('input#ccnumfield{border-right:none;}');
669
+ css.push('select#ccexpirymonth{border-right:none;}');
670
+ css.push('select#ccexpiryyear{border-right:none;}');
671
+ }
672
+ }
673
+ else {
674
+ const marginRight = gap === '0' ? '0' : '8px';
675
+ css.push(`input#ccnumfield{width:50%;margin-right:${marginRight};}`);
676
+ css.push(`select#ccexpirymonth{width:15%;margin-right:${marginRight};}`);
677
+ css.push(`select#ccexpiryyear{width:20%;margin-right:${marginRight};}`);
678
+ css.push('input#cccvvfield{width:15%;}');
679
+ if ((_c = styles.input) === null || _c === void 0 ? void 0 : _c.borderRadius) {
680
+ css.push(`input,select{border-radius:${styles.input.borderRadius};}`);
681
+ }
682
+ }
683
+ if (styles.focus) {
684
+ const focusStyles = [];
685
+ if (styles.focus.borderColor)
686
+ focusStyles.push(`border-color:${styles.focus.borderColor}`);
687
+ if (styles.focus.outline) {
688
+ focusStyles.push(`outline:none`);
689
+ }
690
+ if (styles.focus.boxShadow)
691
+ focusStyles.push(`box-shadow:${styles.focus.boxShadow}`);
692
+ if (focusStyles.length > 0) {
693
+ css.push(`input:focus{${focusStyles.join(';')};}`);
694
+ css.push(`select:focus{${focusStyles.join(';')};}`);
695
+ if (isConnected && ((_d = styles.input) === null || _d === void 0 ? void 0 : _d.border)) {
696
+ css.push(`input#ccnumfield:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
697
+ css.push(`select#ccexpirymonth:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
698
+ css.push(`select#ccexpiryyear:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
699
+ }
700
+ }
701
+ }
702
+ if (styles.error) {
703
+ const errorStyles = [];
704
+ if (styles.error.borderColor)
705
+ errorStyles.push(`border-color:${styles.error.borderColor}`);
706
+ if (styles.error.backgroundColor)
707
+ errorStyles.push(`background-color:${styles.error.backgroundColor}`);
708
+ if (errorStyles.length > 0) {
709
+ css.push(`.error{${errorStyles.join(';')};}`);
710
+ }
711
+ }
712
+ return encodeURIComponent(css.join(''));
713
+ }
714
+ static tokenizeBankAccount(routingNumber, accountNumber, config) {
715
+ return fetch(`${config.cardConnectBaseUrl}/cardsecure/api/v1/ccn/tokenize`, {
716
+ method: 'POST',
717
+ headers: {
718
+ 'Content-Type': 'application/json',
719
+ },
720
+ body: JSON.stringify({
721
+ account: `${routingNumber}/${accountNumber}`,
722
+ unique: true,
723
+ }),
724
+ }).then((response) => response.json());
725
+ }
726
+ }