@idonatedev/idonate-sdk 1.2.0-dev15 → 1.2.0-dev17

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.
@@ -166,6 +166,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
166
166
  token,
167
167
  lastFour: pmData.last_four_digits,
168
168
  cardType: this.normalizeCardType(pmData.card_type),
169
+ paymentMethodType: 'credit_card',
169
170
  provider: 'spreedly',
170
171
  };
171
172
  if (this.tokenizationResolve) {
@@ -176,13 +177,12 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
176
177
  }
177
178
  });
178
179
  this.spreedly.on('errors', (errors) => {
180
+ const error = new types_1.TokenizationError(errors);
181
+ this.emit('error', error);
179
182
  if (this.tokenizationReject) {
180
183
  const rejectTokenization = this.tokenizationReject;
181
184
  this.clearTokenizationState();
182
- rejectTokenization(new types_1.TokenizationError(errors));
183
- }
184
- else {
185
- this.emit('error', new types_1.TokenizationError(errors));
185
+ rejectTokenization(error);
186
186
  }
187
187
  });
188
188
  this.spreedly.on('fieldEvent', (fieldName, eventType, activeEl, inputProperties) => {
@@ -209,15 +209,21 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
209
209
  });
210
210
  }
211
211
  tokenizeCardInternal(cardData) {
212
+ if (this.tokenizationPromise) {
213
+ return this.tokenizationPromise;
214
+ }
215
+ const expiry = this.parseExpiry(this.expiryEl);
216
+ if (!expiry) {
217
+ return Promise.reject(new types_1.TokenizationError('Expiration date is required', 'VALIDATION_ERROR'));
218
+ }
219
+ this.tokenizationPromise = this.executeCardTokenization(cardData, expiry).finally(() => {
220
+ this.tokenizationPromise = undefined;
221
+ });
222
+ return this.tokenizationPromise;
223
+ }
224
+ executeCardTokenization(cardData, expiry) {
212
225
  return __awaiter(this, void 0, void 0, function* () {
213
226
  var _a;
214
- if (this.tokenizationPromise) {
215
- return this.tokenizationPromise;
216
- }
217
- const expiry = this.parseExpiry(this.expiryEl);
218
- if (!expiry) {
219
- throw new types_1.TokenizationError('Expiration date is required', 'VALIDATION_ERROR');
220
- }
221
227
  let securityFields = {};
222
228
  if ((_a = this.clientConfig) === null || _a === void 0 ? void 0 : _a.enableSpreedlySecureTokenization) {
223
229
  try {
@@ -225,7 +231,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
225
231
  securityFields = {
226
232
  nonce: args.nonce,
227
233
  timestamp: args.timestamp,
228
- certificate_token: args.certificate_token,
234
+ certificateToken: args.certificate_token,
229
235
  signature: args.signature,
230
236
  };
231
237
  }
@@ -233,7 +239,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
233
239
  throw new types_1.TokenizationError(`Secure tokenization failed: ${error instanceof Error ? error.message : 'Unknown error'}`, 'SECURE_TOKENIZATION_ERROR');
234
240
  }
235
241
  }
236
- this.tokenizationPromise = new Promise((resolve, reject) => {
242
+ return new Promise((resolve, reject) => {
237
243
  var _a, _b, _c, _d, _e, _f;
238
244
  this.tokenizationResolve = resolve;
239
245
  this.tokenizationReject = reject;
@@ -242,10 +248,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
242
248
  reject(new types_1.TokenizationError('Tokenization timeout', 'TIMEOUT'));
243
249
  }, tokenizer_constants_1.TOKENIZE_TIMEOUT);
244
250
  this.spreedly.tokenizeCreditCard(Object.assign({ first_name: cardData.firstName, last_name: cardData.lastName, month: expiry.month, year: expiry.year, email: cardData.email, phone_number: cardData.phone, address1: (_a = cardData.address) === null || _a === void 0 ? void 0 : _a.address1, address2: (_b = cardData.address) === null || _b === void 0 ? void 0 : _b.address2, city: (_c = cardData.address) === null || _c === void 0 ? void 0 : _c.city, state: (_d = cardData.address) === null || _d === void 0 ? void 0 : _d.state, zip: (_e = cardData.address) === null || _e === void 0 ? void 0 : _e.zip, country: (_f = cardData.address) === null || _f === void 0 ? void 0 : _f.country }, securityFields));
245
- }).finally(() => {
246
- this.tokenizationPromise = undefined;
247
251
  });
248
- return this.tokenizationPromise;
249
252
  });
