@idonatedev/idonate-sdk 1.2.0-dev2 → 1.2.0-dev20

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 (56) hide show
  1. package/dist/constants.js +11 -2
  2. package/dist/esm/constants.js +11 -2
  3. package/dist/esm/idonate-client.js +18 -8
  4. package/dist/esm/recaptcha.d.ts +1 -0
  5. package/dist/esm/recaptcha.js +26 -2
  6. package/dist/esm/shared.js +9 -1
  7. package/dist/esm/tokenize/CardConnectTokenizer.d.ts +8 -1
  8. package/dist/esm/tokenize/CardConnectTokenizer.js +353 -104
  9. package/dist/esm/tokenize/PayPalTokenizer.d.ts +6 -0
  10. package/dist/esm/tokenize/PayPalTokenizer.js +66 -16
  11. package/dist/esm/tokenize/SpreedlyTokenizer.d.ts +17 -1
  12. package/dist/esm/tokenize/SpreedlyTokenizer.js +270 -138
  13. package/dist/esm/tokenize/Tokenizer.d.ts +6 -2
  14. package/dist/esm/tokenize/Tokenizer.js +24 -8
  15. package/dist/esm/tokenize/gateway-utils.d.ts +2 -0
  16. package/dist/esm/tokenize/gateway-utils.js +27 -0
  17. package/dist/esm/tokenize/index.d.ts +3 -2
  18. package/dist/esm/tokenize/index.js +1 -0
  19. package/dist/esm/tokenize/spreedly-secure.js +6 -2
  20. package/dist/esm/tokenize/styles.d.ts +1 -1
  21. package/dist/esm/tokenize/styles.js +20 -1
  22. package/dist/esm/tokenize/tokenizer-constants.d.ts +1 -0
  23. package/dist/esm/tokenize/tokenizer-constants.js +1 -0
  24. package/dist/esm/tokenize/tokenizer-utils.d.ts +4 -1
  25. package/dist/esm/tokenize/tokenizer-utils.js +77 -4
  26. package/dist/esm/tokenize/types.d.ts +33 -8
  27. package/dist/esm/typeAdapters.js +6 -4
  28. package/dist/esm/types.d.ts +4 -10
  29. package/dist/idonate-client.js +18 -8
  30. package/dist/recaptcha.d.ts +1 -0
  31. package/dist/recaptcha.js +27 -2
  32. package/dist/shared.js +9 -1
  33. package/dist/tokenize/CardConnectTokenizer.d.ts +8 -1
  34. package/dist/tokenize/CardConnectTokenizer.js +351 -105
  35. package/dist/tokenize/PayPalTokenizer.d.ts +6 -0
  36. package/dist/tokenize/PayPalTokenizer.js +66 -16
  37. package/dist/tokenize/SpreedlyTokenizer.d.ts +17 -1
  38. package/dist/tokenize/SpreedlyTokenizer.js +267 -135
  39. package/dist/tokenize/Tokenizer.d.ts +6 -2
  40. package/dist/tokenize/Tokenizer.js +24 -8
  41. package/dist/tokenize/gateway-utils.d.ts +2 -0
  42. package/dist/tokenize/gateway-utils.js +30 -0
  43. package/dist/tokenize/index.d.ts +3 -2
  44. package/dist/tokenize/index.js +3 -1
  45. package/dist/tokenize/spreedly-secure.js +6 -2
  46. package/dist/tokenize/styles.d.ts +1 -1
  47. package/dist/tokenize/styles.js +20 -1
  48. package/dist/tokenize/tokenizer-constants.d.ts +1 -0
  49. package/dist/tokenize/tokenizer-constants.js +2 -1
  50. package/dist/tokenize/tokenizer-utils.d.ts +4 -1
  51. package/dist/tokenize/tokenizer-utils.js +80 -4
  52. package/dist/tokenize/types.d.ts +33 -8
  53. package/dist/typeAdapters.js +6 -4
  54. package/dist/types.d.ts +4 -10
  55. package/package.json +33 -2
  56. package/umd/idonate-sdk.js +1 -1
