fix race condition issue
This commit is contained in:
@@ -1,43 +1,10 @@
|
||||
import { randomBytes } from "crypto";
|
||||
import { Prisma } from "@/app/generated/prisma/client";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { refundRepo } from "@/server/repositories/refund.repo";
|
||||
import { calculateRefundAmount, daysUntilDeparture } from "@/lib/refund-policy";
|
||||
import { isTripDepartureDayPast } from "@/lib/trip-dates";
|
||||
import { payoutService } from "@/server/services/payout.service";
|
||||
|
||||
const SERIAL_TX_ATTEMPTS = 6;
|
||||
|
||||
function isSerializationConflict(err: unknown): boolean {
|
||||
return (
|
||||
typeof err === "object" &&
|
||||
err !== null &&
|
||||
"code" in err &&
|
||||
(err as { code: string }).code === "P2034"
|
||||
);
|
||||
}
|
||||
|
||||
async function runSerializable<T>(fn: (tx: Prisma.TransactionClient) => Promise<T>): Promise<T> {
|
||||
let lastErr: unknown;
|
||||
for (let attempt = 0; attempt < SERIAL_TX_ATTEMPTS; attempt++) {
|
||||
try {
|
||||
return await prisma.$transaction(fn, {
|
||||
isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
|
||||
maxWait: 5000,
|
||||
timeout: 15000,
|
||||
});
|
||||
} catch (e) {
|
||||
lastErr = e;
|
||||
if (isSerializationConflict(e) && attempt < SERIAL_TX_ATTEMPTS - 1) {
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw lastErr instanceof Error
|
||||
? lastErr
|
||||
: new Error("Gagal memproses refund. Coba lagi sebentar.");
|
||||
}
|
||||
import { runSerializable } from "@/lib/serializable-tx";
|
||||
|
||||
function newIdempotencyKey(): string {
|
||||
return `refund_${randomBytes(16).toString("hex")}`;
|
||||
@@ -358,6 +325,14 @@ export const refundService = {
|
||||
if (booking.userId !== input.userId) {
|
||||
throw new Error("Booking ini bukan milikmu");
|
||||
}
|
||||
if (
|
||||
booking.trip.status === "CLOSED" ||
|
||||
booking.trip.status === "COMPLETED"
|
||||
) {
|
||||
throw new Error(
|
||||
"Trip sudah dibatalkan/selesai — pembatalan mandiri ditutup. Hubungi admin untuk proses refund."
|
||||
);
|
||||
}
|
||||
if (booking.status !== "PAID") {
|
||||
throw new Error(
|
||||
"Hanya booking PAID yang bisa cancel dengan refund. Booking yang belum lunas bisa cancel dari tombol 'Batal Ikut'."
|
||||
|
||||
Reference in New Issue
Block a user