@reevit/react 0.3.2 → 0.3.4

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.js CHANGED
@@ -113,7 +113,7 @@ var ReevitAPIClient = class {
113
113
  "Content-Type": "application/json",
114
114
  "X-Reevit-Key": this.publicKey,
115
115
  "X-Reevit-Client": "@reevit/react",
116
- "X-Reevit-Client-Version": "0.2.5"
116
+ "X-Reevit-Client-Version": "0.3.2"
117
117
  };
118
118
  if (method === "POST" || method === "PATCH" || method === "PUT") {
119
119
  headers["Idempotency-Key"] = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
@@ -269,6 +269,9 @@ function mapProviderToPsp(provider) {
269
269
  if (providerLower.includes("paystack")) return "paystack";
270
270
  if (providerLower.includes("hubtel")) return "hubtel";
271
271
  if (providerLower.includes("flutterwave")) return "flutterwave";
272
+ if (providerLower.includes("monnify")) return "monnify";
273
+ if (providerLower.includes("mpesa") || providerLower.includes("m-pesa")) return "mpesa";
274
+ if (providerLower.includes("stripe")) return "stripe";
272
275
  return "paystack";
273
276
  }
274
277
  function mapToPaymentIntent(response, config) {
@@ -803,384 +806,102 @@ function PaystackBridge({
803
806
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Connecting to Paystack..." })
804
807
  ] }) });
805
808
  }
806
- var ReevitContext = react.createContext(null);
807
- function useReevitContext() {
808
- const context = react.useContext(ReevitContext);
809
- if (!context) {
810
- throw new Error("useReevitContext must be used within ReevitCheckout");
811
- }
812
- return context;
809
+ function loadHubtelScript() {
810
+ return new Promise((resolve, reject) => {
811
+ if (window.HubtelCheckout) {
812
+ resolve();
813
+ return;
814
+ }
815
+ const script = document.createElement("script");
816
+ script.src = "https://checkout.hubtel.com/checkout.js";
817
+ script.async = true;
818
+ script.onload = () => resolve();
819
+ script.onerror = () => reject(new Error("Failed to load Hubtel script"));
820
+ document.head.appendChild(script);
821
+ });
813
822
  }
814
- function ReevitCheckout({
815
- // Config
816
- publicKey,
823
+ function HubtelBridge({
824
+ merchantAccount,
817
825
  amount,
818
- currency,
819
- email = "",
820
- phone = "",
826
+ currency = "GHS",
821
827
  reference,
822
- metadata,
823
- paymentMethods = ["card", "mobile_money"],
824
- initialPaymentIntent,
825
- // Callbacks
828
+ email,
829
+ phone,
830
+ description = "Payment",
826
831
  onSuccess,
827
832
  onError,
828
833
  onClose,
829
- onStateChange,
830
- // UI
831
- children,
832
- autoOpen = false,
833
- isOpen: controlledIsOpen,
834
- onOpenChange,
835
- theme,
836
- apiBaseUrl
834
+ autoStart = true
837
835
  }) {
838
- const [internalIsOpen, setInternalIsOpen] = react.useState(autoOpen);
839
- const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
840
- const setIsOpen = react.useCallback(
841
- (value) => {
842
- if (onOpenChange) {
843
- onOpenChange(value);
844
- } else {
845
- setInternalIsOpen(value);
836
+ const initialized = react.useRef(false);
837
+ const startPayment = react.useCallback(async () => {
838
+ try {
839
+ await loadHubtelScript();
840
+ if (!window.HubtelCheckout) {
841
+ throw new Error("Hubtel checkout not available");
846
842
  }
847
- },
848
- [onOpenChange]
849
- );
850
- const [showPSPBridge, setShowPSPBridge] = react.useState(false);
851
- const [momoData, setMomoData] = react.useState(null);
852
- const {
853
- status,
854
- paymentIntent,
855
- selectedMethod,
856
- error,
857
- result,
858
- initialize,
859
- selectMethod,
860
- processPayment,
861
- reset,
862
- close: closeCheckout,
863
- isLoading,
864
- isComplete
865
- } = useReevit({
866
- config: {
867
- publicKey,
868
- amount,
869
- currency,
870
- email,
871
- phone,
872
- reference,
873
- metadata,
874
- paymentMethods,
875
- initialPaymentIntent
876
- },
877
- apiBaseUrl,
878
- onSuccess: (result2) => {
879
- onSuccess?.(result2);
880
- setTimeout(() => {
881
- setIsOpen(false);
882
- }, 2e3);
883
- },
884
- onError,
885
- onClose: () => {
886
- setIsOpen(false);
887
- onClose?.();
888
- },
889
- onStateChange
890
- });
891
- react.useEffect(() => {
892
- if (isOpen && status === "idle" && !initialPaymentIntent) {
893
- initialize();
843
+ window.HubtelCheckout.initPayment({
844
+ merchantAccount,
845
+ basicDescription: description,
846
+ totalAmount: amount / 100,
847
+ // Hubtel expects amount in major units (GHS, not pesewas)
848
+ currency,
849
+ clientReference: reference || `hubtel_${Date.now()}`,
850
+ customerEmail: email,
851
+ customerMsisdn: phone,
852
+ onComplete: (response) => {
853
+ if (response.status === "Success") {
854
+ const result = {
855
+ paymentId: response.transactionId,
856
+ reference: response.clientReference,
857
+ amount: Math.round(response.amount * 100),
858
+ // Convert back to pesewas
859
+ currency: response.currency,
860
+ paymentMethod: "mobile_money",
861
+ psp: "hubtel",
862
+ pspReference: response.transactionId,
863
+ status: "success"
864
+ };
865
+ onSuccess(result);
866
+ } else {
867
+ const error = {
868
+ code: "PAYMENT_FAILED",
869
+ message: response.message || "Payment failed",
870
+ recoverable: true
871
+ };
872
+ onError(error);
873
+ }
874
+ },
875
+ onCancel: () => {
876
+ onClose();
877
+ }
878
+ });
879
+ } catch (err) {
880
+ const error = {
881
+ code: "PSP_ERROR",
882
+ message: "Failed to initialize Hubtel",
883
+ recoverable: true,
884
+ originalError: err
885
+ };
886
+ onError(error);
894
887
  }
895
- }, [isOpen, status, initialize, initialPaymentIntent]);
888
+ }, [merchantAccount, amount, currency, reference, email, phone, description, onSuccess, onError, onClose]);
896
889
  react.useEffect(() => {
897
- if (isOpen && selectedMethod && paymentIntent && !showPSPBridge) {
898
- if (selectedMethod === "card") {
899
- setShowPSPBridge(true);
900
- } else if (selectedMethod === "mobile_money" && (momoData?.phone || phone)) {
901
- setShowPSPBridge(true);
902
- }
903
- }
904
- }, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
905
- const handleOpen = react.useCallback(() => {
906
- if (controlledIsOpen !== void 0) return;
907
- setIsOpen(true);
908
- setShowPSPBridge(false);
909
- setMomoData(null);
910
- }, [controlledIsOpen, setIsOpen]);
911
- const handleClose = react.useCallback(() => {
912
- closeCheckout();
913
- setIsOpen(false);
914
- setShowPSPBridge(false);
915
- setMomoData(null);
916
- }, [closeCheckout, setIsOpen]);
917
- const handleMethodSelect = react.useCallback(
918
- (method) => {
919
- selectMethod(method);
920
- },
921
- [selectMethod]
922
- );
923
- const handleContinue = react.useCallback(() => {
924
- if (!selectedMethod) return;
925
- if (selectedMethod === "card") {
926
- setShowPSPBridge(true);
927
- }
928
- }, [selectedMethod]);
929
- const handleMomoSubmit = react.useCallback(
930
- (data) => {
931
- setMomoData(data);
932
- setShowPSPBridge(true);
933
- },
934
- []
935
- );
936
- const handlePSPSuccess = react.useCallback(
937
- (pspResult) => {
938
- processPayment({ ...pspResult, momoData });
939
- },
940
- [processPayment, momoData]
941
- );
942
- const handlePSPError = react.useCallback(
943
- (error2) => {
944
- setShowPSPBridge(false);
945
- onError?.(error2);
946
- },
947
- [onError]
948
- );
949
- const handlePSPClose = react.useCallback(() => {
950
- setShowPSPBridge(false);
951
- }, []);
952
- const handleBack = react.useCallback(() => {
953
- reset();
954
- setMomoData(null);
955
- setShowPSPBridge(false);
956
- }, [reset]);
957
- const themeStyles = theme ? createThemeVariables(theme) : {};
958
- const isControlled = controlledIsOpen !== void 0;
959
- const trigger = children ? /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: isControlled ? void 0 : handleOpen, role: isControlled ? void 0 : "button", tabIndex: isControlled ? void 0 : 0, children }) : !isControlled ? /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "reevit-trigger-btn", onClick: handleOpen, children: [
960
- "Pay ",
961
- formatAmount(amount, currency)
962
- ] }) : null;
963
- const renderContent = () => {
964
- if (status === "loading" || status === "processing") {
965
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-loading", children: [
966
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
967
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
968
- ] });
969
- }
970
- if (status === "success" && result) {
971
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-success", children: [
972
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-success__icon", children: "\u2713" }),
973
- /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Successful" }),
974
- /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
975
- "Reference: ",
976
- result.reference
977
- ] })
978
- ] });
890
+ if (autoStart && !initialized.current) {
891
+ initialized.current = true;
892
+ startPayment();
979
893
  }
