@idonatedev/idonate-sdk 1.2.0-dev14 → 1.2.0-dev16

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 (34) hide show
  1. package/dist/constants.js +1 -1
  2. package/dist/esm/constants.js +1 -1
  3. package/dist/esm/tokenize/CardConnectTokenizer.d.ts +6 -1
  4. package/dist/esm/tokenize/CardConnectTokenizer.js +184 -53
  5. package/dist/esm/tokenize/PayPalTokenizer.d.ts +1 -0
  6. package/dist/esm/tokenize/PayPalTokenizer.js +52 -16
  7. package/dist/esm/tokenize/SpreedlyTokenizer.d.ts +6 -0
  8. package/dist/esm/tokenize/SpreedlyTokenizer.js +113 -63
  9. package/dist/esm/tokenize/Tokenizer.d.ts +6 -2
  10. package/dist/esm/tokenize/Tokenizer.js +13 -3
  11. package/dist/esm/tokenize/gateway-utils.js +3 -2
  12. package/dist/esm/tokenize/index.d.ts +1 -1
  13. package/dist/esm/tokenize/spreedly-secure.js +6 -2
  14. package/dist/esm/tokenize/tokenizer-utils.d.ts +1 -1
  15. package/dist/esm/tokenize/tokenizer-utils.js +25 -4
  16. package/dist/esm/tokenize/types.d.ts +3 -2
  17. package/dist/esm/typeAdapters.js +6 -4
  18. package/dist/tokenize/CardConnectTokenizer.d.ts +6 -1
  19. package/dist/tokenize/CardConnectTokenizer.js +184 -56
  20. package/dist/tokenize/PayPalTokenizer.d.ts +1 -0
  21. package/dist/tokenize/PayPalTokenizer.js +52 -16
  22. package/dist/tokenize/SpreedlyTokenizer.d.ts +6 -0
  23. package/dist/tokenize/SpreedlyTokenizer.js +113 -63
  24. package/dist/tokenize/Tokenizer.d.ts +6 -2
  25. package/dist/tokenize/Tokenizer.js +13 -3
  26. package/dist/tokenize/gateway-utils.js +3 -2
  27. package/dist/tokenize/index.d.ts +1 -1
  28. package/dist/tokenize/spreedly-secure.js +6 -2
  29. package/dist/tokenize/tokenizer-utils.d.ts +1 -1
  30. package/dist/tokenize/tokenizer-utils.js +25 -4
  31. package/dist/tokenize/types.d.ts +3 -2
  32. package/dist/typeAdapters.js +6 -4
  33. package/package.json +1 -1
  34. package/umd/idonate-sdk.js +1 -1
@@ -8,14 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.CardConnectTokenizer = void 0;
16
13
  const Tokenizer_1 = require("./Tokenizer");
17
14
  const types_1 = require("./types");
18
- const config_handler_1 = __importDefault(require("../config-handler"));
19
15
  const styles_1 = require("./styles");
20
16
  const tokenizer_constants_1 = require("./tokenizer-constants");
21
17
  const tokenizer_utils_1 = require("./tokenizer-utils");
@@ -26,6 +22,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
26
22
  this.containerId = containerId;
27
23
  this.layout = layout;
28
24
  this.enableTestMode = false;
25
+ this.acceptAutoToken = true;
29
26
  this.mode = mode;
30
27
  this.enableTestMode = enableTestMode;
31
28
  this.iframe = iframe;
@@ -40,7 +37,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
40
37
  }
