@idonatedev/idonate-sdk 1.1.0-dev13 → 1.1.0-dev14
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/esm/apple-pay.d.ts +12 -0
- package/dist/esm/apple-pay.js +74 -0
- package/dist/esm/cloudflare-challenge-handler.d.ts +5 -0
- package/dist/esm/cloudflare-challenge-handler.js +77 -0
- package/dist/esm/config-handler.d.ts +22 -0
- package/dist/esm/config-handler.js +47 -0
- package/dist/esm/constants.d.ts +18 -0
- package/dist/esm/constants.js +82 -0
- package/dist/esm/google-pay.d.ts +18 -0
- package/dist/esm/google-pay.js +140 -0
- package/dist/esm/idonate-client.d.ts +28 -0
- package/dist/esm/idonate-client.js +256 -0
- package/dist/esm/index.d.ts +11 -0
- package/dist/esm/index.js +11 -0
- package/dist/esm/recaptcha.d.ts +12 -0
- package/dist/esm/recaptcha.js +89 -0
- package/dist/esm/shared.d.ts +3 -0
- package/dist/esm/shared.js +13 -0
- package/dist/esm/test-utils.d.ts +81 -0
- package/dist/esm/test-utils.js +94 -0
- package/dist/esm/tokenize/CardConnectTokenizer.d.ts +51 -0
- package/dist/esm/tokenize/CardConnectTokenizer.js +706 -0
- package/dist/esm/tokenize/SpreedlyTokenizer.d.ts +92 -0
- package/dist/esm/tokenize/SpreedlyTokenizer.js +1140 -0
- package/dist/esm/tokenize/Tokenizer.d.ts +37 -0
- package/dist/esm/tokenize/Tokenizer.js +146 -0
- package/dist/esm/tokenize/iats.d.ts +3 -0
- package/dist/esm/tokenize/iats.js +48 -0
- package/dist/esm/tokenize/index.d.ts +6 -0
- package/dist/esm/tokenize/index.js +6 -0
- package/dist/esm/tokenize/spreedly-secure.d.ts +8 -0
- package/dist/esm/tokenize/spreedly-secure.js +40 -0
- package/dist/esm/tokenize/styles.d.ts +4 -0
- package/dist/esm/tokenize/styles.js +46 -0
- package/dist/esm/tokenize/tokenizer-constants.d.ts +62 -0
- package/dist/esm/tokenize/tokenizer-constants.js +62 -0
- package/dist/esm/tokenize/tokenizer-utils.d.ts +19 -0
- package/dist/esm/tokenize/tokenizer-utils.js +139 -0
- package/dist/esm/tokenize/types.d.ts +144 -0
- package/dist/esm/tokenize/types.js +26 -0
- package/dist/esm/typeAdapters.d.ts +29 -0
- package/dist/esm/typeAdapters.js +188 -0
- package/dist/esm/types.d.ts +367 -0
- package/dist/esm/types.js +14 -0
- package/dist/esm/util.d.ts +17 -0
- package/dist/esm/util.js +113 -0
- package/package.json +12 -2
- package/umd/idonate-sdk.js +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CardData, BankAccountData, PaymentData, PaymentToken, ValidationResult, TokenizerEvent, TokenizerStylingComplete, CardType, PaymentGateway, TokenizerContainer, PaymentMethodMode } from './types';
|
|
2
|
+
import ConfigHandler from '../config-handler';
|
|
3
|
+
export declare abstract class Tokenizer {
|
|
4
|
+
protected mode: PaymentMethodMode;
|
|
5
|
+
protected eventHandlers: Map<TokenizerEvent, Set<(data?: any) => void>>;
|
|
6
|
+
abstract tokenize(paymentData: PaymentData): Promise<PaymentToken>;
|
|
7
|
+
abstract validate(): Promise<ValidationResult>;
|
|
8
|
+
abstract clear(): void;
|
|
9
|
+
abstract focus(field: 'cardNumber' | 'cvv' | 'expiry' | 'routingNumber' | 'accountNumber'): void;
|
|
10
|
+
abstract destroy(): void;
|
|
11
|
+
abstract hasToken(): boolean;
|
|
12
|
+
abstract getToken(): PaymentToken | null;
|
|
13
|
+
abstract get tokenizationMode(): 'auto' | 'manual';
|
|
14
|
+
getMode(): PaymentMethodMode;
|
|
15
|
+
on(event: TokenizerEvent, handler: (data?: any) => void): void;
|
|
16
|
+
off(event: TokenizerEvent, handler: (data?: any) => void): void;
|
|
17
|
+
protected emit(event: TokenizerEvent, data?: any): void;
|
|
18
|
+
protected isCardData(data: PaymentData): data is CardData;
|
|
19
|
+
protected isBankAccountData(data: PaymentData): data is BankAccountData;
|
|
20
|
+
protected normalizeCardType(providerCardType: string): CardType;
|
|
21
|
+
protected addExpiryFormatter(expiryEl: HTMLInputElement): void;
|
|
22
|
+
protected parseExpiry(expiryEl?: HTMLInputElement): {
|
|
23
|
+
month: string;
|
|
24
|
+
year: string;
|
|
25
|
+
} | null;
|
|
26
|
+
protected applyInputStyles(element: HTMLElement, styles: TokenizerStylingComplete, position?: 'left' | 'middle' | 'right'): void;
|
|
27
|
+
protected generateFieldIds(containerId: string): {
|
|
28
|
+
numberId: string;
|
|
29
|
+
expiryId: string;
|
|
30
|
+
cvvId: string;
|
|
31
|
+
};
|
|
32
|
+
static create(gateway: PaymentGateway, container: TokenizerContainer, config: {
|
|
33
|
+
organizationId: string;
|
|
34
|
+
embedId: string;
|
|
35
|
+
clientConfig: ConfigHandler;
|
|
36
|
+
}): Promise<Tokenizer>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { formatExpiryInput, parseExpiryDate, addFocusHandlers, getConnectedBorderRadius, } from './tokenizer-utils';
|
|
11
|
+
export class Tokenizer {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.mode = 'credit_card';
|
|
14
|
+
this.eventHandlers = new Map();
|
|
15
|
+
}
|
|
16
|
+
getMode() {
|
|
17
|
+
return this.mode;
|
|
18
|
+
}
|
|
19
|
+
on(event, handler) {
|
|
20
|
+
if (!this.eventHandlers.has(event)) {
|
|
21
|
+
this.eventHandlers.set(event, new Set());
|
|
22
|
+
}
|
|
23
|
+
this.eventHandlers.get(event).add(handler);
|
|
24
|
+
}
|
|
25
|
+
off(event, handler) {
|
|
26
|
+
const handlers = this.eventHandlers.get(event);
|
|
27
|
+
if (handlers) {
|
|
28
|
+
handlers.delete(handler);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
emit(event, data) {
|
|
32
|
+
const handlers = this.eventHandlers.get(event);
|
|
33
|
+
if (handlers) {
|
|
34
|
+
handlers.forEach((handler) => {
|
|
35
|
+
try {
|
|
36
|
+
handler(data);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
isCardData(data) {
|
|
44
|
+
return this.mode === 'credit_card';
|
|
45
|
+
}
|
|
46
|
+
isBankAccountData(data) {
|
|
47
|
+
return this.mode === 'bank_account';
|
|
48
|
+
}
|
|
49
|
+
normalizeCardType(providerCardType) {
|
|
50
|
+
const normalized = providerCardType.toLowerCase().replace(/[\s-_]/g, '');
|
|
51
|
+
const mappings = {
|
|
52
|
+
visa: 'visa',
|
|
53
|
+
mastercard: 'mastercard',
|
|
54
|
+
master: 'mastercard',
|
|
55
|
+
americanexpress: 'amex',
|
|
56
|
+
amex: 'amex',
|
|
57
|
+
discover: 'discover',
|
|
58
|
+
dinersclub: 'diners',
|
|
59
|
+
diners: 'diners',
|
|
60
|
+
jcb: 'jcb',
|
|
61
|
+
};
|
|
62
|
+
return mappings[normalized] || 'unknown';
|
|
63
|
+
}
|
|
64
|
+
addExpiryFormatter(expiryEl) {
|
|
65
|
+
if (!expiryEl)
|
|
66
|
+
return;
|
|
67
|
+
expiryEl.addEventListener('input', (e) => {
|
|
68
|
+
const target = e.target;
|
|
69
|
+
target.value = formatExpiryInput(target.value);
|
|
70
|
+
this.emit('change', { field: 'expiry' });
|
|
71
|
+
});
|
|
72
|
+
expiryEl.addEventListener('keypress', (e) => {
|
|
73
|
+
const char = String.fromCharCode(e.which);
|
|
74
|
+
if (!/[0-9\/]/.test(char) && e.which !== 8) {
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
parseExpiry(expiryEl) {
|
|
80
|
+
if (!(expiryEl === null || expiryEl === void 0 ? void 0 : expiryEl.value))
|
|
81
|
+
return null;
|
|
82
|
+
return parseExpiryDate(expiryEl.value);
|
|
83
|
+
}
|
|
84
|
+
applyInputStyles(element, styles, position) {
|
|
85
|
+
const inputStyles = styles.input;
|
|
86
|
+
const isConnected = styles.container.gap === '0';
|
|
87
|
+
Object.assign(element.style, {
|
|
88
|
+
height: inputStyles.height,
|
|
89
|
+
padding: inputStyles.padding,
|
|
90
|
+
fontSize: inputStyles.fontSize,
|
|
91
|
+
fontFamily: inputStyles.fontFamily,
|
|
92
|
+
backgroundColor: inputStyles.backgroundColor,
|
|
93
|
+
color: inputStyles.color,
|
|
94
|
+
boxSizing: 'border-box',
|
|
95
|
+
width: '100%',
|
|
96
|
+
transition: inputStyles.transition,
|
|
97
|
+
});
|
|
98
|
+
if (isConnected && position) {
|
|
99
|
+
element.style.borderTop = inputStyles.border;
|
|
100
|
+
element.style.borderBottom = inputStyles.border;
|
|
101
|
+
switch (position) {
|
|
102
|
+
case 'left':
|
|
103
|
+
element.style.borderLeft = inputStyles.border;
|
|
104
|
+
element.style.borderRight = 'none';
|
|
105
|
+
break;
|
|
106
|
+
case 'middle':
|
|
107
|
+
element.style.borderLeft = inputStyles.border;
|
|
108
|
+
element.style.borderRight = 'none';
|
|
109
|
+
break;
|
|
110
|
+
case 'right':
|
|
111
|
+
element.style.borderLeft = inputStyles.border;
|
|
112
|
+
element.style.borderRight = inputStyles.border;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
element.style.borderRadius = getConnectedBorderRadius(inputStyles.borderRadius, position, true);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
element.style.border = inputStyles.border;
|
|
119
|
+
element.style.borderRadius = inputStyles.borderRadius;
|
|
120
|
+
}
|
|
121
|
+
addFocusHandlers(element, styles);
|
|
122
|
+
}
|
|
123
|
+
generateFieldIds(containerId) {
|
|
124
|
+
return {
|
|
125
|
+
numberId: `${containerId}-card-number`,
|
|
126
|
+
expiryId: `${containerId}-expiry`,
|
|
127
|
+
cvvId: `${containerId}-cvv`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
static create(gateway, container, config) {
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
switch (gateway.backend_name) {
|
|
133
|
+
case 'spreedly': {
|
|
134
|
+
const { SpreedlyTokenizer } = yield import('./SpreedlyTokenizer');
|
|
135
|
+
return SpreedlyTokenizer.create(gateway, container, config);
|
|
136
|
+
}
|
|
137
|
+
case 'card_connect': {
|
|
138
|
+
const { CardConnectTokenizer } = yield import('./CardConnectTokenizer');
|
|
139
|
+
return CardConnectTokenizer.create(gateway, container, config);
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
throw new Error(`Unsupported payment backend: ${gateway.backend_name}`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function tokenizePaymentBankCanada(bankNumber: string, transitNumber: string, accountNumber: string, accountType: string, processId: string, relayUrl: string, gatewayId: string, contact: any, address: any): Promise<string>;
|
|
2
|
+
export declare function tokenizePaymentBankUs(): void;
|
|
3
|
+
export declare function tokenizePaymentCard(): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
export function tokenizePaymentBankCanada(bankNumber, transitNumber, accountNumber, accountType, processId, relayUrl, gatewayId, contact, address) {
|
|
3
|
+
const newPaymentId = uuidv4();
|
|
4
|
+
let streetAddress = address.address1;
|
|
5
|
+
if (address.address2) {
|
|
6
|
+
streetAddress += '\n' + address.address2;
|
|
7
|
+
}
|
|
8
|
+
const payload = {
|
|
9
|
+
IATS_DPM_ProcessOption: 'TOKEN',
|
|
10
|
+
IATS_DPM_RecurringOn: 'FALSE',
|
|
11
|
+
IATS_DPM_ProcessID: processId,
|
|
12
|
+
IATS_DPM_RelayURL: relayUrl,
|
|
13
|
+
IATS_DPM_ClientDefined_PaymentId: newPaymentId,
|
|
14
|
+
IATS_DPM_ClientDefined_GatewayId: gatewayId,
|
|
15
|
+
IATS_DPM_Title: contact.salutation,
|
|
16
|
+
IATS_DPM_FirstName: contact.firstName,
|
|
17
|
+
IATS_DPM_LastName: contact.lastName,
|
|
18
|
+
IATS_DPM_Address: streetAddress,
|
|
19
|
+
IATS_DPM_City: address.city,
|
|
20
|
+
IATS_DPM_Province: address.state,
|
|
21
|
+
IATS_DPM_Country: address.country,
|
|
22
|
+
IATS_DPM_ZipCode: address.zip,
|
|
23
|
+
IATS_DPM_Phone: contact.primaryPhone,
|
|
24
|
+
IATS_DPM_EMAIL: contact.email,
|
|
25
|
+
IATS_DPM_AccountNumber: bankNumber + transitNumber + accountNumber,
|
|
26
|
+
IATS_DPM_MOP: 'ACHEFT',
|
|
27
|
+
IATS_DPM_AccountType: accountType,
|
|
28
|
+
};
|
|
29
|
+
const formData = new FormData();
|
|
30
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
31
|
+
formData.append(key, value);
|
|
32
|
+
}
|
|
33
|
+
return fetch('https://www.iatspayments.com/netgate/IATSDPMProcess.aspx', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
mode: 'no-cors',
|
|
36
|
+
body: formData,
|
|
37
|
+
}).then((response) => {
|
|
38
|
+
return response.text().then(() => {
|
|
39
|
+
return newPaymentId;
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export function tokenizePaymentBankUs() {
|
|
44
|
+
throw new Error('US Bank Tokenization Not Yet Implemented');
|
|
45
|
+
}
|
|
46
|
+
export function tokenizePaymentCard() {
|
|
47
|
+
throw new Error('Card Tokenization Not Yet Implemented');
|
|
48
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as iats from './iats';
|
|
2
|
+
export { Tokenizer } from './Tokenizer';
|
|
3
|
+
export { SpreedlyTokenizer } from './SpreedlyTokenizer';
|
|
4
|
+
export { CardConnectTokenizer } from './CardConnectTokenizer';
|
|
5
|
+
export { PaymentMethodType, PaymentGateway, TokenizerContainer, TokenizerStyling, CardData, PaymentData, PaymentToken, CardType, TokenizerEvent, ValidationState, ValidationResult, ValidationError, TokenizationError, } from './types';
|
|
6
|
+
export { iats };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import ConfigHandler from '../config-handler';
|
|
2
|
+
export interface SpreedlySecurityArgs {
|
|
3
|
+
nonce: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
certificate_token: string;
|
|
6
|
+
signature: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function fetchSpreedlySecurityArgs(config: ConfigHandler, organizationId: string, embedId: string): Promise<SpreedlySecurityArgs>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export function fetchSpreedlySecurityArgs(config, organizationId, embedId) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
if (!config.enableSpreedlySecureTokenization) {
|
|
13
|
+
throw new Error('Secure tokenization is not enabled');
|
|
14
|
+
}
|
|
15
|
+
const makeRequest = () => __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
const response = yield fetch(`${config.embedApiBaseUrl}/spreedly/security-args`, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
organization_id: organizationId,
|
|
21
|
+
embed_id: embedId,
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Security args request failed: ${response.status}`);
|
|
26
|
+
}
|
|
27
|
+
return response.json();
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
return yield makeRequest();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
34
|
+
yield new Promise((resolve) => setTimeout(resolve, 1000));
|
|
35
|
+
return makeRequest();
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TokenizerStyling, TokenizerStylingComplete } from './types';
|
|
2
|
+
export declare const DEFAULT_UNIFIED_STYLES: TokenizerStylingComplete;
|
|
3
|
+
export declare function mergeStyles(defaults: TokenizerStylingComplete, userStyles?: TokenizerStyling): TokenizerStylingComplete;
|
|
4
|
+
export declare function getContainerStylesForLayout(baseStyles: TokenizerStylingComplete, layout?: 'single-line' | 'two-line'): TokenizerStylingComplete;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const DEFAULT_UNIFIED_STYLES = {
|
|
2
|
+
input: {
|
|
3
|
+
height: '40px',
|
|
4
|
+
padding: '10px 12px',
|
|
5
|
+
fontSize: '14px',
|
|
6
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
7
|
+
color: '#333',
|
|
8
|
+
backgroundColor: 'white',
|
|
9
|
+
border: '1px solid #ccc',
|
|
10
|
+
borderRadius: '4px',
|
|
11
|
+
boxSizing: 'border-box',
|
|
12
|
+
width: '100%',
|
|
13
|
+
transition: 'all 0.15s ease-in-out',
|
|
14
|
+
},
|
|
15
|
+
focus: {
|
|
16
|
+
borderColor: '#007bff',
|
|
17
|
+
outline: 'none',
|
|
18
|
+
boxShadow: '0 0 0 2px rgba(0, 123, 255, 0.25)',
|
|
19
|
+
},
|
|
20
|
+
error: {
|
|
21
|
+
borderColor: '#dc3545',
|
|
22
|
+
},
|
|
23
|
+
container: {
|
|
24
|
+
display: 'flex',
|
|
25
|
+
gap: '1rem',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
flexWrap: 'nowrap',
|
|
28
|
+
rowGap: '1rem',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export function mergeStyles(defaults, userStyles) {
|
|
32
|
+
if (!userStyles)
|
|
33
|
+
return defaults;
|
|
34
|
+
return {
|
|
35
|
+
input: Object.assign(Object.assign({}, defaults.input), userStyles.input),
|
|
36
|
+
focus: Object.assign(Object.assign({}, defaults.focus), userStyles.focus),
|
|
37
|
+
error: Object.assign(Object.assign({}, defaults.error), userStyles.error),
|
|
38
|
+
container: Object.assign(Object.assign({}, defaults.container), userStyles.container),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getContainerStylesForLayout(baseStyles, layout = 'single-line') {
|
|
42
|
+
if (layout === 'two-line') {
|
|
43
|
+
return Object.assign(Object.assign({}, baseStyles), { container: Object.assign(Object.assign({}, baseStyles.container), { flexWrap: 'wrap', rowGap: baseStyles.container.rowGap || baseStyles.container.gap }) });
|
|
44
|
+
}
|
|
45
|
+
return baseStyles;
|
|
46
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export declare const INIT_TIMEOUT = 10000;
|
|
2
|
+
export declare const TOKENIZE_TIMEOUT = 30000;
|
|
3
|
+
export declare const FIELD_FLEX: {
|
|
4
|
+
readonly cardNumber: {
|
|
5
|
+
readonly flex: "1 1 60%";
|
|
6
|
+
readonly minWidth: "150px";
|
|
7
|
+
};
|
|
8
|
+
readonly expiry: {
|
|
9
|
+
readonly flex: "0 1 20%";
|
|
10
|
+
readonly minWidth: "70px";
|
|
11
|
+
};
|
|
12
|
+
readonly cvv: {
|
|
13
|
+
readonly flex: "0 1 20%";
|
|
14
|
+
readonly minWidth: "60px";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare const BANK_FIELD_FLEX: {
|
|
18
|
+
readonly routingNumber: {
|
|
19
|
+
readonly flex: "0 1 30%";
|
|
20
|
+
readonly minWidth: "100px";
|
|
21
|
+
};
|
|
22
|
+
readonly accountNumber: {
|
|
23
|
+
readonly flex: "1 1 45%";
|
|
24
|
+
readonly minWidth: "120px";
|
|
25
|
+
};
|
|
26
|
+
readonly accountType: {
|
|
27
|
+
readonly flex: "0 1 25%";
|
|
28
|
+
readonly minWidth: "90px";
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare const CANADIAN_BANK_FIELD_FLEX: {
|
|
32
|
+
readonly institutionNumber: {
|
|
33
|
+
readonly flex: "0 1 15%";
|
|
34
|
+
readonly minWidth: "60px";
|
|
35
|
+
};
|
|
36
|
+
readonly transitNumber: {
|
|
37
|
+
readonly flex: "0 1 20%";
|
|
38
|
+
readonly minWidth: "80px";
|
|
39
|
+
};
|
|
40
|
+
readonly accountNumber: {
|
|
41
|
+
readonly flex: "1 1 40%";
|
|
42
|
+
readonly minWidth: "110px";
|
|
43
|
+
};
|
|
44
|
+
readonly accountType: {
|
|
45
|
+
readonly flex: "0 1 25%";
|
|
46
|
+
readonly minWidth: "90px";
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export declare const PLACEHOLDERS: {
|
|
50
|
+
readonly cardNumber: "Card Number *";
|
|
51
|
+
readonly expiry: "MM/YY *";
|
|
52
|
+
readonly cvv: "CVV *";
|
|
53
|
+
};
|
|
54
|
+
export declare const FIELD_CONSTRAINTS: {
|
|
55
|
+
readonly expiry: {
|
|
56
|
+
readonly maxLength: 5;
|
|
57
|
+
readonly pattern: RegExp;
|
|
58
|
+
};
|
|
59
|
+
readonly cvv: {
|
|
60
|
+
readonly maxLength: 4;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const INIT_TIMEOUT = 10000;
|
|
2
|
+
export const TOKENIZE_TIMEOUT = 30000;
|
|
3
|
+
export const FIELD_FLEX = {
|
|
4
|
+
cardNumber: {
|
|
5
|
+
flex: '1 1 60%',
|
|
6
|
+
minWidth: '150px',
|
|
7
|
+
},
|
|
8
|
+
expiry: {
|
|
9
|
+
flex: '0 1 20%',
|
|
10
|
+
minWidth: '70px',
|
|
11
|
+
},
|
|
12
|
+
cvv: {
|
|
13
|
+
flex: '0 1 20%',
|
|
14
|
+
minWidth: '60px',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export const BANK_FIELD_FLEX = {
|
|
18
|
+
routingNumber: {
|
|
19
|
+
flex: '0 1 30%',
|
|
20
|
+
minWidth: '100px',
|
|
21
|
+
},
|
|
22
|
+
accountNumber: {
|
|
23
|
+
flex: '1 1 45%',
|
|
24
|
+
minWidth: '120px',
|
|
25
|
+
},
|
|
26
|
+
accountType: {
|
|
27
|
+
flex: '0 1 25%',
|
|
28
|
+
minWidth: '90px',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export const CANADIAN_BANK_FIELD_FLEX = {
|
|
32
|
+
institutionNumber: {
|
|
33
|
+
flex: '0 1 15%',
|
|
34
|
+
minWidth: '60px',
|
|
35
|
+
},
|
|
36
|
+
transitNumber: {
|
|
37
|
+
flex: '0 1 20%',
|
|
38
|
+
minWidth: '80px',
|
|
39
|
+
},
|
|
40
|
+
accountNumber: {
|
|
41
|
+
flex: '1 1 40%',
|
|
42
|
+
minWidth: '110px',
|
|
43
|
+
},
|
|
44
|
+
accountType: {
|
|
45
|
+
flex: '0 1 25%',
|
|
46
|
+
minWidth: '90px',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export const PLACEHOLDERS = {
|
|
50
|
+
cardNumber: 'Card Number *',
|
|
51
|
+
expiry: 'MM/YY *',
|
|
52
|
+
cvv: 'CVV *',
|
|
53
|
+
};
|
|
54
|
+
export const FIELD_CONSTRAINTS = {
|
|
55
|
+
expiry: {
|
|
56
|
+
maxLength: 5,
|
|
57
|
+
pattern: /^\d{0,2}\/?\d{0,2}$/,
|
|
58
|
+
},
|
|
59
|
+
cvv: {
|
|
60
|
+
maxLength: 4,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TokenizerStylingComplete } from './types';
|
|
2
|
+
export declare function parseExpiryDate(value: string): {
|
|
3
|
+
month: string;
|
|
4
|
+
year: string;
|
|
5
|
+
} | null;
|
|
6
|
+
export declare function formatExpiryInput(value: string): string;
|
|
7
|
+
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T>;
|
|
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;
|
|
10
|
+
export declare function createFieldContainer(id: string, flex: string, minWidth?: string, maxWidth?: string): HTMLDivElement;
|
|
11
|
+
export declare function createInputElement(id: string, type: string, placeholder: string, maxLength?: number): HTMLInputElement;
|
|
12
|
+
export declare function validateRoutingNumber(routingNumber: string): boolean;
|
|
13
|
+
export declare function validateAccountNumber(accountNumber: string): boolean;
|
|
14
|
+
export declare function maskAccountNumber(accountNumber: string): string;
|
|
15
|
+
export declare function createAccountTypeSelect(id: string): HTMLSelectElement;
|
|
16
|
+
export declare function validateInstitutionNumber(institutionNumber: string): boolean;
|
|
17
|
+
export declare function validateTransitNumber(transitNumber: string): boolean;
|
|
18
|
+
export declare function validateCanadianAccountNumber(accountNumber: string): boolean;
|
|
19
|
+
export declare function formatCanadianRoutingNumber(institutionNumber: string, transitNumber: string): string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export function parseExpiryDate(value) {
|
|
2
|
+
const parts = value.split('/');
|
|
3
|
+
if (parts.length !== 2)
|
|
4
|
+
return null;
|
|
5
|
+
const month = parts[0];
|
|
6
|
+
const year = parts[1];
|
|
7
|
+
if (month.length !== 2 || year.length !== 2)
|
|
8
|
+
return null;
|
|
9
|
+
return {
|
|
10
|
+
month,
|
|
11
|
+
year: '20' + year,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function formatExpiryInput(value) {
|
|
15
|
+
let digits = value.replace(/\D/g, '');
|
|
16
|
+
digits = digits.substring(0, 4);
|
|
17
|
+
if (digits.length >= 2) {
|
|
18
|
+
return digits.substring(0, 2) + '/' + digits.substring(2, 4);
|
|
19
|
+
}
|
|
20
|
+
return digits;
|
|
21
|
+
}
|
|
22
|
+
export function withTimeout(promise, timeoutMs, errorMessage) {
|
|
23
|
+
return Promise.race([
|
|
24
|
+
promise,
|
|
25
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs)),
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
export function getConnectedBorderRadius(baseRadius, position, isConnected) {
|
|
29
|
+
if (!isConnected)
|
|
30
|
+
return baseRadius;
|
|
31
|
+
switch (position) {
|
|
32
|
+
case 'left':
|
|
33
|
+
return `${baseRadius} 0 0 ${baseRadius}`;
|
|
34
|
+
case 'middle':
|
|
35
|
+
return '0';
|
|
36
|
+
case 'right':
|
|
37
|
+
return `0 ${baseRadius} ${baseRadius} 0`;
|
|
38
|
+
default:
|
|
39
|
+
return baseRadius;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function addFocusHandlers(element, styles, onFocus, onBlur) {
|
|
43
|
+
element.addEventListener('focus', () => {
|
|
44
|
+
if (styles.focus) {
|
|
45
|
+
Object.assign(element.style, styles.focus);
|
|
46
|
+
}
|
|
47
|
+
onFocus === null || onFocus === void 0 ? void 0 : onFocus();
|
|
48
|
+
});
|
|
49
|
+
element.addEventListener('blur', () => {
|
|
50
|
+
element.style.borderColor = '';
|
|
51
|
+
element.style.outline = 'none';
|
|
52
|
+
element.style.boxShadow = 'none';
|
|
53
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function createFieldContainer(id, flex, minWidth, maxWidth) {
|
|
57
|
+
const div = document.createElement('div');
|
|
58
|
+
div.id = id;
|
|
59
|
+
div.style.flex = flex;
|
|
60
|
+
div.style.display = 'flex';
|
|
61
|
+
if (minWidth)
|
|
62
|
+
div.style.minWidth = minWidth;
|
|
63
|
+
if (maxWidth)
|
|
64
|
+
div.style.maxWidth = maxWidth;
|
|
65
|
+
return div;
|
|
66
|
+
}
|
|
67
|
+
export function createInputElement(id, type, placeholder, maxLength) {
|
|
68
|
+
const input = document.createElement('input');
|
|
69
|
+
input.id = id;
|
|
70
|
+
input.type = type;
|
|
71
|
+
input.placeholder = placeholder;
|
|
72
|
+
if (maxLength)
|
|
73
|
+
input.maxLength = maxLength;
|
|
74
|
+
return input;
|
|
75
|
+
}
|
|
76
|
+
export function validateRoutingNumber(routingNumber) {
|
|
77
|
+
const cleaned = routingNumber.replace(/\D/g, '');
|
|
78
|
+
if (cleaned.length !== 9) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const digits = cleaned.split('').map(Number);
|
|
82
|
+
const checksum = (digits[0] * 3 +
|
|
83
|
+
digits[1] * 7 +
|
|
84
|
+
digits[2] * 1 +
|
|
85
|
+
digits[3] * 3 +
|
|
86
|
+
digits[4] * 7 +
|
|
87
|
+
digits[5] * 1 +
|
|
88
|
+
digits[6] * 3 +
|
|
89
|
+
digits[7] * 7 +
|
|
90
|
+
digits[8] * 1) %
|
|
91
|
+
10;
|
|
92
|
+
return checksum === 0;
|
|
93
|
+
}
|
|
94
|
+
export function validateAccountNumber(accountNumber) {
|
|
95
|
+
const cleaned = accountNumber.replace(/\D/g, '');
|
|
96
|
+
if (cleaned.length < 4 || cleaned.length > 17) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return /^\d+$/.test(cleaned);
|
|
100
|
+
}
|
|
101
|
+
export function maskAccountNumber(accountNumber) {
|
|
102
|
+
const cleaned = accountNumber.replace(/\D/g, '');
|
|
103
|
+
if (cleaned.length <= 4) {
|
|
104
|
+
return cleaned;
|
|
105
|
+
}
|
|
106
|
+
const lastFour = cleaned.slice(-4);
|
|
107
|
+
const masked = '*'.repeat(cleaned.length - 4) + lastFour;
|
|
108
|
+
return masked;
|
|
109
|
+
}
|
|
110
|
+
export function createAccountTypeSelect(id) {
|
|
111
|
+
const select = document.createElement('select');
|
|
112
|
+
select.id = id;
|
|
113
|
+
const checkingOption = document.createElement('option');
|
|
114
|
+
checkingOption.value = 'checking';
|
|
115
|
+
checkingOption.text = 'Checking';
|
|
116
|
+
const savingsOption = document.createElement('option');
|
|
117
|
+
savingsOption.value = 'savings';
|
|
118
|
+
savingsOption.text = 'Savings';
|
|
119
|
+
select.appendChild(checkingOption);
|
|
120
|
+
select.appendChild(savingsOption);
|
|
121
|
+
return select;
|
|
122
|
+
}
|
|
123
|
+
export function validateInstitutionNumber(institutionNumber) {
|
|
124
|
+
const cleaned = institutionNumber.replace(/\D/g, '');
|
|
125
|
+
return cleaned.length === 3;
|
|
126
|
+
}
|
|
127
|
+
export function validateTransitNumber(transitNumber) {
|
|
128
|
+
const cleaned = transitNumber.replace(/\D/g, '');
|
|
129
|
+
return cleaned.length === 5;
|
|
130
|
+
}
|
|
131
|
+
export function validateCanadianAccountNumber(accountNumber) {
|
|
132
|
+
const cleaned = accountNumber.replace(/\D/g, '');
|
|
133
|
+
return cleaned.length >= 7;
|
|
134
|
+
}
|
|
135
|
+
export function formatCanadianRoutingNumber(institutionNumber, transitNumber) {
|
|
136
|
+
const institution = institutionNumber.replace(/\D/g, '').padStart(3, '0');
|
|
137
|
+
const transit = transitNumber.replace(/\D/g, '').padStart(5, '0');
|
|
138
|
+
return '0' + institution + transit;
|
|
139
|
+
}
|