980
- if (status === "failed" && error && !error.recoverable) {
981
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-error", children: [
982
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: "\u2715" }),
983
- /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Failed" }),
984
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: error.message }),
985
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "reevit-btn reevit-btn--primary", onClick: handleBack, children: "Try Again" })
986
- ] });
987
- }
988
- if (showPSPBridge) {
989
- const pspKey = paymentIntent?.pspPublicKey || publicKey;
990
- return /* @__PURE__ */ jsxRuntime.jsx(
991
- PaystackBridge,
992
- {
993
- publicKey: pspKey,
994
- email,
995
- phone: momoData?.phone || phone,
996
- amount: paymentIntent?.amount ?? amount,
997
- currency: paymentIntent?.currency ?? currency,
998
- reference,
999
- accessCode: paymentIntent?.clientSecret,
1000
- metadata: {
1001
- ...metadata,
1002
- // Override with correct payment intent ID for webhook routing
1003
- // This ensures Paystack webhook includes the correct ID to find the payment
1004
- payment_id: paymentIntent?.id,
1005
- connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
1006
- customer_phone: momoData?.phone || phone
1007
- },
1008
- channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
1009
- onSuccess: handlePSPSuccess,
1010
- onError: handlePSPError,
1011
- onClose: handlePSPClose
1012
- }
1013
- );
1014
- }
1015
- if (selectedMethod === "mobile_money" && !showPSPBridge) {
1016
- return /* @__PURE__ */ jsxRuntime.jsx(
1017
- MobileMoneyForm,
1018
- {
1019
- onSubmit: handleMomoSubmit,
1020
- onCancel: handleBack,
1021
- isLoading,
1022
- initialPhone: phone
1023
- }
1024
- );
1025
- }
1026
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-method-step", children: [
1027
- /* @__PURE__ */ jsxRuntime.jsx(
1028
- PaymentMethodSelector,
1029
- {
1030
- methods: paymentMethods,
1031
- selectedMethod,
1032
- onSelect: handleMethodSelect,
1033
- disabled: isLoading
1034
- }
1035
- ),
1036
- selectedMethod && selectedMethod !== "mobile_money" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-method-step__actions", children: /* @__PURE__ */ jsxRuntime.jsx(
1037
- "button",
1038
- {
1039
- className: "reevit-btn reevit-btn--primary",
1040
- onClick: handleContinue,
1041
- disabled: isLoading,
1042
- children: "Continue"
1043
- }
1044
- ) })
1045
- ] });
1046
- };
1047
- return /* @__PURE__ */ jsxRuntime.jsxs(ReevitContext.Provider, { value: { publicKey, amount, currency }, children: [
1048
- trigger,
1049
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-overlay", onClick: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
1050
- "div",
1051
- {
1052
- className: cn("reevit-modal", isComplete && "reevit-modal--success"),
1053
- style: themeStyles,
1054
- onClick: (e) => e.stopPropagation(),
1055
- role: "dialog",
1056
- "aria-modal": "true",
1057
- children: [
1058
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-modal__header", children: [
1059
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__branding", children: /* @__PURE__ */ jsxRuntime.jsx(
1060
- "img",
1061
- {
1062
- src: "https://i.imgur.com/bzUR5Lm.png",
1063
- alt: "Reevit",
1064
- className: "reevit-modal__logo"
1065
- }
1066
- ) }),
1067
- /* @__PURE__ */ jsxRuntime.jsx(
1068
- "button",
1069
- {
1070
- className: "reevit-modal__close",
1071
- onClick: handleClose,
1072
- "aria-label": "Close",
1073
- children: "\u2715"
1074
- }
1075
- )
1076
- ] }),
1077
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-modal__amount", children: [
1078
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__amount-label", children: "Amount" }),
1079
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__amount-value", children: formatAmount(amount, currency) })
1080
- ] }),
1081
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__content", children: renderContent() }),
1082
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__footer", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__secured", children: "\u{1F512} Secured by Reevit" }) })
1083
- ]
1084
- }
1085
- ) })
1086
- ] });
1087
- }
1088
- function loadHubtelScript() {
1089
- return new Promise((resolve, reject) => {
1090
- if (window.HubtelCheckout) {
1091
- resolve();
1092
- return;
1093
- }
1094
- const script = document.createElement("script");
1095
- script.src = "https://checkout.hubtel.com/checkout.js";
1096
- script.async = true;
1097
- script.onload = () => resolve();
1098
- script.onerror = () => reject(new Error("Failed to load Hubtel script"));
1099
- document.head.appendChild(script);
1100
- });
1101
- }
1102
- function HubtelBridge({
1103
- merchantAccount,
1104
- amount,
1105
- currency = "GHS",
1106
- reference,
1107
- email,
1108
- phone,
1109
- description = "Payment",
1110
- onSuccess,
1111
- onError,
1112
- onClose,
1113
- autoStart = true
1114
- }) {
1115
- const initialized = react.useRef(false);
1116
- const startPayment = react.useCallback(async () => {
1117
- try {
1118
- await loadHubtelScript();
1119
- if (!window.HubtelCheckout) {
1120
- throw new Error("Hubtel checkout not available");
1121
- }
1122
- window.HubtelCheckout.initPayment({
1123
- merchantAccount,
1124
- basicDescription: description,
1125
- totalAmount: amount / 100,
1126
- // Hubtel expects amount in major units (GHS, not pesewas)
1127
- currency,
1128
- clientReference: reference || `hubtel_${Date.now()}`,
1129
- customerEmail: email,
1130
- customerMsisdn: phone,
1131
- onComplete: (response) => {
1132
- if (response.status === "Success") {
1133
- const result = {
1134
- paymentId: response.transactionId,
1135
- reference: response.clientReference,
1136
- amount: Math.round(response.amount * 100),
1137
- // Convert back to pesewas
1138
- currency: response.currency,
1139
- paymentMethod: "mobile_money",
1140
- psp: "hubtel",
1141
- pspReference: response.transactionId,
1142
- status: "success"
1143
- };
1144
- onSuccess(result);
1145
- } else {
1146
- const error = {
1147
- code: "PAYMENT_FAILED",
1148
- message: response.message || "Payment failed",
1149
- recoverable: true
1150
- };
1151
- onError(error);
1152
- }
1153
- },
1154
- onCancel: () => {
1155
- onClose();
1156
- }
1157
- });
1158
- } catch (err) {
1159
- const error = {
1160
- code: "PSP_ERROR",
1161
- message: "Failed to initialize Hubtel",
1162
- recoverable: true,
1163
- originalError: err
1164
- };
1165
- onError(error);
1166
- }
1167
- }, [merchantAccount, amount, currency, reference, email, phone, description, onSuccess, onError, onClose]);
1168
- react.useEffect(() => {
1169
- if (autoStart && !initialized.current) {
1170
- initialized.current = true;
1171
- startPayment();
1172
- }
1173
- }, [autoStart, startPayment]);
1174
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-psp-bridge reevit-psp-bridge--hubtel", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-psp-bridge__loading", children: [
1175
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
1176
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Connecting to Hubtel..." })
1177
- ] }) });
1178
- }
1179
- function loadFlutterwaveScript() {
1180
- return new Promise((resolve, reject) => {
1181
- if (window.FlutterwaveCheckout) {
1182
- resolve();
1183
- return;
894
+ }, [autoStart, startPayment]);
895
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-psp-bridge reevit-psp-bridge--hubtel", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-psp-bridge__loading", children: [
896
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
897
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Connecting to Hubtel..." })
898
+ ] }) });
899
+ }
900
+ function loadFlutterwaveScript() {
901
+ return new Promise((resolve, reject) => {
902
+ if (window.FlutterwaveCheckout) {
903
+ resolve();
904
+ return;
1184
905
  }
1185
906
  const script = document.createElement("script");
1186
907
  script.src = "https://checkout.flutterwave.com/v3.js";
@@ -1301,202 +1022,41 @@ function FlutterwaveBridge({
1301
1022
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Connecting to Flutterwave..." })
1302
1023
  ] }) });