41
38
  static create(gateway, container, config) {
42
39
  return __awaiter(this, void 0, void 0, function* () {
43
- var _a;
40
+ var _a, _b;
44
41
  if (container.mode === 'bank_account' && container.bankCountry === 'CA') {
45
42
  throw new Error('CardConnect does not support Canadian bank accounts');
46
43
  }
@@ -48,7 +45,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
48
45
  if (container.layout === 'responsive') {
49
46
  const containerEl = document.getElementById(container.containerId);
50
47
  const containerWidth = (containerEl === null || containerEl === void 0 ? void 0 : containerEl.offsetWidth) || 0;
51
- const breakpoint = container.responsiveBreakpoint || tokenizer_constants_1.RESPONSIVE_BREAKPOINT;
48
+ const breakpoint = (_a = container.responsiveBreakpoint) !== null && _a !== void 0 ? _a : tokenizer_constants_1.RESPONSIVE_BREAKPOINT;
52
49
  effectiveLayout =
53
50
  containerWidth < breakpoint ? 'two-line' : 'single-line';
54
51
  }
@@ -56,8 +53,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
56
53
  effectiveLayout = 'two-line';
57
54
  }
58
55
  const resolvedContainer = Object.assign(Object.assign({}, container), { layout: effectiveLayout });
59
- let baseUrl = ((_a = gateway.config) === null || _a === void 0 ? void 0 : _a.baseUrl) || config.clientConfig.cardConnectBaseUrl;
60
- config.cardConnectBaseUrl = baseUrl;
56
+ const baseUrl = ((_b = gateway.config) === null || _b === void 0 ? void 0 : _b.baseUrl) || config.clientConfig.cardConnectBaseUrl;
61
57
  const mergedStyles = (0, styles_1.mergeStyles)(styles_1.DEFAULT_UNIFIED_STYLES, container.styling);
62
58
  const iframeUrl = CardConnectTokenizer.generateIframeUrl(baseUrl, resolvedContainer);
63
59
  const iframe = CardConnectTokenizer.createIframe(iframeUrl, mergedStyles);
@@ -145,6 +141,66 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
145
141
  this.applyInputStyles(this.accountTypeEl, mergedStyles, 'right');
146
142
  containerEl.appendChild(this.accountTypeEl);
147
143
  }
144
+ this.mergedStyles = mergedStyles;
145
+ this.currentValidationState = {
146
+ isValid: false,
147
+ routingNumber: { isValid: false, isEmpty: true },
148
+ accountNumber: { isValid: false, isEmpty: true },
149
+ };
150
+ this.addBankAccountValidationListeners(mergedStyles);
151
+ }
152
+ addBankAccountValidationListeners(styles) {
153
+ if (this.routingNumberEl) {
154
+ this.routingNumberEl.addEventListener('blur', () => {
155
+ this.updateBankAccountValidation(styles);
156
+ });
157
+ this.routingNumberEl.addEventListener('input', () => {
158
+ this.updateBankAccountValidation(styles);
159
+ });
160
+ }
161
+ if (this.accountNumberEl) {
162
+ this.accountNumberEl.addEventListener('blur', () => {
163
+ this.updateBankAccountValidation(styles);
164
+ });
165
+ this.accountNumberEl.addEventListener('input', () => {
166
+ this.updateBankAccountValidation(styles);
167
+ });
168
+ }
169
+ }
170
+ updateBankAccountValidation(styles) {
171
+ var _a, _b;
172
+ const routingValue = ((_a = this.routingNumberEl) === null || _a === void 0 ? void 0 : _a.value) || '';
173
+ const accountValue = ((_b = this.accountNumberEl) === null || _b === void 0 ? void 0 : _b.value) || '';
174
+ const routingValid = routingValue.length > 0 && (0, tokenizer_utils_1.validateRoutingNumber)(routingValue);
175
+ const accountValid = accountValue.length > 0 && (0, tokenizer_utils_1.validateAccountNumber)(accountValue);
176
+ if (this.routingNumberEl) {
177
+ if (routingValue.length > 0 && !routingValid) {
178
+ this.routingNumberEl.style.borderColor = styles.error.borderColor;
179
+ }
180
+ else {
181
+ this.routingNumberEl.style.borderColor = '';
182
+ }
183
+ }
184
+ if (this.accountNumberEl) {
185
+ if (accountValue.length > 0 && !accountValid) {
186
+ this.accountNumberEl.style.borderColor = styles.error.borderColor;
187
+ }
188
+ else {
189
+ this.accountNumberEl.style.borderColor = '';
190
+ }
191
+ }
192
+ this.currentValidationState = {
193
+ isValid: routingValid && accountValid,
194
+ routingNumber: {
195
+ isValid: routingValid,
196
+ isEmpty: routingValue.length === 0,
197
+ },
198
+ accountNumber: {
199
+ isValid: accountValid,
200
+ isEmpty: accountValue.length === 0,
201
+ },
202
+ };
203
+ this.emit('validation', this.currentValidationState);
148
204
  }
