@reevit/core 0.4.1 → 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 +52 -7
- package/dist/index.d.ts +52 -7
- package/dist/index.js +53 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +53 -8
- 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;
|
|
@@ -148,12 +176,18 @@ 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;
|
|
151
183
|
}
|
|
152
184
|
interface HubtelSessionResponse {
|
|
153
185
|
/** Session token to use with Hubtel SDK */
|
|
154
186
|
token: string;
|
|
155
187
|
/** Merchant account number */
|
|
156
188
|
merchantAccount: string | number;
|
|
189
|
+
/** Base64 basic auth for Hubtel checkout (exposes credentials) */
|
|
190
|
+
basicAuth?: string;
|
|
157
191
|
/** Token expiration time in seconds */
|
|
158
192
|
expiresInSeconds: number;
|
|
159
193
|
/** Unix timestamp when token expires */
|
|
@@ -169,13 +203,14 @@ interface HubtelSessionResponse {
|
|
|
169
203
|
interface CreatePaymentIntentRequest {
|
|
170
204
|
amount: number;
|
|
171
205
|
currency: string;
|
|
172
|
-
method
|
|
206
|
+
method?: string;
|
|
173
207
|
country: string;
|
|
174
208
|
customer_id?: string;
|
|
175
209
|
metadata?: Record<string, unknown>;
|
|
176
210
|
description?: string;
|
|
177
211
|
policy?: {
|
|
178
212
|
prefer?: string[];
|
|
213
|
+
allowed_providers?: string[];
|
|
179
214
|
max_amount?: number;
|
|
180
215
|
blocked_bins?: string[];
|
|
181
216
|
allowed_bins?: string[];
|
|
@@ -200,6 +235,13 @@ interface PaymentIntentResponse {
|
|
|
200
235
|
fee_currency: string;
|
|
201
236
|
net_amount: number;
|
|
202
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>;
|
|
203
245
|
}
|
|
204
246
|
interface ConfirmPaymentRequest {
|
|
205
247
|
provider_ref_id: string;
|
|
@@ -236,7 +278,7 @@ interface APIErrorResponse {
|
|
|
236
278
|
}
|
|
237
279
|
interface ReevitAPIClientConfig {
|
|
238
280
|
/** Your Reevit public key */
|
|
239
|
-
publicKey
|
|
281
|
+
publicKey?: string;
|
|
240
282
|
/** Base URL for the Reevit API (defaults to production) */
|
|
241
283
|
baseUrl?: string;
|
|
242
284
|
/** Request timeout in milliseconds */
|
|
@@ -257,7 +299,10 @@ declare class ReevitAPIClient {
|
|
|
257
299
|
/**
|
|
258
300
|
* Creates a payment intent
|
|
259
301
|
*/
|
|
260
|
-
createPaymentIntent(config: ReevitCheckoutConfig, method
|
|
302
|
+
createPaymentIntent(config: ReevitCheckoutConfig, method?: PaymentMethod, country?: string, options?: {
|
|
303
|
+
preferredProviders?: string[];
|
|
304
|
+
allowedProviders?: string[];
|
|
305
|
+
}): Promise<{
|
|
261
306
|
data?: PaymentIntentResponse;
|
|
262
307
|
error?: PaymentError;
|
|
263
308
|
}>;
|
|
@@ -294,7 +339,7 @@ declare class ReevitAPIClient {
|
|
|
294
339
|
* Returns a short-lived token that contains Hubtel credentials
|
|
295
340
|
* Credentials are never exposed to the client directly
|
|
296
341
|
*/
|
|
297
|
-
createHubtelSession(paymentId: string): Promise<{
|
|
342
|
+
createHubtelSession(paymentId: string, clientSecret?: string): Promise<{
|
|
298
343
|
data?: HubtelSessionResponse;
|
|
299
344
|
error?: PaymentError;
|
|
300
345
|
}>;
|
|
@@ -391,4 +436,4 @@ declare function createInitialState(): ReevitState;
|
|
|
391
436
|
*/
|
|
392
437
|
declare function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState;
|
|
393
438
|
|
|
394
|
-
export { type APIErrorResponse, type CardFormData, 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 };
|
|
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;
|
|
@@ -148,12 +176,18 @@ 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;
|
|
151
183
|
}
|
|
152
184
|
interface HubtelSessionResponse {
|
|
153
185
|
/** Session token to use with Hubtel SDK */
|
|
154
186
|
token: string;
|
|
155
187
|
/** Merchant account number */
|
|
156
188
|
merchantAccount: string | number;
|
|
189
|
+
/** Base64 basic auth for Hubtel checkout (exposes credentials) */
|
|
190
|
+
basicAuth?: string;
|
|
157
191
|
/** Token expiration time in seconds */
|
|
158
192
|
expiresInSeconds: number;
|
|
159
193
|
/** Unix timestamp when token expires */
|
|
@@ -169,13 +203,14 @@ interface HubtelSessionResponse {
|
|
|
169
203
|
interface CreatePaymentIntentRequest {
|
|
170
204
|
amount: number;
|
|
171
205
|
currency: string;
|
|
172
|
-
method
|
|
206
|
+
method?: string;
|
|
173
207
|
country: string;
|
|
174
208
|
customer_id?: string;
|
|
175
209
|
metadata?: Record<string, unknown>;
|
|
176
210
|
description?: string;
|
|
177
211
|
policy?: {
|
|
178
212
|
prefer?: string[];
|
|
213
|
+
allowed_providers?: string[];
|
|
179
214
|
max_amount?: number;
|
|
180
215
|
blocked_bins?: string[];
|
|
181
216
|
allowed_bins?: string[];
|
|
@@ -200,6 +235,13 @@ interface PaymentIntentResponse {
|
|
|
200
235
|
fee_currency: string;
|
|
201
236
|
net_amount: number;
|
|
202
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>;
|
|
203
245
|
}
|
|
204
246
|
interface ConfirmPaymentRequest {
|
|
205
247
|
provider_ref_id: string;
|
|
@@ -236,7 +278,7 @@ interface APIErrorResponse {
|
|
|
236
278
|
}
|
|
237
279
|
interface ReevitAPIClientConfig {
|
|
238
280
|
/** Your Reevit public key */
|
|
239
|
-
publicKey
|
|
281
|
+
publicKey?: string;
|
|
240
282
|
/** Base URL for the Reevit API (defaults to production) */
|
|
241
283
|
baseUrl?: string;
|
|
242
284
|
/** Request timeout in milliseconds */
|
|
@@ -257,7 +299,10 @@ declare class ReevitAPIClient {
|
|
|
257
299
|
/**
|
|
258
300
|
* Creates a payment intent
|
|
259
301
|
*/
|
|
260
|
-
createPaymentIntent(config: ReevitCheckoutConfig, method
|
|
302
|
+
createPaymentIntent(config: ReevitCheckoutConfig, method?: PaymentMethod, country?: string, options?: {
|
|
303
|
+
preferredProviders?: string[];
|
|
304
|
+
allowedProviders?: string[];
|
|
305
|
+
}): Promise<{
|
|
261
306
|
data?: PaymentIntentResponse;
|
|
262
307
|
error?: PaymentError;
|
|
263
308
|
}>;
|
|
@@ -294,7 +339,7 @@ declare class ReevitAPIClient {
|
|
|
294
339
|
* Returns a short-lived token that contains Hubtel credentials
|
|
295
340
|
* Credentials are never exposed to the client directly
|
|
296
341
|
*/
|
|
297
|
-
createHubtelSession(paymentId: string): Promise<{
|
|
342
|
+
createHubtelSession(paymentId: string, clientSecret?: string): Promise<{
|
|
298
343
|
data?: HubtelSessionResponse;
|
|
299
344
|
error?: PaymentError;
|
|
300
345
|
}>;
|
|
@@ -391,4 +436,4 @@ declare function createInitialState(): ReevitState;
|
|
|
391
436
|
*/
|
|
392
437
|
declare function reevitReducer(state: ReevitState, action: ReevitAction): ReevitState;
|
|
393
438
|
|
|
394
|
-
export { type APIErrorResponse, type CardFormData, 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 };
|
|
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
|
/**
|
|
@@ -166,8 +176,9 @@ var ReevitAPIClient = class {
|
|
|
166
176
|
* Returns a short-lived token that contains Hubtel credentials
|
|
167
177
|
* Credentials are never exposed to the client directly
|
|
168
178
|
*/
|
|
169
|
-
async createHubtelSession(paymentId) {
|
|
170
|
-
|
|
179
|
+
async createHubtelSession(paymentId, clientSecret) {
|
|
180
|
+
const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : "";
|
|
181
|
+
return this.request("POST", `/v1/payments/hubtel/sessions/${paymentId}${query}`);
|
|
171
182
|
}
|
|
172
183
|
/**
|
|
173
184
|
* Maps SDK payment method to backend format
|
|
@@ -265,21 +276,55 @@ function createThemeVariables(theme) {
|
|
|
265
276
|
const variables = {};
|
|
266
277
|
if (theme.primaryColor) {
|
|
267
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
|
+
}
|
|
268
287
|
}
|
|
269
288
|
if (theme.backgroundColor) {
|
|
270
|
-
variables["--reevit-
|
|
289
|
+
variables["--reevit-background"] = theme.backgroundColor;
|
|
290
|
+
}
|
|
291
|
+
if (theme.surfaceColor) {
|
|
292
|
+
variables["--reevit-surface"] = theme.surfaceColor;
|
|
271
293
|
}
|
|
272
294
|
if (theme.textColor) {
|
|
273
295
|
variables["--reevit-text"] = theme.textColor;
|
|
274
296
|
}
|
|
297
|
+
if (theme.mutedTextColor) {
|
|
298
|
+
variables["--reevit-text-secondary"] = theme.mutedTextColor;
|
|
299
|
+
}
|
|
275
300
|
if (theme.borderRadius) {
|
|
276
301
|
variables["--reevit-radius"] = theme.borderRadius;
|
|
302
|
+
variables["--reevit-radius-sm"] = theme.borderRadius;
|
|
303
|
+
variables["--reevit-radius-lg"] = theme.borderRadius;
|
|
277
304
|
}
|
|
278
305
|
if (theme.fontFamily) {
|
|
279
306
|
variables["--reevit-font"] = theme.fontFamily;
|
|
280
307
|
}
|
|
281
308
|
return variables;
|
|
282
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
|
+
}
|
|
283
328
|
function cn(...classes) {
|
|
284
329
|
return classes.filter(Boolean).join(" ");
|
|
285
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 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 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 * 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(paymentId: string): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}`);\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
|
/**
|
|
@@ -129,8 +139,9 @@ var ReevitAPIClient = class {
|
|
|
129
139
|
* Returns a short-lived token that contains Hubtel credentials
|
|
130
140
|
* Credentials are never exposed to the client directly
|
|
131
141
|
*/
|
|
132
|
-
async createHubtelSession(paymentId) {
|
|
133
|
-
|
|
142
|
+
async createHubtelSession(paymentId, clientSecret) {
|
|
143
|
+
const query = clientSecret ? `?client_secret=${encodeURIComponent(clientSecret)}` : "";
|
|
144
|
+
return this.request("POST", `/v1/payments/hubtel/sessions/${paymentId}${query}`);
|
|
134
145
|
}
|
|
135
146
|
/**
|
|
136
147
|
* Maps SDK payment method to backend format
|
|
@@ -228,21 +239,55 @@ function createThemeVariables(theme) {
|
|
|
228
239
|
const variables = {};
|
|
229
240
|
if (theme.primaryColor) {
|
|
230
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
|
+
}
|
|
231
250
|
}
|
|
232
251
|
if (theme.backgroundColor) {
|
|
233
|
-
variables["--reevit-
|
|
252
|
+
variables["--reevit-background"] = theme.backgroundColor;
|
|
253
|
+
}
|
|
254
|
+
if (theme.surfaceColor) {
|
|
255
|
+
variables["--reevit-surface"] = theme.surfaceColor;
|
|
234
256
|
}
|
|
235
257
|
if (theme.textColor) {
|
|
236
258
|
variables["--reevit-text"] = theme.textColor;
|
|
237
259
|
}
|
|
260
|
+
if (theme.mutedTextColor) {
|
|
261
|
+
variables["--reevit-text-secondary"] = theme.mutedTextColor;
|
|
262
|
+
}
|
|
238
263
|
if (theme.borderRadius) {
|
|
239
264
|
variables["--reevit-radius"] = theme.borderRadius;
|
|
265
|
+
variables["--reevit-radius-sm"] = theme.borderRadius;
|
|
266
|
+
variables["--reevit-radius-lg"] = theme.borderRadius;
|
|
240
267
|
}
|
|
241
268
|
if (theme.fontFamily) {
|
|
242
269
|
variables["--reevit-font"] = theme.fontFamily;
|
|
243
270
|
}
|
|
244
271
|
return variables;
|
|
245
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
|
+
}
|
|
246
291
|
function cn(...classes) {
|
|
247
292
|
return classes.filter(Boolean).join(" ");
|
|
248
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, 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 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 * 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(paymentId: string): Promise<{ data?: HubtelSessionResponse; error?: PaymentError }> {\n return this.request<HubtelSessionResponse>('POST', `/v1/payments/hubtel/sessions/${paymentId}`);\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;
|