1303
1024
  }
1304
- var STRIPE_SCRIPT_URL = "https://js.stripe.com/v3/";
1305
- var stripeScriptPromise = null;
1306
- function loadStripeScript() {
1307
- if (stripeScriptPromise) return stripeScriptPromise;
1308
- if (document.getElementById("stripe-js-script")) {
1309
- stripeScriptPromise = Promise.resolve();
1310
- return stripeScriptPromise;
1025
+ var MONNIFY_SCRIPT_URL = "https://sdk.monnify.com/plugin/monnify.js";
1026
+ var monnifyScriptPromise = null;
1027
+ function loadMonnifyScript() {
1028
+ if (monnifyScriptPromise) return monnifyScriptPromise;
1029
+ if (document.getElementById("monnify-sdk-script")) {
1030
+ monnifyScriptPromise = Promise.resolve();
1031
+ return monnifyScriptPromise;
1311
1032
  }
1312
- stripeScriptPromise = new Promise((resolve, reject) => {
1033
+ monnifyScriptPromise = new Promise((resolve, reject) => {
1313
1034
  const script = document.createElement("script");
1314
- script.id = "stripe-js-script";
1315
- script.src = STRIPE_SCRIPT_URL;
1035
+ script.id = "monnify-sdk-script";
1036
+ script.src = MONNIFY_SCRIPT_URL;
1316
1037
  script.async = true;
1317
1038
  script.onload = () => resolve();
1318
- script.onerror = () => reject(new Error("Failed to load Stripe.js"));
1039
+ script.onerror = () => reject(new Error("Failed to load Monnify SDK"));
1319
1040
  document.head.appendChild(script);
1320
1041
  });
1321
- return stripeScriptPromise;
1042
+ return monnifyScriptPromise;
1322
1043
  }
1323
- function StripeBridge({
1324
- publishableKey,
1325
- clientSecret,
1044
+ function MonnifyBridge({
1045
+ apiKey,
1046
+ contractCode,
1326
1047
  amount,
1327
1048
  currency,
1328
- appearance,
1049
+ reference,
1050
+ customerName,
1051
+ customerEmail,
1052
+ customerPhone,
1053
+ paymentDescription,
1054
+ isTestMode = false,
1055
+ metadata,
1056
+ autoOpen = true,
1329
1057
  onSuccess,
1330
1058
  onError,
1331
- onReady,
1332
- onCancel
1333
- }) {
1334
- const [isLoading, setIsLoading] = react.useState(true);
1335
- const [isSubmitting, setIsSubmitting] = react.useState(false);
1336
- const [error, setError] = react.useState(null);
1337
- const stripeRef = react.useRef(null);
1338
- const elementsRef = react.useRef(null);
1339
- const paymentElementRef = react.useRef(null);
1340
- const containerRef = react.useRef(null);
1341
- react.useEffect(() => {
1342
- let mounted = true;
1343
- const initStripe = async () => {
1344
- try {
1345
- await loadStripeScript();
1346
- if (!mounted || !window.Stripe) {
1347
- throw new Error("Stripe not available");
1348
- }
1349
- stripeRef.current = window.Stripe(publishableKey);
1350
- elementsRef.current = stripeRef.current.elements({
1351
- clientSecret,
1352
- appearance: appearance || { theme: "stripe" }
1353
- });
1354
- paymentElementRef.current = elementsRef.current.create("payment");
1355
- if (containerRef.current) {
1356
- paymentElementRef.current.mount(containerRef.current);
1357
- }
1358
- paymentElementRef.current.on("ready", () => {
1359
- if (mounted) {
1360
- setIsLoading(false);
1361
- onReady?.();
1362
- }
1363
- });
1364
- paymentElementRef.current.on("change", (event) => {
1365
- if (event.error) {
1366
- setError(event.error.message);
1367
- } else {
1368
- setError(null);
1369
- }
1370
- });
1371
- } catch (err) {
1372
- if (mounted) {
1373
- const message = err instanceof Error ? err.message : "Failed to initialize Stripe";
1374
- setError(message);
1375
- setIsLoading(false);
1376
- onError({ code: "STRIPE_INIT_ERROR", message });
1377
- }
1378
- }
1379
- };
1380
- initStripe();
1381
- return () => {
1382
- mounted = false;
1383
- paymentElementRef.current?.destroy();
1384
- };
1385
- }, [publishableKey, clientSecret, appearance, onReady, onError]);
1386
- const handleSubmit = react.useCallback(async () => {
1387
- if (!stripeRef.current || !elementsRef.current) {
1388
- onError({ code: "NOT_INITIALIZED", message: "Stripe not initialized" });
1389
- return;
1390
- }
1391
- setIsSubmitting(true);
1392
- setError(null);
1393
- try {
1394
- const { error: submitError } = await elementsRef.current.submit();
1395
- if (submitError) {
1396
- setError(submitError.message);
1397
- onError({ code: submitError.code || "VALIDATION_ERROR", message: submitError.message });
1398
- setIsSubmitting(false);
1399
- return;
1400
- }
1401
- const { error: confirmError, paymentIntent } = await stripeRef.current.confirmPayment({
1402
- elements: elementsRef.current,
1403
- redirect: "if_required"
1404
- });
1405
- if (confirmError) {
1406
- setError(confirmError.message);
1407
- onError({ code: confirmError.code || "PAYMENT_ERROR", message: confirmError.message });
1408
- } else if (paymentIntent) {
1409
- onSuccess({
1410
- paymentIntentId: paymentIntent.id,
1411
- status: paymentIntent.status
1412
- });
1413
- }
1414
- } catch (err) {
1415
- const message = err instanceof Error ? err.message : "Payment failed";
1416
- setError(message);
1417
- onError({ code: "UNKNOWN_ERROR", message });
1418
- } finally {
1419
- setIsSubmitting(false);
1420
- }
1421
- }, [onSuccess, onError]);
1422
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-bridge", children: [
1423
- isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-loading", children: [
1424
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
1425
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Loading secure payment form..." })
1426
- ] }),
1427
- /* @__PURE__ */ jsxRuntime.jsx(
1428
- "div",
1429
- {
1430
- ref: containerRef,
1431
- className: "reevit-stripe-element",
1432
- style: { display: isLoading ? "none" : "block", minHeight: "200px" }
1433
- }
1434
- ),
1435
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-stripe-error", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }) }),
1436
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-actions", children: [
1437
- /* @__PURE__ */ jsxRuntime.jsx(
1438
- "button",
1439
- {
1440
- type: "button",
1441
- className: "reevit-submit-btn",
1442
- onClick: handleSubmit,
1443
- disabled: isLoading || isSubmitting,
1444
- children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-spinner" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1445
- "Pay ",
1446
- currency,
1447
- " ",
1448
- (amount / 100).toFixed(2)
1449
- ] })
1450
- }
1451
- ),
1452
- onCancel && /* @__PURE__ */ jsxRuntime.jsx(
1453
- "button",
1454
- {
1455
- type: "button",
1456
- className: "reevit-cancel-btn",
1457
- onClick: onCancel,
1458
- disabled: isSubmitting,
1459
- children: "Cancel"
1460
- }
1461
- )
1462
- ] })
1463
- ] });
1464
- }
1465
- var MONNIFY_SCRIPT_URL = "https://sdk.monnify.com/plugin/monnify.js";
1466
- var monnifyScriptPromise = null;
1467
- function loadMonnifyScript() {
1468
- if (monnifyScriptPromise) return monnifyScriptPromise;
1469
- if (document.getElementById("monnify-sdk-script")) {
1470
- monnifyScriptPromise = Promise.resolve();
1471
- return monnifyScriptPromise;
1472
- }
1473
- monnifyScriptPromise = new Promise((resolve, reject) => {
1474
- const script = document.createElement("script");
1475
- script.id = "monnify-sdk-script";
1476
- script.src = MONNIFY_SCRIPT_URL;
1477
- script.async = true;
1478
- script.onload = () => resolve();
1479
- script.onerror = () => reject(new Error("Failed to load Monnify SDK"));
1480
- document.head.appendChild(script);
1481
- });
1482
- return monnifyScriptPromise;
1483
- }
1484
- function MonnifyBridge({
1485
- apiKey,
1486
- contractCode,
1487
- amount,
1488
- currency,
1489
- reference,
1490
- customerName,
1491
- customerEmail,
1492
- customerPhone,
1493
- paymentDescription,
1494
- isTestMode = false,
1495
- metadata,
1496
- autoOpen = true,
1497
- onSuccess,
1498
- onError,
1499
- onClose
1059
+ onClose
1500
1060
  }) {
1501
1061
  const [isLoading, setIsLoading] = react.useState(true);
1502
1062
  const [isReady, setIsReady] = react.useState(false);
@@ -1753,6 +1313,582 @@ function useMPesaStatusPolling(statusEndpoint, checkoutRequestId, options) {
1753
1313
  }, [checkoutRequestId, statusEndpoint, interval, maxAttempts, headers, onSuccess, onFailed, onTimeout]);