250
253
  }
251
254
  createCanadianBankAccountFields(container) {
@@ -437,13 +440,15 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
437
440
  accountHolderType: accountHolderType,
438
441
  },
439
442
  }, this.clientConfig, this.environmentKey);
440
- return {
443
+ const paymentToken = {
441
444
  token: result,
442
445
  lastFour: accountNumber.slice(-4),
443
446
  accountType: accountType,
444
447
  paymentMethodType: 'bank_account',
445
448
  provider: 'spreedly',
446
449
  };
450
+ this.emit('tokenReady', paymentToken);
451
+ return paymentToken;
447
452
  });
448
453
  }
449
454
  validate() {
@@ -656,7 +661,14 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
656
661
  expiry: { isValid: false, isEmpty: true },
657
662
  };
658
663
  }
659
- this.clearTokenizationState();
664
+ if (this.tokenizationReject) {
665
+ const reject = this.tokenizationReject;
666
+ this.clearTokenizationState();
667
+ reject(new types_1.TokenizationError('Tokenizer cleared', 'CLEARED'));
668
+ }
669
+ else {
670
+ this.clearTokenizationState();
671
+ }
660
672
  this.emit('validation', this.currentValidationState);
661
673
  }
662
674
  focus(field) {
@@ -758,6 +770,7 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
758
770
  }
