@idonatedev/idonate-sdk 1.2.0-dev18 → 1.2.0-dev19

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.
package/dist/constants.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CLIENT_HEADERS = exports.CARD_CONNECT_DEFAULT_STYLE = exports.DEFAULT_APP_NAME = exports.DEFAULT_CF_TURNSTILE_CDN_EXPLICIT_URL = exports.FALLBACK_CF_TURNSTILE_SITE_KEY = exports.FALLBACK_PC_SCRIPT_ID = exports.FALLBACK_PC_SCRIPT_URL = exports.SANDBOX_APPLE_PAY_URL = exports.APPLE_PAY_URL = exports.SPREEDLY_TOKENIZER_URL = exports.SANDBOX_CARD_CONNECT_BASE_URL = exports.PRODUCTION_CARD_CONNECT_BASE_URL = exports.SANDBOX_BASE_URL = exports.PRODUCTION_BASE_URL = exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = '1.2.0-dev18';
4
+ exports.SDK_VERSION = '1.2.0-dev19';
5
5
  exports.PRODUCTION_BASE_URL = 'https://secure-api.idonate.com';
6
6
  exports.SANDBOX_BASE_URL = 'https://api.qa-idonate.com';
7
7
  exports.PRODUCTION_CARD_CONNECT_BASE_URL = 'https://boltgw.cardconnect.com:8443';
@@ -1,4 +1,4 @@
1
- export const SDK_VERSION = '1.2.0-dev18';
1
+ export const SDK_VERSION = '1.2.0-dev19';
2
2
  export const PRODUCTION_BASE_URL = 'https://secure-api.idonate.com';
3
3
  export const SANDBOX_BASE_URL = 'https://api.qa-idonate.com';
4
4
  export const PRODUCTION_CARD_CONNECT_BASE_URL = 'https://boltgw.cardconnect.com:8443';
@@ -6,6 +6,7 @@ export declare class CardConnectTokenizer extends Tokenizer {
6
6
  private iframeUrl;
7
7
  private containerId;
8
8
  private layout;
9
+ private customPlaceholders?;
9
10
  private iframe;
10
11
  private messageHandler?;
11
12
  private expectedOrigin;
@@ -49,6 +50,7 @@ export declare class CardConnectTokenizer extends Tokenizer {
49
50
  get tokenizationMode(): 'auto' | 'manual';
50
51
  private handleMessage;
51
52
  private handleValidationMessage;
53
+ private static measureCvvLabelMarginLeft;
52
54
  private static generateIframeUrl;
53
55
  private static createIframe;
54
56
  private static generateCardConnectCss;
@@ -11,13 +11,14 @@ import { Tokenizer } from './Tokenizer';
11
11
  import { TokenizationError, } from './types';
12
12
  import { DEFAULT_UNIFIED_STYLES, mergeStyles, getContainerStylesForLayout, } from './styles';
13
13
  import { INIT_TIMEOUT, TOKENIZE_TIMEOUT, BANK_FIELD_FLEX, RESPONSIVE_BREAKPOINT, } from './tokenizer-constants';
14
- import { createInputElement, validateRoutingNumber, validateAccountNumber, createAccountTypeSelect, } from './tokenizer-utils';
14
+ import { createInputElement, validateRoutingNumber, validateAccountNumber, createAccountTypeSelect, wrapFieldWithLabel, cssLengthToPixels, } from './tokenizer-utils';
15
15
  export class CardConnectTokenizer extends Tokenizer {
16
- constructor(iframe, iframeUrl, containerId, mode = 'credit_card', enableTestMode = false, layout = 'single-line') {
16
+ constructor(iframe, iframeUrl, containerId, mode = 'credit_card', enableTestMode = false, layout = 'single-line', customPlaceholders) {
17
17
  super();
18
18
  this.iframeUrl = iframeUrl;
19
19
  this.containerId = containerId;
20
20
  this.layout = layout;
21
+ this.customPlaceholders = customPlaceholders;
21
22
  this.enableTestMode = false;
22
23
  this.acceptAutoToken = true;
23
24
  this.mode = mode;
@@ -54,7 +55,7 @@ export class CardConnectTokenizer extends Tokenizer {
54
55
  const mergedStyles = mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling);
55
56
  const iframeUrl = CardConnectTokenizer.generateIframeUrl(baseUrl, resolvedContainer);
56
57
  const iframe = CardConnectTokenizer.createIframe(iframeUrl, mergedStyles);
57
- const tokenizer = new CardConnectTokenizer(iframe, iframeUrl, container.containerId, container.mode || 'credit_card', container.enableTestMode || false, effectiveLayout);
58
+ const tokenizer = new CardConnectTokenizer(iframe, iframeUrl, container.containerId, container.mode || 'credit_card', container.enableTestMode || false, effectiveLayout, container.placeholders);
58
59
  tokenizer.createInternalElements(resolvedContainer);
59
60
  yield tokenizer.init();
60
61
  return tokenizer;
@@ -77,19 +78,32 @@ export class CardConnectTokenizer extends Tokenizer {
77
78
  }
78
79
  }
79
80
  createCreditCardFields(containerEl, mergedStyles) {
81
+ var _a;
80
82
  this.iframe.style.width = '100%';
83
+ const hasLabels = ((_a = mergedStyles.label) === null || _a === void 0 ? void 0 : _a.show) &&
84
+ (this.layout === 'two-line' || this.layout === 'responsive');
85
+ const inputHeight = mergedStyles.input.height || '40px';
81
86
  if (this.layout === 'two-line' || this.layout === 'responsive') {
82
- const inputHeight = mergedStyles.input.height || '40px';
83
- this.iframe.style.height = `calc((${inputHeight}) * 2 + 10px)`;
87
+ if (hasLabels) {
88
+ const labelFontPx = cssLengthToPixels(mergedStyles.label.fontSize);
89
+ const labelMarginPx = parseFloat(mergedStyles.label.marginBottom) || 0;
90
+ const labelRowHeight = Math.ceil(labelFontPx * 1.2) + labelMarginPx;
91
+ const extraHeight = Math.ceil(labelRowHeight * 2) + 34;
92
+ this.iframe.style.height = `calc(${inputHeight} * 2 + ${extraHeight}px)`;
93
+ }
94
+ else {
95
+ this.iframe.style.height = `calc((${inputHeight}) * 2 + 10px)`;
96
+ }
84
97
  }
85
98
  else {
86
- this.iframe.style.height = mergedStyles.input.height || '40px';
99
+ this.iframe.style.height = inputHeight;
87
100
  }
88
101
  this.iframe.style.border = 'none';
89
102
  this.iframe.style.display = 'block';
90
103
  containerEl.appendChild(this.iframe);
91
104
  }
92
105
  createBankAccountFields(containerEl, mergedStyles) {
106
+ var _a, _b, _c, _d;
93
107
  this.iframe.style.display = 'none';
94
108
  if (this.layout === 'two-line') {
95
109
  this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
@@ -98,45 +112,45 @@ export class CardConnectTokenizer extends Tokenizer {
98
112
  minWidth: BANK_FIELD_FLEX.accountType.minWidth,
99
113
  });
100
114
  this.applyInputStyles(this.accountTypeEl, mergedStyles, 'left');
101
- containerEl.appendChild(this.accountTypeEl);
102
- this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
115
+ containerEl.appendChild(wrapFieldWithLabel(this.accountTypeEl, 'Account Type', mergedStyles));
116
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.routingNumber) || 'Routing *', 9);
103
117
  Object.assign(this.routingNumberEl.style, {
104
118
  flex: '1',
105
119
  minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
106
120
  });
107
121
  this.applyInputStyles(this.routingNumberEl, mergedStyles, 'right');
108
- containerEl.appendChild(this.routingNumberEl);
109
- this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
122
+ containerEl.appendChild(wrapFieldWithLabel(this.routingNumberEl, 'Routing Number', mergedStyles));
123
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.accountNumber) || 'Account Number *', 17);
110
124
  Object.assign(this.accountNumberEl.style, {
111
125
  flex: '1',
112
126
  flexBasis: '100%',
113
127
  minWidth: BANK_FIELD_FLEX.accountNumber.minWidth,
114
128
  });
115
129
  this.applyInputStyles(this.accountNumberEl, mergedStyles);
116
- containerEl.appendChild(this.accountNumberEl);
130
+ containerEl.appendChild(wrapFieldWithLabel(this.accountNumberEl, 'Account Number', mergedStyles));
117
131
  }