149
205
  init() {
150
206
  return __awaiter(this, void 0, void 0, function* () {
@@ -214,31 +270,39 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
214
270
  }
215
271
  tokenizeCardInternal(cardData) {
216
272
  return __awaiter(this, void 0, void 0, function* () {
273
+ var _a, _b, _c;
217
274
  if (this.cachedTokenResult && this.cachedTokenResult.errorCode === '0') {
275
+ const cardType = this.cachedTokenResult.cardType
276
+ ? this.normalizeCardType(this.cachedTokenResult.cardType)
277
+ : this.normalizeCardType('unknown');
218
278
  return {
219
279
  token: this.cachedTokenResult.token,
220
- lastFour: this.cachedTokenResult.token.slice(-4),
221
- cardType: this.normalizeCardType('unknown'),
280
+ lastFour: this.cachedTokenResult.last4 ||
281
+ this.cachedTokenResult.token.slice(-4),
282
+ cardType,
283
+ paymentMethodType: 'credit_card',
222
284
  provider: 'cardconnect',
223
285
  };
224
286
  }
225
287
  if (this.tokenizationPromise) {
226
288
  return this.tokenizationPromise;
227
289
  }
290
+ if (!this.currentValidationState.isValid &&
291
+ (((_a = this.currentValidationState.cardNumber) === null || _a === void 0 ? void 0 : _a.isEmpty) ||
292
+ ((_b = this.currentValidationState.cvv) === null || _b === void 0 ? void 0 : _b.isEmpty) ||
293
+ ((_c = this.currentValidationState.expiry) === null || _c === void 0 ? void 0 : _c.isEmpty))) {
294
+ throw new types_1.TokenizationError('Card fields are incomplete', 'VALIDATION_ERROR');
295
+ }
228
296
  this.tokenizationPromise = new Promise((resolve, reject) => {
229
297
  this.tokenizationResolve = resolve;
230
298
  this.tokenizationReject = reject;
231
- const timeout = setTimeout(() => {
299
+ this.tokenizationTimeout = setTimeout(() => {
300
+ this.tokenizationTimeout = undefined;
232
301
  this.tokenizationPromise = undefined;
233
302
  this.tokenizationResolve = undefined;
234
303
  this.tokenizationReject = undefined;
235
304
  reject(new types_1.TokenizationError('Tokenization timeout - ensure all card fields are filled in iframe', 'TIMEOUT'));
236
305
  }, tokenizer_constants_1.TOKENIZE_TIMEOUT);
237
- const tokenizationHandler = (data) => {
238
- clearTimeout(timeout);
239
- this.off('tokenization', tokenizationHandler);
240
- };
241
- this.on('tokenization', tokenizationHandler);
242
306
  });
243
307
  return this.tokenizationPromise;
244
308
  });
@@ -260,16 +324,16 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
260
324
  throw new types_1.TokenizationError('Invalid account number', 'VALIDATION_ERROR');
261
325
  }
262
326
  const baseUrl = new URL(this.iframeUrl).origin;
263
- const config = new config_handler_1.default({});
264
- config.cardConnectBaseUrl = baseUrl;
265
- const result = yield CardConnectTokenizer.tokenizeBankAccount(routingNumber, accountNumber, config);
266
- return {
327
+ const result = yield CardConnectTokenizer.tokenizeBankAccount(routingNumber, accountNumber, baseUrl);
328
+ const paymentToken = {
267
329
  token: result.token,
268
330
  lastFour: accountNumber.slice(-4),
269
331
  accountType: accountType,
270
332
  paymentMethodType: 'bank_account',
271
333
  provider: 'cardconnect',
272
334
  };
335
+ this.emit('tokenReady', paymentToken);
336
+ return paymentToken;
273
337
  });
274
338
  }
275
339
  validate() {
@@ -406,12 +470,39 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
406
470
  action: 'clear',
407
471
  }, this.expectedOrigin);
408
472
  }