759
771
  destroy() {
760
772
  var _a;
773
+ this.markDestroyed();
761
774
  (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
762
775
  if (this.tokenizationReject) {
763
776
  const reject = this.tokenizationReject;
@@ -1072,13 +1085,14 @@ class SpreedlyTokenizer extends Tokenizer_1.Tokenizer {
1072
1085
  }
1073
1086
  else {
1074
1087
  const now = new Date();
1075
- const expiryDate = new Date(year, month - 1);
1076
- if (expiryDate < now) {
1088
+ const expiryEndDate = new Date(year, month);
1089
+ if (expiryEndDate <= now) {
1077
1090
  this.applyErrorStyles(this.expiryEl);
1078
1091
  }
1079
1092
  else {
1080
1093
  isValid = true;
1081
- this.applyInputStyles(this.expiryEl, this.mergedStyles, 'middle');
1094
+ const expiryPosition = this.effectiveLayout === 'two-line' ? 'left' : 'middle';
1095
+ this.applyInputStyles(this.expiryEl, this.mergedStyles, expiryPosition);
1082
1096
  }
1083
1097
  }
1084
1098
  }
@@ -3,8 +3,12 @@ import ConfigHandler from '../config-handler';
3
3
  export declare abstract class Tokenizer {
4
4
  protected mode: PaymentMethodMode;
5
5
  protected eventHandlers: Map<string, Set<(data?: any) => void>>;
6
+ private focusHandlerElements;
6
7
  private _isReady;
8
+ private _isDestroyed;
7
9
  get isReady(): boolean;
10
+ get isDestroyed(): boolean;
11
+ protected markDestroyed(): void;
8
12
  abstract tokenize(paymentData: PaymentData): Promise<PaymentToken>;
9
13
  abstract validate(): Promise<ValidationResult>;
10
14
  abstract clear(): void;
@@ -16,7 +20,7 @@ export declare abstract class Tokenizer {
16
20
  getMode(): PaymentMethodMode;
17
21
  on(event: TokenizerEvent, handler: (data?: any) => void): void;
18
22
  off(event: TokenizerEvent, handler: (data?: any) => void): void;
19
- protected emit(event: string, data?: any): void;
23
+ protected emit(event: TokenizerEvent, data?: any): void;
20
24
  protected isCardData(data: PaymentData): data is CardData;
21
25
  protected isBankAccountData(data: PaymentData): data is BankAccountData;
22
26
  protected normalizeCardType(providerCardType: string): CardType;
@@ -49,10 +49,18 @@ class Tokenizer {
49
49
  constructor() {
50
50
  this.mode = 'credit_card';
51
51
  this.eventHandlers = new Map();
52
+ this.focusHandlerElements = new WeakSet();
52
53
  this._isReady = false;
54
+ this._isDestroyed = false;
53
55
  }
54
56
  get isReady() {
55
- return this._isReady;
57
+ return this._isReady && !this._isDestroyed;
58
+ }
59
+ get isDestroyed() {
60
+ return this._isDestroyed;
61
+ }
62
+ markDestroyed() {
63
+ this._isDestroyed = true;
56
64
  }
57
65
  getMode() {
58
66
  return this.mode;
@@ -168,7 +176,10 @@ class Tokenizer {
168
176
  element.style.border = inputStyles.border;
169
177
  element.style.borderRadius = inputStyles.borderRadius;
170
178
  }
171
- (0, tokenizer_utils_1.addFocusHandlers)(element, styles);
179
+ if (!this.focusHandlerElements.has(element)) {
180
+ (0, tokenizer_utils_1.addFocusHandlers)(element, styles, position);
181
+ this.focusHandlerElements.add(element);
182
+ }
172
183
  }
173
184
  generateFieldIds(containerId) {
174
185
  return {
@@ -3,6 +3,6 @@ export { Tokenizer } from './Tokenizer';
3
3
  export { SpreedlyTokenizer } from './SpreedlyTokenizer';
4
4
  export { CardConnectTokenizer } from './CardConnectTokenizer';
5
5
  export { PayPalTokenizer, PayPalCreateOrderData } from './PayPalTokenizer';
6
- export { PaymentMethodType, PaymentMethodMode, PaymentGateway, TokenizerContainer, TokenizerStyling, CardData, BankAccountData, PaymentData, PaymentToken, CardType, TokenizerEvent, ValidationState, ValidationResult, ValidationError, TokenizationError, } from './types';
6
+ export { BackendName, PaymentMethodType, PaymentMethodMode, PaymentGateway, TokenizerContainer, TokenizerStyling, CardData, BankAccountData, PaymentData, PaymentToken, CardType, TokenizerEvent, ValidationState, ValidationResult, ValidationError, TokenizationError, } from './types';
7
7
  export { normalizeGateway } from './gateway-utils';
8
8
  export { iats };
@@ -16,6 +16,7 @@ function fetchSpreedlySecurityArgs(config, organizationId, embedId) {
16
16
  throw new Error('Secure tokenization is not enabled');
17
17
  }
18
18
  const makeRequest = () => __awaiter(this, void 0, void 0, function* () {
19
+ var _a;
19
20
  const response = yield fetch(`${config.embedApiBaseUrl}/spreedly/security-args`, {
20
21
  method: 'POST',
21
22
  headers: { 'Content-Type': 'application/json' },
@@ -27,7 +28,8 @@ function fetchSpreedlySecurityArgs(config, organizationId, embedId) {
27
28
  if (!response.ok) {
28
29
  throw new Error(`Security args request failed: ${response.status}`);
29
30
  }
30
- return response.json();
31
+ const data = yield response.json();
32
+ return (_a = data.result) !== null && _a !== void 0 ? _a : data;
31
33
  });
32
34
  try {
33
35
  return yield makeRequest();
@@ -6,7 +6,7 @@ export declare function parseExpiryDate(value: string): {
6
6
  export declare function formatExpiryInput(value: string): string;
7
7
  export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T>;
8
8
  export declare function getConnectedBorderRadius(baseRadius: string, position: 'left' | 'middle' | 'right', isConnected: boolean): string;
9
- export declare function addFocusHandlers(element: HTMLElement, styles: TokenizerStylingComplete, onFocus?: () => void, onBlur?: () => void): void;
9
+ export declare function addFocusHandlers(element: HTMLElement, styles: TokenizerStylingComplete, position?: 'left' | 'middle' | 'right', onFocus?: () => void, onBlur?: () => void): void;
10
10
  export declare function createFieldContainer(id: string, flex: string, minWidth?: string, maxWidth?: string): HTMLDivElement;
11
11
  export declare function createInputElement(id: string, type: string, placeholder: string, maxLength?: number): HTMLInputElement;
12
12
  export declare function validateRoutingNumber(routingNumber: string): boolean;
@@ -56,7 +56,7 @@ function getConnectedBorderRadius(baseRadius, position, isConnected) {
56
56
  return baseRadius;
57
57
  }
58
58
  }
59
- function addFocusHandlers(element, styles, onFocus, onBlur) {
59
+ function addFocusHandlers(element, styles, position, onFocus, onBlur) {
60
60
  element.addEventListener('focus', () => {
61
61
  if (styles.focus) {
62
62
  Object.assign(element.style, styles.focus);
@@ -64,7 +64,28 @@ function addFocusHandlers(element, styles, onFocus, onBlur) {
64
64
  onFocus === null || onFocus === void 0 ? void 0 : onFocus();
65
65
  });
66
66
  element.addEventListener('blur', () => {
67
- element.style.border = styles.input.border;
67
+ const isConnected = styles.container.gap === '0';
68
+ if (isConnected && position) {
69
+ element.style.borderTop = styles.input.border;
70
+ element.style.borderBottom = styles.input.border;
71
+ switch (position) {
72
+ case 'left':
73
+ element.style.borderLeft = styles.input.border;
74
+ element.style.borderRight = 'none';
75
+ break;
76
+ case 'middle':
77
+ element.style.borderLeft = styles.input.border;
78
+ element.style.borderRight = 'none';
79
+ break;
80
+ case 'right':
81
+ element.style.borderLeft = styles.input.border;
82
+ element.style.borderRight = styles.input.border;
83
+ break;
84
+ }
85
+ }
86
+ else {
87
+ element.style.border = styles.input.border;
88
+ }
68
89
  element.style.outline = '';
69
90
  element.style.boxShadow = '';
70
91
  onBlur === null || onBlur === void 0 ? void 0 : onBlur();
@@ -1,8 +1,9 @@
1
1
  export type PaymentMethodType = 'credit_card' | 'bank_account' | 'paypal' | 'apple_pay' | 'google_pay';
2
2
  export type PaymentMethodMode = 'credit_card' | 'bank_account' | 'paypal';
3
+ export type BackendName = 'spreedly' | 'card_connect' | 'iats' | 'paypal_checkout';
3
4
  export interface PaymentGateway {
4
5
  id: string;
5
- backendName: string;
6
+ backendName: BackendName;
6
7
  gatewayType?: string;
7
8
  name: string;
8
9
  config?: {
@@ -111,16 +111,18 @@ function buildCashPaymentPayload(organizationId, input) {
111
111
  utmFields.utm_term = input.utm.term;
112
112
  }
113
113
  }
114
+ let remainingMeta = input.customerMeta
115
+ ? Object.assign({}, input.customerMeta) : {};
114
116
  if (input.customerMeta) {
115
117
  [1, 2, 3, 4, 5].forEach((customNoteIndex) => {
116
118
  const key = `custom_note_${customNoteIndex}`;
117
- if (input.customerMeta[key]) {
118
- donationOptions[key] = input.customerMeta[key];
119
- delete input.customerMeta[key];
119
+ if (remainingMeta[key]) {
120
+ donationOptions[key] = remainingMeta[key];
121
+ delete remainingMeta[key];
120
122
  }
121
123
  });
122
124
  }
123
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, donationOptions), billingAddress), billingContact), payment), utmFields), { customer_meta: input.customerMeta || {}, recaptcha_token: input.recaptchaToken, recaptcha_type: input.recaptchaType });
125
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, donationOptions), billingAddress), billingContact), payment), utmFields), { customer_meta: remainingMeta, recaptcha_token: input.recaptchaToken, recaptcha_type: input.recaptchaType });
124
126
  }
125
127
  function buildDonationResult(response) {
126
128
  let campaign;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@idonatedev/idonate-sdk",
3
3
  "author": "iDonate",
4
- "version": "1.2.0-dev15",
4
+ "version": "1.2.0-dev17",
5
5
  "sideEffects": false,
6
6
  "description": "iDonate Web SDK",
7
7
  "engines": {