@reevit/react 0.3.8 → 0.4.1
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 +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +159 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +159 -13
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +152 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -206,6 +206,17 @@ var ReevitAPIClient = class {
|
|
|
206
206
|
async cancelPaymentIntent(paymentId) {
|
|
207
207
|
return this.request("POST", `/v1/payments/${paymentId}/cancel`);
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Creates a Hubtel session token for secure checkout.
|
|
211
|
+
* This endpoint generates a short-lived token that maps to Hubtel credentials server-side,
|
|
212
|
+
* avoiding exposure of sensitive credentials to the client.
|
|
213
|
+
*
|
|
214
|
+
* @param paymentId - The payment intent ID for Hubtel checkout
|
|
215
|
+
* @returns Hubtel session with token, merchant account, and expiry information
|
|
216
|
+
*/
|
|
217
|
+
async createHubtelSession(paymentId) {
|
|
218
|
+
return this.request("POST", `/v1/payments/hubtel/sessions/${paymentId}`);
|
|
219
|
+
}
|
|
209
220
|
/**
|
|
210
221
|
* Maps SDK payment method to backend format
|
|
211
222
|
*/
|
|
@@ -831,12 +842,15 @@ function PaystackBridge({
|
|
|
831
842
|
] }) });
|
|
832
843
|
}
|
|
833
844
|
function HubtelBridge({
|
|
845
|
+
paymentId,
|
|
846
|
+
publicKey,
|
|
834
847
|
merchantAccount,
|
|
835
848
|
amount,
|
|
836
849
|
reference,
|
|
837
850
|
phone,
|
|
838
851
|
description = "Payment",
|
|
839
852
|
callbackUrl,
|
|
853
|
+
hubtelSessionToken,
|
|
840
854
|
basicAuth,
|
|
841
855
|
onSuccess,
|
|
842
856
|
onError,
|
|
@@ -845,7 +859,46 @@ function HubtelBridge({
|
|
|
845
859
|
}) {
|
|
846
860
|
const initialized = useRef(false);
|
|
847
861
|
const checkoutRef = useRef(null);
|
|
862
|
+
const [authValue, setAuthValue] = useState("");
|
|
863
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
864
|
+
useEffect(() => {
|
|
865
|
+
const fetchAuth = async () => {
|
|
866
|
+
if (hubtelSessionToken) {
|
|
867
|
+
setIsLoading(true);
|
|
868
|
+
try {
|
|
869
|
+
const client = createReevitClient({ publicKey });
|
|
870
|
+
const { data, error } = await client.createHubtelSession(paymentId);
|
|
871
|
+
if (error) {
|
|
872
|
+
onError({
|
|
873
|
+
code: "SESSION_ERROR",
|
|
874
|
+
message: error.message || "Failed to create Hubtel session",
|
|
875
|
+
recoverable: true
|
|
876
|
+
});
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (data) {
|
|
880
|
+
setAuthValue(data.token);
|
|
881
|
+
}
|
|
882
|
+
} catch (err) {
|
|
883
|
+
onError({
|
|
884
|
+
code: "SESSION_ERROR",
|
|
885
|
+
message: "Failed to create Hubtel session",
|
|
886
|
+
recoverable: true,
|
|
887
|
+
originalError: err
|
|
888
|
+
});
|
|
889
|
+
} finally {
|
|
890
|
+
setIsLoading(false);
|
|
891
|
+
}
|
|
892
|
+
} else if (basicAuth) {
|
|
893
|
+
setAuthValue(basicAuth);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
fetchAuth();
|
|
897
|
+
}, [paymentId, publicKey, hubtelSessionToken, basicAuth, onError]);
|
|
848
898
|
const startPayment = useCallback(async () => {
|
|
899
|
+
if (isLoading || !authValue) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
849
902
|
try {
|
|
850
903
|
const checkout = new CheckoutSdk();
|
|
851
904
|
checkoutRef.current = checkout;
|
|
@@ -860,7 +913,9 @@ function HubtelBridge({
|
|
|
860
913
|
branding: "enabled",
|
|
861
914
|
callbackUrl: callbackUrl || window.location.href,
|
|
862
915
|
merchantAccount: typeof merchantAccount === "string" ? parseInt(merchantAccount, 10) : merchantAccount,
|
|
863
|
-
|
|
916
|
+
// Use session token or basicAuth for authentication
|
|
917
|
+
// Session tokens are base64-encoded credentials fetched securely from the server
|
|
918
|
+
basicAuth: authValue || ""
|
|
864
919
|
};
|
|
865
920
|
checkout.openModal({
|
|
866
921
|
purchaseInfo,
|
|
@@ -904,13 +959,13 @@ function HubtelBridge({
|
|
|
904
959
|
};
|
|
905
960
|
onError(error);
|
|
906
961
|
}
|
|
907
|
-
}, [merchantAccount, amount, reference, phone, description, callbackUrl,
|
|
962
|
+
}, [merchantAccount, amount, reference, phone, description, callbackUrl, authValue, isLoading, onSuccess, onError, onClose]);
|
|
908
963
|
useEffect(() => {
|
|
909
|
-
if (autoStart && !initialized.current) {
|
|
964
|
+
if (autoStart && !initialized.current && !isLoading && authValue) {
|
|
910
965
|
initialized.current = true;
|
|
911
966
|
startPayment();
|
|
912
967
|
}
|
|
913
|
-
}, [autoStart, startPayment]);
|
|
968
|
+
}, [autoStart, startPayment, isLoading, authValue]);
|
|
914
969
|
return /* @__PURE__ */ jsx("div", { className: "reevit-psp-bridge reevit-psp-bridge--hubtel", children: /* @__PURE__ */ jsxs("div", { className: "reevit-psp-bridge__loading", children: [
|
|
915
970
|
/* @__PURE__ */ jsx("div", { className: "reevit-spinner" }),
|
|
916
971
|
/* @__PURE__ */ jsx("p", { children: "Connecting to Hubtel..." })
|
|
@@ -1382,6 +1437,87 @@ function loadStripeScript() {
|
|
|
1382
1437
|
});
|
|
1383
1438
|
return stripeScriptPromise;
|
|
1384
1439
|
}
|
|
1440
|
+
var getReevitStripeAppearance = () => {
|
|
1441
|
+
const isDarkMode = typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
1442
|
+
return {
|
|
1443
|
+
theme: isDarkMode ? "night" : "stripe",
|
|
1444
|
+
variables: {
|
|
1445
|
+
colorPrimary: isDarkMode ? "#fafafa" : "#171717",
|
|
1446
|
+
colorBackground: isDarkMode ? "#171717" : "#ffffff",
|
|
1447
|
+
colorText: isDarkMode ? "#fafafa" : "#171717",
|
|
1448
|
+
colorTextSecondary: isDarkMode ? "#a3a3a3" : "#737373",
|
|
1449
|
+
colorTextPlaceholder: isDarkMode ? "#737373" : "#a3a3a3",
|
|
1450
|
+
colorDanger: "#dc2626",
|
|
1451
|
+
fontFamily: '"Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
1452
|
+
fontSizeBase: "14px",
|
|
1453
|
+
fontSizeSm: "12px",
|
|
1454
|
+
fontSizeLg: "16px",
|
|
1455
|
+
fontWeightNormal: "400",
|
|
1456
|
+
fontWeightMedium: "500",
|
|
1457
|
+
fontWeightBold: "600",
|
|
1458
|
+
borderRadius: "10px",
|
|
1459
|
+
spacingUnit: "4px",
|
|
1460
|
+
spacingGridRow: "16px",
|
|
1461
|
+
spacingGridColumn: "16px"
|
|
1462
|
+
},
|
|
1463
|
+
rules: {
|
|
1464
|
+
".Input": {
|
|
1465
|
+
border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid #e5e5e5",
|
|
1466
|
+
boxShadow: "none",
|
|
1467
|
+
padding: "12px 14px",
|
|
1468
|
+
fontSize: "14px",
|
|
1469
|
+
backgroundColor: isDarkMode ? "#262626" : "#ffffff"
|
|
1470
|
+
},
|
|
1471
|
+
".Input:focus": {
|
|
1472
|
+
border: isDarkMode ? "1px solid #a3a3a3" : "1px solid #737373",
|
|
1473
|
+
boxShadow: "none",
|
|
1474
|
+
backgroundColor: isDarkMode ? "#262626" : "#fafafa"
|
|
1475
|
+
},
|
|
1476
|
+
".Input--invalid": {
|
|
1477
|
+
border: "1px solid #dc2626",
|
|
1478
|
+
boxShadow: "none"
|
|
1479
|
+
},
|
|
1480
|
+
".Label": {
|
|
1481
|
+
fontSize: "12px",
|
|
1482
|
+
fontWeight: "500",
|
|
1483
|
+
marginBottom: "6px",
|
|
1484
|
+
color: isDarkMode ? "#a3a3a3" : "#737373",
|
|
1485
|
+
textTransform: "none",
|
|
1486
|
+
letterSpacing: "0.02em"
|
|
1487
|
+
},
|
|
1488
|
+
".Tab": {
|
|
1489
|
+
border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid #e5e5e5",
|
|
1490
|
+
backgroundColor: "transparent",
|
|
1491
|
+
borderRadius: "10px",
|
|
1492
|
+
padding: "12px 14px"
|
|
1493
|
+
},
|
|
1494
|
+
".Tab:hover": {
|
|
1495
|
+
border: isDarkMode ? "1px solid #a3a3a3" : "1px solid #737373",
|
|
1496
|
+
backgroundColor: isDarkMode ? "#262626" : "#fafafa"
|
|
1497
|
+
},
|
|
1498
|
+
".Tab--selected": {
|
|
1499
|
+
border: isDarkMode ? "1px solid #fafafa" : "1px solid #171717",
|
|
1500
|
+
backgroundColor: isDarkMode ? "#262626" : "#fafafa"
|
|
1501
|
+
},
|
|
1502
|
+
".TabIcon": {
|
|
1503
|
+
marginRight: "8px"
|
|
1504
|
+
},
|
|
1505
|
+
".Block": {
|
|
1506
|
+
border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid #e5e5e5",
|
|
1507
|
+
borderRadius: "10px",
|
|
1508
|
+
backgroundColor: isDarkMode ? "#262626" : "#ffffff",
|
|
1509
|
+
padding: "12px 14px"
|
|
1510
|
+
},
|
|
1511
|
+
".CheckboxInput": {
|
|
1512
|
+
border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid #e5e5e5"
|
|
1513
|
+
},
|
|
1514
|
+
".CheckboxInput--checked": {
|
|
1515
|
+
backgroundColor: isDarkMode ? "#fafafa" : "#171717",
|
|
1516
|
+
borderColor: isDarkMode ? "#fafafa" : "#171717"
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
};
|
|
1385
1521
|
function StripeBridge({
|
|
1386
1522
|
publishableKey,
|
|
1387
1523
|
clientSecret,
|
|
@@ -1409,9 +1545,10 @@ function StripeBridge({
|
|
|
1409
1545
|
throw new Error("Stripe not available");
|
|
1410
1546
|
}
|
|
1411
1547
|
stripeRef.current = window.Stripe(publishableKey);
|
|
1548
|
+
const stripeAppearance = appearance || getReevitStripeAppearance();
|
|
1412
1549
|
elementsRef.current = stripeRef.current.elements({
|
|
1413
1550
|
clientSecret,
|
|
1414
|
-
appearance:
|
|
1551
|
+
appearance: stripeAppearance
|
|
1415
1552
|
});
|
|
1416
1553
|
paymentElementRef.current = elementsRef.current.create("payment");
|
|
1417
1554
|
if (containerRef.current) {
|
|
@@ -1481,6 +1618,12 @@ function StripeBridge({
|
|
|
1481
1618
|
setIsSubmitting(false);
|
|
1482
1619
|
}
|
|
1483
1620
|
}, [onSuccess, onError]);
|
|
1621
|
+
const formattedAmount = new Intl.NumberFormat("en-US", {
|
|
1622
|
+
style: "currency",
|
|
1623
|
+
currency: currency.toUpperCase(),
|
|
1624
|
+
minimumFractionDigits: 2,
|
|
1625
|
+
maximumFractionDigits: 2
|
|
1626
|
+
}).format(amount / 100);
|
|
1484
1627
|
return /* @__PURE__ */ jsxs("div", { className: "reevit-stripe-bridge", children: [
|
|
1485
1628
|
isLoading && /* @__PURE__ */ jsxs("div", { className: "reevit-stripe-loading", children: [
|
|
1486
1629
|
/* @__PURE__ */ jsx("div", { className: "reevit-spinner" }),
|
|
@@ -1494,8 +1637,8 @@ function StripeBridge({
|
|
|
1494
1637
|
style: { display: isLoading ? "none" : "block", minHeight: "200px" }
|
|
1495
1638
|
}
|
|
1496
1639
|
),
|
|
1497
|
-
error && /* @__PURE__ */ jsx("div", { className: "reevit-stripe-error", children: /* @__PURE__ */ jsx("p", { children: error }) }),
|
|
1498
|
-
/* @__PURE__ */ jsxs("div", { className: "reevit-stripe-actions", children: [
|
|
1640
|
+
error && !isLoading && /* @__PURE__ */ jsx("div", { className: "reevit-stripe-error", children: /* @__PURE__ */ jsx("p", { children: error }) }),
|
|
1641
|
+
!isLoading && /* @__PURE__ */ jsxs("div", { className: "reevit-stripe-actions", children: [
|
|
1499
1642
|
/* @__PURE__ */ jsx(
|
|
1500
1643
|
"button",
|
|
1501
1644
|
{
|
|
@@ -1503,11 +1646,12 @@ function StripeBridge({
|
|
|
1503
1646
|
className: "reevit-submit-btn",
|
|
1504
1647
|
onClick: handleSubmit,
|
|
1505
1648
|
disabled: isLoading || isSubmitting,
|
|
1506
|
-
children: isSubmitting ? /* @__PURE__ */
|
|
1649
|
+
children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1650
|
+
/* @__PURE__ */ jsx("span", { className: "reevit-spinner", style: { width: "16px", height: "16px" } }),
|
|
1651
|
+
/* @__PURE__ */ jsx("span", { children: "Processing..." })
|
|
1652
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1507
1653
|
"Pay ",
|
|
1508
|
-
|
|
1509
|
-
" ",
|
|
1510
|
-
(amount / 100).toFixed(2)
|
|
1654
|
+
formattedAmount
|
|
1511
1655
|
] })
|
|
1512
1656
|
}
|
|
1513
1657
|
),
|
|
@@ -1740,14 +1884,16 @@ function ReevitCheckout({
|
|
|
1740
1884
|
return /* @__PURE__ */ jsx(
|
|
1741
1885
|
HubtelBridge,
|
|
1742
1886
|
{
|
|
1743
|
-
|
|
1887
|
+
paymentId: paymentIntent?.id || "",
|
|
1888
|
+
publicKey,
|
|
1889
|
+
merchantAccount: pspKey,
|
|
1744
1890
|
amount: paymentIntent?.amount ?? amount,
|
|
1745
1891
|
currency: paymentIntent?.currency ?? currency,
|
|
1746
1892
|
reference: paymentIntent?.reference || reference,
|
|
1747
1893
|
email,
|
|
1748
1894
|
phone: momoData?.phone || phone,
|
|
1749
1895
|
description: `Payment ${paymentIntent?.reference || reference || ""}`,
|
|
1750
|
-
|
|
1896
|
+
hubtelSessionToken: paymentIntent?.id ? paymentIntent.id : void 0,
|
|
1751
1897
|
onSuccess: handlePSPSuccess,
|
|
1752
1898
|
onError: (err) => handlePSPError(err),
|
|
1753
1899
|
onClose: handlePSPClose
|