@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 +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +106 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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);
|
|
@@ -187,7 +188,13 @@ var ReevitAPIClient = class {
|
|
|
187
188
|
return this.request("GET", `/v1/payments/${paymentId}`);
|
|
188
189
|
}
|
|
189
190
|
/**
|
|
190
|
-
* Confirms a payment after PSP callback
|
|
191
|
+
* Confirms a payment intent after PSP callback (public endpoint)
|
|
192
|
+
*/
|
|
193
|
+
async confirmPaymentIntent(paymentId, clientSecret) {
|
|
194
|
+
return this.request("POST", `/v1/payments/${paymentId}/confirm-intent?client_secret=${clientSecret}`);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Confirms a payment after PSP callback (authenticated endpoint)
|
|
191
198
|
*/
|
|
192
199
|
async confirmPayment(paymentId) {
|
|
193
200
|
return this.request("POST", `/v1/payments/${paymentId}/confirm`);
|
|
@@ -231,7 +238,12 @@ function reevitReducer(state, action) {
|
|
|
231
238
|
case "INIT_START":
|
|
232
239
|
return { ...state, status: "loading", error: null };
|
|
233
240
|
case "INIT_SUCCESS":
|
|
234
|
-
return {
|
|
241
|
+
return {
|
|
242
|
+
...state,
|
|
243
|
+
status: "ready",
|
|
244
|
+
paymentIntent: action.payload,
|
|
245
|
+
selectedMethod: action.payload.availableMethods?.length === 1 ? action.payload.availableMethods[0] : state.selectedMethod
|
|
246
|
+
};
|
|
235
247
|
case "INIT_ERROR":
|
|
236
248
|
return { ...state, status: "failed", error: action.payload };
|
|
237
249
|
case "SELECT_METHOD":
|
|
@@ -267,6 +279,8 @@ function mapToPaymentIntent(response, config) {
|
|
|
267
279
|
status: response.status,
|
|
268
280
|
recommendedPsp: mapProviderToPsp(response.provider),
|
|
269
281
|
availableMethods: config.paymentMethods || ["card", "mobile_money"],
|
|
282
|
+
reference: response.reference || response.id,
|
|
283
|
+
// Use backend reference or fallback to ID
|
|
270
284
|
connectionId: response.connection_id,
|
|
271
285
|
provider: response.provider,
|
|
272
286
|
feeAmount: response.fee_amount,
|
|
@@ -280,14 +294,17 @@ function useReevit(options) {
|
|
|
280
294
|
const [state, dispatch] = useReducer(reevitReducer, {
|
|
281
295
|
...initialState,
|
|
282
296
|
status: config.initialPaymentIntent ? "ready" : "idle",
|
|
283
|
-
paymentIntent: config.initialPaymentIntent || null
|
|
297
|
+
paymentIntent: config.initialPaymentIntent || null,
|
|
298
|
+
selectedMethod: config.initialPaymentIntent?.availableMethods?.length === 1 ? config.initialPaymentIntent.availableMethods[0] : null
|
|
284
299
|
});
|
|
285
300
|
const apiClientRef = useRef(null);
|
|
286
301
|
const initializingRef = useRef(!!config.initialPaymentIntent);
|
|
287
302
|
useEffect(() => {
|
|
288
|
-
if (config.initialPaymentIntent
|
|
289
|
-
|
|
290
|
-
|
|
303
|
+
if (config.initialPaymentIntent) {
|
|
304
|
+
if (!state.paymentIntent || state.paymentIntent.id !== config.initialPaymentIntent.id) {
|
|
305
|
+
dispatch({ type: "INIT_SUCCESS", payload: config.initialPaymentIntent });
|
|
306
|
+
initializingRef.current = true;
|
|
307
|
+
}
|
|
291
308
|
}
|
|
292
309
|
}, [config.initialPaymentIntent, state.paymentIntent?.id]);
|
|
293
310
|
if (!apiClientRef.current) {
|
|
@@ -366,8 +383,10 @@ function useReevit(options) {
|
|
|
366
383
|
if (!apiClient) {
|
|
367
384
|
throw new Error("API client not initialized");
|
|
368
385
|
}
|
|
369
|
-
const
|
|
386
|
+
const clientSecret = state.paymentIntent.clientSecret;
|
|
387
|
+
const { data, error } = clientSecret ? await apiClient.confirmPaymentIntent(state.paymentIntent.id, clientSecret) : await apiClient.confirmPayment(state.paymentIntent.id);
|
|
370
388
|
if (error) {
|
|
389
|
+
console.error("[useReevit] Confirmation error:", error);
|
|
371
390
|
dispatch({ type: "PROCESS_ERROR", payload: error });
|
|
372
391
|
onError?.(error);
|
|
373
392
|
return;
|
|
@@ -380,11 +399,17 @@ function useReevit(options) {
|
|
|
380
399
|
paymentMethod: state.selectedMethod,
|
|
381
400
|
psp: state.paymentIntent.recommendedPsp,
|
|
382
401
|
pspReference: paymentData.pspReference || data?.provider_ref_id || "",
|
|
383
|
-
status: "success",
|
|
384
|
-
metadata: paymentData
|
|
402
|
+
status: data?.status === "succeeded" ? "success" : "pending",
|
|
403
|
+
metadata: { ...paymentData, backend_status: data?.status }
|
|
385
404
|
};
|
|
386
|
-
|
|
387
|
-
|
|
405
|
+
console.log("[useReevit] Process result:", result);
|
|
406
|
+
if (result.status === "success") {
|
|
407
|
+
dispatch({ type: "PROCESS_SUCCESS", payload: result });
|
|
408
|
+
onSuccess?.(result);
|
|
409
|
+
} else {
|
|
410
|
+
dispatch({ type: "PROCESS_SUCCESS", payload: result });
|
|
411
|
+
onSuccess?.(result);
|
|
412
|
+
}
|
|
388
413
|
} catch (err) {
|
|
389
414
|
const error = {
|
|
390
415
|
code: "PAYMENT_FAILED",
|
|
@@ -674,9 +699,11 @@ function loadPaystackScript() {
|
|
|
674
699
|
function PaystackBridge({
|
|
675
700
|
publicKey,
|
|
676
701
|
email,
|
|
702
|
+
phone,
|
|
677
703
|
amount,
|
|
678
704
|
currency = "GHS",
|
|
679
705
|
reference,
|
|
706
|
+
accessCode,
|
|
680
707
|
metadata,
|
|
681
708
|
channels = ["card", "mobile_money"],
|
|
682
709
|
onSuccess,
|
|
@@ -687,47 +714,70 @@ function PaystackBridge({
|
|
|
687
714
|
const initialized = useRef(false);
|
|
688
715
|
const startPayment = useCallback(async () => {
|
|
689
716
|
try {
|
|
717
|
+
console.log("[PaystackBridge] Starting payment", {
|
|
718
|
+
hasPublicKey: !!publicKey,
|
|
719
|
+
email,
|
|
720
|
+
amount,
|
|
721
|
+
reference,
|
|
722
|
+
hasAccessCode: !!accessCode
|
|
723
|
+
});
|
|
690
724
|
if (!publicKey) {
|
|
691
725
|
throw new Error("Paystack public key is required but was empty");
|
|
692
726
|
}
|
|
693
|
-
if (!email) {
|
|
694
|
-
throw new Error("Email is required for Paystack payments");
|
|
727
|
+
if (!email && !accessCode) {
|
|
728
|
+
throw new Error("Email is required for Paystack payments when no access code is provided");
|
|
695
729
|
}
|
|
696
|
-
if (!amount
|
|
697
|
-
throw new Error("Valid amount is required for Paystack payments");
|
|
730
|
+
if (!amount && !accessCode) {
|
|
731
|
+
throw new Error("Valid amount is required for Paystack payments when no access code is provided");
|
|
698
732
|
}
|
|
699
733
|
await loadPaystackScript();
|
|
700
734
|
if (!window.PaystackPop) {
|
|
701
735
|
throw new Error("Paystack script loaded but PaystackPop not available");
|
|
702
736
|
}
|
|
703
|
-
const
|
|
737
|
+
const setupConfig = {
|
|
704
738
|
key: publicKey,
|
|
705
739
|
email,
|
|
740
|
+
phone,
|
|
706
741
|
amount,
|
|
707
|
-
// Paystack expects amount in kobo/pesewas (smallest unit)
|
|
708
742
|
currency,
|
|
709
743
|
ref: reference,
|
|
744
|
+
access_code: accessCode,
|
|
710
745
|
metadata,
|
|
711
746
|
channels,
|
|
712
747
|
callback: (response) => {
|
|
748
|
+
console.log("[PaystackBridge] Callback received", response);
|
|
749
|
+
let usedMethod = "card";
|
|
750
|
+
if (channels && channels.length === 1) {
|
|
751
|
+
usedMethod = channels[0];
|
|
752
|
+
} else if (response.message?.toLowerCase().includes("mobile money")) {
|
|
753
|
+
usedMethod = "mobile_money";
|
|
754
|
+
}
|
|
713
755
|
const result = {
|
|
714
|
-
paymentId: response.
|
|
756
|
+
paymentId: response.reference,
|
|
757
|
+
// Use the reference as paymentId because we set it to Reevit's UUID
|
|
715
758
|
reference: response.reference,
|
|
716
759
|
amount,
|
|
717
760
|
currency,
|
|
718
|
-
paymentMethod:
|
|
719
|
-
// Paystack handles this internally
|
|
761
|
+
paymentMethod: usedMethod,
|
|
720
762
|
psp: "paystack",
|
|
721
|
-
pspReference: response.
|
|
763
|
+
pspReference: response.transaction,
|
|
764
|
+
// Paystack's internal transaction ID
|
|
722
765
|
status: response.status === "success" ? "success" : "pending",
|
|
723
|
-
metadata: {
|
|
766
|
+
metadata: {
|
|
767
|
+
...response,
|
|
768
|
+
trxref: response.trxref,
|
|
769
|
+
paystack_transaction_id: response.transaction,
|
|
770
|
+
paystack_trans: response.trans
|
|
771
|
+
}
|
|
724
772
|
};
|
|
725
773
|
onSuccess(result);
|
|
726
774
|
},
|
|
727
775
|
onClose: () => {
|
|
776
|
+
console.log("[PaystackBridge] Modal closed");
|
|
728
777
|
onClose();
|
|
729
778
|
}
|
|
730
|
-
}
|
|
779
|
+
};
|
|
780
|
+
const handler = window.PaystackPop.setup(setupConfig);
|
|
731
781
|
handler.openIframe();
|
|
732
782
|
} catch (err) {
|
|
733
783
|
const errorMessage = err instanceof Error ? err.message : "Failed to initialize Paystack";
|
|
@@ -739,7 +789,7 @@ function PaystackBridge({
|
|
|
739
789
|
};
|
|
740
790
|
onError(error);
|
|
741
791
|
}
|
|
742
|
-
}, [publicKey, email, amount, currency, reference, metadata, channels, onSuccess, onError, onClose]);
|
|
792
|
+
}, [publicKey, email, amount, currency, reference, accessCode, metadata, channels, onSuccess, onError, onClose]);
|
|
743
793
|
useEffect(() => {
|
|
744
794
|
if (autoStart && !initialized.current) {
|
|
745
795
|
initialized.current = true;
|
|
@@ -769,6 +819,7 @@ function ReevitCheckout({
|
|
|
769
819
|
reference,
|
|
770
820
|
metadata,
|
|
771
821
|
paymentMethods = ["card", "mobile_money"],
|
|
822
|
+
initialPaymentIntent,
|
|
772
823
|
// Callbacks
|
|
773
824
|
onSuccess,
|
|
774
825
|
onError,
|
|
@@ -810,7 +861,17 @@ function ReevitCheckout({
|
|
|
810
861
|
isLoading,
|
|
811
862
|
isComplete
|
|
812
863
|
} = useReevit({
|
|
813
|
-
config: {
|
|
864
|
+
config: {
|
|
865
|
+
publicKey,
|
|
866
|
+
amount,
|
|
867
|
+
currency,
|
|
868
|
+
email,
|
|
869
|
+
phone,
|
|
870
|
+
reference,
|
|
871
|
+
metadata,
|
|
872
|
+
paymentMethods,
|
|
873
|
+
initialPaymentIntent
|
|
874
|
+
},
|
|
814
875
|
apiBaseUrl,
|
|
815
876
|
onSuccess: (result2) => {
|
|
816
877
|
onSuccess?.(result2);
|
|
@@ -826,10 +887,19 @@ function ReevitCheckout({
|
|
|
826
887
|
onStateChange
|
|
827
888
|
});
|
|
828
889
|
useEffect(() => {
|
|
829
|
-
if (isOpen && status === "idle") {
|
|
890
|
+
if (isOpen && status === "idle" && !initialPaymentIntent) {
|
|
830
891
|
initialize();
|
|
831
892
|
}
|
|
832
|
-
}, [isOpen, status, initialize]);
|
|
893
|
+
}, [isOpen, status, initialize, initialPaymentIntent]);
|
|
894
|
+
useEffect(() => {
|
|
895
|
+
if (isOpen && (selectedMethod === "card" || selectedMethod === "mobile_money") && !showPSPBridge) {
|
|
896
|
+
if (selectedMethod === "card" && paymentIntent) {
|
|
897
|
+
setShowPSPBridge(true);
|
|
898
|
+
} else if (selectedMethod === "mobile_money" && paymentIntent && (momoData?.phone || phone)) {
|
|
899
|
+
setShowPSPBridge(true);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}, [isOpen, selectedMethod, showPSPBridge, paymentIntent, momoData, phone]);
|
|
833
903
|
const handleOpen = useCallback(() => {
|
|
834
904
|
if (controlledIsOpen !== void 0) return;
|
|
835
905
|
setIsOpen(true);
|
|
@@ -883,15 +953,16 @@ function ReevitCheckout({
|
|
|
883
953
|
setShowPSPBridge(false);
|
|
884
954
|
}, [reset]);
|
|
885
955
|
const themeStyles = theme ? createThemeVariables(theme) : {};
|
|
886
|
-
const
|
|
956
|
+
const isControlled = controlledIsOpen !== void 0;
|
|
957
|
+
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: [
|
|
887
958
|
"Pay ",
|
|
888
959
|
formatAmount(amount, currency)
|
|
889
|
-
] });
|
|
960
|
+
] }) : null;
|
|
890
961
|
const renderContent = () => {
|
|
891
|
-
if (status === "loading") {
|
|
962
|
+
if (status === "loading" || status === "processing") {
|
|
892
963
|
return /* @__PURE__ */ jsxs("div", { className: "reevit-loading", children: [
|
|
893
964
|
/* @__PURE__ */ jsx("div", { className: "reevit-spinner" }),
|
|
894
|
-
/* @__PURE__ */ jsx("p", { children: "Preparing checkout..." })
|
|
965
|
+
/* @__PURE__ */ jsx("p", { children: status === "loading" ? "Preparing checkout..." : "Processing payment..." })
|
|
895
966
|
] });
|
|
896
967
|
}
|
|
897
968
|
if (status === "success" && result) {
|
|
@@ -919,15 +990,18 @@ function ReevitCheckout({
|
|
|
919
990
|
{
|
|
920
991
|
publicKey: pspKey,
|
|
921
992
|
email,
|
|
993
|
+
phone: momoData?.phone || phone,
|
|
922
994
|
amount: paymentIntent?.amount ?? amount,
|
|
923
995
|
currency: paymentIntent?.currency ?? currency,
|
|
924
996
|
reference,
|
|
997
|
+
accessCode: paymentIntent?.clientSecret,
|
|
925
998
|
metadata: {
|
|
926
999
|
...metadata,
|
|
927
1000
|
// Override with correct payment intent ID for webhook routing
|
|
928
1001
|
// This ensures Paystack webhook includes the correct ID to find the payment
|
|
929
1002
|
payment_id: paymentIntent?.id,
|
|
930
|
-
connection_id: paymentIntent?.connectionId ?? metadata?.connection_id
|
|
1003
|
+
connection_id: paymentIntent?.connectionId ?? metadata?.connection_id,
|
|
1004
|
+
customer_phone: momoData?.phone || phone
|
|
931
1005
|
},
|
|
932
1006
|
channels: selectedMethod === "mobile_money" ? ["mobile_money"] : ["card"],
|
|
933
1007
|
onSuccess: handlePSPSuccess,
|