@reevit/react 0.2.9 → 0.3.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 CHANGED
@@ -118,6 +118,8 @@ interface PaymentIntent {
118
118
  recommendedPsp: 'paystack' | 'hubtel' | 'flutterwave';
119
119
  /** Available payment methods for this intent */
120
120
  availableMethods: PaymentMethod[];
121
+ /** Reference provided or generated */
122
+ reference?: string;
121
123
  /** Connection ID (from Reevit backend) */
122
124
  connectionId?: string;
123
125
  /** Provider name (from backend) */
@@ -138,7 +140,7 @@ interface ReevitContextValue {
138
140
  currency: string;
139
141
  }
140
142
  declare function useReevitContext(): ReevitContextValue;
141
- declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, onSuccess, onError, onClose, onStateChange, children, autoOpen, isOpen: controlledIsOpen, onOpenChange, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
143
+ 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;
142
144
 
143
145
  interface PaymentMethodSelectorProps {
144
146
  methods: PaymentMethod[];
@@ -202,9 +204,11 @@ interface PaystackPopupInterface {
202
204
  interface PaystackConfig {
203
205
  key: string;
204
206
  email: string;
205
- amount: number;
207
+ phone?: string;
208
+ amount?: number;
206
209
  currency?: string;
207
210
  ref?: string;
211
+ access_code?: string;
208
212
  metadata?: Record<string, unknown>;
209
213
  channels?: string[];
210
214
  callback: (response: PaystackResponse) => void;
@@ -221,9 +225,11 @@ interface PaystackResponse {
221
225
  interface PaystackBridgeProps {
222
226
  publicKey: string;
223
227
  email: string;
228
+ phone?: string;
224
229
  amount: number;
225
230
  currency?: string;
226
231
  reference?: string;
232
+ accessCode?: string;
227
233
  metadata?: Record<string, unknown>;
228
234
  channels?: ('card' | 'bank' | 'ussd' | 'qr' | 'mobile_money' | 'bank_transfer')[];
229
235
  onSuccess: (result: PaymentResult) => void;
@@ -232,7 +238,7 @@ interface PaystackBridgeProps {
232
238
  autoStart?: boolean;
233
239
  }
234
240
  declare function loadPaystackScript(): Promise<void>;
235
- declare function PaystackBridge({ publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
241
+ declare function PaystackBridge({ publicKey, email, phone, amount, currency, reference, accessCode, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
236
242
 
237
243
  declare global {
238
244
  interface Window {
@@ -539,6 +545,7 @@ interface PaymentIntentResponse {
539
545
  fee_amount: number;
540
546
  fee_currency: string;
541
547
  net_amount: number;
548
+ reference?: string;
542
549
  }
543
550
  interface PaymentDetailResponse {
544
551
  id: string;
@@ -593,7 +600,14 @@ declare class ReevitAPIClient {
593
600
  error?: PaymentError;
594
601
  }>;
595
602
  /**
596
- * Confirms a payment after PSP callback
603
+ * Confirms a payment intent after PSP callback (public endpoint)
604
+ */
605
+ confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{
606
+ data?: PaymentDetailResponse;
607
+ error?: PaymentError;
608
+ }>;
609
+ /**
610
+ * Confirms a payment after PSP callback (authenticated endpoint)
597
611
  */
598
612
  confirmPayment(paymentId: string): Promise<{
599
613
  data?: PaymentDetailResponse;
package/dist/index.d.ts CHANGED
@@ -118,6 +118,8 @@ interface PaymentIntent {
118
118
  recommendedPsp: 'paystack' | 'hubtel' | 'flutterwave';
119
119
  /** Available payment methods for this intent */
120
120
  availableMethods: PaymentMethod[];
121
+ /** Reference provided or generated */
122
+ reference?: string;
121
123
  /** Connection ID (from Reevit backend) */
122
124
  connectionId?: string;
123
125
  /** Provider name (from backend) */
@@ -138,7 +140,7 @@ interface ReevitContextValue {
138
140
  currency: string;
139
141
  }
140
142
  declare function useReevitContext(): ReevitContextValue;
141
- declare function ReevitCheckout({ publicKey, amount, currency, email, phone, reference, metadata, paymentMethods, onSuccess, onError, onClose, onStateChange, children, autoOpen, isOpen: controlledIsOpen, onOpenChange, theme, apiBaseUrl, }: ReevitCheckoutProps): react_jsx_runtime.JSX.Element;
143
+ 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;
142
144
 
143
145
  interface PaymentMethodSelectorProps {
144
146
  methods: PaymentMethod[];
@@ -202,9 +204,11 @@ interface PaystackPopupInterface {
202
204
  interface PaystackConfig {
203
205
  key: string;
204
206
  email: string;
205
- amount: number;
207
+ phone?: string;
208
+ amount?: number;
206
209
  currency?: string;
207
210
  ref?: string;
211
+ access_code?: string;
208
212
  metadata?: Record<string, unknown>;
209
213
  channels?: string[];
210
214
  callback: (response: PaystackResponse) => void;
@@ -221,9 +225,11 @@ interface PaystackResponse {
221
225
  interface PaystackBridgeProps {
222
226
  publicKey: string;
223
227
  email: string;
228
+ phone?: string;
224
229
  amount: number;
225
230
  currency?: string;
226
231
  reference?: string;
232
+ accessCode?: string;
227
233
  metadata?: Record<string, unknown>;
228
234
  channels?: ('card' | 'bank' | 'ussd' | 'qr' | 'mobile_money' | 'bank_transfer')[];
229
235
  onSuccess: (result: PaymentResult) => void;
@@ -232,7 +238,7 @@ interface PaystackBridgeProps {
232
238
  autoStart?: boolean;
233
239
  }
234
240
  declare function loadPaystackScript(): Promise<void>;
235
- declare function PaystackBridge({ publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
241
+ declare function PaystackBridge({ publicKey, email, phone, amount, currency, reference, accessCode, metadata, channels, onSuccess, onError, onClose, autoStart, }: PaystackBridgeProps): react_jsx_runtime.JSX.Element;
236
242
 
237
243
  declare global {
238
244
  interface Window {
@@ -539,6 +545,7 @@ interface PaymentIntentResponse {
539
545
  fee_amount: number;
540
546
  fee_currency: string;
541
547
  net_amount: number;
548
+ reference?: string;
542
549
  }
543
550
  interface PaymentDetailResponse {
544
551
  id: string;
@@ -593,7 +600,14 @@ declare class ReevitAPIClient {
593
600
  error?: PaymentError;
594
601
  }>;
595
602
  /**
596
- * Confirms a payment after PSP callback
603
+ * Confirms a payment intent after PSP callback (public endpoint)
604
+ */
605
+ confirmPaymentIntent(paymentId: string, clientSecret: string): Promise<{
606
+ data?: PaymentDetailResponse;
607
+ error?: PaymentError;
608
+ }>;
609
+ /**
610
+ * Confirms a payment after PSP callback (authenticated endpoint)
597
611
  */
598
612
  confirmPayment(paymentId: string): Promise<{
599
613
  data?: PaymentDetailResponse;
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);
@@ -189,7 +190,13 @@ var ReevitAPIClient = class {
189
190
  return this.request("GET", `/v1/payments/${paymentId}`);
190
191
  }
191
192
  /**
192
- * Confirms a payment after PSP callback
193
+ * Confirms a payment intent after PSP callback (public endpoint)
194
+ */
195
+ async confirmPaymentIntent(paymentId, clientSecret) {
196
+ return this.request("POST", `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);
197
+ }
198
+ /**
199
+ * Confirms a payment after PSP callback (authenticated endpoint)
193
200
  */
194
201
  async confirmPayment(paymentId) {
195
202
  return this.request("POST", `/v1/payments/${paymentId}/confirm`);
@@ -233,7 +240,12 @@ function reevitReducer(state, action) {
233
240
  case "INIT_START":
234
241
  return { ...state, status: "loading", error: null };
235
242
  case "INIT_SUCCESS":
236
- return { ...state, status: "ready", paymentIntent: action.payload };
243
+ return {
244
+ ...state,
245
+ status: "ready",
246
+ paymentIntent: action.payload,
247
+ selectedMethod: action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : state.selectedMethod
248
+ };
237
249
  case "INIT_ERROR":
238
250
  return { ...state, status: "failed", error: action.payload };
239
251
  case "SELECT_METHOD":
@@ -269,6 +281,8 @@ function mapToPaymentIntent(response, config) {
269
281
  status: response.status,
270
282
  recommendedPsp: mapProviderToPsp(response.provider),
271
283
  availableMethods: config.paymentMethods || ["card", "mobile_money"],
284
+ reference: response.reference || response.id,
285
+ // Use backend reference or fallback to ID
272
286
  connectionId: response.connection_id,
273
287
  provider: response.provider,
274
288
  feeAmount: response.fee_amount,
@@ -282,14 +296,17 @@ function useReevit(options) {
282
296
  const [state, dispatch] = react.useReducer(reevitReducer, {
283
297
  ...initialState,
284
298
  status: config.initialPaymentIntent ? "ready" : "idle",
285
- paymentIntent: config.initialPaymentIntent || null
299
+ paymentIntent: config.initialPaymentIntent || null,
300
+ selectedMethod: config.initialPaymentIntent?.availableMethods?.length === 1 ? config.initialPaymentIntent.availableMethods[0] : null
286
301
  });
287
302
  const apiClientRef = react.useRef(null);
288
303
  const initializingRef = react.useRef(!!config.initialPaymentIntent);
289
304
  react.useEffect(() => {
290
- if (config.initialPaymentIntent && (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id)) {
291
- dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
292
- initializingRef.current = true;
305
+ if (config.initialPaymentIntent) {
306
+ if (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id) {
307
+ dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
308
+ initializingRef.current = true;
309
+ }
293
310
  }
294
311
  }, [config.initialPaymentIntent, state.paymentIntent?.id]);
295
312
  if (!apiClientRef.current) {
@@ -368,8 +385,10 @@ function useReevit(options) {
368
385
  if (!apiClient) {
369
386
  throw new Error("API client not initialized");
370
387
  }
371
- const { data, error } = await apiClient.confirmPayment(state.paymentIntent.id);
388
+ const clientSecret = state.paymentIntent.clientSecret;
389
+ const { data, error } = clientSecret ? await apiClient.confirmPaymentIntent(state.paymentIntent.id, clientSecret) : await apiClient.confirmPayment(state.paymentIntent.id);
372
390
  if (error) {
391
+ console.error("[useReevit] Confirmation error:", error);
373
392
  dispatch({ type: "PROCESS_ERROR", payload: error });
374
393
  onError?.(error);
375
394
  return;
@@ -382,11 +401,17 @@ function useReevit(options) {
382
401
  paymentMethod: state.selectedMethod,
383
402
  psp: state.paymentIntent.recommendedPsp,
384
403
  pspReference: paymentData.pspReference || data?.provider_ref_id || "",
385
- status: "success",
386
- metadata: paymentData
404
+ status: data?.status === "succeeded" ? "success" : "pending",
405
+ metadata: { ...paymentData, backend_status: data?.status }
387
406
  };
388
- dispatch({ type: "PROCESS_SUCCESS", payload: result });
389
- onSuccess?.(result);
407
+ console.log("[useReevit] Process result:", result);
408
+ if (result.status === "success") {
409
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
410
+ onSuccess?.(result);
411
+ } else {
412
+ dispatch({ type: "PROCESS_SUCCESS", payload: result });
413
+ onSuccess?.(result);
414
+ }
390
415
  } catch (err) {
391
416
  const error = {
392
417
  code: "PAYMENT_FAILED",
@@ -676,9 +701,11 @@ function loadPaystackScript() {
676
701
  function PaystackBridge({
677
702
  publicKey,
678
703
  email,
704
+ phone,
679
705
  amount,
680
706
  currency = "GHS",
681
707
  reference,
708
+ accessCode,
682
709
  metadata,
683
710
  channels = ["card", "mobile_money"],
684
711
  onSuccess,
@@ -689,47 +716,70 @@ function PaystackBridge({
689
716
  const initialized = react.useRef(false);
690
717
  const startPayment = react.useCallback(async () => {
691
718
  try {
719
+ console.log("[PaystackBridge] Starting payment", {
720
+ hasPublicKey: !!publicKey,
721
+ email,
722
+ amount,
723
+ reference,
724
+ hasAccessCode: !!accessCode
725
+ });
692
726
  if (!publicKey) {
693
727
  throw new Error("Paystack public key is required but was empty");
694
728
  }
695
- if (!email) {
696
- throw new Error("Email is required for Paystack payments");
729
+ if (!email && !accessCode) {
730
+ throw new Error("Email is required for Paystack payments when no access code is provided");
697
731
  }
698
- if (!amount || amount <= 0) {
699
- throw new Error("Valid amount is required for Paystack payments");
732
+ if (!amount && !accessCode) {
733
+ throw new Error("Valid amount is required for Paystack payments when no access code is provided");
700
734
  }
701
735
  await loadPaystackScript();
702
736
  if (!window.PaystackPop) {
703
737
  throw new Error("Paystack script loaded but PaystackPop not available");
704
738
  }
705
- const handler = window.PaystackPop.setup({
739
+ const setupConfig = {
706
740
  key: publicKey,
707
741
  email,
742
+ phone,
708
743
  amount,
709
- // Paystack expects amount in kobo/pesewas (smallest unit)
710
744
  currency,
711
745
  ref: reference,
746
+ access_code: accessCode,
712
747
  metadata,
713
748
  channels,
714
749
  callback: (response) => {
750
+ console.log("[PaystackBridge] Callback received", response);
751
+ let usedMethod = "card";
752
+ if (channels && channels.length === 1) {
753
+ usedMethod = channels[0];
754
+ } else if (response.message?.toLowerCase().includes("mobile money")) {
755
+ usedMethod = "mobile_money";
756
+ }
715
757
  const result = {
716
- paymentId: response.transaction,
758
+ paymentId: response.reference,
759
+ // Use the reference as paymentId because we set it to Reevit's UUID
717
760
  reference: response.reference,
718
761
  amount,
719
762
  currency,
720
- paymentMethod: "card",
721
- // Paystack handles this internally
763
+ paymentMethod: usedMethod,
722
764
  psp: "paystack",
723
- pspReference: response.trans,
765
+ pspReference: response.transaction,
766
+ // Paystack's internal transaction ID
724
767
  status: response.status === "success" ? "success" : "pending",
725
- metadata: { trxref: response.trxref }
768
+ metadata: {
769
+ ...response,
770
+ trxref: response.trxref,
771
+ paystack_transaction_id: response.transaction,
772
+ paystack_trans: response.trans
773
+ }
726
774
  };
727
775
  onSuccess(result);
728
776
  },
729
777
  onClose: () => {
778
+ console.log("[PaystackBridge] Modal closed");
730
779
  onClose();
731
780
  }
732
- });
781
+ };
782
+ const handler = window.PaystackPop.setup(setupConfig);
733
783
  handler.openIframe();
734
784
  } catch (err) {
735
785
  const errorMessage = err instanceof Error ? err.message : "Failed to initialize Paystack";
@@ -741,7 +791,7 @@ function PaystackBridge({
741
791
  };
742
792
  onError(error);
743
793
  }
744
- }, [publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose]);
794
+ }, [publicKey, email, amount, currency, reference, accessCode, metadata, channels, onSuccess, onError, onClose]);
745
795
  react.useEffect(() => {
746
796
  if (autoStart && !initialized.current) {
747
797
  initialized.current = true;
@@ -771,6 +821,7 @@ function ReevitCheckout({
771
821
  reference,
772
822
  metadata,
773
823
  paymentMethods = ["card", "mobile_money"],
824
+ initialPaymentIntent,
774
825
  // Callbacks
775
826
  onSuccess,
776
827
  onError,
@@ -812,7 +863,17 @@ function ReevitCheckout({
812
863
  isLoading,
813
864
  isComplete
814
865
  } = useReevit({
815
- config: { publicKey, amount, currency, email, phone, reference, metadata, paymentMethods },
866
+ config: {
867
+ publicKey,
868
+ amount,
869
+ currency,
870
+ email,
871
+ phone,
872
+ reference,
873
+ metadata,
874
+ paymentMethods,
875
+ initialPaymentIntent
876
+ },
816
877
  apiBaseUrl,
817
878
  onSuccess: (result2) => {
818
879
  onSuccess?.(result2);
@@ -828,10 +889,19 @@ function ReevitCheckout({
828
889
  onStateChange
829
890
  });
830
891
  react.useEffect(() => {
831
- if (isOpen && status === "idle") {
892
+ if (isOpen && status === "idle" && !initialPaymentIntent) {
832
893
  initialize();
833
894
  }
834
- }, [isOpen, status, initialize]);
895
+ }, [isOpen, status, initialize, initialPaymentIntent]);
896
+ react.useEffect(() => {
897
+ if (isOpen && (selectedMethod === "card" || selectedMethod === "mobile_money") && !showPSPBridge) {
898
+ if (selectedMethod === "card" && paymentIntent) {
899
+ setShowPSPBridge(true);
900
+ } else if (selectedMethod === "mobile_money" && paymentIntent && (momoData?.phone || phone)) {
901
+ setShowPSPBridge(true);
902
+ }
903
+ }
904
+ }, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
835
905
  const handleOpen = react.useCallback(() => {
836
906
  if (controlledIsOpen !== void 0) return;
837
907
  setIsOpen(true);
@@ -885,15 +955,16 @@ function ReevitCheckout({
885
955
  setShowPSPBridge(false);
886
956
  }, [reset]);
887
957
  const themeStyles = theme ? createThemeVariables(theme) : {};
888
- 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: [
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: [
889
960
  "Pay ",
890
961
  formatAmount(amount, currency)
891
- ] });
962
+ ] }) : null;
892
963
  const renderContent = () => {
893
- if (status === "loading") {
964
+ if (status === "loading" || status === "processing") {
894
965
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "reevit-loading", children: [
895
966
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "reevit-spinner" }),
896
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Preparing checkout..." })
967
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
897
968
  ] });
898
969
  }
899
970
  if (status === "success" && result) {
@@ -921,15 +992,18 @@ function ReevitCheckout({
921
992
  {
922
993
  publicKey: pspKey,
923
994
  email,
995
+ phone: momoData?.phone || phone,
924
996
  amount: paymentIntent?.amount ?? amount,
925
997
  currency: paymentIntent?.currency ?? currency,
926
998
  reference,
999
+ accessCode: paymentIntent?.clientSecret,
927
1000
  metadata: {
928
1001
  ...metadata,
929
1002
  // Override with correct payment intent ID for webhook routing
930
1003
  // This ensures Paystack webhook includes the correct ID to find the payment
931
1004
  payment_id: paymentIntent?.id,
932
- connection_id: paymentIntent?.connectionId ?? metadata?.connection_id
1005
+ connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
1006
+ customer_phone: momoData?.phone || phone
933
1007
  },
934
1008
  channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
935
1009
  onSuccess: handlePSPSuccess,