@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.mjs CHANGED
@@ -176,6 +176,7 @@ var ReevitAPIClient = class {
176
176
  method: this.mapPaymentMethod(method),
177
177
  country,
178
178
  customer_id: config.email || config.metadata?.customerId,
179
+ phone: config.phone,
179
180
  metadata
180
181
  };
181
182
  return this.request("POST", "/v1/payments/intents", request);
@@ -231,7 +232,12 @@ function reevitReducer(state, action) {
231
232
  case "INIT_START":
232
233
  return { ...state, status: "loading", error: null };
233
234
  case "INIT_SUCCESS":
234
- return { ...state, status: "ready", paymentIntent: action.payload };
235
+ return {
236
+ ...state,
237
+ status: "ready",
238
+ paymentIntent: action.payload,
239
+ selectedMethod: action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : state.selectedMethod
240
+ };
235
241
  case "INIT_ERROR":
236
242
  return { ...state, status: "failed", error: action.payload };
237
243
  case "SELECT_METHOD":
@@ -277,9 +283,22 @@ function mapToPaymentIntent(response, config) {
277
283
  }
278
284
  function useReevit(options) {
279
285
  const { config, onSuccess, onError, onClose, onStateChange, apiBaseUrl } = options;
280
- const [state, dispatch] = useReducer(reevitReducer, initialState);
286
+ const [state, dispatch] = useReducer(reevitReducer, {
287
+ ...initialState,
288
+ status: config.initialPaymentIntent ? "ready" : "idle",
289
+ paymentIntent: config.initialPaymentIntent || null,
290
+ selectedMethod: config.initialPaymentIntent?.availableMethods?.length === 1 ? config.initialPaymentIntent.availableMethods[0] : null
291
+ });
281
292
  const apiClientRef = useRef(null);
282
- const initializingRef = useRef(false);
293
+ const initializingRef = useRef(!!config.initialPaymentIntent);
294
+ useEffect(() => {
295
+ if (config.initialPaymentIntent) {
296
+ if (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id) {
297
+ dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
298
+ initializingRef.current = true;
299
+ }
300
+ }
301
+ }, [config.initialPaymentIntent, state.paymentIntent?.id]);
283
302
  if (!apiClientRef.current) {
284
303
  apiClientRef.current = new ReevitAPIClient({
285
304
  publicKey: config.publicKey,
@@ -370,11 +389,16 @@ function useReevit(options) {
370
389
  paymentMethod: state.selectedMethod,
371
390
  psp: state.paymentIntent.recommendedPsp,
372
391
  pspReference: paymentData.pspReference || data?.provider_ref_id || "",
373
- status: "success",
392
+ status: data?.status === "succeeded" ? "success" : "pending",
374
393
  metadata: paymentData
375
394
  };
376
- dispatch({ type: "PROCESS_SUCCESS", payload: result });
377
- onSuccess?.(result);
395
+ if (result.status === "success") {
396
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
397
+ onSuccess?.(result);
398
+ } else {
399
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
400
+ onSuccess?.(result);
401
+ }
378
402
  } catch (err) {
379
403
  const error = {
380
404
  code: "PAYMENT_FAILED",
@@ -664,6 +688,7 @@ function loadPaystackScript() {
664
688
  function PaystackBridge({
665
689
  publicKey,
666
690
  email,
691
+ phone,
667
692
  amount,
668
693
  currency = "GHS",
669
694
  reference,
@@ -693,6 +718,7 @@ function PaystackBridge({
693
718
  const handler = window.PaystackPop.setup({
694
719
  key: publicKey,
695
720
  email,
721
+ phone,
696
722
  amount,
697
723
  // Paystack expects amount in kobo/pesewas (smallest unit)
698
724
  currency,
@@ -700,13 +726,18 @@ function PaystackBridge({
700
726
  metadata,
701
727
  channels,
702
728
  callback: (response) => {
729
+ let usedMethod = "card";
730
+ if (channels && channels.length === 1) {
731
+ usedMethod = channels[0];
732
+ } else if (response.message?.toLowerCase().includes("mobile money")) {
733
+ usedMethod = "mobile_money";
734
+ }
703
735
  const result = {
704
736
  paymentId: response.transaction,
705
737
  reference: response.reference,
706
738
  amount,
707
739
  currency,
708
- paymentMethod: "card",
709
- // Paystack handles this internally
740
+ paymentMethod: usedMethod,
710
741
  psp: "paystack",
711
742
  pspReference: response.trans,
712
743
  status: response.status === "success" ? "success" : "pending",
@@ -759,6 +790,7 @@ function ReevitCheckout({
759
790
  reference,
760
791
  metadata,
761
792
  paymentMethods = ["card", "mobile_money"],
793
+ initialPaymentIntent,
762
794
  // Callbacks
763
795
  onSuccess,
764
796
  onError,
@@ -767,10 +799,23 @@ function ReevitCheckout({
767
799
  // UI
768
800
  children,
769
801
  autoOpen = false,
802
+ isOpen: controlledIsOpen,
803
+ onOpenChange,
770
804
  theme,
771
805
  apiBaseUrl
772
806
  }) {
773
- const [isOpen, setIsOpen] = useState(autoOpen);
807
+ const [internalIsOpen, setInternalIsOpen] = useState(autoOpen);
808
+ const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
809
+ const setIsOpen = useCallback(
810
+ (value) => {
811
+ if (onOpenChange) {
812
+ onOpenChange(value);
813
+ } else {
814
+ setInternalIsOpen(value);
815
+ }
816
+ },
817
+ [onOpenChange]
818
+ );
774
819
  const [showPSPBridge, setShowPSPBridge] = useState(false);
775
820
  const [momoData, setMomoData] = useState(null);
776
821
  const {
@@ -787,7 +832,17 @@ function ReevitCheckout({
787
832
  isLoading,
788
833
  isComplete
789
834
  } = useReevit({
790
- config: { publicKey, amount, currency, email, phone, reference, metadata, paymentMethods },
835
+ config: {
836
+ publicKey,
837
+ amount,
838
+ currency,
839
+ email,
840
+ phone,
841
+ reference,
842
+ metadata,
843
+ paymentMethods,
844
+ initialPaymentIntent
845
+ },
791
846
  apiBaseUrl,
792
847
  onSuccess: (result2) => {
793
848
  onSuccess?.(result2);
@@ -803,21 +858,31 @@ function ReevitCheckout({
803
858
  onStateChange
804
859
  });
805
860
  useEffect(() => {
806
- if (isOpen && status === "idle") {
861
+ if (isOpen && status === "idle" && !initialPaymentIntent) {
807
862
  initialize();
808
863
  }
809
- }, [isOpen, status, initialize]);
864
+ }, [isOpen, status, initialize, initialPaymentIntent]);
865
+ useEffect(() => {
866
+ if (isOpen && (selectedMethod === "card" || selectedMethod === "mobile_money") && !showPSPBridge) {
867
+ if (selectedMethod === "card" && paymentIntent) {
868
+ setShowPSPBridge(true);
869
+ } else if (selectedMethod === "mobile_money" && paymentIntent && (momoData?.phone || phone)) {
870
+ setShowPSPBridge(true);
871
+ }
872
+ }
873
+ }, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
810
874
  const handleOpen = useCallback(() => {
875
+ if (controlledIsOpen !== void 0) return;
811
876
  setIsOpen(true);
812
877
  setShowPSPBridge(false);
813
878
  setMomoData(null);
814
- }, []);
879
+ }, [controlledIsOpen, setIsOpen]);
815
880
  const handleClose = useCallback(() => {
816
881
  closeCheckout();
817
882
  setIsOpen(false);
818
883
  setShowPSPBridge(false);
819
884
  setMomoData(null);
820
- }, [closeCheckout]);
885
+ }, [closeCheckout, setIsOpen]);
821
886
  const handleMethodSelect = useCallback(
822
887
  (method) => {
823
888
  selectMethod(method);
@@ -859,10 +924,11 @@ function ReevitCheckout({
859
924
  setShowPSPBridge(false);
860
925
  }, [reset]);
861
926
  const themeStyles = theme ? createThemeVariables(theme) : {};
862
- const trigger = children ? /* @__PURE__ */ jsx("span", { onClick: handleOpen, role: "button", tabIndex: 0, children }) : /* @__PURE__ */ jsxs("button", { className: "reevit-trigger-btn", onClick: handleOpen, children: [
927
+ const isControlled = controlledIsOpen !== void 0;
928
+ const trigger = children ? /* @__PURE__ */ jsx("span", { onClick: isControlled ? void 0 : handleOpen, role: isControlled ? void 0 : "button", tabIndex: isControlled ? void 0 : 0, children }) : !isControlled ? /* @__PURE__ */ jsxs("button", { className: "reevit-trigger-btn", onClick: handleOpen, children: [
863
929
  "Pay ",
864
930
  formatAmount(amount, currency)
865
- ] });
931
+ ] }) : null;
866
932
  const renderContent = () => {
867
933
  if (status === "loading") {
868
934
  return /* @__PURE__ */ jsxs("div", { className: "reevit-loading", children: [
@@ -895,6 +961,7 @@ function ReevitCheckout({
895
961
  {
896
962
  publicKey: pspKey,
897
963
  email,
964
+ phone: momoData?.phone || phone,
898
965
  amount: paymentIntent?.amount ?? amount,
899
966
  currency: paymentIntent?.currency ?? currency,
900
967
  reference,
@@ -903,7 +970,8 @@ function ReevitCheckout({
903
970
  // Override with correct payment intent ID for webhook routing
904
971
  // This ensures Paystack webhook includes the correct ID to find the payment
905
972
  payment_id: paymentIntent?.id,
906
- connection_id: paymentIntent?.connectionId ?? metadata?.connection_id
973
+ connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
974
+ customer_phone: momoData?.phone || phone
907
975
  },
908
976
  channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
909
977
  onSuccess: handlePSPSuccess,