Skip to content

Dev#147

Merged
abhishek-nexgen-dev merged 2 commits into
masterfrom
dev
Jun 10, 2026
Merged

Dev#147
abhishek-nexgen-dev merged 2 commits into
masterfrom
dev

Conversation

@abhishek-nexgen-dev

Copy link
Copy Markdown
Member

No description provided.

@abhishek-nexgen-dev abhishek-nexgen-dev merged commit a8afa05 into master Jun 10, 2026
1 of 2 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request integrates Cashfree payments into the billing system, adds a payment architecture guide, and refactors webhook routing and various UI components. Critical feedback highlights a security regression in OrgRoute.tsx where role-based protection for webhooks was removed, and several bugs in QuickRecharge.tsx where swallowed errors could falsely credit wallets and stale closures exist due to empty dependency arrays. Additionally, reviewers noted hardcoded customer details, a redundant base URL in the payment hook, and a potential race condition when initializing the Cashfree instance.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +20 to +38
const handlePayment = useCallback(async (Data: handlePayType) => {
try {
console.log("Payment Data echo echo:--->", Data);
const cashfree = await getCashfree();

const result = await cashfree.checkout({
paymentSessionId: Data.session_id,

redirectTarget: "_modal",
});

if (result.error) {
throw new Error(result.error.message);
}
} catch (error) {
console.error("Payment failed:", error);
addToast("error", "Payment Failed", "An error occurred during payment. Please try again.");
}
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The handlePayment function catches errors but does not rethrow them. This causes the calling function createPayment (and subsequently handleRecharge) to assume the payment succeeded, proceeding to credit the user's wallet even if the payment failed or was cancelled. Additionally, addToast is missing from the useCallback dependency array.

Suggested change
const handlePayment = useCallback(async (Data: handlePayType) => {
try {
console.log("Payment Data echo echo:--->", Data);
const cashfree = await getCashfree();
const result = await cashfree.checkout({
paymentSessionId: Data.session_id,
redirectTarget: "_modal",
});
if (result.error) {
throw new Error(result.error.message);
}
} catch (error) {
console.error("Payment failed:", error);
addToast("error", "Payment Failed", "An error occurred during payment. Please try again.");
}
}, []);
const handlePayment = useCallback(async (Data: handlePayType) => {
try {
console.log("Payment Data echo echo:--->", Data);
const cashfree = await getCashfree();
const result = await cashfree.checkout({
paymentSessionId: Data.session_id,
redirectTarget: "_modal",
});
if (result.error) {
throw new Error(result.error.message);
}
} catch (error) {
console.error("Payment failed:", error);
addToast("error", "Payment Failed", "An error occurred during payment. Please try again.");
throw error;
}
}, [addToast]);

Comment thread src/routes/OrgRoute.tsx
Comment on lines +54 to +57
<Route path="webhooks/create" element={<CreateWebhookPage />} />
<Route path="webhooks/:id" element={<WebhookDetailsPage />} />
<Route path="webhooks/:id/edit" element={<EditWebhookPage />} />
<Route path="webhooks/:id/logs" element={<WebhookLogsPage />} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

This change introduces two major issues:

  1. Security Regression: The ProtectedRoute wrapper restricting access to webhook routes based on roles (CommunityOwner, Admin, Organizer) has been completely removed. This allows unauthorized users to view, create, edit, and access logs for webhooks.
  2. Missing Route: The route for WebhookListPage (the main webhooks list view) has been omitted in the refactored routes, even though the component is still imported.

Please restore the role-based protection and the list route. Note that you will also need to restore the import of ProtectedRoute from ./ProtectedRoute on line 16.

Suggested change
<Route path="webhooks/create" element={<CreateWebhookPage />} />
<Route path="webhooks/:id" element={<WebhookDetailsPage />} />
<Route path="webhooks/:id/edit" element={<EditWebhookPage />} />
<Route path="webhooks/:id/logs" element={<WebhookLogsPage />} />
<Route
path="webhooks/*"
element={
<ProtectedRoute allowedRoles={["CommunityOwner", "Admin", "Organizer"]}>
<Routes>
<Route index element={<WebhookListPage />} />
<Route path="create" element={<CreateWebhookPage />} />
<Route path=":id" element={<WebhookDetailsPage />} />
<Route path=":id/edit" element={<EditWebhookPage />} />
<Route path=":id/logs" element={<WebhookLogsPage />} />
</Routes>
</ProtectedRoute>
}
/>

Comment on lines +40 to +63
let createPayment = useCallback(async () => {
try {
let res = await createPaymentIntent.mutateAsync({
customerEmail: "test@gmail.com",
customerName: "Test User",
customerPhone: "9999999999",
order_amount: Number(amountStr), // Convert to paise
});

console.log("Payment Intent Created -->", res.data.order_id, res.data.payment_session_id);

console.log("BEFORE HANDLE PAYMENT");

await handlePayment({
orderId: res.data.order_id,
session_id: res.data.payment_session_id,
});

console.log("AFTER HANDLE PAYMENT");
} catch (error) {
console.error("Error creating payment intent:", error);
addToast("error", "Payment Failed", "Unable to create payment intent.");
}
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The createPayment function has several issues:

  1. It catches errors from createPaymentIntent.mutateAsync and handlePayment but does not rethrow them, allowing handleRecharge to proceed with crediting the wallet even on failure.
  2. The dependency array for useCallback is empty [], which creates a stale closure bug because it references amountStr, createPaymentIntent, handlePayment, and addToast.
  3. Customer details are hardcoded (test@gmail.com, Test User, 9999999999). These should be dynamically retrieved from the active user session or profile context.
  const createPayment = useCallback(async () => {
    try {
      // TODO: Retrieve actual customer details from user session/context
      const res = await createPaymentIntent.mutateAsync({
        customerEmail: "test@gmail.com",
        customerName: "Test User",
        customerPhone: "9999999999",
        order_amount: Number(amountStr),
      });

      console.log("Payment Intent Created -->", res.data.order_id, res.data.payment_session_id);

      await handlePayment({
        orderId: res.data.order_id,
        session_id: res.data.payment_session_id,
      });
    } catch (error) {
      console.error("Error creating payment intent:", error);
      addToast("error", "Payment Failed", "Unable to create payment intent.");
      throw error;
    }
  }, [amountStr, createPaymentIntent, handlePayment, addToast]);

Comment on lines +4 to +18
const baseUrl = import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api/v1";

export const useCreatePaymentIntent = () => {
return useMutation({
mutationKey: ["createPaymentIntent"],

mutationFn: async (paymentData: {
customerEmail: string;
customerName: string;
customerPhone: string;
order_amount: number;
}) => {
const response = await api.post(`${baseUrl}/payment/create-payment-intent`, paymentData);
return response.data;
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the shared api axios instance (imported from @/utils/axios.utils) is already configured with a baseURL, prepending ${baseUrl} manually is redundant and can lead to incorrect request URLs (e.g., double base URLs). It is cleaner and more robust to use relative paths like /payment/create-payment-intent and let the axios instance handle the base URL configuration.

export const useCreatePaymentIntent = () => {
  return useMutation({
    mutationKey: ["createPaymentIntent"],

    mutationFn: async (paymentData: {
      customerEmail: string;
      customerName: string;
      customerPhone: string;
      order_amount: number;
    }) => {
      const response = await api.post("/payment/create-payment-intent", paymentData);
      return response.data;
    },

Comment thread src/lib/cashfree.ts
Comment on lines +3 to +16
let cashfreeInstance: Cashfree | null = null;

export async function getCashfree(): Promise<Cashfree> {
if (cashfreeInstance) {
return cashfreeInstance;
}

cashfreeInstance = await load({
mode: import.meta.env.VITE_CASHFREE_ENV === "production" ? "production" : "sandbox",

});

return cashfreeInstance;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If getCashfree() is called concurrently before the first load promise resolves, cashfreeInstance will still be null, leading to multiple redundant calls to load(). Storing the initialization promise instead of the resolved instance avoids this race condition.

let cashfreePromise: Promise<Cashfree> | null = null;

export function getCashfree(): Promise<Cashfree> {
  if (!cashfreePromise) {
    cashfreePromise = load({
      mode: import.meta.env.VITE_CASHFREE_ENV === "production" ? "production" : "sandbox",
    });
  }

  return cashfreePromise;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant