enh
This commit is contained in:
parent
3c836e274d
commit
4414f9f64b
6 changed files with 81 additions and 199 deletions
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue