This commit is contained in:
shafi54 2026-03-25 19:58:40 +05:30
parent 3c836e274d
commit 4414f9f64b
6 changed files with 81 additions and 199 deletions

View file

@ -63,7 +63,8 @@ export default function OrderDetails() {
onSuccess: (result) => {
Alert.alert(
"Success",
`Refund initiated successfully!\n\nAmount: ₹${result.amount}\nStatus: ${result.status}`
`Refund initiated successfully!\n\nAmount: `
// `Refund initiated successfully!\n\nAmount: ₹${result.amount}\nStatus: ${result.status}`
);
setInitiateRefundDialogOpen(false);
},

View file

@ -2,7 +2,6 @@ import * as cron from 'node-cron';
import { db } from '@/src/db/db_index'
import { payments, orders, deliverySlotInfo, refunds } from '@/src/db/schema'
import { eq, and, gt, isNotNull } from 'drizzle-orm';
import { RazorpayPaymentService } from '@/src/lib/payments-utils'
interface PendingPaymentRecord {
payment: typeof payments.$inferSelect;
@ -20,34 +19,34 @@ export const createPaymentNotification = (record: PendingPaymentRecord) => {
export const checkRefundStatuses = async () => {
try {
const initiatedRefunds = await db
.select()
.from(refunds)
.where(and(
eq(refunds.refundStatus, 'initiated'),
isNotNull(refunds.merchantRefundId)
));
// Process refunds concurrently using Promise.allSettled
const promises = initiatedRefunds.map(async (refund) => {
if (!refund.merchantRefundId) return;
try {
const razorpayRefund = await RazorpayPaymentService.fetchRefund(refund.merchantRefundId);
if (razorpayRefund.status === 'processed') {
await db
.update(refunds)
.set({ refundStatus: 'success', refundProcessedAt: new Date() })
.where(eq(refunds.id, refund.id));
}
} catch (error) {
console.error(`Error checking refund ${refund.id}:`, error);
}
});
// Wait for all promises to complete
await Promise.allSettled(promises);
// const initiatedRefunds = await db
// .select()
// .from(refunds)
// .where(and(
// eq(refunds.refundStatus, 'initiated'),
// isNotNull(refunds.merchantRefundId)
// ));
//
// // Process refunds concurrently using Promise.allSettled
// const promises = initiatedRefunds.map(async (refund) => {
// if (!refund.merchantRefundId) return;
//
// try {
// const razorpayRefund = await RazorpayPaymentService.fetchRefund(refund.merchantRefundId);
//
// if (razorpayRefund.status === 'processed') {
// await db
// .update(refunds)
// .set({ refundStatus: 'success', refundProcessedAt: new Date() })
// .where(eq(refunds.id, refund.id));
// }
// } catch (error) {
// console.error(`Error checking refund ${refund.id}:`, error);
// }
// });
//
// // Wait for all promises to complete
// await Promise.allSettled(promises);
} catch (error) {
console.error('Error in checkRefundStatuses:', error);
}

View file

@ -1,4 +1,4 @@
import Razorpay from "razorpay";
// import Razorpay from "razorpay";
import { razorpayId, razorpaySecret } from "@/src/lib/env-exporter"
import { db } from "@/src/db/db_index"
import { payments } from "@/src/db/schema"
@ -6,54 +6,54 @@ import { payments } from "@/src/db/schema"
type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0];
export class RazorpayPaymentService {
private static instance = new Razorpay({
key_id: razorpayId,
key_secret: razorpaySecret,
});
// private static instance = new Razorpay({
// key_id: razorpayId,
// key_secret: razorpaySecret,
// });
//
static async createOrder(orderId: number, amount: string) {
// Create Razorpay order
const razorpayOrder = await this.instance.orders.create({
amount: parseFloat(amount) * 100, // Convert to paisa
currency: 'INR',
receipt: `order_${orderId}`,
notes: {
customerOrderId: orderId.toString(),
},
});
return razorpayOrder;
// const razorpayOrder = await this.instance.orders.create({
// amount: parseFloat(amount) * 100, // Convert to paisa
// currency: 'INR',
// receipt: `order_${orderId}`,
// notes: {
// customerOrderId: orderId.toString(),
// },
// });
//
// return razorpayOrder;
}
static async insertPaymentRecord(orderId: number, razorpayOrder: any, tx?: Tx) {
// Use transaction if provided, otherwise use db
const dbInstance = tx || db;
// Insert payment record
const [payment] = await dbInstance
.insert(payments)
.values({
status: 'pending',
gateway: 'razorpay',
orderId,
token: orderId.toString(),
merchantOrderId: razorpayOrder.id,
payload: razorpayOrder,
})
.returning();
return payment;
// const dbInstance = tx || db;
//
// // Insert payment record
// const [payment] = await dbInstance
// .insert(payments)
// .values({
// status: 'pending',
// gateway: 'razorpay',
// orderId,
// token: orderId.toString(),
// merchantOrderId: razorpayOrder.id,
// payload: razorpayOrder,
// })
// .returning();
//
// return payment;
}
static async initiateRefund(paymentId: string, amount: number) {
const refund = await this.instance.payments.refund(paymentId, {
amount,
});
return refund;
// const refund = await this.instance.payments.refund(paymentId, {
// amount,
// });
// return refund;
}
static async fetchRefund(refundId: string) {
const refund = await this.instance.refunds.fetch(refundId);
return refund;
// const refund = await this.instance.refunds.fetch(refundId);
// return refund;
}
}
}

View file

@ -1,15 +1,5 @@
import { router, protectedProcedure } from "@/src/trpc/trpc-index"
import { z } from "zod";
import { db } from "@/src/db/db_index"
import {
orders,
orderStatus,
payments,
refunds,
} from "@/src/db/schema";
import { and, eq } from "drizzle-orm";
import { ApiError } from "@/src/lib/api-error"
import { RazorpayPaymentService } from "@/src/lib/payments-utils"
const initiateRefundSchema = z
.object({
@ -33,114 +23,6 @@ export const adminPaymentsRouter = router({
initiateRefund: protectedProcedure
.input(initiateRefundSchema)
.mutation(async ({ input }) => {
try {
const { orderId, refundPercent, refundAmount } = input;
// Validate order exists
const order = await db.query.orders.findFirst({
where: eq(orders.id, orderId),
});
if (!order) {
throw new ApiError("Order not found", 404);
}
// Check if order is paid
const orderStatusRecord = await db.query.orderStatus.findFirst({
where: eq(orderStatus.orderId, orderId),
});
if(order.isCod) {
throw new ApiError("Order is a Cash On Delivery. Not eligible for refund")
}
if (
!orderStatusRecord ||
(orderStatusRecord.paymentStatus !== "success" &&
!(order.isCod && orderStatusRecord.isDelivered))
) {
throw new ApiError("Order payment not verified or not eligible for refund", 400);
}
// Calculate refund amount
let calculatedRefundAmount: number;
if (refundPercent !== undefined) {
calculatedRefundAmount =
(parseFloat(order.totalAmount) * refundPercent) / 100;
} else if (refundAmount !== undefined) {
calculatedRefundAmount = refundAmount;
if (calculatedRefundAmount > parseFloat(order.totalAmount)) {
throw new ApiError("Refund amount cannot exceed order total", 400);
}
} else {
throw new ApiError("Invalid refund parameters", 400);
}
let razorpayRefund = null;
let merchantRefundId = null;
// Get payment record for online payments
const payment = await db.query.payments.findFirst({
where: and(
eq(payments.orderId, orderId),
eq(payments.status, "success")
),
});
if (!payment || payment.status !== "success") {
throw new ApiError("Payment not found or not successful", 404);
}
const payload = payment.payload as any;
// Initiate Razorpay refund
razorpayRefund = await RazorpayPaymentService.initiateRefund(
payload.payment_id,
Math.round(calculatedRefundAmount * 100) // Convert to paisa
);
merchantRefundId = razorpayRefund.id;
// Check if refund already exists for this order
const existingRefund = await db.query.refunds.findFirst({
where: eq(refunds.orderId, orderId),
});
const refundStatus = "initiated";
if (existingRefund) {
// Update existing refund
await db
.update(refunds)
.set({
refundAmount: calculatedRefundAmount.toString(),
refundStatus,
merchantRefundId,
refundProcessedAt: order.isCod ? new Date() : null,
})
.where(eq(refunds.id, existingRefund.id));
} else {
// Insert new refund
await db
.insert(refunds)
.values({
orderId,
refundAmount: calculatedRefundAmount.toString(),
refundStatus,
merchantRefundId,
});
}
return {
refundId: merchantRefundId || `cod_${orderId}`,
amount: calculatedRefundAmount,
status: refundStatus,
message: order.isCod ? "COD refund processed successfully" : "Refund initiated successfully",
};
}
catch(e) {
console.log(e);
throw new ApiError("Failed to initiate refund")
}
return {}
}),
});

View file

@ -23,7 +23,6 @@ import {
sendOrderPlacedNotification,
sendOrderCancelledNotification,
} from "@/src/lib/notif-job";
import { RazorpayPaymentService } from "@/src/lib/payments-utils";
import { getNextDeliveryDate } from "@/src/trpc/apis/common-apis/common";
import { CONST_KEYS, getConstant, getConstants } from "@/src/lib/const-store";
import { publishFormattedOrder, publishCancellation } from "@/src/lib/post-order-handler";
@ -316,15 +315,15 @@ const placeOrderUtil = async (params: {
await tx.insert(orderStatus).values(allOrderStatuses);
if (paymentMethod === "online" && sharedPaymentInfoId) {
const razorpayOrder = await RazorpayPaymentService.createOrder(
sharedPaymentInfoId,
totalWithDelivery.toString()
);
await RazorpayPaymentService.insertPaymentRecord(
sharedPaymentInfoId,
razorpayOrder,
tx
);
// const razorpayOrder = await RazorpayPaymentService.createOrder(
// sharedPaymentInfoId,
// totalWithDelivery.toString()
// );
// await RazorpayPaymentService.insertPaymentRecord(
// sharedPaymentInfoId,
// razorpayOrder,
// tx
// );
}
return insertedOrders;

View file

@ -52,7 +52,8 @@ export const paymentRouter = router({
await RazorpayPaymentService.insertPaymentRecord(parseInt(orderId), razorpayOrder);
return {
razorpayOrderId: razorpayOrder.id,
razorpayOrderId: 0,
// razorpayOrderId: razorpayOrder.id,
key: razorpayId,
};
}),