409
- this.currentValidationState = {
410
- isValid: false,
411
- cardNumber: { isValid: false, isEmpty: true },
412
- cvv: { isValid: false, isEmpty: true },
413
- expiry: { isValid: false, isEmpty: true },
414
- };
473
+ this.cachedTokenResult = undefined;
474
+ this.acceptAutoToken = false;
475
+ if (this.tokenizationTimeout) {
476
+ clearTimeout(this.tokenizationTimeout);
477
+ this.tokenizationTimeout = undefined;
478
+ }
479
+ if (this.tokenizationReject) {
480
+ const reject = this.tokenizationReject;
481
+ this.tokenizationPromise = undefined;
482
+ this.tokenizationResolve = undefined;
483
+ this.tokenizationReject = undefined;
484
+ reject(new types_1.TokenizationError('Tokenizer cleared', 'CLEARED'));
485
+ }
486
+ else {
487
+ this.tokenizationPromise = undefined;
488
+ this.tokenizationResolve = undefined;
489
+ this.tokenizationReject = undefined;
490
+ }
491
+ if (this.mode === 'bank_account') {
492
+ this.currentValidationState = {
493
+ isValid: false,
494
+ routingNumber: { isValid: false, isEmpty: true },
495
+ accountNumber: { isValid: false, isEmpty: true },
496
+ };
497
+ }
498
+ else {
499
+ this.currentValidationState = {
500
+ isValid: false,
501
+ cardNumber: { isValid: false, isEmpty: true },
502
+ cvv: { isValid: false, isEmpty: true },
503
+ expiry: { isValid: false, isEmpty: true },
504
+ };
505
+ }
415
506
  this.emit('validation', this.currentValidationState);
416
507
  }
417
508
  focus(field) {
@@ -440,8 +531,22 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
440
531
  }
441
532
  }
442
533
  destroy() {
534
+ this.markDestroyed();
535
+ if (this.tokenizationTimeout) {
536
+ clearTimeout(this.tokenizationTimeout);
537
+ this.tokenizationTimeout = undefined;
538
+ }
539
+ if (this.tokenizationReject) {
540
+ const reject = this.tokenizationReject;
541
+ this.tokenizationPromise = undefined;
542
+ this.tokenizationResolve = undefined;
543
+ this.tokenizationReject = undefined;
544
+ reject(new types_1.TokenizationError('Tokenizer destroyed', 'DESTROYED'));
545
+ }
546
+ this.cachedTokenResult = undefined;
443
547
  if (this.messageHandler) {
444
548
  window.removeEventListener('message', this.messageHandler);
549
+ this.messageHandler = undefined;
445
550
  }
446
551
  if (this.containerEl) {
447
552
  this.containerEl.innerHTML = '';
@@ -468,6 +573,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
468
573
  lastFour: this.cachedTokenResult.last4 ||
469
574
  this.cachedTokenResult.token.slice(-4),
470
575
  cardType,
576
+ paymentMethodType: 'credit_card',
471
577
  provider: 'cardconnect',
472
578
  };
473
579
  }
@@ -480,8 +586,14 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
480
586
  try {
481
587
  const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
482
588
  if (data.token && data.errorCode !== undefined) {
589
+ if (!this.acceptAutoToken && !this.tokenizationResolve) {
590
+ return;
591
+ }
483
592
  this.cachedTokenResult = data;
484
- this.emit('tokenization', data);
593
+ if (this.tokenizationTimeout) {
594
+ clearTimeout(this.tokenizationTimeout);
595
+ this.tokenizationTimeout = undefined;
596
+ }
485
597
  if (data.errorCode === '0') {
486
598
  const cardType = data.cardType
487
599
  ? this.normalizeCardType(data.cardType)
@@ -494,6 +606,7 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
494
606
  token: data.token,
495
607
  lastFour: data.last4 || data.token.slice(-4),
496
608
  cardType,
609
+ paymentMethodType: 'credit_card',
497
610
  provider: 'cardconnect',
498
611
  };
499
612
  if (this.tokenizationResolve) {
@@ -508,12 +621,14 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
508
621
  cvv: { isValid: true, isEmpty: false },
509
622
  expiry: { isValid: true, isEmpty: false },
510
623
  };
511
- this.emit('validation', { isValid: true });
624
+ this.emit('validation', this.currentValidationState);
512
625
  this.emit('tokenReady', token);
513
626
  }
514
627
  else {
628
+ const error = new types_1.TokenizationError(data.errorMessage || 'Tokenization failed', data.errorCode || 'UNKNOWN');
629
+ this.emit('error', error);
515
630
  if (this.tokenizationReject) {
516
- this.tokenizationReject(new types_1.TokenizationError(data.errorMessage || 'Tokenization failed', data.errorCode || 'UNKNOWN'));
631
+ this.tokenizationReject(error);
517
632
  this.tokenizationPromise = undefined;
518
633
  this.tokenizationResolve = undefined;
519
634
  this.tokenizationReject = undefined;
@@ -521,19 +636,16 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
521
636
  }
522
637
  }
523
638
  if (data.event === 'validation' || data.validationError !== undefined) {
639
+ this.acceptAutoToken = true;
524
640
  this.handleValidationMessage(data);
525
641
  }
526
- if (data.event === 'focus' || data.event === 'blur') {
527
- this.emit(data.event, { field: data.data });
528
- }
529
- if (data.event === 'input' || data.event === 'change') {
530
- this.emit('change', { field: data.data });
531
- if (data.cardType) {
532
- const cardType = this.normalizeCardType(data.cardType);
533
- if (cardType !== this.currentCardType) {
534
- this.currentCardType = cardType;
535
- this.emit('cardTypeChange', { cardType });
536
- }
642
+ if ((data.event === 'input' || data.event === 'change') &&
643
+ data.cardType) {
644
+ this.acceptAutoToken = true;
645
+ const cardType = this.normalizeCardType(data.cardType);
646
+ if (cardType !== this.currentCardType) {
647
+ this.currentCardType = cardType;
648
+ this.emit('cardTypeChange', { cardType });
537
649
  }
538
650
  }
539
651
  }
@@ -541,7 +653,6 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
541
653
  }
542
654
  }
