integrated payment and selft geneartion subscription api

This commit is contained in:
pramod.mahajan 2025-10-29 19:25:55 +05:30
parent c00ab582cc
commit d81ffe86b7
7 changed files with 143 additions and 36 deletions

View File

@ -28,7 +28,6 @@ const ProcessedPayment = ({
const { details: client, planId: selectedPlanId,frequency } = useSelector( const { details: client, planId: selectedPlanId,frequency } = useSelector(
(store) => store.localVariables.selfTenant (store) => store.localVariables.selfTenant
); );
console.log(frequency)
const [selectedPlan, setSelectedPlan] = useState(null); const [selectedPlan, setSelectedPlan] = useState(null);
const [currentPlan, setCurrentPlan] = useState(null); const [currentPlan, setCurrentPlan] = useState(null);
const [failPayment, setFailPayment] = useState(null); const [failPayment, setFailPayment] = useState(null);
@ -144,7 +143,7 @@ const ProcessedPayment = ({
); );
} }
return ( return (
<div className="container-sm text-start "> <div className="container-md text-start ">
<div className="row gx-1 gy-3 justify-content-between"> <div className="row gx-1 gy-3 justify-content-between">
<div className="col-12 col-md-6"> <div className="col-12 col-md-6">
<div className="row"> <div className="row">
@ -309,7 +308,7 @@ const ProcessedPayment = ({
<div className="col-sm-6"> <div className="col-sm-6">
<strong>Email:</strong> <strong>Email:</strong>
</div> </div>
<div className="col-sm-6 mb-2">{client.email}</div> <div className="col-sm-6 mb-2 text-wrap">{client.email}</div>
<div className="col-sm-6 mb-2"> <div className="col-sm-6 mb-2">
<strong>Contact Number:</strong> <strong>Contact Number:</strong>
@ -357,7 +356,7 @@ const ProcessedPayment = ({
className="btn btn-label-primary d-flex align-items-center me-2" className="btn btn-label-primary d-flex align-items-center me-2"
onClick={() => ProcessToPayment(currentPlan?.price)} onClick={() => ProcessToPayment(currentPlan?.price)}
> >
{isPending ? "Please Wait..." : "Processed To Payment"} {isPending ? <span><i className='bx bx-loader-alt bx-md bx-spin me-2'></i>Please Wait...</span> : "Processed To Payment"}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,23 +1,86 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import GlobalModel from "../common/GlobalModel"; import GlobalModel from "../common/GlobalModel";
import Invoice from "./Invoice"; import Invoice from "./Invoice";
import { useSelector } from "react-redux";
import { blockUI, unblockUI } from "../../utils/blockUI";
const VerifiedPayment = ({ onNext, responsePayment }) => { import { error } from "pdf-lib";
import { useSelfGetSubscription } from "../../hooks/useAuth";
const VerifiedPayment = ({ responsePayment, setStepStatus }) => {
const [isGenerateInvoice, setIsGenerateInvoice] = useState(false); const [isGenerateInvoice, setIsGenerateInvoice] = useState(false);
useEffect(() => {
if (responsePayment?.success) { const { tenantEnquireId, paymentDetailId, planId } = useSelector(
onNext(); (store) => store.localVariables.selfTenant
} );
}, [responsePayment]);
if (responsePayment) { const {
mutate: getSubscription,
isPending,
isError,
isSuccess,
error,
} = useSelfGetSubscription(
() => {
unblockUI();
setStepStatus?.((prev) => ({ ...prev, 5: "success" }));
},
() => {
unblockUI();
setStepStatus?.((prev) => ({ ...prev, 5: "failed" }));
}
);
useEffect(() => {
if (responsePayment?.success) {
const payload = { tenantEnquireId, paymentDetailId, planId };
getSubscription(payload);
}
}, [responsePayment]);
if (isError) {
return ( return (
<div className="container-md mt-5 text-center"> <div className="container-md mt-5 text-center">
<div className="d-flex flex-column align-items-center justify-content-center"> <div className="d-flex flex-column align-items-center justify-content-center">
<div <div
className="spinner-border text-primary mb-3 p-1" className="bg-danger p-3 rounded-circle mb-3 d-flex align-items-center justify-content-center"
role="status" style={{ width: "70px", height: "70px" }}
></div> >
<h4 className="text-primary mb-2">Verifying payment...</h4> <i className="bx bx-x fs-1 text-white fw-bold"></i>
</div>
<h4 className="text-danger mb-2">Subscription Generation Failed!</h4>
<p className="text-muted">
Unfortunately, your subscription transaction could not be completed.
</p>
<div className="mt-4 d-flex gap-3 flex-column flex-md-row justify-content-center">
<a href="/" className="px-4 py-2 fw-semibold text-muted">
Please review your payment details carefully and contact our
Support Team for assistance.
</a>
</div>
<div className="alert alert-light-danger mt-4 w-75 mx-auto text-start">
<strong>Error Details:</strong>
<pre className="small mb-0 mt-2 text-wrap">
{JSON.stringify(error, null, 2)}
</pre>
</div>
</div>
</div>
);
}
if (isPending) {
return (
<div className="container-md mt-5 text-center">
<div className="d-flex flex-column align-items-center justify-content-center">
<div className="spinner-border text-primary mb-3 p-1" role="status" />
<h4 className="text-primary mb-2">Verifying Payment...</h4>
<p className="text-muted"> <p className="text-muted">
Please wait while we verify your transaction. Do not refresh or Please wait while we verify your transaction. Do not refresh or
close this page. close this page.
@ -27,8 +90,8 @@ useEffect(() => {
); );
} }
if (!responsePayment?.success) {
if (isSuccess) {
return ( return (
<div className="container-md mt-3 text-center h-auto"> <div className="container-md mt-3 text-center h-auto">
{isGenerateInvoice && ( {isGenerateInvoice && (
@ -39,28 +102,29 @@ useEffect(() => {
<Invoice invoiceData={responsePayment?.data} /> <Invoice invoiceData={responsePayment?.data} />
</GlobalModel> </GlobalModel>
)} )}
<div className="d-flex align-items-center justify-content-center"> <div className="d-flex align-items-center justify-content-center">
<span className="bg-success p-2 p-md-3 rounded-circle"> <span className="bg-success p-2 p-md-3 rounded-circle">
<i className="bx bx-check fs-2 fw-bold text-white"></i> <i className="bx bx-check fs-2 fw-bold text-white"></i>
</span> </span>
<span className="fs-3 fs-md-2 ms-3 text-success"> <span className="fs-3 fs-md-2 ms-3 text-success">
Payment Successful! Payment Successful!
</span> </span>
</div> </div>
<p className="text-muted mb-4 fs-6 fs-md-5 text-center mt-8"> <p className="text-muted mb-4 fs-6 fs-md-5 text-center mt-4">
Thank you for your payment. Your <strong>subscription</strong> has Thank you for your payment. Your <strong>subscription</strong> has
been successfully activated. been successfully activated.
</p> </p>
<div className="mt-8"> <div className="mt-3">
<small className="text-muted "> <small className="text-muted">
A Set Password link has been sent to your registered email address . A <strong>Set Password</strong> link has been sent to your
Please check your inbox . registered email address. Please check your inbox.
</small> </small>
</div> </div>
<div className="d-flex flex-column flex-md-row justify-content-center gap-3 my-12 "> <div className="d-flex flex-column flex-md-row justify-content-center gap-3 my-4">
<a href="/" className="btn btn-info px-4 py-2 fw-semibold"> <a href="/" className="btn btn-info px-4 py-2 fw-semibold">
Go to Dashboard Go to Dashboard
</a> </a>
@ -79,3 +143,5 @@ useEffect(() => {
}; };
export default VerifiedPayment; export default VerifiedPayment;

View File

@ -16,6 +16,7 @@ import {
import { removeSession } from "../utils/authUtils.js"; import { removeSession } from "../utils/authUtils.js";
import showToast from "../services/toastService.tsx"; import showToast from "../services/toastService.tsx";
import eventBus from "../services/eventBus.js"; import eventBus from "../services/eventBus.js";
import { blockUI } from "../utils/blockUI.js";
// ----------------------------Modal-------------------------- // ----------------------------Modal--------------------------
@ -39,7 +40,7 @@ export const useSubscription = (frequency) => {
const resp = await AuthRepository.getSubscription(frequency); const resp = await AuthRepository.getSubscription(frequency);
return resp.data; return resp.data;
}, },
enabled: frequency !== null && frequency !== undefined enabled: frequency !== null && frequency !== undefined,
}); });
}; };
@ -88,18 +89,53 @@ export const useCreateSelfTenant = (onSuccessCallBack, onFailureCallBack) => {
return resp.data; return resp.data;
}, },
onSuccess: (response, variables) => { onSuccess: (response, variables) => {
dispatch( dispatch(
setSelfTenant({ setSelfTenant({
tenantEnquireId: response?.id, tenantEnquireId: response?.id,
planId: null, planId: null,
details:response details: response,
}) })
); );
if (onSuccessCallBack) onSuccessCallBack(response); if (onSuccessCallBack) onSuccessCallBack(response);
}, },
onError: (error) => { onError: (error) => {
showToast("Somthing worng went happend", "error"); showToast(
`${error?.response?.data?.errors || ""} ${
error?.response?.data?.message || ""
} ${error?.response?.data?.statusCode || ""}`.trim() ||
error?.message ||
"Something went wrong, please try again!",
"error"
);
if (onFailureCallBack) onFailureCallBack();
},
});
};
export const useSelfGetSubscription = (
onSuccessCallBack,
onFailureCallBack
) => {
const dispatch = useDispatch();
return useMutation({
mutationFn: async (payload) => {
blockUI();
const resp = await AuthRepository.selfCreateSubscription(payload);
return resp.data;
},
onSuccess: (response, variables) => {
if (onSuccessCallBack) onSuccessCallBack(response);
},
onError: (error) => {
showToast(
`${error?.response?.data?.errors || ""} ${
error?.response?.data?.message || ""
} ${error?.response?.data?.statusCode || ""}`.trim() ||
error?.message ||
"Something went wrong, please try again!",
"error"
);
if (onFailureCallBack) onFailureCallBack(); if (onFailureCallBack) onFailureCallBack();
}, },
}); });

View File

@ -1,8 +1,9 @@
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { PaymentRepository } from "../repositories/PaymentRepository"; import { PaymentRepository } from "../repositories/PaymentRepository";
import showToast from "../services/toastService"; import showToast from "../services/toastService";
import { useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { blockUI, unblockUI } from "../utils/blockUI"; import { blockUI, unblockUI } from "../utils/blockUI";
import { setSelfTenant } from "../slices/localVariablesSlice";
export const removeRazorpayArtifacts=()=> { export const removeRazorpayArtifacts=()=> {
try { try {
@ -56,11 +57,13 @@ const closeRazorpayPopup=()=> {
export const useVerifyPayment = (onSuccessCallBack, onFailureCallBack) => { export const useVerifyPayment = (onSuccessCallBack, onFailureCallBack) => {
const client = useQueryClient(); const client = useQueryClient();
const dispatch = useDispatch()
return useMutation({ return useMutation({
mutationFn: (payload) => PaymentRepository.verifyPayment(payload), mutationFn: (payload) => PaymentRepository.verifyPayment(payload),
onSuccess: (data) => { onSuccess: (data) => {
dispatch(setSelfTenant({ paymentDetailId: data?.data?.id }));
if (onSuccessCallBack) onSuccessCallBack(data); if (onSuccessCallBack) onSuccessCallBack(data);
}, },

View File

@ -97,10 +97,9 @@ const MakeSubscription = () => {
name: "Verified", name: "Verified",
component: () => ( component: () => (
<VerifiedPayment <VerifiedPayment
onNext={() => { responsePayment={responsePayment}
setStepStatus((prev) => ({ ...prev, 5: "success" })) setStepStatus={setStepStatus}
}}
responsePayment={responsePayment}
/> />
), ),
}, },

View File

@ -12,7 +12,9 @@ const AuthRepository = {
sendMail: (data) => api.postPublic("/api/auth/sendmail", data), sendMail: (data) => api.postPublic("/api/auth/sendmail", data),
getSubscription: (frequency) => getSubscription: (frequency) =>
api.getPublic(`/api/market/list/subscription-plan?frequency=${frequency}`), api.getPublic(`/api/market/list/subscription-plan?frequency=${frequency}`),
createSuscription:(data)=>api.post(`/api/Tenant/self/create`,data), createSuscription: (data) => api.post(`/api/Tenant/self/create`, data), // this will put entry inside enquiry table
selfCreateSubscription: (data) =>
api.post(`/api/Tenant/self/subscription`, data),
// Protected routes (require auth token) // Protected routes (require auth token)
logout: (data) => api.post("/api/auth/logout", data), logout: (data) => api.post("/api/auth/logout", data),

View File

@ -37,6 +37,7 @@ const localVariablesSlice = createSlice({
planId: null, planId: null,
details:null, details:null,
frequency:null, frequency:null,
paymentDetailId:null
}, },
}, },
reducers: { reducers: {
@ -111,6 +112,7 @@ const localVariablesSlice = createSlice({
state.selfTenant.details = state.selfTenant.details =
action.payload.details ?? state.selfTenant.details; action.payload.details ?? state.selfTenant.details;
state.selfTenant.frequency = action.payload.frequency ?? state.selfTenant.frequency; state.selfTenant.frequency = action.payload.frequency ?? state.selfTenant.frequency;
state.selfTenant.paymentDetailId = action.payload.paymentDetailId ?? state.selfTenant.paymentDetailId;
}, },
}, },
}); });