@voxepay/checkout 0.0.1-security → 0.5.19
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.
Potentially problematic release.
This version of @voxepay/checkout might be problematic. Click here for more details.
- package/README.md +140 -3
- package/dist/api/client.d.ts +146 -0
- package/dist/components/modal.d.ts +190 -0
- package/dist/index.cjs.js +2272 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.esm.js +2253 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/types.d.ts +170 -0
- package/dist/utils/card-validator.d.ts +47 -0
- package/dist/utils/encryption.d.ts +28 -0
- package/dist/utils/formatter.d.ts +31 -0
- package/dist/voxepay-checkout.min.js +2 -0
- package/dist/voxepay-checkout.min.js.map +1 -0
- package/dist/voxepay.d.ts +63 -0
- package/package.json +41 -3
- package/src/api/client.ts +301 -0
- package/src/components/modal.ts +1783 -0
- package/src/index.ts +76 -0
- package/src/types.ts +178 -0
- package/src/utils/card-validator.ts +198 -0
- package/src/utils/encryption.ts +182 -0
- package/src/utils/formatter.ts +91 -0
- package/src/voxepay.ts +200 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input formatting utilities for VoxePay Checkout
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format card number with spaces every 4 digits
|
|
7
|
+
*/
|
|
8
|
+
export function formatCardNumber(value: string): string {
|
|
9
|
+
const cleaned = value.replace(/\D/g, '');
|
|
10
|
+
const groups = cleaned.match(/.{1,4}/g) || [];
|
|
11
|
+
return groups.join(' ').slice(0, 23); // Max: 19 digits + 4 spaces
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Format expiry date as MM/YY
|
|
16
|
+
*/
|
|
17
|
+
export function formatExpiry(value: string): string {
|
|
18
|
+
const cleaned = value.replace(/\D/g, '');
|
|
19
|
+
|
|
20
|
+
if (cleaned.length === 0) return '';
|
|
21
|
+
if (cleaned.length === 1) {
|
|
22
|
+
return parseInt(cleaned) > 1 ? `0${cleaned}` : cleaned;
|
|
23
|
+
}
|
|
24
|
+
if (cleaned.length === 2) {
|
|
25
|
+
const month = parseInt(cleaned);
|
|
26
|
+
if (month > 12) return '12';
|
|
27
|
+
if (month === 0) return '01';
|
|
28
|
+
return cleaned;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const month = cleaned.slice(0, 2);
|
|
32
|
+
const year = cleaned.slice(2, 4);
|
|
33
|
+
return `${month}/${year}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format CVV (numbers only, max 4 digits)
|
|
38
|
+
*/
|
|
39
|
+
export function formatCVV(value: string): string {
|
|
40
|
+
return value.replace(/\D/g, '').slice(0, 4);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Format currency amount
|
|
45
|
+
*/
|
|
46
|
+
export function formatAmount(amount: number, currency: string): string {
|
|
47
|
+
const formatter = new Intl.NumberFormat('en-US', {
|
|
48
|
+
style: 'currency',
|
|
49
|
+
currency: currency,
|
|
50
|
+
minimumFractionDigits: 2,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Convert from smallest unit (cents/kobo) to main unit
|
|
54
|
+
return formatter.format(amount / 100);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Format currency amount when already in main unit
|
|
59
|
+
*/
|
|
60
|
+
export function formatMainUnitAmount(amount: number, currency: string): string {
|
|
61
|
+
const formatter = new Intl.NumberFormat('en-US', {
|
|
62
|
+
style: 'currency',
|
|
63
|
+
currency: currency,
|
|
64
|
+
minimumFractionDigits: 2,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return formatter.format(amount);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get currency symbol
|
|
72
|
+
*/
|
|
73
|
+
export function getCurrencySymbol(currency: string): string {
|
|
74
|
+
const symbols: Record<string, string> = {
|
|
75
|
+
NGN: '₦',
|
|
76
|
+
USD: '$',
|
|
77
|
+
EUR: '€',
|
|
78
|
+
GBP: '£',
|
|
79
|
+
GHS: '₵',
|
|
80
|
+
KES: 'KSh',
|
|
81
|
+
ZAR: 'R',
|
|
82
|
+
};
|
|
83
|
+
return symbols[currency.toUpperCase()] || currency;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Format card PIN (numbers only, max 4 digits)
|
|
88
|
+
*/
|
|
89
|
+
export function formatPIN(value: string): string {
|
|
90
|
+
return value.replace(/\D/g, '').slice(0, 4);
|
|
91
|
+
}
|
package/src/voxepay.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoxePay - Modern Payment Checkout SDK
|
|
3
|
+
*
|
|
4
|
+
* A beautiful, modern payment modal for the web.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```javascript
|
|
8
|
+
* // Initialize VoxePay
|
|
9
|
+
* VoxePay.init({ apiKey: 'pk_live_xxxxx' });
|
|
10
|
+
*
|
|
11
|
+
* // Open checkout
|
|
12
|
+
* VoxePay.checkout({
|
|
13
|
+
* amount: 4999,
|
|
14
|
+
* currency: 'NGN',
|
|
15
|
+
* description: 'Premium Plan',
|
|
16
|
+
* onSuccess: (result) => console.log('Paid!', result),
|
|
17
|
+
* onError: (error) => console.error('Failed', error),
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { VoxePayModal } from './components/modal';
|
|
23
|
+
import type { VoxePayConfig, CheckoutOptions, VoxePayTheme } from './types';
|
|
24
|
+
|
|
25
|
+
class VoxePaySDK {
|
|
26
|
+
private config: VoxePayConfig | null = null;
|
|
27
|
+
private currentModal: VoxePayModal | null = null;
|
|
28
|
+
private initialized = false;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize VoxePay with your configuration
|
|
32
|
+
* @param config - Configuration object with required organizationId and optional settings
|
|
33
|
+
*/
|
|
34
|
+
init(config: VoxePayConfig): void {
|
|
35
|
+
if (!config.organizationId) {
|
|
36
|
+
console.error('[VoxePay] Organization ID is required. Find it in your VoxePay dashboard.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.config = {
|
|
41
|
+
theme: 'dark',
|
|
42
|
+
locale: 'en-US',
|
|
43
|
+
...config,
|
|
44
|
+
};
|
|
45
|
+
this.initialized = true;
|
|
46
|
+
|
|
47
|
+
// Apply theme
|
|
48
|
+
if (config.theme === 'auto') {
|
|
49
|
+
this.applyAutoTheme();
|
|
50
|
+
} else if (config.theme === 'light') {
|
|
51
|
+
document.documentElement.classList.add('voxepay-light');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Apply custom styles
|
|
55
|
+
if (config.customStyles) {
|
|
56
|
+
this.applyCustomStyles(config.customStyles);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('[VoxePay] Initialized successfully');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Open the checkout modal
|
|
64
|
+
* @param options - Checkout options including amount, currency, and callbacks
|
|
65
|
+
*/
|
|
66
|
+
checkout(options: CheckoutOptions): void {
|
|
67
|
+
if (!this.initialized) {
|
|
68
|
+
console.error('[VoxePay] Not initialized. Call VoxePay.init() first.');
|
|
69
|
+
options.onError({
|
|
70
|
+
code: 'NOT_INITIALIZED',
|
|
71
|
+
message: 'VoxePay SDK not initialized. Call VoxePay.init() first.',
|
|
72
|
+
recoverable: false,
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!options.amount || options.amount <= 0) {
|
|
78
|
+
console.error('[VoxePay] Invalid amount');
|
|
79
|
+
options.onError({
|
|
80
|
+
code: 'INVALID_AMOUNT',
|
|
81
|
+
message: 'Payment amount must be greater than 0',
|
|
82
|
+
recoverable: false,
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!options.currency) {
|
|
88
|
+
console.error('[VoxePay] Currency is required');
|
|
89
|
+
options.onError({
|
|
90
|
+
code: 'INVALID_CURRENCY',
|
|
91
|
+
message: 'Currency code is required',
|
|
92
|
+
recoverable: false,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Close any existing modal
|
|
98
|
+
this.closeModal();
|
|
99
|
+
|
|
100
|
+
// Create and open new modal
|
|
101
|
+
this.currentModal = new VoxePayModal({
|
|
102
|
+
...options,
|
|
103
|
+
_sdkConfig: {
|
|
104
|
+
apiKey: this.config!.apiKey,
|
|
105
|
+
organizationId: this.config!.organizationId,
|
|
106
|
+
baseUrl: this.config!.baseUrl,
|
|
107
|
+
paymentLinkSlug: this.config!.paymentLinkSlug,
|
|
108
|
+
simulateWebhook: this.config!.simulateWebhook,
|
|
109
|
+
bypassKey: this.config!.bypassKey,
|
|
110
|
+
},
|
|
111
|
+
onClose: () => {
|
|
112
|
+
this.currentModal = null;
|
|
113
|
+
options.onClose?.();
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.currentModal.open();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Close the current checkout modal
|
|
122
|
+
*/
|
|
123
|
+
closeModal(): void {
|
|
124
|
+
if (this.currentModal) {
|
|
125
|
+
this.currentModal.close();
|
|
126
|
+
this.currentModal = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Set the theme
|
|
132
|
+
* @param theme - 'dark', 'light', or 'auto'
|
|
133
|
+
*/
|
|
134
|
+
setTheme(theme: 'dark' | 'light' | 'auto'): void {
|
|
135
|
+
document.documentElement.classList.remove('voxepay-light');
|
|
136
|
+
|
|
137
|
+
if (theme === 'light') {
|
|
138
|
+
document.documentElement.classList.add('voxepay-light');
|
|
139
|
+
} else if (theme === 'auto') {
|
|
140
|
+
this.applyAutoTheme();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this.config) {
|
|
144
|
+
this.config.theme = theme;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Apply system theme preference
|
|
150
|
+
*/
|
|
151
|
+
private applyAutoTheme(): void {
|
|
152
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
153
|
+
if (!prefersDark) {
|
|
154
|
+
document.documentElement.classList.add('voxepay-light');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
158
|
+
if (this.config?.theme === 'auto') {
|
|
159
|
+
document.documentElement.classList.toggle('voxepay-light', !e.matches);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Apply custom CSS variables
|
|
166
|
+
*/
|
|
167
|
+
private applyCustomStyles(styles: Partial<VoxePayTheme>): void {
|
|
168
|
+
const root = document.documentElement;
|
|
169
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
170
|
+
if (value) {
|
|
171
|
+
root.style.setProperty(key, value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the SDK version
|
|
178
|
+
*/
|
|
179
|
+
get version(): string {
|
|
180
|
+
return '0.5.7';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check if SDK is initialized
|
|
185
|
+
*/
|
|
186
|
+
get isInitialized(): boolean {
|
|
187
|
+
return this.initialized;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Create singleton instance
|
|
192
|
+
const VoxePay = new VoxePaySDK();
|
|
193
|
+
|
|
194
|
+
// Export for different module systems
|
|
195
|
+
export { VoxePay, VoxePaySDK };
|
|
196
|
+
|
|
197
|
+
// Make available on window for script tag usage
|
|
198
|
+
if (typeof window !== 'undefined') {
|
|
199
|
+
(window as unknown as { VoxePay: VoxePaySDK }).VoxePay = VoxePay;
|
|
200
|
+
}
|