543
655
  handleValidationMessage(data) {
544
- var _a, _b, _c, _d, _e, _f;
545
656
  if (data.validationError) {
546
657
  const errorLower = data.validationError.toLowerCase();
547
658
  if (errorLower.includes('card') || errorLower.includes('number')) {
@@ -563,10 +674,14 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
563
674
  this.emit('validation', this.currentValidationState);
564
675
  }
565
676
  else {
566
- this.currentValidationState.isValid =
567
- ((_b = (_a = this.currentValidationState.cardNumber) === null || _a === void 0 ? void 0 : _a.isValid) !== null && _b !== void 0 ? _b : false) &&
568
- ((_d = (_c = this.currentValidationState.cvv) === null || _c === void 0 ? void 0 : _c.isValid) !== null && _d !== void 0 ? _d : false) &&
569
- ((_f = (_e = this.currentValidationState.expiry) === null || _e === void 0 ? void 0 : _e.isValid) !== null && _f !== void 0 ? _f : false);
677
+ this.currentValidationState.cardNumber = {
678
+ isValid: true,
679
+ isEmpty: false,
680
+ };
681
+ this.currentValidationState.cvv = { isValid: true, isEmpty: false };
682
+ this.currentValidationState.expiry = { isValid: true, isEmpty: false };
683
+ this.currentValidationState.isValid = true;
684
+ this.emit('validation', this.currentValidationState);
570
685
  }
571
686
  }
572
687
  static generateIframeUrl(baseUrl, container) {
@@ -746,17 +861,30 @@ class CardConnectTokenizer extends Tokenizer_1.Tokenizer {
746
861
  }
747
862
  return encodeURIComponent(css.join(''));
748
863
  }
749
- static tokenizeBankAccount(routingNumber, accountNumber, config) {
750
- return fetch(`${config.cardConnectBaseUrl}/cardsecure/api/v1/ccn/tokenize`, {
751
- method: 'POST',
752
- headers: {
753
- 'Content-Type': 'application/json',
754
- },
755
- body: JSON.stringify({
756
- account: `${routingNumber}/${accountNumber}`,
757
- unique: true,
758
- }),
759
- }).then((response) => response.json());
864
+ static tokenizeBankAccount(routingNumber, accountNumber, configOrBaseUrl) {
865
+ return __awaiter(this, void 0, void 0, function* () {
866
+ const baseUrl = typeof configOrBaseUrl === 'string'
867
+ ? configOrBaseUrl
868
+ : configOrBaseUrl.cardConnectBaseUrl;
869
+ const response = yield fetch(`${baseUrl}/cardsecure/api/v1/ccn/tokenize`, {
870
+ method: 'POST',
871
+ headers: {
872
+ 'Content-Type': 'application/json',
873
+ },
874
+ body: JSON.stringify({
875
+ account: `${routingNumber}/${accountNumber}`,
876
+ unique: true,
877
+ }),
878
+ });
879
+ if (!response.ok) {
880
+ throw new types_1.TokenizationError(`Bank account tokenization failed: HTTP ${response.status}`, 'API_ERROR');
881
+ }
882
+ const result = yield response.json();
883
+ if (!result.token) {
884
+ throw new types_1.TokenizationError(result.message || 'Bank account tokenization failed: no token returned', 'API_ERROR');
885
+ }
886
+ return result;
887
+ });
760
888
  }
761
889
  }
762
890
  exports.CardConnectTokenizer = CardConnectTokenizer;
@@ -41,6 +41,7 @@ export declare class PayPalTokenizer extends Tokenizer {
41
41
  private configContext;
42
42
  private containerEl?;
43
43
  private paypalButtonContainer?;
44
+ private buttonsInstance?;
44
45
  private cachedToken?;
45
46
  private currency;
46
47
  private amount?;
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.PayPalTokenizer = void 0;
13
13
  const Tokenizer_1 = require("./Tokenizer");
14
14
  const types_1 = require("./types");
15
+ const constants_1 = require("../constants");
15
16
  const PAYPAL_SDK_URL = 'https://www.paypal.com/sdk/js';
16
17
  const PAYPAL_SCRIPT_ID = 'paypal-sdk-script';
17
18
  const INIT_TIMEOUT = 10000;
@@ -22,7 +23,7 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
22
23
  this.gateway = gateway;
23
24
  this.containerId = containerId;
24
25
  this.configContext = configContext;
25
- this.mode = 'credit_card';
26
+ this.mode = 'paypal';
26
27
  const gatewayConfig = gateway.config;
27
28
  if (!(gatewayConfig === null || gatewayConfig === void 0 ? void 0 : gatewayConfig.client_id)) {
28
29
  throw new Error('PayPal gateway configuration missing client_id');
@@ -69,7 +70,7 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
69
70
  })
70
71
  .then(() => {
71
72
  this.emit('ready');
72
- this.emit('validation', { isValid: true });
73
+ this.emit('validation', { isValid: true, hasToken: false });
73
74
  resolve();
74
75
  })
75
76
  .catch((error) => {
@@ -82,11 +83,20 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
82
83
  }
83
84
  loadPayPalSDK() {
84
85
  return __awaiter(this, void 0, void 0, function* () {
85
- if (window.paypal) {
86
- return Promise.resolve();
87
- }
88
- if (document.getElementById(PAYPAL_SCRIPT_ID)) {
89
- return this.waitForPayPalGlobal();
86
+ const configKey = `${this.clientId}|${this.currency}`;
87
+ const existingScript = document.getElementById(PAYPAL_SCRIPT_ID);
88
+ if (existingScript) {
89
+ const loadedConfig = existingScript.getAttribute('data-config-key');
90
+ if (loadedConfig && loadedConfig !== configKey) {
91
+ existingScript.remove();
92
+ delete window.paypal;
93
+ }
94
+ else if (window.paypal) {
95
+ return Promise.resolve();
96
+ }
97
+ else {
98
+ return this.waitForPayPalGlobal();
99
+ }
90
100
  }
91
101
  const params = new URLSearchParams({
92
102
  'client-id': this.clientId,
@@ -105,6 +115,7 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
105
115
  script.id = PAYPAL_SCRIPT_ID;
106
116
  script.src = `${PAYPAL_SDK_URL}?${params.toString()}`;
107
117
  script.async = true;
118
+ script.setAttribute('data-config-key', configKey);
108
119
  return new Promise((resolve, reject) => {
109
120
  script.onload = () => {
110
121
  this.waitForPayPalGlobal().then(resolve).catch(reject);
@@ -148,7 +159,7 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
148
159
  if (!window.paypal || !this.paypalButtonContainer) {
149
160
  return;
150
161
  }
151
- const button = window.paypal.Buttons({
162
+ this.buttonsInstance = window.paypal.Buttons({
152
163
  createOrder: () => this.createOrder(),
153
164
  onApprove: (data) => this.handleApprove(data),
154
165
  onCancel: (data) => this.handleCancel(data),
@@ -159,36 +170,48 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
159
170
  height: 40,
160
171
  },
161
172
  });
162
- yield button.render(this.paypalButtonContainer);
173
+ yield this.buttonsInstance.render(this.paypalButtonContainer);
163
174
  });
164
175
  }
165
176
  createOrder() {
166
177
  const endpoint = `${this.clientConfig.embedApiBaseUrl}/payment/paypal/create-order`;
178
+ this.paymentTransactionId = undefined;
179
+ this.paymentMethodId = undefined;
167
180
  let orderAmount;
168
181
  let orderCurrency = this.currency;
169
182
  if (this.onCreateOrder) {
170
- const orderData = this.onCreateOrder();
171
- orderAmount = orderData.amount;
172
- orderCurrency = orderData.currency || this.currency;
183
+ try {
184
+ const orderData = this.onCreateOrder();
185
+ orderAmount = orderData.amount;
186
+ orderCurrency = orderData.currency || this.currency;
187
+ }
188
+ catch (err) {
189
+ const error = err instanceof Error
190
+ ? err
191
+ : new Error('onCreateOrder callback failed');
192
+ this.handleError(error, 'Order creation failed');
193
+ return Promise.reject(error);
194
+ }
173
195
  }
174
196
  else {
175
197
  orderAmount = this.amount;
176
198
  }
177
- if (orderAmount === undefined || orderAmount <= 0) {
199
+ if (orderAmount === undefined || isNaN(orderAmount) || orderAmount <= 0) {
178
200
  const error = new Error('Amount is required to create PayPal order');
179
201
  this.handleError(error, 'Order creation failed');
180
202
  return Promise.reject(error);
181
203
  }
182
204
  const requestBody = {
183
205
  payment_gateway_id: this.gateway.id,
206
+ organization_id: this.organizationId,
207
+ embed_id: this.embedId,
184
208
  currency: orderCurrency,
185
209
  amount: orderAmount,
186
210
  };
187
211
  return fetch(endpoint, {
188
212
  method: 'POST',
189
- headers: {
190
- 'Content-Type': 'application/json',
191
- },
213
+ headers: constants_1.CLIENT_HEADERS,
214
+ credentials: 'include',
192
215
  body: JSON.stringify(requestBody),
193
216
  })
194
217
  .then((response) => {
@@ -215,6 +238,7 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
215
238
  const token = {
216
239
  token: data.orderID,
217
240
  lastFour: data.orderID.slice(-4),
241
+ paymentMethodType: 'paypal',
218
242
  provider: 'paypal_checkout',
219
243
  paymentMethodId: this.paymentMethodId,
220
244
  paymentTransactionId: this.paymentTransactionId,
@@ -244,6 +268,9 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
244
268
  }
245
269
  validate() {
246
270
  return __awaiter(this, void 0, void 0, function* () {
271
+ if (!this.isReady) {
272
+ throw new Error('Tokenizer not initialized');
273
+ }
247
274
  return {
248
275
  isValid: true,
249
276
  errors: [],
@@ -257,6 +284,15 @@ class PayPalTokenizer extends Tokenizer_1.Tokenizer {
257
284
  focus(field) {
258
285
  }
259
286
  destroy() {
287
+ this.markDestroyed();
288
+ if (this.buttonsInstance) {
289
+ try {
290
+ this.buttonsInstance.close();
291
+ }
292
+ catch (_a) {
293
+ }
294
+ this.buttonsInstance = undefined;
295
+ }
260
296
  if (this.containerEl) {
261
297
  this.containerEl.innerHTML = '';
262
298
  }
@@ -29,6 +29,10 @@ export declare class SpreedlyTokenizer extends Tokenizer {
29
29
  private cardNumberDiv?;
30
30
  private cvvDiv?;
31
31
  private responsiveBreakpoint;
32
+ private tokenizationPromise?;
33
+ private tokenizationResolve?;
34
+ private tokenizationReject?;
35
+ private tokenizationTimeout?;
32
36
  private constructor();
33
37
  static create(gateway: PaymentGateway, container: TokenizerContainer, config: {
34
38
  organizationId: string;
@@ -38,6 +42,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
38
42
  private init;
39
43
  tokenize(paymentData: PaymentData): Promise<PaymentToken>;
40
44
  private tokenizeCardInternal;
45
+ private executeCardTokenization;
41
46
  private createCanadianBankAccountFields;
42
47
  private tokenizeBankAccountInternal;
43
48
  validate(): Promise<ValidationResult>;
@@ -50,6 +55,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
50
55
  private determineLayout;
51
56
  private setupResizeObserver;
52
57
  private applyLayoutStyles;
58
+ private clearTokenizationState;
53
59
  destroy(): void;
54
60
  hasToken(): boolean;
55
61
  getToken(): PaymentToken | null;