@@ -2,9 +2,13 @@ import { CardData, BankAccountData, PaymentData, PaymentToken, ValidationResult,
2
2
  import ConfigHandler from '../config-handler';
3
3
  export declare abstract class Tokenizer {
4
4
  protected mode: PaymentMethodMode;
5
- protected eventHandlers: Map<TokenizerEvent, Set<(data?: any) => void>>;
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;
@@ -31,7 +35,7 @@ export declare abstract class Tokenizer {
31
35
  expiryId: string;
32
36
  cvvId: string;
33
37
  };
34
- static create(gateway: PaymentGateway, container: TokenizerContainer, config: {
38
+ static create(rawGateway: PaymentGateway, container: TokenizerContainer, config: {
35
39
  organizationId: string;
36
40
  embedId: string;
37
41
  clientConfig: ConfigHandler;
@@ -7,15 +7,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { normalizeGateway } from './gateway-utils';
10
11
  import { formatExpiryInput, parseExpiryDate, addFocusHandlers, getConnectedBorderRadius, } from './tokenizer-utils';
11
12
  export class Tokenizer {
12
13
  constructor() {
13
14
  this.mode = 'credit_card';
14
15
  this.eventHandlers = new Map();
16
+ this.focusHandlerElements = new WeakSet();
15
17
  this._isReady = false;
18
+ this._isDestroyed = false;
16
19
  }
17
20
  get isReady() {
18
- return this._isReady;
21
+ return this._isReady && !this._isDestroyed;
22
+ }
23
+ get isDestroyed() {
24
+ return this._isDestroyed;
25
+ }
26
+ markDestroyed() {
27
+ this._isDestroyed = true;
19
28
  }
20
29
  getMode() {
21
30
  return this.mode;
@@ -81,7 +90,6 @@ export class Tokenizer {
81
90
  expiryEl.addEventListener('input', (e) => {
82
91
  const target = e.target;
83
92
  target.value = formatExpiryInput(target.value);
84
- this.emit('change', { field: 'expiry' });
85
93
  });
86
94
  expiryEl.addEventListener('keypress', (e) => {
87
95
  const char = String.fromCharCode(e.which);
@@ -115,11 +123,15 @@ export class Tokenizer {
115
123
  switch (position) {
116
124
  case 'left':
117
125
  element.style.borderLeft = inputStyles.border;
118
- element.style.borderRight = 'none';
126
+ element.style.borderRightStyle = 'solid';
127
+ element.style.borderRightWidth = '1px';
128
+ element.style.borderRightColor = 'transparent';
119
129
  break;
120
130
  case 'middle':
121
131
  element.style.borderLeft = inputStyles.border;
122
- element.style.borderRight = 'none';
132
+ element.style.borderRightStyle = 'solid';
133
+ element.style.borderRightWidth = '1px';
134
+ element.style.borderRightColor = 'transparent';
123
135
  break;
124
136
  case 'right':
125
137
  element.style.borderLeft = inputStyles.border;
@@ -132,7 +144,10 @@ export class Tokenizer {
132
144
  element.style.border = inputStyles.border;
133
145
  element.style.borderRadius = inputStyles.borderRadius;
134
146
  }
135
- addFocusHandlers(element, styles);
147
+ if (!this.focusHandlerElements.has(element)) {
148
+ addFocusHandlers(element, styles, position);
149
+ this.focusHandlerElements.add(element);
150
+ }
136
151
  }
137
152
  generateFieldIds(containerId) {
138
153
  return {
@@ -141,9 +156,10 @@ export class Tokenizer {
141
156
  cvvId: `${containerId}-cvv`,
142
157
  };
143
158
  }
144
- static create(gateway, container, config) {
159
+ static create(rawGateway, container, config) {
145
160
  return __awaiter(this, void 0, void 0, function* () {
146
- switch (gateway.backend_name) {
161
+ const gateway = normalizeGateway(rawGateway);
162
+ switch (gateway.backendName) {
147
163
  case 'spreedly': {
148
164
  const { SpreedlyTokenizer } = yield import('./SpreedlyTokenizer');
149
165
  return SpreedlyTokenizer.create(gateway, container, config);
@@ -157,7 +173,7 @@ export class Tokenizer {
157
173
  return PayPalTokenizer.create(gateway, container, config);
158
174
  }
159
175
  default:
160
- throw new Error(`Unsupported payment backend: ${gateway.backend_name}`);
176
+ throw new Error(`Unsupported payment backend: ${gateway.backendName}`);
161
177
  }
162
178
  });
163
179
  }
@@ -0,0 +1,2 @@
1
+ import { PaymentGateway } from './types';
2
+ export declare function normalizeGateway(input: any): PaymentGateway;
@@ -0,0 +1,27 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ export function normalizeGateway(input) {
13
+ if (!input) {
14
+ throw new Error('Gateway is required but received ' + String(input));
15
+ }
16
+ if (input.backendName !== undefined)
17
+ return input;
18
+ const _a = input.config || {}, { environment_key, base_url, merchant_id } = _a, extraConfigKeys = __rest(_a, ["environment_key", "base_url", "merchant_id"]);
19
+ return {
20
+ id: input.id,
21
+ backendName: input.backend_name,
22
+ gatewayType: input.gateway_type,
23
+ name: input.name,
24
+ config: input.config
25
+ ? Object.assign({ environmentKey: environment_key, baseUrl: base_url, merchantId: merchant_id }, extraConfigKeys) : undefined,
26
+ };
27
+ }
@@ -2,6 +2,7 @@ import * as iats from './iats';
2
2
  export { Tokenizer } from './Tokenizer';
3
3
  export { SpreedlyTokenizer } from './SpreedlyTokenizer';
4
4
  export { CardConnectTokenizer } from './CardConnectTokenizer';
5
- export { PayPalTokenizer } from './PayPalTokenizer';
6
- export { PaymentMethodType, PaymentGateway, TokenizerContainer, TokenizerStyling, CardData, PaymentData, PaymentToken, CardType, TokenizerEvent, ValidationState, ValidationResult, ValidationError, TokenizationError, } from './types';
5
+ export { PayPalTokenizer, PayPalCreateOrderData } from './PayPalTokenizer';
6
+ export { BackendName, PaymentMethodType, PaymentMethodMode, PaymentGateway, TokenizerContainer, TokenizerStyling, CardData, BankAccountData, PaymentData, PaymentToken, CardType, TokenizerEvent, ValidationState, ValidationResult, ValidationError, TokenizationError, } from './types';
7
+ export { normalizeGateway } from './gateway-utils';
7
8
  export { iats };
@@ -4,4 +4,5 @@ export { SpreedlyTokenizer } from './SpreedlyTokenizer';
4
4
  export { CardConnectTokenizer } from './CardConnectTokenizer';
5
5
  export { PayPalTokenizer } from './PayPalTokenizer';
6
6
  export { TokenizationError, } from './types';
7
+ export { normalizeGateway } from './gateway-utils';
7
8
  export { iats };
@@ -13,6 +13,7 @@ export function fetchSpreedlySecurityArgs(config, organizationId, embedId) {
13
13
  throw new Error('Secure tokenization is not enabled');
14
14
  }
15
15
  const makeRequest = () => __awaiter(this, void 0, void 0, function* () {
16
+ var _a;
16
17
  const response = yield fetch(`${config.embedApiBaseUrl}/spreedly/security-args`, {
17
18
  method: 'POST',
18
19
  headers: { 'Content-Type': 'application/json' },
@@ -24,13 +25,16 @@ export function fetchSpreedlySecurityArgs(config, organizationId, embedId) {
24
25
  if (!response.ok) {
25
26
  throw new Error(`Security args request failed: ${response.status}`);
26
27
  }
27
- return response.json();
28
+ const data = yield response.json();
29
+ return (_a = data.result) !== null && _a !== void 0 ? _a : data;
28
30
  });
29
31
  try {
30
32
  return yield makeRequest();
31
33
  }
32
34
  catch (error) {
33
- if (error instanceof TypeError && error.message.includes('fetch')) {
35
+ const isNetworkError = error instanceof TypeError && error.message.includes('fetch');
36
+ const isServerError = error instanceof Error && error.message.includes('failed: 5');
37
+ if (isNetworkError || isServerError) {
34
38
  yield new Promise((resolve) => setTimeout(resolve, 1000));
35
39
  return makeRequest();
36
40
  }
@@ -1,4 +1,4 @@
1
1
  import { TokenizerStyling, TokenizerStylingComplete } from './types';
2
2
  export declare const DEFAULT_UNIFIED_STYLES: TokenizerStylingComplete;
3
3
  export declare function mergeStyles(defaults: TokenizerStylingComplete, userStyles?: TokenizerStyling): TokenizerStylingComplete;
4
- export declare function getContainerStylesForLayout(baseStyles: TokenizerStylingComplete, layout?: 'single-line' | 'two-line'): TokenizerStylingComplete;
4
+ export declare function getContainerStylesForLayout(baseStyles: TokenizerStylingComplete, layout?: 'single-line' | 'two-line' | 'responsive'): TokenizerStylingComplete;
@@ -27,16 +27,35 @@ export const DEFAULT_UNIFIED_STYLES = {
27
27
  flexWrap: 'nowrap',
28
28
  rowGap: '1rem',
29
29
  },
30
+ twoLine: {
31
+ padding: '',
32
+ fontSize: '',
33
+ textAlign: '',
34
+ },
35
+ label: {
36
+ show: false,
37
+ fontSize: '0.875rem',
38
+ fontWeight: 'bold',
39
+ fontFamily: '',
40
+ color: '#333',
41
+ marginBottom: '0.25rem',
42
+ },
30
43
  };
31
44
  export function mergeStyles(defaults, userStyles) {
32
45
  if (!userStyles)
33
46
  return defaults;
34
- return {
47
+ const merged = {
35
48
  input: Object.assign(Object.assign({}, defaults.input), userStyles.input),
36
49
  focus: Object.assign(Object.assign({}, defaults.focus), userStyles.focus),
37
50
  error: Object.assign(Object.assign({}, defaults.error), userStyles.error),
38
51
  container: Object.assign(Object.assign({}, defaults.container), userStyles.container),
52
+ twoLine: Object.assign(Object.assign({}, defaults.twoLine), userStyles.twoLine),
53
+ label: Object.assign(Object.assign({}, defaults.label), userStyles.label),
39
54
  };
55
+ if (!merged.label.fontFamily) {
56
+ merged.label.fontFamily = merged.input.fontFamily;
57
+ }
58
+ return merged;
40
59
  }
41
60
  export function getContainerStylesForLayout(baseStyles, layout = 'single-line') {
42
61
  if (layout === 'two-line') {
@@ -60,3 +60,4 @@ export declare const FIELD_CONSTRAINTS: {
60
60
  readonly maxLength: 4;
61
61
  };
62
62
  };
63
+ export declare const RESPONSIVE_BREAKPOINT = 400;
@@ -60,3 +60,4 @@ export const FIELD_CONSTRAINTS = {
60
60
  maxLength: 4,
61
61
  },
62
62
  };
63
+ export const RESPONSIVE_BREAKPOINT = 400;
@@ -6,7 +6,9 @@ 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
+ export declare function createFieldLabel(text: string, forId: string, styles: TokenizerStylingComplete): HTMLLabelElement;
11
+ export declare function wrapFieldWithLabel(field: HTMLElement, labelText: string, styles: TokenizerStylingComplete): HTMLElement;
10
12
  export declare function createFieldContainer(id: string, flex: string, minWidth?: string, maxWidth?: string): HTMLDivElement;
11
13
  export declare function createInputElement(id: string, type: string, placeholder: string, maxLength?: number): HTMLInputElement;
12
14
  export declare function validateRoutingNumber(routingNumber: string): boolean;
@@ -17,3 +19,4 @@ export declare function validateInstitutionNumber(institutionNumber: string): bo
17
19
  export declare function validateTransitNumber(transitNumber: string): boolean;
18
20
  export declare function validateCanadianAccountNumber(accountNumber: string): boolean;
19
21
  export declare function formatCanadianRoutingNumber(institutionNumber: string, transitNumber: string): string;
22
+ export declare function cssLengthToPixels(value: string): number;
@@ -39,7 +39,7 @@ export function getConnectedBorderRadius(baseRadius, position, isConnected) {
39
39
  return baseRadius;
40
40
  }
41
41
  }
42
- export function addFocusHandlers(element, styles, onFocus, onBlur) {
42
+ export function addFocusHandlers(element, styles, position, onFocus, onBlur) {
43
43
  element.addEventListener('focus', () => {
44
44
  if (styles.focus) {
45
45
  Object.assign(element.style, styles.focus);
@@ -47,12 +47,69 @@ export function addFocusHandlers(element, styles, onFocus, onBlur) {
47
47
  onFocus === null || onFocus === void 0 ? void 0 : onFocus();
48
48
  });
49
49
  element.addEventListener('blur', () => {
50
- element.style.border = styles.input.border;
51
- element.style.outline = 'none';
52
- element.style.boxShadow = 'none';
50
+ const isConnected = styles.container.gap === '0';
51
+ if (isConnected && position) {
52
+ element.style.borderTop = styles.input.border;
53
+ element.style.borderBottom = styles.input.border;
54
+ switch (position) {
55
+ case 'left':
56
+ case 'middle':
57
+ element.style.borderLeft = styles.input.border;
58
+ element.style.borderRightStyle = 'solid';
59
+ element.style.borderRightWidth = '1px';
60
+ element.style.borderRightColor = 'transparent';
61
+ break;
62
+ case 'right':
63
+ element.style.borderLeft = styles.input.border;
64
+ element.style.borderRight = styles.input.border;
65
+ break;
66
+ }
67
+ }
68
+ else {
69
+ element.style.border = styles.input.border;
70
+ }
71
+ element.style.outline = '';
72
+ element.style.boxShadow = '';
53
73
  onBlur === null || onBlur === void 0 ? void 0 : onBlur();
54
74
  });
55
75
  }
76
+ export function createFieldLabel(text, forId, styles) {
77
+ const label = document.createElement('label');
78
+ label.htmlFor = forId;
79
+ label.textContent = text;
80
+ label.style.display = styles.label.show ? 'block' : 'none';
81
+ label.style.fontSize = styles.label.fontSize;
82
+ label.style.fontWeight = styles.label.fontWeight;
83
+ label.style.fontFamily = styles.label.fontFamily;
84
+ label.style.color = styles.label.color;
85
+ label.style.marginBottom = styles.label.marginBottom;
86
+ return label;
87
+ }
88
+ export function wrapFieldWithLabel(field, labelText, styles) {
89
+ if (!styles.label.show)
90
+ return field;
91
+ const wrapper = document.createElement('div');
92
+ wrapper.style.display = 'flex';
93
+ wrapper.style.flexDirection = 'column';
94
+ if (field.style.flex) {
95
+ wrapper.style.flex = field.style.flex;
96
+ field.style.flex = '';
97
+ }
98
+ if (field.style.minWidth) {
99
+ wrapper.style.minWidth = field.style.minWidth;
100
+ }
101
+ if (field.style.maxWidth) {
102
+ wrapper.style.maxWidth = field.style.maxWidth;
103
+ }
104
+ if (field.style.flexBasis) {
105
+ wrapper.style.flexBasis = field.style.flexBasis;
106
+ field.style.flexBasis = '';
107
+ }
108
+ const label = createFieldLabel(labelText, field.id, styles);
109
+ wrapper.appendChild(label);
110
+ wrapper.appendChild(field);
111
+ return wrapper;
112
+ }
56
113
  export function createFieldContainer(id, flex, minWidth, maxWidth) {
57
114
  const div = document.createElement('div');
58
115
  div.id = id;
@@ -137,3 +194,19 @@ export function formatCanadianRoutingNumber(institutionNumber, transitNumber) {
137
194
  const transit = transitNumber.replace(/\D/g, '').padStart(5, '0');
138
195
  return '0' + institution + transit;
139
196
  }
197
+ export function cssLengthToPixels(value) {
198
+ const num = parseFloat(value);
199
+ if (isNaN(num)) {
200
+ console.warn(`[cssLengthToPixels] Cannot parse "${value}", defaulting to 14px`);
201
+ return 14;
202
+ }
203
+ if (value.endsWith('rem'))
204
+ return num * 16;
205
+ if (value.endsWith('pt'))
206
+ return num * 1.333;
207
+ if (value.endsWith('px') || !value.match(/[a-z%]/i))
208
+ return num;
209
+ console.warn(`[cssLengthToPixels] Unsupported unit in "${value}". ` +
210
+ `Only px, rem, and pt are supported. Using ${num}px as approximation.`);
211
+ return num;
212
+ }
@@ -1,14 +1,15 @@
1
1
  export type PaymentMethodType = 'credit_card' | 'bank_account' | 'paypal' | 'apple_pay' | 'google_pay';
2
- export type PaymentMethodMode = 'credit_card' | 'bank_account';
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
- backend_name: string;
6
- gateway_type?: string;
6
+ backendName: BackendName;
7
+ gatewayType?: string;
7
8
  name: string;
8
9
  config?: {
9
- environment_key?: string;
10
- base_url?: string;
11
- merchant_id?: string;
10
+ environmentKey?: string;
11
+ baseUrl?: string;
12
+ merchantId?: string;
12
13
  [key: string]: any;
13
14
  };
14
15
  }
@@ -18,7 +19,17 @@ export interface TokenizerContainer {
18
19
  mode?: PaymentMethodMode;
19
20
  bankCountry?: 'US' | 'CA';
20
21
  enableTestMode?: boolean;
21
- layout?: 'single-line' | 'two-line';
22
+ layout?: 'single-line' | 'two-line' | 'responsive';
23
+ responsiveBreakpoint?: number;
24
+ placeholders?: {
25
+ cardNumber?: string;
26
+ expiry?: string;
27
+ expiryMonth?: string;
28
+ expiryYear?: string;
29
+ cvv?: string;
30
+ routingNumber?: string;
31
+ accountNumber?: string;
32
+ };
22
33
  }
23
34
  export interface TokenizerStylingComplete {
24
35
  input: {
@@ -52,6 +63,19 @@ export interface TokenizerStylingComplete {
52
63
  flexWrap?: string;
53
64
  rowGap?: string;
54
65
  };
66
+ twoLine: {
67
+ padding: string;
68
+ fontSize: string;
69
+ textAlign: string;
70
+ };
71
+ label: {
72
+ show: boolean;
73
+ fontSize: string;
74
+ fontWeight: string;
75
+ fontFamily: string;
76
+ color: string;
77
+ marginBottom: string;
78
+ };
55
79
  }
56
80
  type DeepPartial<T> = T extends object ? {
57
81
  [P in keyof T]?: DeepPartial<T[P]>;
@@ -100,9 +124,10 @@ export interface PaymentToken {
100
124
  paymentTransactionId?: string;
101
125
  }
102
126
  export type CardType = 'visa' | 'mastercard' | 'amex' | 'discover' | 'diners' | 'jcb' | 'unknown';
103
- export type TokenizerEvent = 'ready' | 'focus' | 'blur' | 'change' | 'validation' | 'cardTypeChange' | 'error' | 'tokenization' | 'tokenReady';
127
+ export type TokenizerEvent = 'ready' | 'validation' | 'cardTypeChange' | 'error' | 'tokenReady';
104
128
  export interface ValidationState {
105
129
  isValid: boolean;
130
+ hasToken?: boolean;
106
131
  cardNumber?: {
107
132
  isValid: boolean;
108
133
  isEmpty: boolean;
@@ -102,16 +102,18 @@ export function buildCashPaymentPayload(organizationId, input) {
102
102
  utmFields.utm_term = input.utm.term;
103
103
  }
104
104
  }
105
+ let remainingMeta = input.customerMeta
106
+ ? Object.assign({}, input.customerMeta) : {};
105
107
  if (input.customerMeta) {
106
108
  [1, 2, 3, 4, 5].forEach((customNoteIndex) => {
107
109
  const key = `custom_note_${customNoteIndex}`;
108
- if (input.customerMeta[key]) {
109
- donationOptions[key] = input.customerMeta[key];
110
- delete input.customerMeta[key];
110
+ if (remainingMeta[key]) {
111
+ donationOptions[key] = remainingMeta[key];
112
+ delete remainingMeta[key];
111
113
  }
112
114
  });
113
115
  }
114
- 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 });
116
+ 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 });
115
117
  }
116
118
  export function buildDonationResult(response) {
117
119
  let campaign;
@@ -53,7 +53,8 @@ export type ACHAccount = {
53
53
  accountHolderType: 'personal' | 'business';
54
54
  accountType: 'checking' | 'savings';
55
55
  };
56
- export type PaymentMethodType = 'credit_card' | 'bank_account' | 'paypal' | 'apple_pay' | 'google_pay';
56
+ import { PaymentMethodType } from './tokenize/types';
57
+ export { PaymentMethodType } from './tokenize/types';
57
58
  export type RecaptchaType = 'v2' | 'v3' | 'invisible' | 'organization' | 'nonce' | 'bypass';
58
59
  export type RecaptchaSecuredRequest = {
59
60
  recaptchaType: RecaptchaType;
@@ -274,15 +275,8 @@ export type TokenizeCardConnectResult = {
274
275
  errorcode: number;
275
276
  };
276
277
  export type BackendName = 'spreedly' | 'card_connect' | 'iats' | 'paypal_checkout';
277
- export type PaymentGateway = {
278
- id: string;
279
- name: string;
280
- backend_name: BackendName;
281
- gateway_type?: string;
282
- config?: {
283
- [key: string]: any;
284
- };
285
- };
278
+ import { PaymentGateway } from './tokenize/types';
279
+ export { PaymentGateway } from './tokenize/types';
286
280
  export interface EmbedConfig {
287
281
  embed_id?: string;
288
282
  organization_id?: string;
@@ -149,10 +149,10 @@ class iDonateClient {
149
149
  if (!this.allowTransaction) {
150
150
  throw new Error('Wow, that was fast - try again');
151
151
  }
152
- if (!donation.embedId) {
153
- donation.embedId = this.embedId;
154
- }
155
- const payload = (0, typeAdapters_1.buildCashPaymentPayload)(this.organizationId, donation);
152
+ const donationWithEmbed = donation.embedId
153
+ ? donation
154
+ : Object.assign(Object.assign({}, donation), { embedId: this.embedId });
155
+ const payload = (0, typeAdapters_1.buildCashPaymentPayload)(this.organizationId, donationWithEmbed);
156
156
  const fetchCall = (clearanceToken) => fetch(`${this.config.embedApiBaseUrl}/donate/cash-payment`, {
157
157
  method: 'POST',
158
158
  headers: Object.assign(Object.assign(Object.assign({}, constants_1.CLIENT_HEADERS), (clearanceToken ? { 'cf-validation-token': clearanceToken } : {})), { 'User-Agent': constants_1.CLIENT_HEADERS['User-Agent'] + ' source:' + this.config.client }),
@@ -180,10 +180,10 @@ class iDonateClient {
180
180
  });
181
181
  }
182
182
  createPaymentMethod(paymentMethod) {
183
- if (!paymentMethod.embedId) {
184
- paymentMethod.embedId = this.embedId;
185
- }
186
- return shared.createPaymentMethod(paymentMethod, this.config);
183
+ const pmWithEmbed = paymentMethod.embedId
184
+ ? paymentMethod
185
+ : Object.assign(Object.assign({}, paymentMethod), { embedId: this.embedId });
186
+ return shared.createPaymentMethod(pmWithEmbed, this.config);
187
187
  }
188
188
  setOrganizationId(organizationId) {
189
189
  this.currentOrganizationId = organizationId;
@@ -195,7 +195,16 @@ class iDonateClient {
195
195
  return new Promise((resolve, reject) => {
196
196
  setTimeout(() => {
197
197
  let pollHandle;
198
+ let attempts = 0;
199
+ const maxAttempts = 90;
198
200
  pollHandle = setInterval(() => {
201
+ attempts++;
202
+ if (attempts > maxAttempts) {
203
+ clearInterval(pollHandle);
204
+ pollHandle = null;
205
+ reject(new types_1.ClientError('Donation processing timed out. Please check your email for a receipt or contact support.'));
206
+ return;
207
+ }
199
208
  fetch(`${this.config.embedApiBaseUrl}/donate/cash-payment/${donationId}/payment_status`, {
200
209
  method: 'GET',
201
210
  headers: {
@@ -212,6 +221,7 @@ class iDonateClient {
212
221
  }
213
222
  if (response.error) {
214
223
  reject(new types_1.ClientError(response.error));
224
+ return;
215
225
  }
216
226
  resolve((0, typeAdapters_1.buildDonationResult)({
217
227
  _raw_response: Object.assign({ campaign: null, designation: {}, donor: {}, form_data: {}, id: null, schedule: {}, transaction: {} }, response),
@@ -10,3 +10,4 @@ export declare class RecaptchaElement {
10
10
  resolveToken(): Promise<string>;
11
11
  }
12
12
  export declare function wrapElement(container: string, sitekey: string, extraParams?: any): RecaptchaElement;
13
+ export declare function wrapElementWithThrow(container: string, sitekey: string, extraParams?: any): Promise<RecaptchaElement>;
package/dist/recaptcha.js CHANGED
@@ -1,8 +1,18 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.RecaptchaElement = void 0;
4
13
  exports.injectScript = injectScript;
5
14
  exports.wrapElement = wrapElement;
15
+ exports.wrapElementWithThrow = wrapElementWithThrow;
6
16
  function resolveLib() {
7
17
  const startWait = new Date();
8
18
  return new Promise((resolve, reject) => {
@@ -57,9 +67,16 @@ class RecaptchaElement {
57
67
  this.widgetId = lib.render(this.container, Object.assign(Object.assign({}, this.params), { callback: (token) => {
58
68
  var _a;
59
69
  this.resolvedToken = token;
60
- (_a = this.executeResolveQueue.pop()) === null || _a === void 0 ? void 0 : _a(token);
70
+ (_a = this.executeResolveQueue.shift()) === null || _a === void 0 ? void 0 : _a.resolve(token);
61
71
  }, 'expired-callback': () => {
62
72
  this.resolvedToken = undefined;
73
+ }, 'error-callback': () => {
74
+ var _a;
75
+ this.resolvedToken = undefined;
76
+ const error = new Error('reCAPTCHA encountered an error. Please try again.');
77
+ while (this.executeResolveQueue.length > 0) {
78
+ (_a = this.executeResolveQueue.shift()) === null || _a === void 0 ? void 0 : _a.reject(error);
79
+ }
63
80
  } }));
64
81
  });
65
82
  }
@@ -73,7 +90,7 @@ class RecaptchaElement {
73
90
  ? resolve(response)
74
91
  : reject(new Error('checkbox recaptcha is not checked'));
75
92
  }
76
- this.executeResolveQueue.push(resolve);
93
+ this.executeResolveQueue.push({ resolve, reject });
77
94
  if (this.resolvedToken !== undefined) {
78
95
  this.resolvedToken = undefined;
79
96
  lib.reset(this.widgetId);
@@ -93,3 +110,11 @@ function wrapElement(container, sitekey, extraParams) {
93
110
  captcha.render();
94
111
  return captcha;
95
112
  }
113
+ function wrapElementWithThrow(container, sitekey, extraParams) {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ const params = Object.assign(Object.assign({}, (extraParams || {})), { sitekey });
116
+ const captcha = new RecaptchaElement(container, params);
117
+ yield captcha.render();
118
+ return captcha;
119
+ });
120
+ }
package/dist/shared.js CHANGED
@@ -5,6 +5,7 @@ exports.fetchEmbedConfig = fetchEmbedConfig;
5
5
  const typeAdapters_1 = require("./typeAdapters");
6
6
  const constants_1 = require("./constants");
7
7
  const util_1 = require("./util");
8
+ const gateway_utils_1 = require("./tokenize/gateway-utils");
8
9
  function createPaymentMethod(paymentMethod, config) {
9
10
  const payload = (0, typeAdapters_1.buildCreatePaymentMethodPayload)(paymentMethod);
10
11
  return fetch(`${config.embedApiBaseUrl}/payment/payment-methods`, {
@@ -21,5 +22,12 @@ function fetchEmbedConfig(embedId, config) {
21
22
  headers: constants_1.CLIENT_HEADERS,
22
23
  })
23
24
  .then((response) => (0, util_1.parseResponse)(response, { throwErrors: true }))
24
- .then((response) => response._raw_response.result);
25
+ .then((response) => {
26
+ const config = response._raw_response.result;
27
+ if (config.available_gateways) {
28
+ config.available_gateways =
29
+ config.available_gateways.map(gateway_utils_1.normalizeGateway);
30
+ }
31
+ return config;
32
+ });
25
33
  }