Files
J-Board-Lite/src/actions/admin/payments.ts
2026-05-01 02:31:29 +10:00

151 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use server";
import { prisma } from "@/lib/prisma";
import { requireAdmin } from "@/lib/require-auth";
import { revalidatePath } from "next/cache";
import {
decryptPaymentConfigForUse,
getPaymentProviderName,
normalizePaymentConfig,
parsePaymentConfig,
preparePaymentConfigForStorage,
} from "@/services/payment/catalog";
import { actorFromSession, recordAuditLog } from "@/services/audit";
import { getErrorMessage } from "@/lib/errors";
import { z } from "zod";
type PaymentActionResult = { ok: true } | { ok: false; error: string };
function formatPaymentConfigError(error: unknown, fallback: string) {
if (error instanceof z.ZodError) {
const details = error.issues.map((issue) => issue.message).filter(Boolean).join("");
return details || getErrorMessage(error, fallback);
}
return getErrorMessage(error, fallback);
}
export async function savePaymentConfig(
provider: string,
config: Record<string, string>,
enabled: boolean
) {
const session = await requireAdmin();
const normalizedConfig = normalizePaymentConfig(config);
const current = await prisma.paymentConfig.findUnique({
where: { provider },
select: { config: true },
});
const storageConfig = preparePaymentConfigForStorage(
provider,
normalizedConfig,
current?.config as Record<string, unknown> | undefined,
);
if (enabled) {
try {
parsePaymentConfig(provider, decryptPaymentConfigForUse(provider, storageConfig));
} catch (error) {
if (error instanceof z.ZodError) {
const messages = error.issues.map((e) => e.message).join("");
throw new Error(messages);
}
throw error;
}
}
const jsonConfig = JSON.parse(JSON.stringify(storageConfig));
await prisma.paymentConfig.upsert({
where: { provider },
create: { provider, config: jsonConfig, enabled },
update: { config: jsonConfig, enabled },
});
await recordAuditLog({
actor: actorFromSession(session),
action: "payment.config",
targetType: "PaymentConfig",
targetId: provider,
targetLabel: getPaymentProviderName(provider),
message: `${enabled ? "启用并更新" : "更新"}支付配置 ${getPaymentProviderName(provider)}`,
});
revalidatePath("/admin/payments");
}
export async function setPaymentConfigEnabled(
provider: string,
enabled: boolean,
): Promise<PaymentActionResult> {
try {
const session = await requireAdmin();
if (provider === "balance") {
const current = await prisma.paymentConfig.findUnique({
where: { provider },
select: { enabled: true, config: true },
});
if (current?.enabled !== enabled) {
await prisma.paymentConfig.upsert({
where: { provider },
create: { provider, enabled, config: current?.config ?? {} },
update: { enabled },
});
await recordAuditLog({
actor: actorFromSession(session),
action: "payment.toggle",
targetType: "PaymentConfig",
targetId: provider,
targetLabel: getPaymentProviderName(provider),
message: `${enabled ? "启用" : "停用"}支付方式 ${getPaymentProviderName(provider)}`,
});
}
revalidatePath("/admin/payments");
return { ok: true };
}
const current = await prisma.paymentConfig.findUnique({
where: { provider },
select: { config: true, enabled: true },
});
if (!current) {
if (!enabled) return { ok: true };
throw new Error("请先编辑并保存完整支付配置,再启用该支付方式");
}
if (enabled) {
try {
parsePaymentConfig(
provider,
decryptPaymentConfigForUse(provider, current.config as Record<string, unknown>),
);
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error("请先编辑并保存完整支付配置,再启用该支付方式");
}
throw error;
}
}
if (current.enabled !== enabled) {
await prisma.paymentConfig.update({
where: { provider },
data: { enabled },
});
await recordAuditLog({
actor: actorFromSession(session),
action: "payment.toggle",
targetType: "PaymentConfig",
targetId: provider,
targetLabel: getPaymentProviderName(provider),
message: `${enabled ? "启用" : "停用"}支付方式 ${getPaymentProviderName(provider)}`,
});
}
revalidatePath("/admin/payments");
return { ok: true };
} catch (error) {
return { ok: false, error: formatPaymentConfigError(error, "更新支付开关失败") };
}
}