style: format code biome

This commit is contained in:
openhands committed 2026-07-13 21:57:41 +02:00
1 parent 8efd032cc6
commit df38dccbf1
735 files changed
+128321 -120870

No files matched your search

+217 -163
View File
@@ -1,14 +1,22 @@
import { NextResponse } from "next/server";
import { env } from "@/env";
import { auth } from "@/lib/auth";
import { sessionUserId } from "@/lib/auth/session-user";
import { logger } from "@/lib/logger";
import { prisma } from "@/lib/prisma";
import { captureOrder, creditsPerUnit, isPayPalConfigured } from "@/lib/services/paypal";
import { logServerError } from "@/lib/server-log";
import {
captureOrder,
creditsPerUnit,
isPayPalConfigured,
} from "@/lib/services/paypal";
import {
authorizeTopupCapture,
claimTopupDelivery,
TopupCaptureError,
} from "@/lib/services/paypal-topup";
import { rcon } from "@/lib/services/rcon";
import { sendCurrency } from "@/lib/services/send-currency";
import { env } from "@/env";
import { logger } from "@/lib/logger";
import { authorizeTopupCapture, claimTopupDelivery, TopupCaptureError } from "@/lib/services/paypal-topup";
import { logServerError } from "@/lib/server-log";
export const dynamic = "force-dynamic";
@@ -28,173 +36,219 @@ export const dynamic = "force-dynamic";
* field, so a captured order always credits the person who is signed in.
*/
export async function POST(req: Request): Promise<Response> {
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json({ error: "You must be signed in." }, { status: 401 });
}
const userId = sessionUserId(session.user.id);
if (!userId) {
return NextResponse.json({ error: "Invalid session." }, { status: 401 });
}
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json(
{ error: "You must be signed in." },
{ status: 401 },
);
}
const userId = sessionUserId(session.user.id);
if (!userId) {
return NextResponse.json({ error: "Invalid session." }, { status: 401 });
}
if (!isPayPalConfigured()) {
return NextResponse.json(
{ error: "PayPal is not configured. Set PAYPAL_CLIENT_ID and PAYPAL_SECRET." },
{ status: 503 },
);
}
if (!isPayPalConfigured()) {
return NextResponse.json(
{
error:
"PayPal is not configured. Set PAYPAL_CLIENT_ID and PAYPAL_SECRET.",
},
{ status: 503 },
);
}
let body: unknown;
try {
body = await req.json();
} catch {
return NextResponse.json({ error: "Invalid JSON body." }, { status: 400 });
}
let body: unknown;
try {
body = await req.json();
} catch {
return NextResponse.json({ error: "Invalid JSON body." }, { status: 400 });
}
const orderId = String((body as { orderId?: unknown })?.orderId ?? "").trim();
if (!orderId) {
return NextResponse.json({ error: "Missing orderId." }, { status: 422 });
}
const orderId = String((body as { orderId?: unknown })?.orderId ?? "").trim();
if (!orderId) {
return NextResponse.json({ error: "Missing orderId." }, { status: 422 });
}
// The create endpoint records ownership before returning the approval URL.
// Do not let a signed-in user submit somebody else's approved order id.
let authorized;
try {
authorized = await authorizeTopupCapture(userId, orderId, async (transactionId) =>
prisma.websitePaypalTransactions.findFirst({
where: { transactionId },
select: { userId: true, status: true, amount: true },
}),
);
} catch (error) {
if (error instanceof TopupCaptureError) {
const status = error.code === "ORDER_NOT_FOUND" ? 404 : 409;
return NextResponse.json({ error: "Order not found or already processed." }, { status });
}
return NextResponse.json({ error: "Could not verify the payment order." }, { status: 500 });
}
// The create endpoint records ownership before returning the approval URL.
// Do not let a signed-in user submit somebody else's approved order id.
let authorized;
try {
authorized = await authorizeTopupCapture(
userId,
orderId,
async (transactionId) =>
prisma.websitePaypalTransactions.findFirst({
where: { transactionId },
select: { userId: true, status: true, amount: true },
}),
);
} catch (error) {
if (error instanceof TopupCaptureError) {
const status = error.code === "ORDER_NOT_FOUND" ? 404 : 409;
return NextResponse.json(
{ error: "Order not found or already processed." },
{ status },
);
}
return NextResponse.json(
{ error: "Could not verify the payment order." },
{ status: 500 },
);
}
if (authorized.action === "DELIVER_CREDITS") {
const amount = authorized.amount ?? 0;
const credits = Math.floor(amount * creditsPerUnit());
try {
await claimTopupDelivery(() =>
prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CAPTURED_PENDING_CREDIT" },
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
}),
);
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
data: { status: "COMPLETED", updatedAt: new Date() },
});
return NextResponse.json({
ok: true,
recovered: true,
status: "COMPLETED",
amount,
credits,
});
} catch (error) {
logServerError("paypal.credit_recovery_failed", error, { userId, orderId });
return NextResponse.json(
{ error: "Payment is recorded but credits could not be delivered. Contact staff." },
{ status: 500 },
);
}
}
if (authorized.action === "DELIVER_CREDITS") {
const amount = authorized.amount ?? 0;
const credits = Math.floor(amount * creditsPerUnit());
try {
await claimTopupDelivery(() =>
prisma.websitePaypalTransactions.updateMany({
where: {
userId,
transactionId: orderId,
status: "CAPTURED_PENDING_CREDIT",
},
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
}),
);
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
data: { status: "COMPLETED", updatedAt: new Date() },
});
return NextResponse.json({
ok: true,
recovered: true,
status: "COMPLETED",
amount,
credits,
});
} catch (error) {
logServerError("paypal.credit_recovery_failed", error, {
userId,
orderId,
});
return NextResponse.json(
{
error:
"Payment is recorded but credits could not be delivered. Contact staff.",
},
{ status: 500 },
);
}
}
let result;
try {
result = await captureOrder(orderId);
} catch (e) {
logger.error("PayPal capture failed", { module: "paypal/capture", error: (e as Error).message });
return NextResponse.json(
{ error: "Could not capture the PayPal payment. If you were charged, contact staff." },
{ status: 502 },
);
}
let result;
try {
result = await captureOrder(orderId);
} catch (e) {
logger.error("PayPal capture failed", {
module: "paypal/capture",
error: (e as Error).message,
});
return NextResponse.json(
{
error:
"Could not capture the PayPal payment. If you were charged, contact staff.",
},
{ status: 502 },
);
}
if (result.status !== "COMPLETED") {
// Record the non-completed attempt so support can trace it.
try {
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREATED" },
data: {
status: result.status,
description: `${env.HOTEL_NAME} top-up (not completed)`,
amount: result.amount,
currency: result.currency,
createdAt: new Date(),
updatedAt: new Date(),
},
});
} catch {
/* best-effort logging */
}
return NextResponse.json(
{ ok: false, status: result.status, error: "Payment was not completed." },
{ status: 402 },
);
}
if (result.status !== "COMPLETED") {
// Record the non-completed attempt so support can trace it.
try {
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREATED" },
data: {
status: result.status,
description: `${env.HOTEL_NAME} top-up (not completed)`,
amount: result.amount,
currency: result.currency,
createdAt: new Date(),
updatedAt: new Date(),
},
});
} catch {
/* best-effort logging */
}
return NextResponse.json(
{ ok: false, status: result.status, error: "Payment was not completed." },
{ status: 402 },
);
}
const credits = Math.floor(result.amount * creditsPerUnit());
const credits = Math.floor(result.amount * creditsPerUnit());
// Record the transaction BEFORE crediting so a crash mid-grant can't be
// reprocessed into a double credit (the idempotency check above keys on this).
try {
const claimed = await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREATED" },
data: {
status: "CAPTURED_PENDING_CREDIT",
description: `${env.HOTEL_NAME} top-up: ${credits} credits`,
amount: result.amount,
currency: result.currency,
createdAt: new Date(),
updatedAt: new Date(),
},
});
if (claimed.count !== 1) throw new Error("Top-up order was already claimed");
} catch (e) {
logger.error("PayPal capture record failed", { module: "paypal/capture", error: (e as Error).message });
return NextResponse.json(
{ error: "Payment captured but could not be recorded. Contact staff with your order id." },
{ status: 500 },
);
}
// Record the transaction BEFORE crediting so a crash mid-grant can't be
// reprocessed into a double credit (the idempotency check above keys on this).
try {
const claimed = await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREATED" },
data: {
status: "CAPTURED_PENDING_CREDIT",
description: `${env.HOTEL_NAME} top-up: ${credits} credits`,
amount: result.amount,
currency: result.currency,
createdAt: new Date(),
updatedAt: new Date(),
},
});
if (claimed.count !== 1)
throw new Error("Top-up order was already claimed");
} catch (e) {
logger.error("PayPal capture record failed", {
module: "paypal/capture",
error: (e as Error).message,
});
return NextResponse.json(
{
error:
"Payment captured but could not be recorded. Contact staff with your order id.",
},
{ status: 500 },
);
}
// Atomically claim delivery so concurrent retries cannot grant twice. If the
// external delivery outcome is ambiguous, CREDIT_DELIVERING remains visible
// for staff reconciliation instead of automatically risking a second grant.
try {
await claimTopupDelivery(() =>
prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CAPTURED_PENDING_CREDIT" },
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
}),
);
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
data: { status: "COMPLETED", updatedAt: new Date() },
});
} catch (e) {
logger.error("PayPal capture credit failed", { module: "paypal/capture", error: (e as Error).message });
return NextResponse.json(
{
ok: false,
error: "Payment recorded but credits could not be delivered. Contact staff.",
},
{ status: 500 },
);
}
// Atomically claim delivery so concurrent retries cannot grant twice. If the
// external delivery outcome is ambiguous, CREDIT_DELIVERING remains visible
// for staff reconciliation instead of automatically risking a second grant.
try {
await claimTopupDelivery(() =>
prisma.websitePaypalTransactions.updateMany({
where: {
userId,
transactionId: orderId,
status: "CAPTURED_PENDING_CREDIT",
},
data: { status: "CREDIT_DELIVERING", updatedAt: new Date() },
}),
);
await sendCurrency({ rcon, db: prisma }, userId, "credits", credits);
await prisma.websitePaypalTransactions.updateMany({
where: { userId, transactionId: orderId, status: "CREDIT_DELIVERING" },
data: { status: "COMPLETED", updatedAt: new Date() },
});
} catch (e) {
logger.error("PayPal capture credit failed", {
module: "paypal/capture",
error: (e as Error).message,
});
return NextResponse.json(
{
ok: false,
error:
"Payment recorded but credits could not be delivered. Contact staff.",
},
{ status: 500 },
);
}
return NextResponse.json({
ok: true,
status: "COMPLETED",
amount: result.amount,
currency: result.currency,
credits,
});
return NextResponse.json({
ok: true,
status: "COMPLETED",
amount: result.amount,
currency: result.currency,
credits,
});
}