@reevit/react 0.2.8 → 0.3.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/dist/index.d.mts CHANGED
@@ -23,6 +23,8 @@ interface ReevitCheckoutConfig {
23
23
  metadata?: Record<string, unknown>;
24
24
  /** Payment methods to display */
25
25
  paymentMethods?: PaymentMethod[];
26
+ /** Optional existing payment intent to use instead of creating a new one */
27
+ initialPaymentIntent?: PaymentIntent;
26
28
  }
27
29
  interface ReevitCheckoutCallbacks {
28
30
  /** Called when payment is successful */
@@ -39,6 +41,10 @@ interface ReevitCheckoutProps extends ReevitCheckoutConfig, ReevitCheckoutCallba
39
41
  children?: React.ReactNode;
40
42
  /** Whether to open automatically */
41
43
  autoOpen?: boolean;
44
+ /** Controlled open state */
45
+ isOpen?: boolean;
46
+ /** Callback for open state changes */
47
+ onOpenChange?: (isOpen: boolean) => void;
42
48
  /** Custom theme */
43
49
  theme?: ReevitTheme;
44
50
  /** Custom API base URL (for testing or self-hosted deployments) */
@@ -132,7 +138,7 @@ interface ReevitContextValue {
132
138
  currency: string;
133
139
  }
134
140
  declare function useReevitContext(): ReevitContextValue;
135
- declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, onSuccess, onError, onClose, onStateChange, children, autoOpen, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
141
+ declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, initialPaymentIntent, onSuccess, onError, onClose, onStateChange, children, autoOpen, isOpen: controlledIsOpen, onOpenChange, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
136
142
 
137
143
  interface PaymentMethodSelectorProps {
138
144
  methods: PaymentMethod[];
@@ -196,6 +202,7 @@ interface PaystackPopupInterface {
196
202
  interface PaystackConfig {
197
203
  key: string;
198
204
  email: string;
205
+ phone?: string;
199
206
  amount: number;
200
207
  currency?: string;
201
208
  ref?: string;
@@ -215,6 +222,7 @@ interface PaystackResponse {
215
222
  interface PaystackBridgeProps {
216
223
  publicKey: string;
217
224
  email: string;
225
+ phone?: string;
218
226
  amount: number;
219
227
  currency?: string;
220
228
  reference?: string;
@@ -226,7 +234,7 @@ interface PaystackBridgeProps {
226
234
  autoStart?: boolean;
227
235
  }
228
236
  declare function loadPaystackScript(): Promise<void>;
229
- declare function PaystackBridge({ publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
237
+ declare function PaystackBridge({ publicKey, email, phone, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
230
238
 
231
239
  declare global {
232
240
  interface Window {
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ interface ReevitCheckoutConfig {
23
23
  metadata?: Record<string, unknown>;
24
24
  /** Payment methods to display */
25
25
  paymentMethods?: PaymentMethod[];
26
+ /** Optional existing payment intent to use instead of creating a new one */
27
+ initialPaymentIntent?: PaymentIntent;
26
28
  }
27
29
  interface ReevitCheckoutCallbacks {
28
30
  /** Called when payment is successful */
@@ -39,6 +41,10 @@ interface ReevitCheckoutProps extends ReevitCheckoutConfig, ReevitCheckoutCallba
39
41
  children?: React.ReactNode;
40
42
  /** Whether to open automatically */
41
43
  autoOpen?: boolean;
44
+ /** Controlled open state */
45
+ isOpen?: boolean;
46
+ /** Callback for open state changes */
47
+ onOpenChange?: (isOpen: boolean) => void;
42
48
  /** Custom theme */
43
49
  theme?: ReevitTheme;
44
50
  /** Custom API base URL (for testing or self-hosted deployments) */
@@ -132,7 +138,7 @@ interface ReevitContextValue {
132
138
  currency: string;
133
139
  }
134
140
  declare function useReevitContext(): ReevitContextValue;
135
- declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, onSuccess, onError, onClose, onStateChange, children, autoOpen, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
141
+ declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, initialPaymentIntent, onSuccess, onError, onClose, onStateChange, children, autoOpen, isOpen: controlledIsOpen, onOpenChange, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
136
142
 
137
143
  interface PaymentMethodSelectorProps {
138
144
  methods: PaymentMethod[];
@@ -196,6 +202,7 @@ interface PaystackPopupInterface {
196
202
  interface PaystackConfig {
197
203
  key: string;
198
204
  email: string;
205
+ phone?: string;
199
206
  amount: number;
200
207
  currency?: string;
201
208
  ref?: string;
@@ -215,6 +222,7 @@ interface PaystackResponse {
215
222
  interface PaystackBridgeProps {
216
223
  publicKey: string;
217
224
  email: string;
225
+ phone?: string;
218
226
  amount: number;
219
227
  currency?: string;
220
228
  reference?: string;
@@ -226,7 +234,7 @@ interface PaystackBridgeProps {
226
234
  autoStart?: boolean;
227
235
  }
228
236
  declare function loadPaystackScript(): Promise<void>;
229
- declare function PaystackBridge({ publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
237
+ declare function PaystackBridge({ publicKey, email, phone, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
230
238
 
231
239
  declare global {
232
240
  interface Window {
package/dist/index.js CHANGED
@@ -178,6 +178,7 @@ var ReevitAPIClient = class {
178
178
  method: this.mapPaymentMethod(method),
179
179
  country,
180
180
  customer_id: config.email || config.metadata?.customerId,
181
+ phone: config.phone,
181
182
  metadata
182
183
  };
183
184
  return this.request("POST", "/v1/payments/intents", request);
@@ -233,7 +234,12 @@ function reevitReducer(state, action) {
233
234
  case "INIT_START":
234
235
  return { ...state, status: "loading", error: null };
235
236
  case "INIT_SUCCESS":
236
- return { ...state, status: "ready", paymentIntent: action.payload };
237
+ return {
238
+ ...state,
239
+ status: "ready",
240
+ paymentIntent: action.payload,
241
+ selectedMethod: action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : state.selectedMethod
242
+ };
237
243
  case "INIT_ERROR":
238
244
  return { ...state, status: "failed", error: action.payload };
239
245
  case "SELECT_METHOD":
@@ -279,9 +285,22 @@ function mapToPaymentIntent(response, config) {
279
285
  }
280
286
  function useReevit(options) {
281
287
  const { config, onSuccess, onError, onClose, onStateChange, apiBaseUrl } = options;
282
- const [state, dispatch] = react.useReducer(reevitReducer, initialState);
288
+ const [state, dispatch] = react.useReducer(reevitReducer, {
289
+ ...initialState,
290
+ status: config.initialPaymentIntent ? "ready" : "idle",
291
+ paymentIntent: config.initialPaymentIntent || null,
292
+ selectedMethod: config.initialPaymentIntent?.availableMethods?.length === 1 ? config.initialPaymentIntent.availableMethods[0] : null
293
+ });
283
294
  const apiClientRef = react.useRef(null);
284
- const initializingRef = react.useRef(false);
295
+ const initializingRef = react.useRef(!!config.initialPaymentIntent);
296
+ react.useEffect(() => {
297
+ if (config.initialPaymentIntent) {
298
+ if (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id) {
299
+ dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
300
+ initializingRef.current = true;
301
+ }
302
+ }
303
+ }, [config.initialPaymentIntent, state.paymentIntent?.id]);
285
304
  if (!apiClientRef.current) {
286
305
  apiClientRef.current = new ReevitAPIClient({
287
306
  publicKey: config.publicKey,
@@ -372,11 +391,16 @@ function useReevit(options) {
372
391
  paymentMethod: state.selectedMethod,
373
392
  psp: state.paymentIntent.recommendedPsp,
374
393
  pspReference: paymentData.pspReference || data?.provider_ref_id || "",
375
- status: "success",
394
+ status: data?.status === "succeeded" ? "success" : "pending",
376
395
  metadata: paymentData
377
396
  };
378
- dispatch({ type: "PROCESS_SUCCESS", payload: result });
379
- onSuccess?.(result);
397
+ if (result.status === "success") {
398
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
399
+ onSuccess?.(result);
400
+ } else {
401
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
402
+ onSuccess?.(result);
403
+ }
380
404
  } catch (err) {
381
405
  const error = {
382
406
  code: "PAYMENT_FAILED",
@@ -666,6 +690,7 @@ function loadPaystackScript() {
666
690
  function PaystackBridge({
667
691
  publicKey,
668
692
  email,
693
+ phone,
669
694
  amount,
670
695
  currency = "GHS",
671
696
  reference,
@@ -695,6 +720,7 @@ function PaystackBridge({
695
720
  const handler = window.PaystackPop.setup({
696
721
  key: publicKey,
697
722
  email,
723
+ phone,
698
724
  amount,
699
725
  // Paystack expects amount in kobo/pesewas (smallest unit)
700
726
  currency,
@@ -702,13 +728,18 @@ function PaystackBridge({
702
728
  metadata,
703
729
  channels,
704
730
  callback: (response) => {
731
+ let usedMethod = "card";
732
+ if (channels && channels.length === 1) {
733
+ usedMethod = channels[0];
734
+ } else if (response.message?.toLowerCase().includes("mobile money")) {
735
+ usedMethod = "mobile_money";
736
+ }
705
737
  const result = {
706
738
  paymentId: response.transaction,
707
739
  reference: response.reference,
708
740
  amount,
709
741
  currency,
710
- paymentMethod: "card",
711
- // Paystack handles this internally
742
+ paymentMethod: usedMethod,
712
743
  psp: "paystack",
713
744
  pspReference: response.trans,
714
745
  status: response.status === "success" ? "success" : "pending",
@@ -761,6 +792,7 @@ function ReevitCheckout({
761
792
  reference,
762
793
  metadata,
763
794
  paymentMethods = ["card", "mobile_money"],
795
+ initialPaymentIntent,
764
796
  // Callbacks
765
797
  onSuccess,
766
798
  onError,
@@ -769,10 +801,23 @@ function ReevitCheckout({
769
801
  // UI
770
802
  children,
771
803
  autoOpen = false,
804
+ isOpen: controlledIsOpen,
805
+ onOpenChange,
772
806
  theme,
773
807
  apiBaseUrl
774
808
  }) {
775
- const [isOpen, setIsOpen] = react.useState(autoOpen);
809
+ const [internalIsOpen, setInternalIsOpen] = react.useState(autoOpen);
810
+ const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
811
+ const setIsOpen = react.useCallback(
812
+ (value) => {
813
+ if (onOpenChange) {
814
+ onOpenChange(value);
815
+ } else {
816
+ setInternalIsOpen(value);
817
+ }
818
+ },
819
+ [onOpenChange]
820
+ );
776
821
  const [showPSPBridge, setShowPSPBridge] = react.useState(false);
777
822
  const [momoData, setMomoData] = react.useState(null);
778
823
  const {
@@ -789,7 +834,17 @@ function ReevitCheckout({
789
834
  isLoading,
790
835
  isComplete
791
836
  } = useReevit({
792
- config: { publicKey, amount, currency, email, phone, reference, metadata, paymentMethods },
837
+ config: {
838
+ publicKey,
839
+ amount,
840
+ currency,
841
+ email,
842
+ phone,
843
+ reference,
844
+ metadata,
845
+ paymentMethods,
846
+ initialPaymentIntent
847
+ },
793
848
  apiBaseUrl,
794
849
  onSuccess: (result2) => {
795
850
  onSuccess?.(result2);
@@ -805,21 +860,31 @@ function ReevitCheckout({
805
860
  onStateChange
806
861
  });
807
862
  react.useEffect(() => {
808
- if (isOpen && status === "idle") {
863
+ if (isOpen && status === "idle" && !initialPaymentIntent) {
809
864
  initialize();
810
865
  }
811
- }, [isOpen, status, initialize]);
866
+ }, [isOpen, status, initialize, initialPaymentIntent]);
867
+ react.useEffect(() => {
868
+ if (isOpen && (selectedMethod === "card" || selectedMethod === "mobile_money") && !showPSPBridge) {
869
+ if (selectedMethod === "card" && paymentIntent) {
870
+ setShowPSPBridge(true);
871
+ } else if (selectedMethod === "mobile_money" && paymentIntent && (momoData?.phone || phone)) {
872
+ setShowPSPBridge(true);
873
+ }
874
+ }
875
+ }, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
812
876
  const handleOpen = react.useCallback(() => {
877
+ if (controlledIsOpen !== void 0) return;
813
878
  setIsOpen(true);
814
879
  setShowPSPBridge(false);
815
880
  setMomoData(null);
816
- }, []);
881
+ }, [controlledIsOpen, setIsOpen]);
817
882
  const handleClose = react.useCallback(() => {
818
883
  closeCheckout();
819
884
  setIsOpen(false);
820
885
  setShowPSPBridge(false);
821
886
  setMomoData(null);
822
- }, [closeCheckout]);
887
+ }, [closeCheckout, setIsOpen]);
823
888
  const handleMethodSelect = react.useCallback(
824
889
  (method) => {
825
890
  selectMethod(method);
@@ -861,10 +926,11 @@ function ReevitCheckout({
861
926
  setShowPSPBridge(false);
862
927
  }, [reset]);
863
928
  const themeStyles = theme ? createThemeVariables(theme) : {};
864
- const trigger = children ? /* @__PURE__ */ jsxRuntime.jsx("span", { onClick: handleOpen, role: "button", tabIndex: 0, children }) : /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "reevit-trigger-btn", onClick: handleOpen, children: [
929
+ const isControlled = controlledIsOpen !== void 0;
930
+ 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: [
865
931
  "Pay ",
866
932
  formatAmount(amount, currency)
867
- ] });
933
+ ] }) : null;
868
934
  const renderContent = () => {
869
935
  if (status === "loading") {
870
936
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-loading", children: [
@@ -897,6 +963,7 @@ function ReevitCheckout({
897
963
  {
898
964
  publicKey: pspKey,
899
965
  email,
966
+ phone: momoData?.phone || phone,
900
967
  amount: paymentIntent?.amount ?? amount,
901
968
  currency: paymentIntent?.currency ?? currency,
902
969
  reference,
@@ -905,7 +972,8 @@ function ReevitCheckout({
905
972
  // Override with correct payment intent ID for webhook routing
906
973
  // This ensures Paystack webhook includes the correct ID to find the payment
907
974
  payment_id: paymentIntent?.id,
908
- connection_id: paymentIntent?.connectionId ?? metadata?.connection_id
975
+ connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
976
+ customer_phone: momoData?.phone || phone
909
977
  },
910
978
  channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
911
979
  onSuccess: handlePSPSuccess,