118
132
  else {
119
- this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
133
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', ((_c = this.customPlaceholders) === null || _c === void 0 ? void 0 : _c.routingNumber) || 'Routing *', 9);
120
134
  Object.assign(this.routingNumberEl.style, {
121
135
  flex: BANK_FIELD_FLEX.routingNumber.flex,
122
136
  minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
123
137
  });
124
138
  this.applyInputStyles(this.routingNumberEl, mergedStyles, 'left');
125
- containerEl.appendChild(this.routingNumberEl);
126
- this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
139
+ containerEl.appendChild(wrapFieldWithLabel(this.routingNumberEl, 'Routing Number', mergedStyles));
140
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', ((_d = this.customPlaceholders) === null || _d === void 0 ? void 0 : _d.accountNumber) || 'Account Number *', 17);
127
141
  Object.assign(this.accountNumberEl.style, {
128
142
  flex: BANK_FIELD_FLEX.accountNumber.flex,
129
143
  minWidth: BANK_FIELD_FLEX.accountNumber.minWidth,
130
144
  });
131
145
  this.applyInputStyles(this.accountNumberEl, mergedStyles, 'middle');
132
- containerEl.appendChild(this.accountNumberEl);
146
+ containerEl.appendChild(wrapFieldWithLabel(this.accountNumberEl, 'Account Number', mergedStyles));
133
147
  this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
134
148
  Object.assign(this.accountTypeEl.style, {
135
149
  flex: BANK_FIELD_FLEX.accountType.flex,
136
150
  minWidth: BANK_FIELD_FLEX.accountType.minWidth,
137
151
  });
138
152
  this.applyInputStyles(this.accountTypeEl, mergedStyles, 'right');
139
- containerEl.appendChild(this.accountTypeEl);
153
+ containerEl.appendChild(wrapFieldWithLabel(this.accountTypeEl, 'Account Type', mergedStyles));
140
154
  }
141
155
  this.mergedStyles = mergedStyles;
