@reevit/core 0.4.0 → 0.4.6
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/index.d.mts +71 -7
- package/dist/index.d.ts +71 -7
- package/dist/index.js +59 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +59 -6
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +6 -5
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -8,8 +8,8 @@ type PSPType = 'paystack' | 'hubtel' | 'flutterwave' | 'stripe' | 'monnify' | 'm
|
|
|
8
8
|
/** Payment source type - indicates where the payment originated from */
|
|
9
9
|
type PaymentSource = 'payment_link' | 'api' | 'subscription';
|
|
10
10
|
interface ReevitCheckoutConfig {
|
|
11
|
-
/** Your Reevit public key (
|
|
12
|
-
publicKey
|
|
11
|
+
/** Your Reevit public key (required for API-created intents; omit for payment links) */
|
|
12
|
+
publicKey?: string;
|
|
13
13
|
/** Amount in the smallest currency unit (e.g., pesewas for GHS) */
|
|
14
14
|
amount: number;
|
|
15
15
|
/** Currency code (e.g., 'GHS', 'NGN', 'USD') */
|
|
@@ -18,10 +18,16 @@ interface ReevitCheckoutConfig {
|
|
|
18
18
|
email?: string;
|
|
19
19
|
/** Customer phone number (required for mobile money) */
|
|
20
20
|
phone?: string;
|
|
21
|
+
/** Customer name (optional, used for payment links) */
|
|
22
|
+
customerName?: string;
|
|
21
23
|
/** Unique reference for this transaction */
|
|
22
24
|
reference?: string;
|
|
23
25
|
/** Additional metadata to attach to the payment */
|
|
24
26
|
metadata?: Record<string, unknown>;
|
|
27
|
+
/** Custom fields for payment links (if applicable) */
|
|
28
|
+
customFields?: Record<string, unknown>;
|
|
29
|
+
/** Payment link code (for public checkout flows) */
|
|
30
|
+
paymentLinkCode?: string;
|
|
25
31
|
/** Payment methods to display */
|
|
26
32
|
paymentMethods?: PaymentMethod[];
|
|
27
33
|
/** Pre-created payment intent to use */
|
|
@@ -79,16 +85,32 @@ interface PaymentError {
|
|
|
79
85
|
interface ReevitTheme {
|
|
80
86
|
/** Primary brand color */
|
|
81
87
|
primaryColor?: string;
|
|
88
|
+
/** Primary text color on brand surfaces */
|
|
89
|
+
primaryForegroundColor?: string;
|
|
82
90
|
/** Background color */
|
|
83
91
|
backgroundColor?: string;
|
|
92
|
+
/** Surface color for cards/panels */
|
|
93
|
+
surfaceColor?: string;
|
|
84
94
|
/** Text color */
|
|
85
95
|
textColor?: string;
|
|
96
|
+
/** Muted text color */
|
|
97
|
+
mutedTextColor?: string;
|
|
86
98
|
/** Border radius for inputs and buttons */
|
|
87
99
|
borderRadius?: string;
|
|
88
100
|
/** Font family to use */
|
|
89
101
|
fontFamily?: string;
|
|
90
102
|
/** Whether to use dark mode */
|
|
91
103
|
darkMode?: boolean;
|
|
104
|
+
/** Custom logo URL to display in checkout header */
|
|
105
|
+
logoUrl?: string;
|
|
106
|
+
/** PSP selector background color */
|
|
107
|
+
pspSelectorBgColor?: string;
|
|
108
|
+
/** PSP selector text color */
|
|
109
|
+
pspSelectorTextColor?: string;
|
|
110
|
+
/** PSP selector border color */
|
|
111
|
+
pspSelectorBorderColor?: string;
|
|
112
|
+
/** Use border-only style for PSP selector (no filled background) */
|
|
113
|
+
pspSelectorUseBorder?: boolean;
|
|
92
114
|
}
|
|
93
115
|
interface PSPConfig {
|
|
94
116
|
id: string;
|
|
@@ -96,6 +118,12 @@ interface PSPConfig {
|
|
|
96
118
|
supportedMethods: PaymentMethod[];
|
|
97
119
|
supportedCurrencies: string[];
|
|
98
120
|
}
|
|
121
|
+
interface CheckoutProviderOption {
|
|
122
|
+
provider: string;
|
|
123
|
+
name: string;
|
|
124
|
+
methods: PaymentMethod[];
|
|
125
|
+
countries?: string[];
|
|
126
|
+
}
|
|
99
127
|
interface MobileMoneyFormData {
|
|
100
128
|
phone: string;
|
|
101
129
|
network: MobileMoneyNetwork;
|
|
@@ -119,7 +147,7 @@ interface PaymentIntent {
|
|
|
119
147
|
pspCredentials?: {
|
|
120
148
|
/** Hubtel merchant account number */
|
|
121
149
|
merchantAccount?: string | number;
|
|
122
|
-
/** Hubtel basic auth header value */
|
|
150
|
+
/** Hubtel basic auth header value (deprecated - use hubtelSessionToken instead) */
|
|
123
151
|
basicAuth?: string;
|
|
124
152
|
/** Any other PSP-specific credential fields */
|
|
125
153
|
[key: string]: unknown;
|
|
@@ -148,6 +176,22 @@ interface PaymentIntent {
|
|
|
148
176
|
netAmount?: number;
|
|
149
177
|
/** Additional metadata */
|
|
150
178
|
metadata?: Record<string, unknown>;
|
|
179
|
+
/** Available PSPs for this checkout session */
|
|
180
|
+
availableProviders?: CheckoutProviderOption[];
|
|
181
|
+
/** Brand theme from checkout settings */
|
|
182
|
+
branding?: ReevitTheme;
|
|
183
|
+
}
|
|
184
|
+
interface HubtelSessionResponse {
|
|
185
|
+
/** Session token to use with Hubtel SDK */
|
|
186
|
+
token: string;
|
|
187
|
+
/** Merchant account number */
|
|
188
|
+
merchantAccount: string | number;
|
|
189
|
+
/** Base64 basic auth for Hubtel checkout (exposes credentials) */
|
|
190
|
+
basicAuth?: string;
|
|
191
|
+
/** Token expiration time in seconds */
|
|
192
|
+
expiresInSeconds: number;
|
|
193
|
+
/** Unix timestamp when token expires */
|
|
194
|
+
expiresAt: number;
|
|
151
195
|
}
|
|
152
196
|
|
|
153
197
|
/**
|
|
@@ -159,13 +203,14 @@ interface PaymentIntent {
|
|
|
159
203
|
interface CreatePaymentIntentRequest {
|
|
160
204
|
amount: number;
|
|
161
205
|
currency: string;
|
|
162
|
-
method
|
|
206
|
+
method?: string;
|
|
163
207
|
country: string;
|
|
164
208
|
customer_id?: string;
|
|
165
209
|
metadata?: Record<string, unknown>;
|
|
166
210
|
description?: string;
|
|
167
211
|
policy?: {
|
|
168
212
|
prefer?: string[];
|
|
213
|
+
allowed_providers?: string[];
|
|
169
214
|
max_amount?: number;
|
|
170
215
|
blocked_bins?: string[];
|
|
171
216
|
allowed_bins?: string[];
|
|
@@ -190,6 +235,13 @@ interface PaymentIntentResponse {
|
|
|
190
235
|
fee_currency: string;
|
|
191
236
|
net_amount: number;
|
|
192
237
|
reference?: string;
|
|
238
|
+
available_psps?: Array<{
|
|
239
|
+
provider: string;
|
|
240
|
+
name: string;
|
|
241
|
+
methods: string[];
|
|
242
|
+
countries?: string[];
|
|
243
|
+
}>;
|
|
244
|
+
branding?: Record<string, unknown>;
|
|
193
245
|
}
|
|
194
246
|
interface ConfirmPaymentRequest {
|
|
195
247
|
provider_ref_id: string;
|
|
@@ -226,7 +278,7 @@ interface APIErrorResponse {
|
|
|
226
278
|
}
|
|
227
279
|
interface ReevitAPIClientConfig {
|
|
228
280
|
/** Your Reevit public key */
|
|
229
|
-
publicKey
|
|
281
|
+
publicKey?: string;
|
|
230
282
|
/** Base URL for the Reevit API (defaults to production) */
|
|
231
283
|
baseUrl?: string;
|
|
232
284
|
/** Request timeout in milliseconds */
|
|
@@ -247,7 +299,10 @@ declare class ReevitAPIClient {
|
|
|
247
299
|
/**
|
|
248
300
|
* Creates a payment intent
|
|
249
301
|
*/
|
|
250
|
-
createPaymentIntent(config: ReevitCheckoutConfig, method
|
|
302
|
+
createPaymentIntent(config: ReevitCheckoutConfig, method?: PaymentMethod, country?: string, options?: {
|
|
303
|
+
preferredProviders?: string[];
|
|
304
|
+
allowedProviders?: string[];
|
|
305
|
+
}): Promise<{
|
|
251
306
|
data?: PaymentIntentResponse;
|
|
252
307
|
error?: PaymentError;
|
|
253
308
|
}>;
|
|
@@ -279,6 +334,15 @@ declare class ReevitAPIClient {
|
|
|
279
334
|
data?: PaymentDetailResponse;
|
|
280
335
|
error?: PaymentError;
|
|
281
336
|
}>;
|
|
337
|
+
/**
|
|
338
|
+
* Creates a Hubtel session token for secure checkout
|
|
339
|
+
* Returns a short-lived token that contains Hubtel credentials
|
|
340
|
+
* Credentials are never exposed to the client directly
|
|
341
|
+
*/
|
|
342
|
+
createHubtelSession(paymentId: string, clientSecret?: string): Promise<{
|
|
343
|
+
data?: HubtelSessionResponse;
|
|
344
|
+
error?: PaymentError;
|
|
345
|
+
}>;
|
|
282
346
|
/**
|
|
283
347
|
* Maps SDK payment method to backend format
|
|
284
348
|
*/
|
|
@@ -372,4 +436,4 @@ declare function createInitialState(): ReevitState;
|
|
|
372
436
|
*/
|
|
373
437
|
declare function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState;
|
|
374
438
|
|
|
375
|
-
export { type APIErrorResponse, type CardFormData, type CheckoutState, type ConfirmPaymentRequest, type CreatePaymentIntentRequest, type MobileMoneyFormData, type MobileMoneyNetwork, type PSPConfig, type PSPType, type PaymentDetailResponse, type PaymentError, type PaymentIntent, type PaymentIntentResponse, type PaymentMethod, type PaymentResult, type PaymentSource, ReevitAPIClient, type ReevitAPIClientConfig, type ReevitAction, type ReevitCheckoutCallbacks, type ReevitCheckoutConfig, type ReevitState, type ReevitTheme, cn, createInitialState, createReevitClient, createThemeVariables, detectCountryFromCurrency, detectNetwork, formatAmount, formatPhone, generateReference, reevitReducer, validatePhone };
|
|
439
|
+
export { type APIErrorResponse, type CardFormData, type CheckoutProviderOption, type CheckoutState, type ConfirmPaymentRequest, type CreatePaymentIntentRequest, type HubtelSessionResponse, type MobileMoneyFormData, type MobileMoneyNetwork, type PSPConfig, type PSPType, type PaymentDetailResponse, type PaymentError, type PaymentIntent, type PaymentIntentResponse, type PaymentMethod, type PaymentResult, type PaymentSource, ReevitAPIClient, type ReevitAPIClientConfig, type ReevitAction, type ReevitCheckoutCallbacks, type ReevitCheckoutConfig, type ReevitState, type ReevitTheme, cn, createInitialState, createReevitClient, createThemeVariables, detectCountryFromCurrency, detectNetwork, formatAmount, formatPhone, generateReference, reevitReducer, validatePhone };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ type PSPType = 'paystack' | 'hubtel' | 'flutterwave' | 'stripe' | 'monnify' | 'm
|
|
|
8
8
|
/** Payment source type - indicates where the payment originated from */
|
|
9
9
|
type PaymentSource = 'payment_link' | 'api' | 'subscription';
|
|
10
10
|
interface ReevitCheckoutConfig {
|
|
11
|
-
/** Your Reevit public key (
|
|
12
|
-
publicKey
|
|
11
|
+
/** Your Reevit public key (required for API-created intents; omit for payment links) */
|
|
12
|
+
publicKey?: string;
|
|
13
13
|
/** Amount in the smallest currency unit (e.g., pesewas for GHS) */
|
|
14
14
|
amount: number;
|
|
15
15
|
/** Currency code (e.g., 'GHS', 'NGN', 'USD') */
|
|
@@ -18,10 +18,16 @@ interface ReevitCheckoutConfig {
|
|
|
18
18
|
email?: string;
|
|
19
19
|
/** Customer phone number (required for mobile money) */
|
|
20
20
|
phone?: string;
|
|
21
|
+
/** Customer name (optional, used for payment links) */
|
|
22
|
+
customerName?: string;
|
|
21
23
|
/** Unique reference for this transaction */
|
|
22
24
|
reference?: string;
|
|
23
25
|
/** Additional metadata to attach to the payment */
|
|
24
26
|
metadata?: Record<string, unknown>;
|
|
27
|
+
/** Custom fields for payment links (if applicable) */
|
|
28
|
+
customFields?: Record<string, unknown>;
|
|
29
|
+
/** Payment link code (for public checkout flows) */
|
|
30
|
+
paymentLinkCode?: string;
|
|
25
31
|
/** Payment methods to display */
|
|
26
32
|
paymentMethods?: PaymentMethod[];
|
|
27
33
|
/** Pre-created payment intent to use */
|
|
@@ -79,16 +85,32 @@ interface PaymentError {
|
|
|
79
85
|
interface ReevitTheme {
|
|
80
86
|
/** Primary brand color */
|
|
81
87
|
primaryColor?: string;
|
|
88
|
+
/** Primary text color on brand surfaces */
|
|
89
|
+
primaryForegroundColor?: string;
|
|
82
90
|
/** Background color */
|
|
83
91
|
backgroundColor?: string;
|
|
92
|
+
/** Surface color for cards/panels */
|
|
93
|
+
surfaceColor?: string;
|
|
84
94
|
/** Text color */
|
|
85
95
|
textColor?: string;
|
|
96
|
+
/** Muted text color */
|
|
97
|
+
mutedTextColor?: string;
|
|
86
98
|
/** Border radius for inputs and buttons */
|
|
87
99
|
borderRadius?: string;
|
|
88
100
|
/** Font family to use */
|
|
89
101
|
fontFamily?: string;
|
|
90
102
|
/** Whether to use dark mode */
|
|
91
103
|
darkMode?: boolean;
|
|
104
|
+
/** Custom logo URL to display in checkout header */
|
|
105
|
+
logoUrl?: string;
|
|
106
|
+
/** PSP selector background color */
|
|
107
|
+
pspSelectorBgColor?: string;
|
|
108
|
+
/** PSP selector text color */
|
|
109
|
+
pspSelectorTextColor?: string;
|
|
110
|
+
/** PSP selector border color */
|
|
111
|
+
pspSelectorBorderColor?: string;
|
|
112
|
+
/** Use border-only style for PSP selector (no filled background) */
|
|
113
|
+
pspSelectorUseBorder?: boolean;
|
|
92
114
|
}
|
|
93
115
|
interface PSPConfig {
|
|
94
116
|
id: string;
|
|
@@ -96,6 +118,12 @@ interface PSPConfig {
|
|
|
96
118
|
supportedMethods: PaymentMethod[];
|
|
97
119
|
supportedCurrencies: string[];
|
|
98
120
|
}
|
|
121
|
+
interface CheckoutProviderOption {
|
|
122
|
+
provider: string;
|
|
123
|
+
name: string;
|
|
124
|
+
methods: PaymentMethod[];
|
|
125
|
+
countries?: string[];
|
|
126
|
+
}
|
|
99
127
|
interface MobileMoneyFormData {
|
|
100
128
|
phone: string;
|
|
101
129
|
network: MobileMoneyNetwork;
|
|
@@ -119,7 +147,7 @@ interface PaymentIntent {
|
|
|
119
147
|
pspCredentials?: {
|
|
120
148
|
/** Hubtel merchant account number */
|
|
121
149
|
merchantAccount?: string | number;
|
|
122
|
-
/** Hubtel basic auth header value */
|
|
150
|
+
/** Hubtel basic auth header value (deprecated - use hubtelSessionToken instead) */
|
|
123
151
|
basicAuth?: string;
|
|
124
152
|
/** Any other PSP-specific credential fields */
|
|
125
153
|
[key: string]: unknown;
|
|
@@ -148,6 +176,22 @@ interface PaymentIntent {
|
|
|
148
176
|
netAmount?: number;
|
|
149
177
|
/** Additional metadata */
|
|
150
178
|
metadata?: Record<string, unknown>;
|
|
179
|
+
/** Available PSPs for this checkout session */
|
|
180
|
+
availableProviders?: CheckoutProviderOption[];
|
|
181
|
+
/** Brand theme from checkout settings */
|
|
182
|
+
branding?: ReevitTheme;
|
|
183
|
+
}
|
|
184
|
+
interface HubtelSessionResponse {
|
|
185
|
+
/** Session token to use with Hubtel SDK */
|
|
186
|
+
token: string;
|
|
187
|
+
/** Merchant account number */
|
|
188
|
+
merchantAccount: string | number;
|
|
189
|
+
/** Base64 basic auth for Hubtel checkout (exposes credentials) */
|
|
190
|
+
basicAuth?: string;
|
|
191
|
+
/** Token expiration time in seconds */
|
|
192
|
+
expiresInSeconds: number;
|
|
193
|
+
/** Unix timestamp when token expires */
|
|
194
|
+
expiresAt: number;
|
|
151
195
|
}
|
|
152
196
|
|
|
153
197
|
/**
|
|
@@ -159,13 +203,14 @@ interface PaymentIntent {
|
|
|
159
203
|
interface CreatePaymentIntentRequest {
|
|
160
204
|
amount: number;
|
|
161
205
|
currency: string;
|
|
162
|
-
method
|
|
206
|
+
method?: string;
|
|
163
207
|
country: string;
|
|
164
208
|
customer_id?: string;
|
|
165
209
|
metadata?: Record<string, unknown>;
|
|
166
210
|
description?: string;
|
|
167
211
|
policy?: {
|
|
168
212
|
prefer?: string[];
|
|
213
|
+
allowed_providers?: string[];
|
|
169
214
|
max_amount?: number;
|
|
170
215
|
blocked_bins?: string[];
|
|
171
216
|
allowed_bins?: string[];
|
|
@@ -190,6 +235,13 @@ interface PaymentIntentResponse {
|
|
|
190
235
|
fee_currency: string;
|
|
191
236
|
net_amount: number;
|
|
192
237
|
reference?: string;
|
|
238
|
+
available_psps?: Array<{
|
|
239
|
+
provider: string;
|
|
240
|
+
name: string;
|
|
241
|
+
methods: string[];
|
|
242
|
+
countries?: string[];
|
|
243
|
+
}>;
|
|
244
|
+
branding?: Record<string, unknown>;
|
|
193
245
|
}
|
|
194
246
|
interface ConfirmPaymentRequest {
|
|
195
247
|
provider_ref_id: string;
|
|
@@ -226,7 +278,7 @@ interface APIErrorResponse {
|
|
|
226
278
|
}
|
|
227
279
|
interface ReevitAPIClientConfig {
|
|
228
280
|
/** Your Reevit public key */
|
|
229
|
-
publicKey
|
|
281
|
+
publicKey?: string;
|
|
230
282
|
/** Base URL for the Reevit API (defaults to production) */
|
|
231
283
|
baseUrl?: string;
|
|
232
284
|
/** Request timeout in milliseconds */
|
|
@@ -247,7 +299,10 @@ declare class ReevitAPIClient {
|
|
|
247
299
|
/**
|
|
248
300
|
* Creates a payment intent
|
|
249
301
|
*/
|
|
250
|
-
createPaymentIntent(config: ReevitCheckoutConfig, method
|
|
302
|
+
createPaymentIntent(config: ReevitCheckoutConfig, method?: PaymentMethod, country?: string, options?: {
|
|
303
|
+
preferredProviders?: string[];
|
|
304
|
+
allowedProviders?: string[];
|
|
305
|
+
}): Promise<{
|
|
251
306
|
data?: PaymentIntentResponse;
|
|
252
307
|
error?: PaymentError;
|
|
253
308
|
}>;
|
|
@@ -279,6 +334,15 @@ declare class ReevitAPIClient {
|
|
|
279
334
|
data?: PaymentDetailResponse;
|
|
280
335
|
error?: PaymentError;
|
|
281
336
|
}>;
|
|
337
|
+
/**
|
|
338
|
+
* Creates a Hubtel session token for secure checkout
|
|
339
|
+
* Returns a short-lived token that contains Hubtel credentials
|
|
340
|
+
* Credentials are never exposed to the client directly
|
|
341
|
+
*/
|
|
342
|
+
createHubtelSession(paymentId: string, clientSecret?: string): Promise<{
|
|
343
|
+
data?: HubtelSessionResponse;
|
|
344
|
+
error?: PaymentError;
|
|
345
|
+
}>;
|
|
282
346
|
/**
|
|
283
347
|
* Maps SDK payment method to backend format
|
|
284
348
|
*/
|
|
@@ -372,4 +436,4 @@ declare function createInitialState(): ReevitState;
|
|
|
372
436
|
*/
|
|
373
437
|
declare function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState;
|
|
374
438
|
|
|
375
|
-
export { type APIErrorResponse, type CardFormData, type CheckoutState, type ConfirmPaymentRequest, type CreatePaymentIntentRequest, type MobileMoneyFormData, type MobileMoneyNetwork, type PSPConfig, type PSPType, type PaymentDetailResponse, type PaymentError, type PaymentIntent, type PaymentIntentResponse, type PaymentMethod, type PaymentResult, type PaymentSource, ReevitAPIClient, type ReevitAPIClientConfig, type ReevitAction, type ReevitCheckoutCallbacks, type ReevitCheckoutConfig, type ReevitState, type ReevitTheme, cn, createInitialState, createReevitClient, createThemeVariables, detectCountryFromCurrency, detectNetwork, formatAmount, formatPhone, generateReference, reevitReducer, validatePhone };
|
|
439
|
+
export { type APIErrorResponse, type CardFormData, type CheckoutProviderOption, type CheckoutState, type ConfirmPaymentRequest, type CreatePaymentIntentRequest, type HubtelSessionResponse, type MobileMoneyFormData, type MobileMoneyNetwork, type PSPConfig, type PSPType, type PaymentDetailResponse, type PaymentError, type PaymentIntent, type PaymentIntentResponse, type PaymentMethod, type PaymentResult, type PaymentSource, ReevitAPIClient, type ReevitAPIClientConfig, type ReevitAction, type ReevitCheckoutCallbacks, type ReevitCheckoutConfig, type ReevitState, type ReevitTheme, cn, createInitialState, createReevitClient, createThemeVariables, detectCountryFromCurrency, detectNetwork, formatAmount, formatPhone, generateReference, reevitReducer, validatePhone };
|
package/dist/index.js
CHANGED
|
@@ -54,8 +54,8 @@ function createPaymentError(response, errorData) {
|
|
|
54
54
|
}
|
|
55
55
|
var ReevitAPIClient = class {
|
|
56
56
|
constructor(config) {
|
|
57
|
-
this.publicKey = config.publicKey;
|
|
58
|
-
this.baseUrl = config.baseUrl || (isSandboxKey(config.publicKey) ? API_BASE_URL_SANDBOX : API_BASE_URL_PRODUCTION);
|
|
57
|
+
this.publicKey = config.publicKey || "";
|
|
58
|
+
this.baseUrl = config.baseUrl || (config.publicKey && isSandboxKey(config.publicKey) ? API_BASE_URL_SANDBOX : API_BASE_URL_PRODUCTION);
|
|
59
59
|
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
@@ -66,10 +66,12 @@ var ReevitAPIClient = class {
|
|
|
66
66
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
67
67
|
const headers = {
|
|
68
68
|
"Content-Type": "application/json",
|
|
69
|
-
"X-Reevit-Key": this.publicKey,
|
|
70
69
|
"X-Reevit-Client": "@reevit/core",
|
|
71
70
|
"X-Reevit-Client-Version": "0.3.2"
|
|
72
71
|
};
|
|
72
|
+
if (this.publicKey) {
|
|
73
|
+
headers["X-Reevit-Key"] = this.publicKey;
|
|
74
|
+
}
|
|
73
75
|
if (method === "POST" || method === "PATCH" || method === "PUT") {
|
|
74
76
|
headers["Idempotency-Key"] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
75
77
|
}
|
|
@@ -119,7 +121,7 @@ var ReevitAPIClient = class {
|
|
|
119
121
|
/**
|
|
120
122
|
* Creates a payment intent
|
|
121
123
|
*/
|
|
122
|
-
async createPaymentIntent(config, method, country = "GH") {
|
|
124
|
+
async createPaymentIntent(config, method, country = "GH", options) {
|
|
123
125
|
const metadata = { ...config.metadata };
|
|
124
126
|
if (config.email) {
|
|
125
127
|
metadata.customer_email = config.email;
|
|
@@ -130,11 +132,19 @@ var ReevitAPIClient = class {
|
|
|
130
132
|
const request = {
|
|
131
133
|
amount: config.amount,
|
|
132
134
|
currency: config.currency,
|
|
133
|
-
method: this.mapPaymentMethod(method),
|
|
134
135
|
country,
|
|
135
136
|
customer_id: config.email || config.metadata?.customerId,
|
|
136
137
|
metadata
|
|
137
138
|
};
|
|
139
|
+
if (method) {
|
|
140
|
+
request.method = this.mapPaymentMethod(method);
|
|
141
|
+
}
|
|
142
|
+
if (options?.preferredProviders?.length || options?.allowedProviders?.length) {
|
|
143
|
+
request.policy = {
|
|
144
|
+
prefer: options?.preferredProviders,
|
|
145
|
+
allowed_providers: options?.allowedProviders
|
|
146
|
+
};
|
|
147
|
+
}
|
|
138
148
|
return this.request("POST", "/v1/payments/intents", request);
|
|
139
149
|
}
|
|
140
150
|
/**
|
|
@@ -161,6 +171,15 @@ var ReevitAPIClient = class {
|
|
|
161
171
|
async cancelPaymentIntent(paymentId) {
|
|
162
172
|
return this.request("POST", `/v1/payments/${paymentId}/cancel`);
|
|
163
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Creates a Hubtel session token for secure checkout
|
|
176
|
+
* Returns a short-lived token that contains Hubtel credentials
|
|
177
|
+
* Credentials are never exposed to the client directly
|
|
178
|
+
*/
|
|
179
|
+
async createHubtelSession(paymentId, clientSecret) {
|
|
180
|
+
const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : "";
|
|
181
|
+
return this.request("POST", `/v1/payments/hubtel/sessions/${paymentId}${query}`);
|
|
182
|
+
}
|
|
164
183
|
/**
|
|
165
184
|
* Maps SDK payment method to backend format
|
|
166
185
|
*/
|
|
@@ -257,21 +276,55 @@ function createThemeVariables(theme) {
|
|
|
257
276
|
const variables = {};
|
|
258
277
|
if (theme.primaryColor) {
|
|
259
278
|
variables["--reevit-primary"] = theme.primaryColor;
|
|
279
|
+
if (theme.primaryForegroundColor) {
|
|
280
|
+
variables["--reevit-primary-foreground"] = theme.primaryForegroundColor;
|
|
281
|
+
} else {
|
|
282
|
+
const contrast = getContrastingColor(theme.primaryColor);
|
|
283
|
+
if (contrast) {
|
|
284
|
+
variables["--reevit-primary-foreground"] = contrast;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
260
287
|
}
|
|
261
288
|
if (theme.backgroundColor) {
|
|
262
|
-
variables["--reevit-
|
|
289
|
+
variables["--reevit-background"] = theme.backgroundColor;
|
|
290
|
+
}
|
|
291
|
+
if (theme.surfaceColor) {
|
|
292
|
+
variables["--reevit-surface"] = theme.surfaceColor;
|
|
263
293
|
}
|
|
264
294
|
if (theme.textColor) {
|
|
265
295
|
variables["--reevit-text"] = theme.textColor;
|
|
266
296
|
}
|
|
297
|
+
if (theme.mutedTextColor) {
|
|
298
|
+
variables["--reevit-text-secondary"] = theme.mutedTextColor;
|
|
299
|
+
}
|
|
267
300
|
if (theme.borderRadius) {
|
|
268
301
|
variables["--reevit-radius"] = theme.borderRadius;
|
|
302
|
+
variables["--reevit-radius-sm"] = theme.borderRadius;
|
|
303
|
+
variables["--reevit-radius-lg"] = theme.borderRadius;
|
|
269
304
|
}
|
|
270
305
|
if (theme.fontFamily) {
|
|
271
306
|
variables["--reevit-font"] = theme.fontFamily;
|
|
272
307
|
}
|
|
273
308
|
return variables;
|
|
274
309
|
}
|
|
310
|
+
function getContrastingColor(color) {
|
|
311
|
+
const hex = color.trim();
|
|
312
|
+
if (!hex.startsWith("#")) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const normalized = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
316
|
+
if (normalized.length !== 7) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
const r = parseInt(normalized.slice(1, 3), 16);
|
|
320
|
+
const g = parseInt(normalized.slice(3, 5), 16);
|
|
321
|
+
const b = parseInt(normalized.slice(5, 7), 16);
|
|
322
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
|
|
326
|
+
return brightness >= 140 ? "#0b1120" : "#ffffff";
|
|
327
|
+
}
|
|
275
328
|
function cn(...classes) {
|
|
276
329
|
return classes.filter(Boolean).join(" ");
|
|
277
330
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/api/client.ts","../src/utils.ts","../src/state.ts"],"sourcesContent":["/**\n * @reevit/core\n * Shared utilities and API client for Reevit payment SDKs\n */\n\n// API Client\nexport {\n ReevitAPIClient,\n createReevitClient,\n type ReevitAPIClientConfig,\n type CreatePaymentIntentRequest,\n type PaymentIntentResponse,\n type PaymentDetailResponse,\n type ConfirmPaymentRequest,\n type APIErrorResponse,\n} from './api/client';\n\n// Types\nexport type {\n PaymentMethod,\n MobileMoneyNetwork,\n ReevitCheckoutConfig,\n ReevitCheckoutCallbacks,\n CheckoutState,\n PaymentResult,\n PaymentError,\n ReevitTheme,\n MobileMoneyFormData,\n CardFormData,\n PaymentIntent,\n PSPConfig,\n PSPType,\n PaymentSource,\n} from './types';\n\n// Utilities\nexport {\n formatAmount,\n generateReference,\n validatePhone,\n formatPhone,\n detectNetwork,\n detectCountryFromCurrency,\n createThemeVariables,\n cn,\n} from './utils';\n\n// State machine helpers\nexport {\n createInitialState,\n reevitReducer,\n type ReevitState,\n type ReevitAction,\n} from './state';\n","/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n connection_id: string;\n provider: string;\n status: string;\n client_secret: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, string>;\n}\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst API_BASE_URL_SANDBOX = 'https://sandbox-api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pk_test_') ||\n publicKey.startsWith('pk_sandbox_') ||\n publicKey.startsWith('pfk_test_') ||\n publicKey.startsWith('pfk_sandbox_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nfunction createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n details: {\n httpStatus: response.status,\n ...errorData.details,\n },\n };\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey;\n this.baseUrl = config.baseUrl || (isSandboxKey(config.publicKey)\n ? API_BASE_URL_SANDBOX\n : API_BASE_URL_PRODUCTION);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<{ data?: T; error?: PaymentError }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Key': this.publicKey,\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.3.2',\n };\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n headers['Idempotency-Key'] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method: PaymentMethod,\n country: string = 'GH'\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n method: this.mapPaymentMethod(method),\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const majorUnit = amount / 100;\n\n const currencyFormats: Record<string, { locale: string; minimumFractionDigits: number }> = {\n GHS: { locale: 'en-GH', minimumFractionDigits: 2 },\n NGN: { locale: 'en-NG', minimumFractionDigits: 2 },\n KES: { locale: 'en-KE', minimumFractionDigits: 2 },\n USD: { locale: 'en-US', minimumFractionDigits: 2 },\n EUR: { locale: 'de-DE', minimumFractionDigits: 2 },\n GBP: { locale: 'en-GB', minimumFractionDigits: 2 },\n };\n\n const format = currencyFormats[currency.toUpperCase()] || { locale: 'en-US', minimumFractionDigits: 2 };\n\n try {\n return new Intl.NumberFormat(format.locale, {\n style: 'currency',\n currency: currency.toUpperCase(),\n minimumFractionDigits: format.minimumFractionDigits,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${currency} ${majorUnit.toFixed(2)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const vodafonePrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (vodafonePrefixes.includes(prefix)) return 'vodafone';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n if (theme.primaryColor) {\n variables['--reevit-primary'] = theme.primaryColor;\n }\n if (theme.backgroundColor) {\n variables['--reevit-bg'] = theme.backgroundColor;\n }\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n }\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api/client.ts","../src/utils.ts","../src/state.ts"],"sourcesContent":["/**\n * @reevit/core\n * Shared utilities and API client for Reevit payment SDKs\n */\n\n// API Client\nexport {\n ReevitAPIClient,\n createReevitClient,\n type ReevitAPIClientConfig,\n type CreatePaymentIntentRequest,\n type PaymentIntentResponse,\n type PaymentDetailResponse,\n type ConfirmPaymentRequest,\n type APIErrorResponse,\n} from './api/client';\n\n// Types\nexport type {\n PaymentMethod,\n MobileMoneyNetwork,\n ReevitCheckoutConfig,\n ReevitCheckoutCallbacks,\n CheckoutState,\n PaymentResult,\n PaymentError,\n ReevitTheme,\n CheckoutProviderOption,\n MobileMoneyFormData,\n CardFormData,\n PaymentIntent,\n PSPConfig,\n PSPType,\n PaymentSource,\n HubtelSessionResponse,\n} from './types';\n\n// Utilities\nexport {\n formatAmount,\n generateReference,\n validatePhone,\n formatPhone,\n detectNetwork,\n detectCountryFromCurrency,\n createThemeVariables,\n cn,\n} from './utils';\n\n// State machine helpers\nexport {\n createInitialState,\n reevitReducer,\n type ReevitState,\n type ReevitAction,\n} from './state';\n","/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError, HubtelSessionResponse } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method?: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n allowed_providers?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n connection_id: string;\n provider: string;\n status: string;\n client_secret: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n available_psps?: Array<{\n provider: string;\n name: string;\n methods: string[];\n countries?: string[];\n }>;\n branding?: Record<string, unknown>;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, string>;\n}\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey?: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst API_BASE_URL_SANDBOX = 'https://sandbox-api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pk_test_') ||\n publicKey.startsWith('pk_sandbox_') ||\n publicKey.startsWith('pfk_test_') ||\n publicKey.startsWith('pfk_sandbox_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nfunction createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n details: {\n httpStatus: response.status,\n ...errorData.details,\n },\n };\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey || '';\n this.baseUrl = config.baseUrl || (config.publicKey && isSandboxKey(config.publicKey)\n ? API_BASE_URL_SANDBOX\n : API_BASE_URL_PRODUCTION);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<{ data?: T; error?: PaymentError }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.3.2',\n };\n if (this.publicKey) {\n headers['X-Reevit-Key'] = this.publicKey;\n }\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n headers['Idempotency-Key'] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method?: PaymentMethod,\n country: string = 'GH',\n options?: { preferredProviders?: string[]; allowedProviders?: string[] }\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n if (method) {\n request.method = this.mapPaymentMethod(method);\n }\n\n if (options?.preferredProviders?.length || options?.allowedProviders?.length) {\n request.policy = {\n prefer: options?.preferredProviders,\n allowed_providers: options?.allowedProviders,\n };\n }\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Creates a Hubtel session token for secure checkout\n * Returns a short-lived token that contains Hubtel credentials\n * Credentials are never exposed to the client directly\n */\n async createHubtelSession(\n paymentId: string,\n clientSecret?: string\n ): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : '';\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}${query}`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const majorUnit = amount / 100;\n\n const currencyFormats: Record<string, { locale: string; minimumFractionDigits: number }> = {\n GHS: { locale: 'en-GH', minimumFractionDigits: 2 },\n NGN: { locale: 'en-NG', minimumFractionDigits: 2 },\n KES: { locale: 'en-KE', minimumFractionDigits: 2 },\n USD: { locale: 'en-US', minimumFractionDigits: 2 },\n EUR: { locale: 'de-DE', minimumFractionDigits: 2 },\n GBP: { locale: 'en-GB', minimumFractionDigits: 2 },\n };\n\n const format = currencyFormats[currency.toUpperCase()] || { locale: 'en-US', minimumFractionDigits: 2 };\n\n try {\n return new Intl.NumberFormat(format.locale, {\n style: 'currency',\n currency: currency.toUpperCase(),\n minimumFractionDigits: format.minimumFractionDigits,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${currency} ${majorUnit.toFixed(2)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const vodafonePrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (vodafonePrefixes.includes(prefix)) return 'vodafone';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n if (theme.primaryColor) {\n variables['--reevit-primary'] = theme.primaryColor;\n if (theme.primaryForegroundColor) {\n variables['--reevit-primary-foreground'] = theme.primaryForegroundColor;\n } else {\n const contrast = getContrastingColor(theme.primaryColor);\n if (contrast) {\n variables['--reevit-primary-foreground'] = contrast;\n }\n }\n }\n if (theme.backgroundColor) {\n variables['--reevit-background'] = theme.backgroundColor;\n }\n if (theme.surfaceColor) {\n variables['--reevit-surface'] = theme.surfaceColor;\n }\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.mutedTextColor) {\n variables['--reevit-text-secondary'] = theme.mutedTextColor;\n }\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n variables['--reevit-radius-sm'] = theme.borderRadius;\n variables['--reevit-radius-lg'] = theme.borderRadius;\n }\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\nfunction getContrastingColor(color: string): string | null {\n const hex = color.trim();\n if (!hex.startsWith('#')) {\n return null;\n }\n\n const normalized = hex.length === 4\n ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`\n : hex;\n\n if (normalized.length !== 7) {\n return null;\n }\n\n const r = parseInt(normalized.slice(1, 3), 16);\n const g = parseInt(normalized.slice(3, 5), 16);\n const b = parseInt(normalized.slice(5, 7), 16);\n\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\n return null;\n }\n\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 140 ? '#0b1120' : '#ffffff';\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -17,8 +17,8 @@ function createPaymentError(response, errorData) {
|
|
|
17
17
|
}
|
|
18
18
|
var ReevitAPIClient = class {
|
|
19
19
|
constructor(config) {
|
|
20
|
-
this.publicKey = config.publicKey;
|
|
21
|
-
this.baseUrl = config.baseUrl || (isSandboxKey(config.publicKey) ? API_BASE_URL_SANDBOX : API_BASE_URL_PRODUCTION);
|
|
20
|
+
this.publicKey = config.publicKey || "";
|
|
21
|
+
this.baseUrl = config.baseUrl || (config.publicKey && isSandboxKey(config.publicKey) ? API_BASE_URL_SANDBOX : API_BASE_URL_PRODUCTION);
|
|
22
22
|
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
@@ -29,10 +29,12 @@ var ReevitAPIClient = class {
|
|
|
29
29
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
30
30
|
const headers = {
|
|
31
31
|
"Content-Type": "application/json",
|
|
32
|
-
"X-Reevit-Key": this.publicKey,
|
|
33
32
|
"X-Reevit-Client": "@reevit/core",
|
|
34
33
|
"X-Reevit-Client-Version": "0.3.2"
|
|
35
34
|
};
|
|
35
|
+
if (this.publicKey) {
|
|
36
|
+
headers["X-Reevit-Key"] = this.publicKey;
|
|
37
|
+
}
|
|
36
38
|
if (method === "POST" || method === "PATCH" || method === "PUT") {
|
|
37
39
|
headers["Idempotency-Key"] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
38
40
|
}
|
|
@@ -82,7 +84,7 @@ var ReevitAPIClient = class {
|
|
|
82
84
|
/**
|
|
83
85
|
* Creates a payment intent
|
|
84
86
|
*/
|
|
85
|
-
async createPaymentIntent(config, method, country = "GH") {
|
|
87
|
+
async createPaymentIntent(config, method, country = "GH", options) {
|
|
86
88
|
const metadata = { ...config.metadata };
|
|
87
89
|
if (config.email) {
|
|
88
90
|
metadata.customer_email = config.email;
|
|
@@ -93,11 +95,19 @@ var ReevitAPIClient = class {
|
|
|
93
95
|
const request = {
|
|
94
96
|
amount: config.amount,
|
|
95
97
|
currency: config.currency,
|
|
96
|
-
method: this.mapPaymentMethod(method),
|
|
97
98
|
country,
|
|
98
99
|
customer_id: config.email || config.metadata?.customerId,
|
|
99
100
|
metadata
|
|
100
101
|
};
|
|
102
|
+
if (method) {
|
|
103
|
+
request.method = this.mapPaymentMethod(method);
|
|
104
|
+
}
|
|
105
|
+
if (options?.preferredProviders?.length || options?.allowedProviders?.length) {
|
|
106
|
+
request.policy = {
|
|
107
|
+
prefer: options?.preferredProviders,
|
|
108
|
+
allowed_providers: options?.allowedProviders
|
|
109
|
+
};
|
|
110
|
+
}
|
|
101
111
|
return this.request("POST", "/v1/payments/intents", request);
|
|
102
112
|
}
|
|
103
113
|
/**
|
|
@@ -124,6 +134,15 @@ var ReevitAPIClient = class {
|
|
|
124
134
|
async cancelPaymentIntent(paymentId) {
|
|
125
135
|
return this.request("POST", `/v1/payments/${paymentId}/cancel`);
|
|
126
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates a Hubtel session token for secure checkout
|
|
139
|
+
* Returns a short-lived token that contains Hubtel credentials
|
|
140
|
+
* Credentials are never exposed to the client directly
|
|
141
|
+
*/
|
|
142
|
+
async createHubtelSession(paymentId, clientSecret) {
|
|
143
|
+
const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : "";
|
|
144
|
+
return this.request("POST", `/v1/payments/hubtel/sessions/${paymentId}${query}`);
|
|
145
|
+
}
|
|
127
146
|
/**
|
|
128
147
|
* Maps SDK payment method to backend format
|
|
129
148
|
*/
|
|
@@ -220,21 +239,55 @@ function createThemeVariables(theme) {
|
|
|
220
239
|
const variables = {};
|
|
221
240
|
if (theme.primaryColor) {
|
|
222
241
|
variables["--reevit-primary"] = theme.primaryColor;
|
|
242
|
+
if (theme.primaryForegroundColor) {
|
|
243
|
+
variables["--reevit-primary-foreground"] = theme.primaryForegroundColor;
|
|
244
|
+
} else {
|
|
245
|
+
const contrast = getContrastingColor(theme.primaryColor);
|
|
246
|
+
if (contrast) {
|
|
247
|
+
variables["--reevit-primary-foreground"] = contrast;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
223
250
|
}
|
|
224
251
|
if (theme.backgroundColor) {
|
|
225
|
-
variables["--reevit-
|
|
252
|
+
variables["--reevit-background"] = theme.backgroundColor;
|
|
253
|
+
}
|
|
254
|
+
if (theme.surfaceColor) {
|
|
255
|
+
variables["--reevit-surface"] = theme.surfaceColor;
|
|
226
256
|
}
|
|
227
257
|
if (theme.textColor) {
|
|
228
258
|
variables["--reevit-text"] = theme.textColor;
|
|
229
259
|
}
|
|
260
|
+
if (theme.mutedTextColor) {
|
|
261
|
+
variables["--reevit-text-secondary"] = theme.mutedTextColor;
|
|
262
|
+
}
|
|
230
263
|
if (theme.borderRadius) {
|
|
231
264
|
variables["--reevit-radius"] = theme.borderRadius;
|
|
265
|
+
variables["--reevit-radius-sm"] = theme.borderRadius;
|
|
266
|
+
variables["--reevit-radius-lg"] = theme.borderRadius;
|
|
232
267
|
}
|
|
233
268
|
if (theme.fontFamily) {
|
|
234
269
|
variables["--reevit-font"] = theme.fontFamily;
|
|
235
270
|
}
|
|
236
271
|
return variables;
|
|
237
272
|
}
|
|
273
|
+
function getContrastingColor(color) {
|
|
274
|
+
const hex = color.trim();
|
|
275
|
+
if (!hex.startsWith("#")) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const normalized = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
279
|
+
if (normalized.length !== 7) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const r = parseInt(normalized.slice(1, 3), 16);
|
|
283
|
+
const g = parseInt(normalized.slice(3, 5), 16);
|
|
284
|
+
const b = parseInt(normalized.slice(5, 7), 16);
|
|
285
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
|
|
289
|
+
return brightness >= 140 ? "#0b1120" : "#ffffff";
|
|
290
|
+
}
|
|
238
291
|
function cn(...classes) {
|
|
239
292
|
return classes.filter(Boolean).join(" ");
|
|
240
293
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api/client.ts","../src/utils.ts","../src/state.ts"],"sourcesContent":["/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n connection_id: string;\n provider: string;\n status: string;\n client_secret: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, string>;\n}\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst API_BASE_URL_SANDBOX = 'https://sandbox-api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pk_test_') ||\n publicKey.startsWith('pk_sandbox_') ||\n publicKey.startsWith('pfk_test_') ||\n publicKey.startsWith('pfk_sandbox_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nfunction createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n details: {\n httpStatus: response.status,\n ...errorData.details,\n },\n };\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey;\n this.baseUrl = config.baseUrl || (isSandboxKey(config.publicKey)\n ? API_BASE_URL_SANDBOX\n : API_BASE_URL_PRODUCTION);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<{ data?: T; error?: PaymentError }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Key': this.publicKey,\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.3.2',\n };\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n headers['Idempotency-Key'] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method: PaymentMethod,\n country: string = 'GH'\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n method: this.mapPaymentMethod(method),\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const majorUnit = amount / 100;\n\n const currencyFormats: Record<string, { locale: string; minimumFractionDigits: number }> = {\n GHS: { locale: 'en-GH', minimumFractionDigits: 2 },\n NGN: { locale: 'en-NG', minimumFractionDigits: 2 },\n KES: { locale: 'en-KE', minimumFractionDigits: 2 },\n USD: { locale: 'en-US', minimumFractionDigits: 2 },\n EUR: { locale: 'de-DE', minimumFractionDigits: 2 },\n GBP: { locale: 'en-GB', minimumFractionDigits: 2 },\n };\n\n const format = currencyFormats[currency.toUpperCase()] || { locale: 'en-US', minimumFractionDigits: 2 };\n\n try {\n return new Intl.NumberFormat(format.locale, {\n style: 'currency',\n currency: currency.toUpperCase(),\n minimumFractionDigits: format.minimumFractionDigits,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${currency} ${majorUnit.toFixed(2)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const vodafonePrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (vodafonePrefixes.includes(prefix)) return 'vodafone';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n if (theme.primaryColor) {\n variables['--reevit-primary'] = theme.primaryColor;\n }\n if (theme.backgroundColor) {\n variables['--reevit-bg'] = theme.backgroundColor;\n }\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n }\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/api/client.ts","../src/utils.ts","../src/state.ts"],"sourcesContent":["/**\n * Reevit API Client\n * \n * Handles communication with the Reevit backend for payment operations.\n */\n\nimport type { PaymentMethod, ReevitCheckoutConfig, PaymentError, HubtelSessionResponse } from '../types';\n\n// API Response Types (matching backend handlers_payments.go)\nexport interface CreatePaymentIntentRequest {\n amount: number;\n currency: string;\n method?: string;\n country: string;\n customer_id?: string;\n metadata?: Record<string, unknown>;\n description?: string;\n policy?: {\n prefer?: string[];\n allowed_providers?: string[];\n max_amount?: number;\n blocked_bins?: string[];\n allowed_bins?: string[];\n velocity_max_per_minute?: number;\n };\n}\n\nexport interface PaymentIntentResponse {\n id: string;\n connection_id: string;\n provider: string;\n status: string;\n client_secret: string;\n psp_public_key: string;\n psp_credentials?: {\n merchantAccount?: string | number;\n basicAuth?: string;\n [key: string]: unknown;\n };\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n reference?: string;\n available_psps?: Array<{\n provider: string;\n name: string;\n methods: string[];\n countries?: string[];\n }>;\n branding?: Record<string, unknown>;\n}\n\nexport interface ConfirmPaymentRequest {\n provider_ref_id: string;\n provider_data?: Record<string, unknown>;\n}\n\nexport interface PaymentDetailResponse {\n id: string;\n connection_id: string;\n provider: string;\n method: string;\n status: string;\n amount: number;\n currency: string;\n fee_amount: number;\n fee_currency: string;\n net_amount: number;\n customer_id?: string;\n client_secret: string;\n provider_ref_id?: string;\n metadata?: Record<string, unknown>;\n created_at: string;\n updated_at: string;\n /** Payment source type (payment_link, api, subscription) */\n source?: 'payment_link' | 'api' | 'subscription';\n /** ID of the source (payment link ID, subscription ID, etc.) */\n source_id?: string;\n /** Human-readable description of the source (e.g., payment link name) */\n source_description?: string;\n}\n\nexport interface APIErrorResponse {\n code: string;\n message: string;\n details?: Record<string, string>;\n}\n\n// API Client configuration\nexport interface ReevitAPIClientConfig {\n /** Your Reevit public key */\n publicKey?: string;\n /** Base URL for the Reevit API (defaults to production) */\n baseUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n}\n\n// Default API base URLs\nconst API_BASE_URL_PRODUCTION = 'https://api.reevit.io';\nconst API_BASE_URL_SANDBOX = 'https://sandbox-api.reevit.io';\nconst DEFAULT_TIMEOUT = 30000; // 30 seconds\n\n/**\n * Determines if a public key is for sandbox mode\n */\nexport function isSandboxKey(publicKey: string): boolean {\n return publicKey.startsWith('pk_test_') ||\n publicKey.startsWith('pk_sandbox_') ||\n publicKey.startsWith('pfk_test_') ||\n publicKey.startsWith('pfk_sandbox_');\n}\n\n/**\n * Creates a PaymentError from an API error response\n */\nfunction createPaymentError(response: Response, errorData: APIErrorResponse): PaymentError {\n return {\n code: errorData.code || 'api_error',\n message: errorData.message || 'An unexpected error occurred',\n details: {\n httpStatus: response.status,\n ...errorData.details,\n },\n };\n}\n\n/**\n * Reevit API Client\n */\nexport class ReevitAPIClient {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: ReevitAPIClientConfig) {\n this.publicKey = config.publicKey || '';\n this.baseUrl = config.baseUrl || (config.publicKey && isSandboxKey(config.publicKey)\n ? API_BASE_URL_SANDBOX\n : API_BASE_URL_PRODUCTION);\n this.timeout = config.timeout || DEFAULT_TIMEOUT;\n }\n\n /**\n * Makes an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<{ data?: T; error?: PaymentError }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Generate headers with idempotency key for mutating requests\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Reevit-Client': '@reevit/core',\n 'X-Reevit-Client-Version': '0.3.2',\n };\n if (this.publicKey) {\n headers['X-Reevit-Key'] = this.publicKey;\n }\n\n if (method === 'POST' || method === 'PATCH' || method === 'PUT') {\n headers['Idempotency-Key'] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const responseData = await response.json().catch(() => ({}));\n\n if (!response.ok) {\n return {\n error: createPaymentError(response, responseData as APIErrorResponse),\n };\n }\n\n return { data: responseData as T };\n } catch (err) {\n clearTimeout(timeoutId);\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n error: {\n code: 'request_timeout',\n message: 'The request timed out. Please try again.',\n },\n };\n }\n\n if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {\n return {\n error: {\n code: 'network_error',\n message: 'Unable to connect to Reevit. Please check your internet connection.',\n },\n };\n }\n }\n\n return {\n error: {\n code: 'unknown_error',\n message: 'An unexpected error occurred. Please try again.',\n },\n };\n }\n }\n\n /**\n * Creates a payment intent\n */\n async createPaymentIntent(\n config: ReevitCheckoutConfig,\n method?: PaymentMethod,\n country: string = 'GH',\n options?: { preferredProviders?: string[]; allowedProviders?: string[] }\n ): Promise<{ data?: PaymentIntentResponse; error?: PaymentError }> {\n // Build metadata with customer_email for PSP providers that require it\n const metadata: Record<string, unknown> = { ...config.metadata };\n if (config.email) {\n metadata.customer_email = config.email;\n }\n if (config.phone) {\n metadata.customer_phone = config.phone;\n }\n\n const request: CreatePaymentIntentRequest = {\n amount: config.amount,\n currency: config.currency,\n country,\n customer_id: config.email || (config.metadata?.customerId as string | undefined),\n metadata,\n };\n\n if (method) {\n request.method = this.mapPaymentMethod(method);\n }\n\n if (options?.preferredProviders?.length || options?.allowedProviders?.length) {\n request.policy = {\n prefer: options?.preferredProviders,\n allowed_providers: options?.allowedProviders,\n };\n }\n\n return this.request<PaymentIntentResponse>('POST', '/v1/payments/intents', request);\n }\n\n /**\n * Retrieves a payment intent by ID\n */\n async getPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('GET', `/v1/payments/${paymentId}`);\n }\n\n /**\n * Confirms a payment after PSP callback\n */\n async confirmPayment(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm`);\n }\n\n /**\n * Confirms a payment intent using client secret (public endpoint)\n */\n async confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);\n }\n\n /**\n * Cancels a payment intent\n */\n async cancelPaymentIntent(paymentId: string): Promise<{ data?: PaymentDetailResponse; error?: PaymentError }> {\n return this.request<PaymentDetailResponse>('POST', `/v1/payments/${paymentId}/cancel`);\n }\n\n /**\n * Creates a Hubtel session token for secure checkout\n * Returns a short-lived token that contains Hubtel credentials\n * Credentials are never exposed to the client directly\n */\n async createHubtelSession(\n paymentId: string,\n clientSecret?: string\n ): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : '';\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}${query}`);\n }\n\n /**\n * Maps SDK payment method to backend format\n */\n private mapPaymentMethod(method: PaymentMethod): string {\n switch (method) {\n case 'card':\n return 'card';\n case 'mobile_money':\n return 'mobile_money';\n case 'bank_transfer':\n return 'bank_transfer';\n default:\n return method;\n }\n }\n}\n\n/**\n * Creates a new Reevit API client instance\n */\nexport function createReevitClient(config: ReevitAPIClientConfig): ReevitAPIClient {\n return new ReevitAPIClient(config);\n}\n","/**\n * Utility Functions\n * Shared utilities for Reevit SDKs\n */\n\nimport type { MobileMoneyNetwork, ReevitTheme } from './types';\n\n/**\n * Formats an amount from smallest currency unit to display format\n */\nexport function formatAmount(amount: number, currency: string): string {\n const majorUnit = amount / 100;\n\n const currencyFormats: Record<string, { locale: string; minimumFractionDigits: number }> = {\n GHS: { locale: 'en-GH', minimumFractionDigits: 2 },\n NGN: { locale: 'en-NG', minimumFractionDigits: 2 },\n KES: { locale: 'en-KE', minimumFractionDigits: 2 },\n USD: { locale: 'en-US', minimumFractionDigits: 2 },\n EUR: { locale: 'de-DE', minimumFractionDigits: 2 },\n GBP: { locale: 'en-GB', minimumFractionDigits: 2 },\n };\n\n const format = currencyFormats[currency.toUpperCase()] || { locale: 'en-US', minimumFractionDigits: 2 };\n\n try {\n return new Intl.NumberFormat(format.locale, {\n style: 'currency',\n currency: currency.toUpperCase(),\n minimumFractionDigits: format.minimumFractionDigits,\n }).format(majorUnit);\n } catch {\n // Fallback for unsupported currencies\n return `${currency} ${majorUnit.toFixed(2)}`;\n }\n}\n\n/**\n * Generates a unique payment reference\n */\nexport function generateReference(prefix: string = 'reevit'): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${prefix}_${timestamp}_${random}`;\n}\n\n/**\n * Validates a phone number for mobile money\n */\nexport function validatePhone(phone: string, country: string = 'GH'): boolean {\n // Remove non-digit characters\n const digits = phone.replace(/\\D/g, '');\n\n const patterns: Record<string, RegExp> = {\n GH: /^(?:233|0)?[235][0-9]{8}$/, // Ghana\n NG: /^(?:234|0)?[789][01][0-9]{8}$/, // Nigeria\n KE: /^(?:254|0)?[17][0-9]{8}$/, // Kenya\n };\n\n const pattern = patterns[country.toUpperCase()];\n if (!pattern) return digits.length >= 10;\n\n return pattern.test(digits);\n}\n\n/**\n * Formats a phone number for display\n */\nexport function formatPhone(phone: string, country: string = 'GH'): string {\n const digits = phone.replace(/\\D/g, '');\n\n if (country === 'GH') {\n // Format as 0XX XXX XXXX\n if (digits.startsWith('233') && digits.length === 12) {\n const local = '0' + digits.slice(3);\n return `${local.slice(0, 3)} ${local.slice(3, 6)} ${local.slice(6)}`;\n }\n if (digits.length === 10 && digits.startsWith('0')) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6)}`;\n }\n }\n\n return phone;\n}\n\n/**\n * Detects mobile money network from phone number (Ghana)\n */\nexport function detectNetwork(phone: string): MobileMoneyNetwork | null {\n const digits = phone.replace(/\\D/g, '');\n\n // Get the network prefix (first 3 digits after country code or 0)\n let prefix: string;\n if (digits.startsWith('233')) {\n prefix = digits.slice(3, 5);\n } else if (digits.startsWith('0')) {\n prefix = digits.slice(1, 3);\n } else {\n prefix = digits.slice(0, 2);\n }\n\n // Ghana network prefixes\n const mtnPrefixes = ['24', '25', '53', '54', '55', '59'];\n const vodafonePrefixes = ['20', '50'];\n const airteltigoPrefixes = ['26', '27', '56', '57'];\n\n if (mtnPrefixes.includes(prefix)) return 'mtn';\n if (vodafonePrefixes.includes(prefix)) return 'vodafone';\n if (airteltigoPrefixes.includes(prefix)) return 'airteltigo';\n\n return null;\n}\n\n/**\n * Creates CSS custom property variables from theme\n */\nexport function createThemeVariables(theme: ReevitTheme): Record<string, string> {\n const variables: Record<string, string> = {};\n\n if (theme.primaryColor) {\n variables['--reevit-primary'] = theme.primaryColor;\n if (theme.primaryForegroundColor) {\n variables['--reevit-primary-foreground'] = theme.primaryForegroundColor;\n } else {\n const contrast = getContrastingColor(theme.primaryColor);\n if (contrast) {\n variables['--reevit-primary-foreground'] = contrast;\n }\n }\n }\n if (theme.backgroundColor) {\n variables['--reevit-background'] = theme.backgroundColor;\n }\n if (theme.surfaceColor) {\n variables['--reevit-surface'] = theme.surfaceColor;\n }\n if (theme.textColor) {\n variables['--reevit-text'] = theme.textColor;\n }\n if (theme.mutedTextColor) {\n variables['--reevit-text-secondary'] = theme.mutedTextColor;\n }\n if (theme.borderRadius) {\n variables['--reevit-radius'] = theme.borderRadius;\n variables['--reevit-radius-sm'] = theme.borderRadius;\n variables['--reevit-radius-lg'] = theme.borderRadius;\n }\n if (theme.fontFamily) {\n variables['--reevit-font'] = theme.fontFamily;\n }\n\n return variables;\n}\n\nfunction getContrastingColor(color: string): string | null {\n const hex = color.trim();\n if (!hex.startsWith('#')) {\n return null;\n }\n\n const normalized = hex.length === 4\n ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`\n : hex;\n\n if (normalized.length !== 7) {\n return null;\n }\n\n const r = parseInt(normalized.slice(1, 3), 16);\n const g = parseInt(normalized.slice(3, 5), 16);\n const b = parseInt(normalized.slice(5, 7), 16);\n\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\n return null;\n }\n\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 140 ? '#0b1120' : '#ffffff';\n}\n\n/**\n * Simple class name concatenation utility\n */\nexport function cn(...classes: (string | boolean | undefined | null)[]): string {\n return classes.filter(Boolean).join(' ');\n}\n\n/**\n * Detects country code from currency\n */\nexport function detectCountryFromCurrency(currency: string): string {\n const currencyToCountry: Record<string, string> = {\n GHS: 'GH',\n NGN: 'NG',\n KES: 'KE',\n UGX: 'UG',\n TZS: 'TZ',\n ZAR: 'ZA',\n XOF: 'CI',\n XAF: 'CM',\n USD: 'US',\n EUR: 'DE',\n GBP: 'GB',\n };\n\n return currencyToCountry[currency.toUpperCase()] || 'GH';\n}\n","/**\n * Reevit State Machine\n * Shared state management logic for all SDKs\n */\n\nimport type { CheckoutState, PaymentIntent, PaymentMethod, PaymentResult, PaymentError } from './types';\n\n// State shape\nexport interface ReevitState {\n status: CheckoutState;\n paymentIntent: PaymentIntent | null;\n selectedMethod: PaymentMethod | null;\n error: PaymentError | null;\n result: PaymentResult | null;\n}\n\n// Actions\nexport type ReevitAction =\n | { type: 'INIT_START' }\n | { type: 'INIT_SUCCESS'; payload: PaymentIntent }\n | { type: 'INIT_ERROR'; payload: PaymentError }\n | { type: 'SELECT_METHOD'; payload: PaymentMethod }\n | { type: 'PROCESS_START' }\n | { type: 'PROCESS_SUCCESS'; payload: PaymentResult }\n | { type: 'PROCESS_ERROR'; payload: PaymentError }\n | { type: 'RESET' }\n | { type: 'CLOSE' };\n\n/**\n * Creates the initial state for the checkout\n */\nexport function createInitialState(): ReevitState {\n return {\n status: 'idle',\n paymentIntent: null,\n selectedMethod: null,\n error: null,\n result: null,\n };\n}\n\n/**\n * State reducer for checkout flow\n */\nexport function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState {\n switch (action.type) {\n case 'INIT_START':\n return { ...state, status: 'loading', error: null };\n case 'INIT_SUCCESS':\n return {\n ...state,\n status: 'ready',\n paymentIntent: action.payload,\n selectedMethod:\n action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : null,\n };\n case 'INIT_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'SELECT_METHOD':\n return { ...state, status: 'method_selected', selectedMethod: action.payload };\n case 'PROCESS_START':\n return { ...state, status: 'processing', error: null };\n case 'PROCESS_SUCCESS':\n return { ...state, status: 'success', result: action.payload };\n case 'PROCESS_ERROR':\n return { ...state, status: 'failed', error: action.payload };\n case 'RESET':\n return { ...createInitialState(), status: 'ready', paymentIntent: state.paymentIntent };\n case 'CLOSE':\n return { ...state, status: 'closed' };\n default:\n return state;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/styles.css
CHANGED
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
--reevit-success: oklch(0.696 0.17 162.48);
|
|
21
21
|
--reevit-error: oklch(0.577 0.245 27.325);
|
|
22
22
|
--reevit-warning: oklch(0.828 0.189 84.429);
|
|
23
|
-
--reevit-radius: 0.
|
|
23
|
+
--reevit-radius: 0.75rem;
|
|
24
24
|
--reevit-radius-sm: 0.5rem;
|
|
25
|
-
--reevit-radius-lg:
|
|
25
|
+
--reevit-radius-lg: 1.25rem;
|
|
26
26
|
--reevit-font: 'Geist Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
27
|
-
--reevit-shadow: 0 25px
|
|
28
|
-
--reevit-shadow-sm: 0 1px
|
|
27
|
+
--reevit-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
28
|
+
--reevit-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
29
|
+
--reevit-shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/* Dark mode support */
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
border: 1px solid var(--reevit-border);
|
|
74
75
|
box-shadow: var(--reevit-shadow);
|
|
75
76
|
width: 100%;
|
|
76
|
-
max-width:
|
|
77
|
+
max-width: 380px;
|
|
77
78
|
max-height: 90vh;
|
|
78
79
|
overflow: hidden;
|
|
79
80
|
display: flex;
|