@reevit/react 0.5.9 → 0.7.0
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/README.md +29 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +192 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -78
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +457 -237
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Unified Payment Widget for React Applications. Accept card and mobile money paym
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @reevit/react
|
|
8
|
+
npm install @reevit/react
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
@@ -23,6 +23,7 @@ function App() {
|
|
|
23
23
|
amount={10000} // Amount in smallest unit (e.g., pesewas for GHS)
|
|
24
24
|
currency="GHS"
|
|
25
25
|
email="customer@example.com"
|
|
26
|
+
idempotencyKey={`order_${Date.now()}`}
|
|
26
27
|
onSuccess={(result) => {
|
|
27
28
|
console.log('Payment success!', result);
|
|
28
29
|
alert(`Payment of ${result.currency} ${result.amount/100} successful!`);
|
|
@@ -37,6 +38,19 @@ function App() {
|
|
|
37
38
|
}
|
|
38
39
|
```
|
|
39
40
|
|
|
41
|
+
## Idempotency
|
|
42
|
+
|
|
43
|
+
Provide an `idempotencyKey` tied to your order/cart to avoid duplicate intent creation and enable safe retries.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<ReevitCheckout
|
|
47
|
+
publicKey="pk_test_your_key"
|
|
48
|
+
amount={10000}
|
|
49
|
+
currency="GHS"
|
|
50
|
+
idempotencyKey="order_12345"
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
53
|
+
|
|
40
54
|
## Payment Links
|
|
41
55
|
|
|
42
56
|
If you have a hosted payment link, pass the link code. The widget will create the payment intent from the public link endpoint.
|
|
@@ -279,6 +293,20 @@ function MpesaPayment() {
|
|
|
279
293
|
| Monnify | NG | Card, Bank Transfer, USSD |
|
|
280
294
|
| M-Pesa | KE, TZ | Mobile Money (STK Push) |
|
|
281
295
|
|
|
296
|
+
## Release Notes
|
|
297
|
+
|
|
298
|
+
### v0.7.0
|
|
299
|
+
|
|
300
|
+
- Redesigned checkout UI with premium visual polish
|
|
301
|
+
- New typography system: Grato Classic for body, ABC Repro Mono for amounts
|
|
302
|
+
- Layered shadow system for natural depth
|
|
303
|
+
- Replaced emoji icons with inline SVG icons (consistent cross-platform rendering)
|
|
304
|
+
- New loading animation (three-dot pulse), success glow effect, and countdown bar
|
|
305
|
+
- Improved dark mode contrast and surfaces
|
|
306
|
+
- Mobile bottom-sheet pattern with touch-friendly targets (44px+)
|
|
307
|
+
- Smoother animations with cubic-bezier easing curves
|
|
308
|
+
- Removed external Google Fonts dependency (zero network requests for fonts)
|
|
309
|
+
|
|
282
310
|
## License
|
|
283
311
|
|
|
284
312
|
MIT © [Reevit](https://reevit.io)
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,8 @@ interface ReevitCheckoutConfig {
|
|
|
22
22
|
customerName?: string;
|
|
23
23
|
/** Unique reference for this transaction */
|
|
24
24
|
reference?: string;
|
|
25
|
+
/** Optional idempotency key to safely retry or dedupe intent creation */
|
|
26
|
+
idempotencyKey?: string;
|
|
25
27
|
/** Additional metadata to attach to the payment */
|
|
26
28
|
metadata?: Record<string, unknown>;
|
|
27
29
|
/** Custom fields for payment links (if applicable) */
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ interface ReevitCheckoutConfig {
|
|
|
22
22
|
customerName?: string;
|
|
23
23
|
/** Unique reference for this transaction */
|
|
24
24
|
reference?: string;
|
|
25
|
+
/** Optional idempotency key to safely retry or dedupe intent creation */
|
|
26
|
+
idempotencyKey?: string;
|
|
25
27
|
/** Additional metadata to attach to the payment */
|
|
26
28
|
metadata?: Record<string, unknown>;
|
|
27
29
|
/** Custom fields for payment links (if applicable) */
|
package/dist/index.js
CHANGED
|
@@ -259,7 +259,7 @@ var ReevitAPIClient = class {
|
|
|
259
259
|
allowed_providers: options?.allowedProviders
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
|
-
const idempotencyKey = generateIdempotencyKey({
|
|
262
|
+
const idempotencyKey = config.idempotencyKey || generateIdempotencyKey({
|
|
263
263
|
amount: config.amount,
|
|
264
264
|
currency: config.currency,
|
|
265
265
|
customer: config.email || config.metadata?.customerId || "",
|
|
@@ -438,6 +438,72 @@ function normalizeBranding(branding) {
|
|
|
438
438
|
setIf("selectedBorderColor", getString(raw.selectedBorderColor ?? raw.selected_border_color));
|
|
439
439
|
return theme;
|
|
440
440
|
}
|
|
441
|
+
var INTENT_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
442
|
+
var intentCache = /* @__PURE__ */ new Map();
|
|
443
|
+
function pruneIntentCache(now = Date.now()) {
|
|
444
|
+
for (const [key, entry] of intentCache) {
|
|
445
|
+
if (entry.expiresAt <= now) {
|
|
446
|
+
intentCache.delete(key);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function getIntentCacheEntry(key) {
|
|
451
|
+
const entry = intentCache.get(key);
|
|
452
|
+
if (!entry) {
|
|
453
|
+
return void 0;
|
|
454
|
+
}
|
|
455
|
+
if (entry.expiresAt <= Date.now()) {
|
|
456
|
+
intentCache.delete(key);
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
459
|
+
return entry;
|
|
460
|
+
}
|
|
461
|
+
function setIntentCacheEntry(key, update) {
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
const existing = getIntentCacheEntry(key);
|
|
464
|
+
const next = {
|
|
465
|
+
...existing,
|
|
466
|
+
...update,
|
|
467
|
+
expiresAt: now + INTENT_CACHE_TTL_MS
|
|
468
|
+
};
|
|
469
|
+
intentCache.set(key, next);
|
|
470
|
+
return next;
|
|
471
|
+
}
|
|
472
|
+
function clearIntentCacheEntry(key) {
|
|
473
|
+
intentCache.delete(key);
|
|
474
|
+
}
|
|
475
|
+
function buildIdempotencyPayload(config, method, options) {
|
|
476
|
+
const payload = {
|
|
477
|
+
amount: config.amount,
|
|
478
|
+
currency: config.currency,
|
|
479
|
+
email: config.email || "",
|
|
480
|
+
phone: config.phone || "",
|
|
481
|
+
customerName: config.customerName || "",
|
|
482
|
+
paymentLinkCode: config.paymentLinkCode || "",
|
|
483
|
+
paymentMethods: config.paymentMethods || [],
|
|
484
|
+
metadata: config.metadata || {},
|
|
485
|
+
customFields: config.customFields || {},
|
|
486
|
+
method: method || "",
|
|
487
|
+
preferredProvider: options?.preferredProvider || "",
|
|
488
|
+
allowedProviders: options?.allowedProviders || [],
|
|
489
|
+
publicKey: config.publicKey || ""
|
|
490
|
+
};
|
|
491
|
+
if (config.reference) {
|
|
492
|
+
payload.reference = config.reference;
|
|
493
|
+
}
|
|
494
|
+
return payload;
|
|
495
|
+
}
|
|
496
|
+
function resolveIntentIdentity(config, method, options) {
|
|
497
|
+
pruneIntentCache();
|
|
498
|
+
const idempotencyKey = config.idempotencyKey || generateIdempotencyKey(buildIdempotencyPayload(config, method, options));
|
|
499
|
+
const existing = getIntentCacheEntry(idempotencyKey);
|
|
500
|
+
const reference = config.reference || existing?.reference || generateReference();
|
|
501
|
+
const cacheEntry = setIntentCacheEntry(idempotencyKey, { reference });
|
|
502
|
+
return { idempotencyKey, reference, cacheEntry };
|
|
503
|
+
}
|
|
504
|
+
function isPaymentError(error) {
|
|
505
|
+
return typeof error === "object" && error !== null && "code" in error && "message" in error;
|
|
506
|
+
}
|
|
441
507
|
function mapToPaymentIntent(response, config) {
|
|
442
508
|
return {
|
|
443
509
|
id: response.id,
|
|
@@ -471,14 +537,22 @@ function useReevit(options) {
|
|
|
471
537
|
selectedMethod: config.initialPaymentIntent?.availableMethods?.length === 1 ? config.initialPaymentIntent.availableMethods[0] : null
|
|
472
538
|
});
|
|
473
539
|
const apiClientRef = react.useRef(null);
|
|
474
|
-
const
|
|
540
|
+
const stateRef = react.useRef(state);
|
|
541
|
+
react.useEffect(() => {
|
|
542
|
+
stateRef.current = state;
|
|
543
|
+
}, [state]);
|
|
544
|
+
const currentIntentKeyRef = react.useRef(
|
|
545
|
+
config.initialPaymentIntent ? `initial:${config.initialPaymentIntent.id}` : null
|
|
546
|
+
);
|
|
475
547
|
const initRequestIdRef = react.useRef(0);
|
|
476
548
|
react.useEffect(() => {
|
|
477
549
|
if (config.initialPaymentIntent) {
|
|
478
550
|
if (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id) {
|
|
479
551
|
dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
|
|
480
|
-
|
|
552
|
+
currentIntentKeyRef.current = `initial:${config.initialPaymentIntent.id}`;
|
|
481
553
|
}
|
|
554
|
+
} else if (currentIntentKeyRef.current?.startsWith("initial:")) {
|
|
555
|
+
currentIntentKeyRef.current = null;
|
|
482
556
|
}
|
|
483
557
|
}, [config.initialPaymentIntent, state.paymentIntent?.id]);
|
|
484
558
|
if (!apiClientRef.current) {
|
|
@@ -492,61 +566,60 @@ function useReevit(options) {
|
|
|
492
566
|
}, [state.status, onStateChange]);
|
|
493
567
|
const initialize = react.useCallback(
|
|
494
568
|
async (method, options2) => {
|
|
495
|
-
if (
|
|
569
|
+
if (config.initialPaymentIntent) {
|
|
496
570
|
return;
|
|
497
571
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
dispatch({ type: "INIT_START" });
|
|
572
|
+
let requestId = 0;
|
|
573
|
+
let intentKey = null;
|
|
501
574
|
try {
|
|
502
575
|
const apiClient = apiClientRef.current;
|
|
503
576
|
if (!apiClient) {
|
|
504
577
|
throw new Error("API client not initialized");
|
|
505
578
|
}
|
|
506
|
-
const reference = config.reference || generateReference();
|
|
507
579
|
const country = detectCountryFromCurrency(config.currency);
|
|
508
580
|
const defaultMethod = config.paymentMethods && config.paymentMethods.length === 1 ? config.paymentMethods[0] : void 0;
|
|
509
581
|
const paymentMethod = method ?? defaultMethod;
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
582
|
+
const identity = resolveIntentIdentity(config, paymentMethod, options2);
|
|
583
|
+
const { idempotencyKey, reference, cacheEntry } = identity;
|
|
584
|
+
intentKey = idempotencyKey;
|
|
585
|
+
if (currentIntentKeyRef.current === idempotencyKey && stateRef.current.paymentIntent) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
currentIntentKeyRef.current = idempotencyKey;
|
|
589
|
+
requestId = ++initRequestIdRef.current;
|
|
590
|
+
if (stateRef.current.status !== "loading") {
|
|
591
|
+
dispatch({ type: "INIT_START" });
|
|
592
|
+
}
|
|
593
|
+
const requestIntent = async () => {
|
|
594
|
+
if (config.paymentLinkCode) {
|
|
595
|
+
const response = await fetch(
|
|
596
|
+
`${apiBaseUrl || DEFAULT_PUBLIC_API_BASE_URL}/v1/pay/${config.paymentLinkCode}/pay`,
|
|
597
|
+
{
|
|
598
|
+
method: "POST",
|
|
599
|
+
headers: {
|
|
600
|
+
"Content-Type": "application/json",
|
|
601
|
+
"Idempotency-Key": idempotencyKey
|
|
602
|
+
},
|
|
603
|
+
body: JSON.stringify({
|
|
604
|
+
amount: config.amount,
|
|
605
|
+
email: config.email || "",
|
|
606
|
+
name: config.customerName || "",
|
|
607
|
+
phone: config.phone || "",
|
|
608
|
+
method: paymentMethod,
|
|
609
|
+
country,
|
|
610
|
+
provider: options2?.preferredProvider || options2?.allowedProviders?.[0],
|
|
611
|
+
custom_fields: config.customFields
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
);
|
|
615
|
+
const responseData = await response.json().catch(() => ({}));
|
|
616
|
+
if (!response.ok) {
|
|
617
|
+
throw buildPaymentLinkError(response, responseData);
|
|
539
618
|
}
|
|
540
|
-
|
|
541
|
-
const responseData = await response.json().catch(() => ({}));
|
|
542
|
-
if (!response.ok) {
|
|
543
|
-
error = buildPaymentLinkError(response, responseData);
|
|
544
|
-
} else {
|
|
545
|
-
data = responseData;
|
|
619
|
+
return responseData;
|
|
546
620
|
}
|
|
547
|
-
} else {
|
|
548
621
|
const result = await apiClient.createPaymentIntent(
|
|
549
|
-
{ ...config, reference },
|
|
622
|
+
{ ...config, reference, idempotencyKey },
|
|
550
623
|
paymentMethod,
|
|
551
624
|
country,
|
|
552
625
|
{
|
|
@@ -554,35 +627,43 @@ function useReevit(options) {
|
|
|
554
627
|
allowedProviders: options2?.allowedProviders
|
|
555
628
|
}
|
|
556
629
|
);
|
|
557
|
-
|
|
558
|
-
|
|
630
|
+
if (result.error) {
|
|
631
|
+
throw result.error;
|
|
632
|
+
}
|
|
633
|
+
if (!result.data) {
|
|
634
|
+
throw {
|
|
635
|
+
code: "INIT_FAILED",
|
|
636
|
+
message: "No data received from API",
|
|
637
|
+
recoverable: true
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
return result.data;
|
|
641
|
+
};
|
|
642
|
+
let data;
|
|
643
|
+
if (cacheEntry?.response) {
|
|
644
|
+
data = cacheEntry.response;
|
|
645
|
+
} else {
|
|
646
|
+
let intentPromise = cacheEntry?.promise;
|
|
647
|
+
if (!intentPromise) {
|
|
648
|
+
intentPromise = requestIntent();
|
|
649
|
+
setIntentCacheEntry(idempotencyKey, { promise: intentPromise });
|
|
650
|
+
}
|
|
651
|
+
data = await intentPromise;
|
|
652
|
+
setIntentCacheEntry(idempotencyKey, { response: data, promise: void 0 });
|
|
559
653
|
}
|
|
560
654
|
if (requestId !== initRequestIdRef.current) {
|
|
561
655
|
return;
|
|
562
656
|
}
|
|
563
|
-
|
|
564
|
-
dispatch({ type: "INIT_ERROR", payload: error });
|
|
565
|
-
onError?.(error);
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
if (!data) {
|
|
569
|
-
const noDataError = {
|
|
570
|
-
code: "INIT_FAILED",
|
|
571
|
-
message: "No data received from API",
|
|
572
|
-
recoverable: true
|
|
573
|
-
};
|
|
574
|
-
dispatch({ type: "INIT_ERROR", payload: noDataError });
|
|
575
|
-
onError?.(noDataError);
|
|
576
|
-
initializingRef.current = false;
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
const paymentIntent = mapToPaymentIntent(data, { ...config, reference });
|
|
657
|
+
const paymentIntent = mapToPaymentIntent(data, { ...config, reference, idempotencyKey });
|
|
580
658
|
dispatch({ type: "INIT_SUCCESS", payload: paymentIntent });
|
|
581
659
|
} catch (err) {
|
|
660
|
+
if (intentKey) {
|
|
661
|
+
clearIntentCacheEntry(intentKey);
|
|
662
|
+
}
|
|
582
663
|
if (requestId !== initRequestIdRef.current) {
|
|
583
664
|
return;
|
|
584
665
|
}
|
|
585
|
-
const error = {
|
|
666
|
+
const error = isPaymentError(err) ? err : {
|
|
586
667
|
code: "INIT_FAILED",
|
|
587
668
|
message: err instanceof Error ? err.message : "Failed to initialize checkout",
|
|
588
669
|
recoverable: true,
|
|
@@ -590,7 +671,6 @@ function useReevit(options) {
|
|
|
590
671
|
};
|
|
591
672
|
dispatch({ type: "INIT_ERROR", payload: error });
|
|
592
673
|
onError?.(error);
|
|
593
|
-
initializingRef.current = false;
|
|
594
674
|
}
|
|
595
675
|
},
|
|
596
676
|
[config, onError, apiBaseUrl]
|
|
@@ -671,7 +751,7 @@ function useReevit(options) {
|
|
|
671
751
|
} catch {
|
|
672
752
|
}
|
|
673
753
|
}
|
|
674
|
-
|
|
754
|
+
currentIntentKeyRef.current = null;
|
|
675
755
|
initRequestIdRef.current += 1;
|
|
676
756
|
dispatch({ type: "RESET" });
|
|
677
757
|
}, [state.paymentIntent, state.status]);
|
|
@@ -737,30 +817,47 @@ function detectCountryFromCurrency(currency) {
|
|
|
737
817
|
};
|
|
738
818
|
return currencyToCountry[currency.toUpperCase()] || "GH";
|
|
739
819
|
}
|
|
820
|
+
var MethodIcons = {
|
|
821
|
+
card: () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "4", width: "22", height: "16", rx: "3" }),
|
|
823
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "10", x2: "23", y2: "10" }),
|
|
824
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "15", x2: "9", y2: "15" })
|
|
825
|
+
] }),
|
|
826
|
+
mobile_money: () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
827
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "2", width: "14", height: "20", rx: "3" }),
|
|
828
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "18", x2: "12", y2: "18.01", strokeWidth: "2", strokeLinecap: "round" })
|
|
829
|
+
] }),
|
|
830
|
+
bank_transfer: () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
831
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 21h18" }),
|
|
832
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10h18" }),
|
|
833
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3l9 7H3l9-7z" }),
|
|
834
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 10v8" }),
|
|
835
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 10v8" }),
|
|
836
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 10v8" }),
|
|
837
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 10v8" })
|
|
838
|
+
] }),
|
|
839
|
+
apple_pay: () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) }),
|
|
840
|
+
google_pay: () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12.24 10.285V14.4h6.806c-.275 1.765-2.056 5.174-6.806 5.174-4.095 0-7.439-3.389-7.439-7.574s3.345-7.574 7.439-7.574c2.33 0 3.891.989 4.785 1.849l3.254-3.138C18.189 1.186 15.479 0 12.24 0c-6.635 0-12 5.365-12 12s5.365 12 12 12c6.926 0 11.52-4.869 11.52-11.726 0-.788-.085-1.39-.189-1.989H12.24z", fill: "currentColor" }) })
|
|
841
|
+
};
|
|
740
842
|
var methodConfig = {
|
|
741
843
|
card: {
|
|
742
844
|
label: "Card",
|
|
743
|
-
icon: "\u{1F4B3}",
|
|
744
845
|
description: "Pay with Visa, Mastercard, or other cards"
|
|
745
846
|
},
|
|
746
847
|
mobile_money: {
|
|
747
848
|
label: "Mobile Money",
|
|
748
|
-
icon: "\u{1F4F1}",
|
|
749
849
|
description: "MTN, Telecel, AirtelTigo Money"
|
|
750
850
|
},
|
|
751
851
|
bank_transfer: {
|
|
752
852
|
label: "Bank Transfer",
|
|
753
|
-
icon: "\u{1F3E6}",
|
|
754
853
|
description: "Pay directly from your bank account"
|
|
755
854
|
},
|
|
756
855
|
apple_pay: {
|
|
757
856
|
label: "Apple Pay",
|
|
758
|
-
icon: "\u{1F34E}",
|
|
759
857
|
description: "Pay with Apple Pay"
|
|
760
858
|
},
|
|
761
859
|
google_pay: {
|
|
762
860
|
label: "Google Pay",
|
|
763
|
-
icon: "\u{1F916}",
|
|
764
861
|
description: "Pay with Google Pay"
|
|
765
862
|
}
|
|
766
863
|
};
|
|
@@ -810,7 +907,6 @@ function PaymentMethodSelector({
|
|
|
810
907
|
),
|
|
811
908
|
style: selectedTheme?.backgroundColor ? { backgroundColor: selectedTheme.backgroundColor } : void 0,
|
|
812
909
|
children: methods.map((method, index) => {
|
|
813
|
-
const config = methodConfig[method];
|
|
814
910
|
const isSelected = selectedMethod === method;
|
|
815
911
|
const methodLabel = getMethodLabel(method);
|
|
816
912
|
const methodDescription = getMethodDescription(method);
|
|
@@ -841,7 +937,7 @@ function PaymentMethodSelector({
|
|
|
841
937
|
className: "reevit-method-option__logo-img"
|
|
842
938
|
},
|
|
843
939
|
i
|
|
844
|
-
)) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-method-option__icon", children:
|
|
940
|
+
)) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-method-option__icon", children: MethodIcons[method]() }) }),
|
|
845
941
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-method-option__content", children: [
|
|
846
942
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-method-option__label", style: selectedTheme?.textColor ? { color: selectedTheme.textColor } : void 0, children: methodLabel }),
|
|
847
943
|
!isGrid && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-method-option__description", style: selectedTheme?.descriptionColor ? { color: selectedTheme.descriptionColor } : void 0, children: methodDescription })
|
|
@@ -2401,7 +2497,11 @@ function ReevitCheckout({
|
|
|
2401
2497
|
const renderContent = () => {
|
|
2402
2498
|
if (status === "loading" || status === "processing") {
|
|
2403
2499
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-loading reevit-animate-fade-in", children: [
|
|
2404
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2500
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-dot-pulse", children: [
|
|
2501
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-dot-pulse__dot" }),
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-dot-pulse__dot" }),
|
|
2503
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-dot-pulse__dot" })
|
|
2504
|
+
] }),
|
|
2405
2505
|
/* @__PURE__ */ jsxRuntime.jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
|
|
2406
2506
|
] });
|
|
2407
2507
|
}
|
|
@@ -2417,12 +2517,22 @@ function ReevitCheckout({
|
|
|
2417
2517
|
"Reference: ",
|
|
2418
2518
|
result.reference
|
|
2419
2519
|
] }),
|
|
2420
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "reevit-success__redirect", children: "Redirecting in a moment..." })
|
|
2520
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "reevit-success__redirect", children: "Redirecting in a moment..." }),
|
|
2521
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2522
|
+
"div",
|
|
2523
|
+
{
|
|
2524
|
+
className: "reevit-success__countdown",
|
|
2525
|
+
style: { animationDuration: `${successDelayMs}ms` }
|
|
2526
|
+
}
|
|
2527
|
+
)
|
|
2421
2528
|
] });
|
|
2422
2529
|
}
|
|
2423
2530
|
if (status === "failed" && error && !error.recoverable) {
|
|
2424
2531
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-error reevit-animate-fade-in", children: [
|
|
2425
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: "
|
|
2532
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2533
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2534
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2535
|
+
] }) }),
|
|
2426
2536
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Failed" }),
|
|
2427
2537
|
/* @__PURE__ */ jsxRuntime.jsx("p", { children: error.message }),
|
|
2428
2538
|
/* @__PURE__ */ jsxRuntime.jsx("button", { className: "reevit-btn reevit-btn--primary", onClick: handleBack, children: "Try Again" })
|
|
@@ -2544,7 +2654,11 @@ function ReevitCheckout({
|
|
|
2544
2654
|
);
|
|
2545
2655
|
default:
|
|
2546
2656
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-error", children: [
|
|
2547
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: "
|
|
2657
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2658
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
|
|
2659
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
|
|
2660
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
2661
|
+
] }) }),
|
|
2548
2662
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Provider Not Supported" }),
|
|
2549
2663
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
2550
2664
|
"Provider (",
|