142
156
  this.currentValidationState = {
@@ -234,8 +248,6 @@ export class CardConnectTokenizer extends Tokenizer {
234
248
  window.addEventListener('message', this.messageHandler);
235
249
  this.iframe.onload = () => {
236
250
  clearTimeout(timeout);
237
- if (this.enableTestMode && this.mode === 'credit_card') {
238
- }
239
251
  this.emit('ready');
240
252
  resolve();
241
253
  };
@@ -681,7 +693,34 @@ export class CardConnectTokenizer extends Tokenizer {
681
693
  this.emit('validation', this.currentValidationState);
682
694
  }
683
695
  }
696
+ static measureCvvLabelMarginLeft(monthWidth, yearWidth, cvvMarginLeft) {
697
+ var _a;
698
+ const targetCvvX = monthWidth + 4 + 4 + yearWidth + 12 + cvvMarginLeft;
699
+ const host = document.createElement('div');
700
+ host.style.cssText =
701
+ 'position:absolute;visibility:hidden;left:0;top:0;width:9999px;height:0;overflow:hidden;';
702
+ document.body.appendChild(host);
703
+ const shadow = host.attachShadow({ mode: 'open' });
704
+ shadow.innerHTML = [
705
+ '<input type="text" size="2" style="margin-right:4px">',
706
+ '<label>/</label>',
707
+ '<input type="text" size="4" style="margin-left:4px;margin-right:12px">',
708
+ '<input type="tel" size="5">',
709
+ ].join('');
710
+ const inputs = shadow.querySelectorAll('input');
711
+ const hostX = host.getBoundingClientRect().x;
712
+ const cvvRect = (_a = inputs[2]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
713
+ const defaultCvvX = cvvRect ? cvvRect.x - hostX : -1;
714
+ document.body.removeChild(host);
715
+ if (defaultCvvX <= 0 || defaultCvvX > 500) {
716
+ console.warn('[CardConnect] CVV measurement out of range, using fallback margin-left:100px');
717
+ return 100;
718
+ }
719
+ const predictedLabelLeft = defaultCvvX + 8;
720
+ return Math.round(targetCvvX - predictedLabelLeft);
721
+ }
684
722
  static generateIframeUrl(baseUrl, container) {
723
+ var _a, _b, _c, _d, _e, _f, _g, _h;
685
724
  const params = new URLSearchParams({
686
725
  invalidinputevent: 'true',
687
726
  enhancedresponse: 'true',
@@ -690,30 +729,46 @@ export class CardConnectTokenizer extends Tokenizer {
690
729
  formatinput: 'true',
691
730
  unique: 'true',
692
731
  norsa: 'true',
693
- placeholder: 'Card Number *',
694
- placeholdercvv: 'CVV',
732
+ placeholder: ((_a = container.placeholders) === null || _a === void 0 ? void 0 : _a.cardNumber) || 'Card Number *',
733
+ placeholdercvv: ((_b = container.placeholders) === null || _b === void 0 ? void 0 : _b.cvv) || 'CVV',
695
734
  invalidcreditcardevent: 'true',
696
735
  invalidexpiry: 'true',
697
736
  invalidcvv: 'true',
698
737
  });
738
+ const mergedStyles = mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling);
739
+ const showLabelsInUrl = ((_c = mergedStyles.label) === null || _c === void 0 ? void 0 : _c.show) && container.layout === 'two-line';
740
+ if (showLabelsInUrl) {
741
+ params.set('cardlabel', 'Card Number');
742
+ params.set('expirylabel', 'Expiration');
743
+ params.set('cvvlabel', 'CVV');
744
+ }
745
+ else {
746
+ params.set('cardlabel', '');
747
+ params.set('expirylabel', '');
748
+ params.set('cvvlabel', '');
749
+ }
699
750
  if (container.layout === 'two-line') {
700
751
  params.set('useexpiryfield', 'true');
701
752
  params.set('orientation', 'horizontal');
702
- params.set('placeholdermonth', 'MM');
703
- params.set('placeholderyear', 'YYYY');
753
+ params.set('placeholdermonth', ((_d = container.placeholders) === null || _d === void 0 ? void 0 : _d.expiryMonth) || 'MM');
754
+ params.set('placeholderyear', ((_e = container.placeholders) === null || _e === void 0 ? void 0 : _e.expiryYear) || 'YYYY');
704
755
  }
705
756
  else {
706
757
  params.set('orientation', 'horizontal');
707
- params.set('placeholdermonth', 'MM');
708
- params.set('placeholderyear', 'YYYY');
758
+ params.set('placeholdermonth', ((_f = container.placeholders) === null || _f === void 0 ? void 0 : _f.expiryMonth) || 'MM');
759
+ params.set('placeholderyear', ((_g = container.placeholders) === null || _g === void 0 ? void 0 : _g.expiryYear) || 'YYYY');
709
760
  }
710
- const mergedStyles = mergeStyles(DEFAULT_UNIFIED_STYLES, container.styling);
711
761
  const isDesktopSafari = typeof navigator !== 'undefined' &&
712
762
  /Safari/.test(navigator.userAgent) &&
713
763
  /Apple Computer/.test(navigator.vendor) &&
714
764
  !/Chrome/.test(navigator.userAgent) &&
715
765
  !/Mobile/.test(navigator.userAgent);
716
- const cssString = CardConnectTokenizer.generateCardConnectCss(mergedStyles, (container.layout || 'single-line'), isDesktopSafari);
766
+ const showLabels = ((_h = mergedStyles.label) === null || _h === void 0 ? void 0 : _h.show) && container.layout === 'two-line';
767
+ let cvvLabelMarginLeft;
768
+ if (showLabels && typeof document !== 'undefined') {
769
+ cvvLabelMarginLeft = CardConnectTokenizer.measureCvvLabelMarginLeft(60, 80, 40);
770
+ }
771
+ const cssString = CardConnectTokenizer.generateCardConnectCss(mergedStyles, (container.layout || 'single-line'), isDesktopSafari, cvvLabelMarginLeft);
717
772
  const queryPairs = [];
718
773
  params.forEach((value, key) => {
719
774
  queryPairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
@@ -728,7 +783,7 @@ export class CardConnectTokenizer extends Tokenizer {
728
783
  iframe.style.width = '100%';
729
784
  iframe.style.height = styles.input.height;
730
785
  iframe.style.border = 'none';
731
- iframe.style.overflow = 'hidden';
786
+ iframe.style.overflow = '';
732
787
  iframe.style.display = 'block';
733
788
  iframe.style.minWidth = '0';
734
789
  if (styles.container) {
@@ -746,18 +801,30 @@ export class CardConnectTokenizer extends Tokenizer {
746
801
  iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
747
802
  return iframe;
748
803
  }
749
- static generateCardConnectCss(styles, layout = 'single-line', isDesktopSafari = false) {
750
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
804
+ static generateCardConnectCss(styles, layout = 'single-line', isDesktopSafari = false, cvvLabelMarginLeft) {
805
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
751
806
  const css = [];
752
- if (layout === 'two-line') {
753
- css.push('html,form,body{margin:0;padding:0;}');
807
+ const showLabelsInIframe = ((_a = styles.label) === null || _a === void 0 ? void 0 : _a.show) && layout === 'two-line';
808
+ if (showLabelsInIframe) {
809
+ const labelCss = `font-size:${styles.label.fontSize};font-weight:${styles.label.fontWeight};font-family:${styles.label.fontFamily};color:${styles.label.color};margin-bottom:${styles.label.marginBottom}`;
810
+ css.push(`label{${labelCss};}`);
811
+ css.push(`label[for="ccexpiryfieldyear"]{display:none;}`);
812
+ css.push(`br{line-height:0;font-size:0;margin:0;}`);
813
+ css.push(`input,select{margin-top:0;margin-bottom:0;}`);
814
+ }
815
+ else {
754
816
  css.push('label{display:none;}');
755
- css.push('br{display:none;}');
817
+ }
818
+ if (layout === 'two-line') {
819
+ css.push('html,body{margin:0;padding:0;overflow:hidden;}');
820
+ css.push('form{margin:0;padding:0;width:100%;box-sizing:border-box;}');
821
+ if (!showLabelsInIframe) {
822
+ css.push('br{display:none;}');
823
+ }
756
824
  }
757
825
  else {
758
826
  css.push('body{margin:0;padding:0;display:flex;align-items:center;}');
759
827
  css.push('form{margin:0;padding:0;display:flex;width:100%;align-items:center;}');
760
- css.push('label{display:none;}');
761
828
  css.push('br{display:none;}');
762
829
  }
763
830
  const isConnected = styles.container.gap === '0';
@@ -790,13 +857,22 @@ export class CardConnectTokenizer extends Tokenizer {
790
857
  }
791
858
  if (layout === 'two-line') {
792
859
  css.push('input#ccnumfield{width:100%;display:block;margin-bottom:8px;}');
793
- const twoLinePadding = ((_a = styles.twoLine) === null || _a === void 0 ? void 0 : _a.padding) || ((_b = styles.input) === null || _b === void 0 ? void 0 : _b.padding) || '10px';
794
- const twoLineFontSize = ((_c = styles.twoLine) === null || _c === void 0 ? void 0 : _c.fontSize) || ((_d = styles.input) === null || _d === void 0 ? void 0 : _d.fontSize) || '14px';
795
- const twoLineTextAlign = ((_e = styles.twoLine) === null || _e === void 0 ? void 0 : _e.textAlign) || 'left';
796
- css.push(`input#ccexpiryfieldmonth{width:25%;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
797
- css.push(`input#ccexpiryfieldyear{width:35%;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
798
- css.push(`input#cccvvfield{width:calc(40% - 16px);padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};margin-left:-4px;}`);
799
- if ((_f = styles.input) === null || _f === void 0 ? void 0 : _f.borderRadius) {
860
+ const twoLinePadding = ((_b = styles.twoLine) === null || _b === void 0 ? void 0 : _b.padding) || ((_c = styles.input) === null || _c === void 0 ? void 0 : _c.padding) || '10px';
861
+ const twoLineFontSize = ((_d = styles.twoLine) === null || _d === void 0 ? void 0 : _d.fontSize) || ((_e = styles.input) === null || _e === void 0 ? void 0 : _e.fontSize) || '14px';
862
+ const twoLineTextAlign = ((_f = styles.twoLine) === null || _f === void 0 ? void 0 : _f.textAlign) || 'left';
863
+ if (showLabelsInIframe) {
864
+ css.push(`input#ccexpiryfieldmonth{width:60px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
865
+ css.push(`input#ccexpiryfieldyear{width:80px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
866
+ css.push(`input#cccvvfield{width:80px;margin-left:40px;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
867
+ const labelMargin = cvvLabelMarginLeft !== null && cvvLabelMarginLeft !== void 0 ? cvvLabelMarginLeft : 100;
868
+ css.push(`label#cccvvlabel{margin-left:${labelMargin}px;}`);
869
+ }
870
+ else {
871
+ css.push(`input#ccexpiryfieldmonth{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
872
+ css.push(`input#ccexpiryfieldyear{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
873
+ css.push(`input#cccvvfield{min-width:4em;padding:${twoLinePadding};font-size:${twoLineFontSize};text-align:${twoLineTextAlign};}`);
874
+ }
875
+ if ((_g = styles.input) === null || _g === void 0 ? void 0 : _g.borderRadius) {
800
876
  css.push(`input{border-radius:${styles.input.borderRadius};}`);
801
877
  }
802
878
  }
@@ -805,7 +881,7 @@ export class CardConnectTokenizer extends Tokenizer {
805
881
  css.push('select#ccexpirymonth{width:16%;margin:0;margin-right:-12px;}');
806
882
  css.push('select#ccexpiryyear{width:20%;}');
807
883
  css.push('input#cccvvfield{width:20%;margin:0;margin-left:-12px;}');
808
- if ((_g = styles.input) === null || _g === void 0 ? void 0 : _g.borderRadius) {
884
+ if ((_h = styles.input) === null || _h === void 0 ? void 0 : _h.borderRadius) {
809
885
  css.push(`input#ccnumfield{border-radius:${styles.input.borderRadius} 0 0 ${styles.input.borderRadius};border-right:none;}`);
810
886
  css.push('select#ccexpirymonth{border-radius:0;border-right:none;}');
811
887
  css.push('select#ccexpiryyear{border-radius:0;border-right:none;}');
@@ -823,7 +899,7 @@ export class CardConnectTokenizer extends Tokenizer {
823
899
  css.push(`select#ccexpirymonth{width:15%;margin-right:${marginRight};}`);
824
900
  css.push(`select#ccexpiryyear{width:20%;margin-right:${marginRight};}`);
825
901
  css.push('input#cccvvfield{width:15%;}');
826
- if ((_h = styles.input) === null || _h === void 0 ? void 0 : _h.borderRadius) {
902
+ if ((_j = styles.input) === null || _j === void 0 ? void 0 : _j.borderRadius) {
827
903
  css.push(`input,select{border-radius:${styles.input.borderRadius};}`);
828
904
  }
829
905
  }
@@ -839,7 +915,7 @@ export class CardConnectTokenizer extends Tokenizer {
839
915
  if (focusStyles.length > 0) {
840
916
  css.push(`input:focus{${focusStyles.join(';')};}`);
841
917
  css.push(`select:focus{${focusStyles.join(';')};}`);
842
- if (isConnected && ((_j = styles.input) === null || _j === void 0 ? void 0 : _j.border)) {
918
+ if (isConnected && ((_k = styles.input) === null || _k === void 0 ? void 0 : _k.border)) {
843
919
  css.push(`input#ccnumfield:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
844
920
  css.push(`select#ccexpirymonth:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
845
921
  css.push(`select#ccexpiryyear:focus{border:${styles.input.border};${focusStyles.join(';')};}`);
@@ -6,6 +6,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
6
6
  private environmentKey;
7
7
  private containerId;
8
8
  private layout;
9
+ private customPlaceholders?;
9
10
  private spreedly;
10
11
  private currentValidationState;
11
12
  private mergedStyles;
@@ -63,6 +64,7 @@ export declare class SpreedlyTokenizer extends Tokenizer {
63
64
  private updateOverallValidation;
64
65
  private handleFieldEvent;
65
66
  private createInternalElements;
67
+ private appendField;
66
68
  private createCreditCardFields;
67
69
  private createBankAccountFields;
68
70
  private createUSBankAccountFields;
@@ -14,15 +14,16 @@ import { extractSpreedlyToken, unpackSpreedlyResponse } from '../typeAdapters';
14
14
  import { fetchSpreedlySecurityArgs } from './spreedly-secure';
15
15
  import { DEFAULT_UNIFIED_STYLES, mergeStyles, getContainerStylesForLayout, } from './styles';
16
16
  import { INIT_TIMEOUT, TOKENIZE_TIMEOUT, FIELD_FLEX, BANK_FIELD_FLEX, CANADIAN_BANK_FIELD_FLEX, PLACEHOLDERS, FIELD_CONSTRAINTS, RESPONSIVE_BREAKPOINT, } from './tokenizer-constants';
17
- import { createFieldContainer, createInputElement, validateRoutingNumber, validateAccountNumber, createAccountTypeSelect, validateInstitutionNumber, validateTransitNumber, validateCanadianAccountNumber, formatCanadianRoutingNumber, } from './tokenizer-utils';
17
+ import { createFieldContainer, createInputElement, validateRoutingNumber, validateAccountNumber, createAccountTypeSelect, validateInstitutionNumber, validateTransitNumber, validateCanadianAccountNumber, formatCanadianRoutingNumber, wrapFieldWithLabel, } from './tokenizer-utils';
18
18
  const SPREEDLY_SCRIPT_URL = 'https://core.spreedly.com/iframe/iframe-v1.min.js';
19
19
  const SPREEDLY_SCRIPT_ID = 'spreedly-iframe-script';
20
20
  export class SpreedlyTokenizer extends Tokenizer {
21
- constructor(environmentKey, containerId, styles, mode = 'credit_card', bankCountry = 'US', enableTestMode = false, layout = 'single-line', responsiveBreakpoint = RESPONSIVE_BREAKPOINT) {
21
+ constructor(environmentKey, containerId, styles, mode = 'credit_card', bankCountry = 'US', enableTestMode = false, layout = 'single-line', responsiveBreakpoint = RESPONSIVE_BREAKPOINT, customPlaceholders) {
22
22
  super();
23
23
  this.environmentKey = environmentKey;
24
24
  this.containerId = containerId;
25
25
  this.layout = layout;
26
+ this.customPlaceholders = customPlaceholders;
26
27
  this.bankCountry = 'US';
27
28
  this.enableTestMode = false;
28
29
  this.effectiveLayout = 'single-line';
@@ -77,7 +78,7 @@ export class SpreedlyTokenizer extends Tokenizer {
77
78
  if (!environmentKey) {
78
79
  throw new Error('Spreedly environment key is required. Provide it via gateway.config.environmentKey or client config.');
79
80
  }
80
- const tokenizer = new SpreedlyTokenizer(environmentKey, container.containerId, container.styling, container.mode || 'credit_card', container.bankCountry || 'US', container.enableTestMode || false, container.layout || 'single-line', (_b = container.responsiveBreakpoint) !== null && _b !== void 0 ? _b : RESPONSIVE_BREAKPOINT);
81
+ const tokenizer = new SpreedlyTokenizer(environmentKey, container.containerId, container.styling, container.mode || 'credit_card', container.bankCountry || 'US', container.enableTestMode || false, container.layout || 'single-line', (_b = container.responsiveBreakpoint) !== null && _b !== void 0 ? _b : RESPONSIVE_BREAKPOINT, container.placeholders);
81
82
  tokenizer.organizationId = config.organizationId;
82
83
  tokenizer.embedId = config.embedId;
83
84
  tokenizer.clientConfig = config.clientConfig;
@@ -136,11 +137,12 @@ export class SpreedlyTokenizer extends Tokenizer {
136
137
  reject(new Error('Spreedly initialization timeout'));
137
138
  }, INIT_TIMEOUT);
138
139
  this.spreedly.on('ready', () => {
140
+ var _a, _b;
139
141
  clearTimeout(timeout);
140
- this.spreedly.setPlaceholder('number', PLACEHOLDERS.cardNumber);
142
+ this.spreedly.setPlaceholder('number', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.cardNumber) || PLACEHOLDERS.cardNumber);
141
143
  this.spreedly.setFieldType('number', 'text');
142
144
  this.spreedly.setNumberFormat('prettyFormat');
143
- this.spreedly.setPlaceholder('cvv', PLACEHOLDERS.cvv);
145
+ this.spreedly.setPlaceholder('cvv', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.cvv) || PLACEHOLDERS.cvv);
144
146
  this.spreedly.setFieldType('cvv', 'text');
145
147
  this.applyUnifiedStyles();
146
148
  if (this.enableTestMode &&
@@ -256,7 +258,7 @@ export class SpreedlyTokenizer extends Tokenizer {
256
258
  minWidth: CANADIAN_BANK_FIELD_FLEX.accountType.minWidth,
257
259
  });
258
260
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'left');
259
- container.appendChild(this.accountTypeEl);
261
+ this.appendField(container, this.accountTypeEl, 'Account Type');
260
262
  this.institutionNumberEl = createInputElement(`${this.containerId}-institution`, 'text', 'Inst *', 3);
261
263
  Object.assign(this.institutionNumberEl.style, {
262
264
  flex: '1',
@@ -273,7 +275,7 @@ export class SpreedlyTokenizer extends Tokenizer {
273
275
  }
274
276
  this.updateBankAccountValidation();
275
277
  });
276
- container.appendChild(this.institutionNumberEl);
278
+ this.appendField(container, this.institutionNumberEl, 'Institution');
277
279
  this.transitNumberEl = createInputElement(`${this.containerId}-transit`, 'text', 'Transit *', 5);
278
280
  Object.assign(this.transitNumberEl.style, {
279
281
  flex: '2',
@@ -290,7 +292,7 @@ export class SpreedlyTokenizer extends Tokenizer {
290
292
  }
291
293
  this.updateBankAccountValidation();
292
294
  });
293
- container.appendChild(this.transitNumberEl);
295
+ this.appendField(container, this.transitNumberEl, 'Transit');
294
296
  this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *');
295
297
  Object.assign(this.accountNumberEl.style, {
296
298
  flex: '1',
@@ -308,7 +310,7 @@ export class SpreedlyTokenizer extends Tokenizer {
308
310
  }
309
311
  this.updateBankAccountValidation();
310
312
  });
311
- container.appendChild(this.accountNumberEl);
313
+ this.appendField(container, this.accountNumberEl, 'Account Number');
312
314
  }
313
315
  else {
314
316
  this.institutionNumberEl = createInputElement(`${this.containerId}-institution`, 'text', 'Inst *', 3);
@@ -327,7 +329,7 @@ export class SpreedlyTokenizer extends Tokenizer {
327
329
  }
328
330
  this.updateBankAccountValidation();
329
331
  });
330
- container.appendChild(this.institutionNumberEl);
332
+ this.appendField(container, this.institutionNumberEl, 'Institution');
331
333
  this.transitNumberEl = createInputElement(`${this.containerId}-transit`, 'text', 'Transit *', 5);
332
334
  Object.assign(this.transitNumberEl.style, {
333
335
  flex: CANADIAN_BANK_FIELD_FLEX.transitNumber.flex,
@@ -344,7 +346,7 @@ export class SpreedlyTokenizer extends Tokenizer {
344
346
  }
345
347
  this.updateBankAccountValidation();
346
348
  });
347
- container.appendChild(this.transitNumberEl);
349
+ this.appendField(container, this.transitNumberEl, 'Transit');
348
350
  this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *');
349
351
  Object.assign(this.accountNumberEl.style, {
350
352
  flex: CANADIAN_BANK_FIELD_FLEX.accountNumber.flex,
@@ -361,14 +363,14 @@ export class SpreedlyTokenizer extends Tokenizer {
361
363
  }
362
364
  this.updateBankAccountValidation();
363
365
  });
364
- container.appendChild(this.accountNumberEl);
366
+ this.appendField(container, this.accountNumberEl, 'Account Number');
365
367
  this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
366
368
  Object.assign(this.accountTypeEl.style, {
367
369
  flex: CANADIAN_BANK_FIELD_FLEX.accountType.flex,
368
370
  minWidth: CANADIAN_BANK_FIELD_FLEX.accountType.minWidth,
369
371
  });
370
372
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'right');
371
- container.appendChild(this.accountTypeEl);
373
+ this.appendField(container, this.accountTypeEl, 'Account Type');
372
374
  }
373
375
  }
374
376
  tokenizeBankAccountInternal(bankData) {
@@ -841,7 +843,11 @@ export class SpreedlyTokenizer extends Tokenizer {
841
843
  this.createCreditCardFields(container);
842
844
  }
843
845
  }
846
+ appendField(container, field, labelText) {
847
+ container.appendChild(wrapFieldWithLabel(field, labelText, this.mergedStyles));
848
+ }
844
849
  createCreditCardFields(container) {
850
+ var _a, _b;
845
851
  if (this.layout === 'responsive') {
846
852
  this.effectiveLayout = this.determineLayout(container.offsetWidth);
847
853
  }
@@ -854,8 +860,8 @@ export class SpreedlyTokenizer extends Tokenizer {
854
860
  cardNumberDiv.style.height = this.mergedStyles.input.height;
855
861
  cardNumberDiv.style.flexBasis = '100%';
856
862
  this.cardNumberDiv = cardNumberDiv;
857
- container.appendChild(cardNumberDiv);
858
- this.expiryEl = createInputElement(this.expiryId, 'text', PLACEHOLDERS.expiry, FIELD_CONSTRAINTS.expiry.maxLength);
863
+ this.appendField(container, cardNumberDiv, 'Card Number');
864
+ this.expiryEl = createInputElement(this.expiryId, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.expiry) || PLACEHOLDERS.expiry, FIELD_CONSTRAINTS.expiry.maxLength);
859
865
  Object.assign(this.expiryEl.style, {
860
866
  flex: '1',
861
867
  minWidth: FIELD_FLEX.expiry.minWidth,
@@ -875,18 +881,18 @@ export class SpreedlyTokenizer extends Tokenizer {
875
881
  this.validateExpiry();
876
882
  this.updateOverallValidation();
877
883
  });
878
- container.appendChild(this.expiryEl);
884
+ this.appendField(container, this.expiryEl, 'Expiration');
879
885
  const cvvDiv = createFieldContainer(this.cvvEl, '1', FIELD_FLEX.cvv.minWidth);
880
886
  cvvDiv.style.height = this.mergedStyles.input.height;
881
887
  this.cvvDiv = cvvDiv;
882
- container.appendChild(cvvDiv);
888
+ this.appendField(container, cvvDiv, 'CVV');
883
889
  }
884
890
  else {
885
891
  const cardNumberDiv = createFieldContainer(this.numberEl, FIELD_FLEX.cardNumber.flex, FIELD_FLEX.cardNumber.minWidth);
886
892
  cardNumberDiv.style.height = this.mergedStyles.input.height;
887
893
  this.cardNumberDiv = cardNumberDiv;
888
- container.appendChild(cardNumberDiv);
889
- this.expiryEl = createInputElement(this.expiryId, 'text', PLACEHOLDERS.expiry, FIELD_CONSTRAINTS.expiry.maxLength);
894
+ this.appendField(container, cardNumberDiv, 'Card Number');
895
+ this.expiryEl = createInputElement(this.expiryId, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.expiry) || PLACEHOLDERS.expiry, FIELD_CONSTRAINTS.expiry.maxLength);
890
896
  Object.assign(this.expiryEl.style, {
891
897
  flex: FIELD_FLEX.expiry.flex,
892
898
  minWidth: FIELD_FLEX.expiry.minWidth,
@@ -906,11 +912,11 @@ export class SpreedlyTokenizer extends Tokenizer {
906
912
  this.validateExpiry();
907
913
  this.updateOverallValidation();
908
914
  });
909
- container.appendChild(this.expiryEl);
915
+ this.appendField(container, this.expiryEl, 'Expiration');
910
916
  const cvvDiv = createFieldContainer(this.cvvEl, FIELD_FLEX.cvv.flex, FIELD_FLEX.cvv.minWidth);
911
917
  cvvDiv.style.height = this.mergedStyles.input.height;
912
918
  this.cvvDiv = cvvDiv;
913
- container.appendChild(cvvDiv);
919
+ this.appendField(container, cvvDiv, 'CVV');
914
920
  if (this.layout === 'responsive') {
915
921
  this.applyLayoutStyles(this.effectiveLayout);
916
922
  this.setupResizeObserver();
@@ -926,6 +932,7 @@ export class SpreedlyTokenizer extends Tokenizer {
926
932
  }
927
933
  }
928
934
  createUSBankAccountFields(container) {
935
+ var _a, _b, _c, _d;
929
936
  if (this.layout === 'two-line') {
930
937
  this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
931
938
  Object.assign(this.accountTypeEl.style, {
@@ -933,8 +940,8 @@ export class SpreedlyTokenizer extends Tokenizer {
933
940
  minWidth: BANK_FIELD_FLEX.accountType.minWidth,
934
941
  });
935
942
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'left');
936
- container.appendChild(this.accountTypeEl);
937
- this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
943
+ this.appendField(container, this.accountTypeEl, 'Account Type');
944
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', ((_a = this.customPlaceholders) === null || _a === void 0 ? void 0 : _a.routingNumber) || 'Routing *', 9);
938
945
  Object.assign(this.routingNumberEl.style, {
939
946
  flex: '1',
940
947
  minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
@@ -950,8 +957,8 @@ export class SpreedlyTokenizer extends Tokenizer {
950
957
  }
951
958
  this.updateBankAccountValidation();
952
959
  });
953
- container.appendChild(this.routingNumberEl);
954
- this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
960
+ this.appendField(container, this.routingNumberEl, 'Routing Number');
961
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', ((_b = this.customPlaceholders) === null || _b === void 0 ? void 0 : _b.accountNumber) || 'Account Number *', 17);
955
962
  Object.assign(this.accountNumberEl.style, {
956
963
  flex: '1',
957
964
  flexBasis: '100%',
@@ -968,10 +975,10 @@ export class SpreedlyTokenizer extends Tokenizer {
968
975
  }
969
976
  this.updateBankAccountValidation();
970
977
  });
971
- container.appendChild(this.accountNumberEl);
978
+ this.appendField(container, this.accountNumberEl, 'Account Number');
972
979
  }
973
980
  else {
974
- this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', 'Routing *', 9);
981
+ this.routingNumberEl = createInputElement(`${this.containerId}-routing`, 'text', ((_c = this.customPlaceholders) === null || _c === void 0 ? void 0 : _c.routingNumber) || 'Routing *', 9);
975
982
  Object.assign(this.routingNumberEl.style, {
976
983
  flex: BANK_FIELD_FLEX.routingNumber.flex,
977
984
  minWidth: BANK_FIELD_FLEX.routingNumber.minWidth,
@@ -987,8 +994,8 @@ export class SpreedlyTokenizer extends Tokenizer {
987
994
  }
988
995
  this.updateBankAccountValidation();
989
996
  });
990
- container.appendChild(this.routingNumberEl);
991
- this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', 'Account Number *', 17);
997
+ this.appendField(container, this.routingNumberEl, 'Routing Number');
998
+ this.accountNumberEl = createInputElement(`${this.containerId}-account`, 'text', ((_d = this.customPlaceholders) === null || _d === void 0 ? void 0 : _d.accountNumber) || 'Account Number *', 17);
992
999
  Object.assign(this.accountNumberEl.style, {
993
1000
  flex: BANK_FIELD_FLEX.accountNumber.flex,
994
1001
  minWidth: BANK_FIELD_FLEX.accountNumber.minWidth,
@@ -1004,14 +1011,14 @@ export class SpreedlyTokenizer extends Tokenizer {
1004
1011
  }
1005
1012
  this.updateBankAccountValidation();
1006
1013
  });
1007
- container.appendChild(this.accountNumberEl);
1014
+ this.appendField(container, this.accountNumberEl, 'Account Number');
1008
1015
  this.accountTypeEl = createAccountTypeSelect(`${this.containerId}-account-type`);
1009
1016
  Object.assign(this.accountTypeEl.style, {
1010
1017
  flex: BANK_FIELD_FLEX.accountType.flex,
1011
1018
  minWidth: BANK_FIELD_FLEX.accountType.minWidth,
1012
1019
  });
1013
1020
  this.applyInputStyles(this.accountTypeEl, this.mergedStyles, 'right');
1014
- container.appendChild(this.accountTypeEl);
1021
+ this.appendField(container, this.accountTypeEl, 'Account Type');
1015
1022
  }
1016
1023
  }
1017
1024
  updateBankAccountValidation() {