1754
1314
  return { startPolling };
1755
1315
  }
1316
+ var STRIPE_SCRIPT_URL = "https://js.stripe.com/v3/";
1317
+ var stripeScriptPromise = null;
1318
+ function loadStripeScript() {
1319
+ if (stripeScriptPromise) return stripeScriptPromise;
1320
+ if (document.getElementById("stripe-js-script")) {
1321
+ stripeScriptPromise = Promise.resolve();
1322
+ return stripeScriptPromise;
1323
+ }
1324
+ stripeScriptPromise = new Promise((resolve, reject) => {
1325
+ const script = document.createElement("script");
1326
+ script.id = "stripe-js-script";
1327
+ script.src = STRIPE_SCRIPT_URL;
1328
+ script.async = true;
1329
+ script.onload = () => resolve();
1330
+ script.onerror = () => reject(new Error("Failed to load Stripe.js"));
1331
+ document.head.appendChild(script);
1332
+ });
1333
+ return stripeScriptPromise;
1334
+ }
1335
+ function StripeBridge({
1336
+ publishableKey,
1337
+ clientSecret,
1338
+ amount,
1339
+ currency,
1340
+ appearance,
1341
+ onSuccess,
1342
+ onError,
1343
+ onReady,
1344
+ onCancel
1345
+ }) {
1346
+ const [isLoading, setIsLoading] = react.useState(true);
1347
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
1348
+ const [error, setError] = react.useState(null);
1349
+ const stripeRef = react.useRef(null);
1350
+ const elementsRef = react.useRef(null);
1351
+ const paymentElementRef = react.useRef(null);
1352
+ const containerRef = react.useRef(null);
1353
+ react.useEffect(() => {
1354
+ let mounted = true;
1355
+ const initStripe = async () => {
1356
+ try {
1357
+ await loadStripeScript();
1358
+ if (!mounted || !window.Stripe) {
1359
+ throw new Error("Stripe not available");
1360
+ }
1361
+ stripeRef.current = window.Stripe(publishableKey);
1362
+ elementsRef.current = stripeRef.current.elements({
1363
+ clientSecret,
1364
+ appearance: appearance || { theme: "stripe" }
1365
+ });
1366
+ paymentElementRef.current = elementsRef.current.create("payment");
1367
+ if (containerRef.current) {
1368
+ paymentElementRef.current.mount(containerRef.current);
1369
+ }
1370
+ paymentElementRef.current.on("ready", () => {
1371
+ if (mounted) {
1372
+ setIsLoading(false);
1373
+ onReady?.();
1374
+ }
1375
+ });
1376
+ paymentElementRef.current.on("change", (event) => {
1377
+ if (event.error) {
1378
+ setError(event.error.message);
1379
+ } else {
1380
+ setError(null);
1381
+ }
1382
+ });
1383
+ } catch (err) {
1384
+ if (mounted) {
1385
+ const message = err instanceof Error ? err.message : "Failed to initialize Stripe";
1386
+ setError(message);
1387
+ setIsLoading(false);
1388
+ onError({ code: "STRIPE_INIT_ERROR", message });
1389
+ }
1390
+ }
1391
+ };
1392
+ initStripe();
1393
+ return () => {
1394
+ mounted = false;
1395
+ paymentElementRef.current?.destroy();
1396
+ };
1397
+ }, [publishableKey, clientSecret, appearance, onReady, onError]);
1398
+ const handleSubmit = react.useCallback(async () => {
1399
+ if (!stripeRef.current || !elementsRef.current) {
1400
+ onError({ code: "NOT_INITIALIZED", message: "Stripe not initialized" });
1401
+ return;
1402
+ }
1403
+ setIsSubmitting(true);
1404
+ setError(null);
1405
+ try {
1406
+ const { error: submitError } = await elementsRef.current.submit();
1407
+ if (submitError) {
1408
+ setError(submitError.message);
1409
+ onError({ code: submitError.code || "VALIDATION_ERROR", message: submitError.message });
1410
+ setIsSubmitting(false);
1411
+ return;
1412
+ }
1413
+ const { error: confirmError, paymentIntent } = await stripeRef.current.confirmPayment({
1414
+ elements: elementsRef.current,
1415
+ redirect: "if_required"
1416
+ });
1417
+ if (confirmError) {
1418
+ setError(confirmError.message);
1419
+ onError({ code: confirmError.code || "PAYMENT_ERROR", message: confirmError.message });
1420
+ } else if (paymentIntent) {
1421
+ onSuccess({
1422
+ paymentIntentId: paymentIntent.id,
1423
+ status: paymentIntent.status
1424
+ });
1425
+ }
1426
+ } catch (err) {
1427
+ const message = err instanceof Error ? err.message : "Payment failed";
1428
+ setError(message);
1429
+ onError({ code: "UNKNOWN_ERROR", message });
1430
+ } finally {
1431
+ setIsSubmitting(false);
1432
+ }
1433
+ }, [onSuccess, onError]);
1434
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-bridge", children: [
1435
+ isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-loading", children: [
1436
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
1437
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Loading secure payment form..." })
1438
+ ] }),
1439
+ /* @__PURE__ */ jsxRuntime.jsx(
1440
+ "div",
1441
+ {
1442
+ ref: containerRef,
1443
+ className: "reevit-stripe-element",
1444
+ style: { display: isLoading ? "none" : "block", minHeight: "200px" }
1445
+ }
1446
+ ),
1447
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-stripe-error", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }) }),
1448
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-stripe-actions", children: [
1449
+ /* @__PURE__ */ jsxRuntime.jsx(
1450
+ "button",
1451
+ {
1452
+ type: "button",
1453
+ className: "reevit-submit-btn",
1454
+ onClick: handleSubmit,
1455
+ disabled: isLoading || isSubmitting,
1456
+ children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-spinner" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1457
+ "Pay ",
1458
+ currency,
1459
+ " ",
1460
+ (amount / 100).toFixed(2)
1461
+ ] })
1462
+ }
1463
+ ),
1464
+ onCancel && /* @__PURE__ */ jsxRuntime.jsx(
1465
+ "button",
1466
+ {
1467
+ type: "button",
1468
+ className: "reevit-cancel-btn",
1469
+ onClick: onCancel,
1470
+ disabled: isSubmitting,
1471
+ children: "Cancel"
1472
+ }
1473
+ )
1474
+ ] })
1475
+ ] });
1476
+ }
1477
+ var ReevitContext = react.createContext(null);
1478
+ function useReevitContext() {
1479
+ const context = react.useContext(ReevitContext);
1480
+ if (!context) {
1481
+ throw new Error("useReevitContext must be used within ReevitCheckout");
1482
+ }
1483
+ return context;
1484
+ }
1485
+ function ReevitCheckout({
1486
+ // Config
1487
+ publicKey,
1488
+ amount,
1489
+ currency,
1490
+ email = "",
1491
+ phone = "",
1492
+ reference,
1493
+ metadata,
1494
+ paymentMethods = ["card", "mobile_money"],
1495
+ initialPaymentIntent,
1496
+ // Callbacks
1497
+ onSuccess,
1498
+ onError,
1499
+ onClose,
1500
+ onStateChange,
1501
+ // UI
1502
+ children,
1503
+ autoOpen = false,
1504
+ isOpen: controlledIsOpen,
1505
+ onOpenChange,
1506
+ theme,
1507
+ apiBaseUrl
1508
+ }) {
1509
+ const [internalIsOpen, setInternalIsOpen] = react.useState(autoOpen);
1510
+ const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
1511
+ const setIsOpen = react.useCallback(
1512
+ (value) => {
1513
+ if (onOpenChange) {
1514
+ onOpenChange(value);
1515
+ } else {
1516
+ setInternalIsOpen(value);
1517
+ }
1518
+ },
1519
+ [onOpenChange]
1520
+ );
1521
+ const [showPSPBridge, setShowPSPBridge] = react.useState(false);
1522
+ const [momoData, setMomoData] = react.useState(null);
1523
+ const {
1524
+ status,
1525
+ paymentIntent,
1526
+ selectedMethod,
1527
+ error,
1528
+ result,
1529
+ initialize,
1530
+ selectMethod,
1531
+ processPayment,
1532
+ reset,
1533
+ close: closeCheckout,
1534
+ isLoading,
1535
+ isComplete
1536
+ } = useReevit({
1537
+ config: {
1538
+ publicKey,
1539
+ amount,
1540
+ currency,
1541
+ email,
1542
+ phone,
1543
+ reference,
1544
+ metadata,
1545
+ paymentMethods,
1546
+ initialPaymentIntent
1547
+ },
1548
+ apiBaseUrl,
1549
+ onSuccess: (result2) => {
1550
+ onSuccess?.(result2);
1551
+ setTimeout(() => {
1552
+ setIsOpen(false);
1553
+ }, 2e3);
1554
+ },
1555
+ onError,
1556
+ onClose: () => {
1557
+ setIsOpen(false);
1558
+ onClose?.();
1559
+ },
1560
+ onStateChange
1561
+ });
1562
+ react.useEffect(() => {
1563
+ if (isOpen && status === "idle" && !initialPaymentIntent) {
1564
+ initialize();
1565
+ }
1566
+ }, [isOpen, status, initialize, initialPaymentIntent]);
1567
+ react.useEffect(() => {
1568
+ if (isOpen && selectedMethod && paymentIntent && !showPSPBridge) {
1569
+ if (selectedMethod === "card") {
1570
+ setShowPSPBridge(true);
1571
+ } else if (selectedMethod === "mobile_money" && (momoData?.phone || phone)) {
1572
+ setShowPSPBridge(true);
1573
+ }
1574
+ }
1575
+ }, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
1576
+ const handleOpen = react.useCallback(() => {
1577
+ if (controlledIsOpen !== void 0) return;
1578
+ setIsOpen(true);
1579
+ setShowPSPBridge(false);
1580
+ setMomoData(null);
1581
+ }, [controlledIsOpen, setIsOpen]);
1582
+ const handleClose = react.useCallback(() => {
1583
+ closeCheckout();
1584
+ setIsOpen(false);
1585
+ setShowPSPBridge(false);
1586
+ setMomoData(null);
1587
+ }, [closeCheckout, setIsOpen]);
1588
+ const handleMethodSelect = react.useCallback(
1589
+ (method) => {
1590
+ selectMethod(method);
1591
+ },
1592
+ [selectMethod]
1593
+ );
1594
+ const handleContinue = react.useCallback(() => {
1595
+ if (!selectedMethod) return;
1596
+ if (selectedMethod === "card") {
1597
+ setShowPSPBridge(true);
1598
+ }
1599
+ }, [selectedMethod]);
1600
+ const handleMomoSubmit = react.useCallback(
1601
+ (data) => {
1602
+ setMomoData(data);
1603
+ setShowPSPBridge(true);
1604
+ },
1605
+ []
1606
+ );
1607
+ const handlePSPSuccess = react.useCallback(
1608
+ (pspResult) => {
1609
+ processPayment({ ...pspResult, momoData });
1610
+ },
1611
+ [processPayment, momoData]
1612
+ );
1613
+ const handlePSPError = react.useCallback(
1614
+ (error2) => {
1615
+ setShowPSPBridge(false);
1616
+ onError?.(error2);
1617
+ },
1618
+ [onError]
1619
+ );
1620
+ const handlePSPClose = react.useCallback(() => {
1621
+ setShowPSPBridge(false);
1622
+ }, []);
1623
+ const handleBack = react.useCallback(() => {
1624
+ reset();
1625
+ setMomoData(null);
1626
+ setShowPSPBridge(false);
1627
+ }, [reset]);
1628
+ const themeStyles = theme ? createThemeVariables(theme) : {};
1629
+ const isControlled = controlledIsOpen !== void 0;
1630
+ const trigger = children ? /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: isControlled ? void 0 : handleOpen, role: isControlled ? void 0 : "button", tabIndex: isControlled ? void 0 : 0, children }) : !isControlled ? /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "reevit-trigger-btn", onClick: handleOpen, children: [
1631
+ "Pay ",
1632
+ formatAmount(amount, currency)
1633
+ ] }) : null;
1634
+ const renderContent = () => {
1635
+ if (status === "loading" || status === "processing") {
1636
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-loading", children: [
1637
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
1638
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
1639
+ ] });
1640
+ }
1641
+ if (status === "success" && result) {
1642
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-success", children: [
1643
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-success__icon", children: "\u2713" }),
1644
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Successful" }),
1645
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1646
+ "Reference: ",
1647
+ result.reference
1648
+ ] })
1649
+ ] });
1650
+ }
1651
+ if (status === "failed" && error && !error.recoverable) {
1652
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-error", children: [
1653
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: "\u2715" }),
1654
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Failed" }),
1655
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: error.message }),
1656
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "reevit-btn reevit-btn--primary", onClick: handleBack, children: "Try Again" })
1657
+ ] });
1658
+ }
1659
+ if (showPSPBridge) {
1660
+ const pspKey = paymentIntent?.pspPublicKey || publicKey;
1661
+ const psp = paymentIntent?.recommendedPsp || "paystack";
1662
+ const bridgeMetadata = {
1663
+ ...metadata,
1664
+ // Override with correct payment intent ID for webhook routing
1665
+ // This ensures webhook includes the correct ID to find the payment
1666
+ payment_id: paymentIntent?.id,
1667
+ connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
1668
+ customer_phone: momoData?.phone || phone
1669
+ };
1670
+ switch (psp) {
1671
+ case "paystack":
1672
+ return /* @__PURE__ */ jsxRuntime.jsx(
1673
+ PaystackBridge,
1674
+ {
1675
+ publicKey: pspKey,
1676
+ email,
1677
+ phone: momoData?.phone || phone,
1678
+ amount: paymentIntent?.amount ?? amount,
1679
+ currency: paymentIntent?.currency ?? currency,
1680
+ reference,
1681
+ accessCode: paymentIntent?.clientSecret,
1682
+ metadata: bridgeMetadata,
1683
+ channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
1684
+ onSuccess: handlePSPSuccess,
1685
+ onError: handlePSPError,
1686
+ onClose: handlePSPClose
1687
+ }
1688
+ );
1689
+ case "hubtel":
1690
+ return /* @__PURE__ */ jsxRuntime.jsx(
1691
+ HubtelBridge,
1692
+ {
1693
+ merchantAccount: pspKey,
1694
+ amount: paymentIntent?.amount ?? amount,
1695
+ currency: paymentIntent?.currency ?? currency,
1696
+ reference: paymentIntent?.reference || reference,
1697
+ email,
1698
+ phone: momoData?.phone || phone,
1699
+ description: `Payment ${paymentIntent?.reference || reference || ""}`,
1700
+ onSuccess: handlePSPSuccess,
1701
+ onError: (err) => handlePSPError(err),
1702
+ onClose: handlePSPClose
1703
+ }
1704
+ );
1705
+ case "flutterwave":
1706
+ return /* @__PURE__ */ jsxRuntime.jsx(
1707
+ FlutterwaveBridge,
1708
+ {
1709
+ publicKey: pspKey,
1710
+ amount: paymentIntent?.amount ?? amount,
1711
+ currency: paymentIntent?.currency ?? currency,
1712
+ reference: paymentIntent?.reference || reference,
1713
+ email,
1714
+ phone: momoData?.phone || phone,
1715
+ metadata: bridgeMetadata,
1716
+ onSuccess: handlePSPSuccess,
1717
+ onError: (err) => handlePSPError(err),
1718
+ onClose: handlePSPClose
1719
+ }
1720
+ );
1721
+ case "monnify":
1722
+ return /* @__PURE__ */ jsxRuntime.jsx(
1723
+ MonnifyBridge,
1724
+ {
1725
+ apiKey: pspKey,
1726
+ contractCode: metadata?.contract_code || pspKey,
1727
+ amount: paymentIntent?.amount ?? amount,
1728
+ currency: paymentIntent?.currency ?? currency,
1729
+ reference: paymentIntent?.reference || reference || `monnify_${Date.now()}`,
1730
+ customerName: metadata?.customer_name || email,
1731
+ customerEmail: email,
1732
+ customerPhone: momoData?.phone || phone,
1733
+ metadata: bridgeMetadata,
1734
+ onSuccess: (result2) => handlePSPSuccess({
1735
+ paymentId: result2.transactionReference,
1736
+ reference: result2.paymentReference,
1737
+ amount: result2.amount,
1738
+ currency: paymentIntent?.currency ?? currency,
1739
+ paymentMethod: selectedMethod || "card",
1740
+ psp: "monnify",
1741
+ pspReference: result2.transactionReference,
1742
+ status: "success"
1743
+ }),
1744
+ onError: (err) => handlePSPError({
1745
+ code: err.code,
1746
+ message: err.message,
1747
+ recoverable: true
1748
+ }),
1749
+ onClose: handlePSPClose
1750
+ }
1751
+ );
1752
+ case "mpesa":
1753
+ return /* @__PURE__ */ jsxRuntime.jsx(
1754
+ MPesaBridge,
1755
+ {
1756
+ apiEndpoint: `${apiBaseUrl || "https://api.reevit.io"}/v1/payments/${paymentIntent?.id}/mpesa`,
1757
+ phoneNumber: momoData?.phone || phone || "",
1758
+ amount: paymentIntent?.amount ?? amount,
1759
+ currency: paymentIntent?.currency ?? currency,
1760
+ reference: paymentIntent?.reference || reference || `mpesa_${Date.now()}`,
1761
+ description: `Payment ${paymentIntent?.reference || reference || ""}`,
1762
+ headers: { "x-reevit-public-key": publicKey },
1763
+ onSuccess: (result2) => handlePSPSuccess({
1764
+ paymentId: result2.transactionId,
1765
+ reference: result2.reference,
1766
+ amount: paymentIntent?.amount ?? amount,
1767
+ currency: paymentIntent?.currency ?? currency,
1768
+ paymentMethod: "mobile_money",
1769
+ psp: "mpesa",
1770
+ pspReference: result2.transactionId,
1771
+ status: "success"
1772
+ }),
1773
+ onError: (err) => handlePSPError({
1774
+ code: err.code,
1775
+ message: err.message,
1776
+ recoverable: true
1777
+ })
1778
+ }
1779
+ );
1780
+ case "stripe":
1781
+ return /* @__PURE__ */ jsxRuntime.jsx(
1782
+ StripeBridge,
1783
+ {
1784
+ publishableKey: pspKey,
1785
+ clientSecret: paymentIntent?.clientSecret || "",
1786
+ amount: paymentIntent?.amount ?? amount,
1787
+ currency: paymentIntent?.currency ?? currency,
1788
+ onSuccess: (result2) => handlePSPSuccess({
1789
+ paymentId: result2.paymentIntentId,
1790
+ reference: paymentIntent?.reference || reference || result2.paymentIntentId,
1791
+ amount: paymentIntent?.amount ?? amount,
1792
+ currency: paymentIntent?.currency ?? currency,
1793
+ paymentMethod: selectedMethod || "card",
1794
+ psp: "stripe",
1795
+ pspReference: result2.paymentIntentId,
1796
+ status: result2.status === "succeeded" ? "success" : "pending"
1797
+ }),
1798
+ onError: (err) => handlePSPError({
1799
+ code: err.code,
1800
+ message: err.message,
1801
+ recoverable: true
1802
+ }),
1803
+ onCancel: handlePSPClose
1804
+ }
1805
+ );
1806
+ default:
1807
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-error", children: [
1808
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-error__icon", children: "\u26A0\uFE0F" }),
1809
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment Provider Not Supported" }),
1810
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1811
+ "The selected payment provider (",
1812
+ psp,
1813
+ ") is not currently supported in this checkout."
1814
+ ] }),
1815
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "reevit-btn reevit-btn--primary", onClick: handleBack, children: "Go Back" })
1816
+ ] });
1817
+ }
1818
+ }
1819
+ if (selectedMethod === "mobile_money" && !showPSPBridge) {
1820
+ return /* @__PURE__ */ jsxRuntime.jsx(
1821
+ MobileMoneyForm,
1822
+ {
1823
+ onSubmit: handleMomoSubmit,
1824
+ onCancel: handleBack,
1825
+ isLoading,
1826
+ initialPhone: phone
1827
+ }
1828
+ );
1829
+ }
1830
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-method-step", children: [
1831
+ /* @__PURE__ */ jsxRuntime.jsx(
1832
+ PaymentMethodSelector,
1833
+ {
1834
+ methods: paymentMethods,
1835
+ selectedMethod,
1836
+ onSelect: handleMethodSelect,
1837
+ disabled: isLoading
1838
+ }
1839
+ ),
1840
+ selectedMethod && selectedMethod !== "mobile_money" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-method-step__actions", children: /* @__PURE__ */ jsxRuntime.jsx(
1841
+ "button",
1842
+ {
1843
+ className: "reevit-btn reevit-btn--primary",
1844
+ onClick: handleContinue,
1845
+ disabled: isLoading,
1846
+ children: "Continue"
1847
+ }
1848
+ ) })
1849
+ ] });
1850
+ };
1851
+ return /* @__PURE__ */ jsxRuntime.jsxs(ReevitContext.Provider, { value: { publicKey, amount, currency }, children: [
1852
+ trigger,
1853
+ isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-overlay", onClick: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(
1854
+ "div",
1855
+ {
1856
+ className: cn("reevit-modal", isComplete && "reevit-modal--success"),
1857
+ style: themeStyles,
1858
+ onClick: (e) => e.stopPropagation(),
1859
+ role: "dialog",
1860
+ "aria-modal": "true",
1861
+ children: [
1862
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-modal__header", children: [
1863
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__branding", children: /* @__PURE__ */ jsxRuntime.jsx(
1864
+ "img",
1865
+ {
1866
+ src: "https://i.imgur.com/bzUR5Lm.png",
1867
+ alt: "Reevit",
1868
+ className: "reevit-modal__logo"
1869
+ }
1870
+ ) }),
1871
+ /* @__PURE__ */ jsxRuntime.jsx(
1872
+ "button",
1873
+ {
1874
+ className: "reevit-modal__close",
1875
+ onClick: handleClose,
1876
+ "aria-label": "Close",
1877
+ children: "\u2715"
1878
+ }
1879
+ )
1880
+ ] }),
1881
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-modal__amount", children: [
1882
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__amount-label", children: "Amount" }),
1883
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__amount-value", children: formatAmount(amount, currency) })
1884
+ ] }),
1885
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__content", children: renderContent() }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-modal__footer", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "reevit-modal__secured", children: "\u{1F512} Secured by Reevit" }) })
1887
+ ]
1888
+ }
1889
+ ) })
1890
+ ] });
1891
+ }
1756
1892
 
1757
1893
  exports.FlutterwaveBridge = FlutterwaveBridge;
1758
1894
  exports.HubtelBridge = HubtelBridge;