110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
import { assertConfiguredEpayUrl } from "./gateway-policy";
|
|
import { decryptEpayKey } from "./encryption-core";
|
|
|
|
export type EpaySettingsRow = {
|
|
gateway_url: string;
|
|
pid: string;
|
|
encrypted_key: string;
|
|
notify_url: string;
|
|
return_url: string;
|
|
site_name: string;
|
|
chat_enabled: boolean;
|
|
};
|
|
|
|
export class EpayConfigurationError extends Error {
|
|
constructor(message = "易支付配置不可用") {
|
|
super(message);
|
|
this.name = "EpayConfigurationError";
|
|
}
|
|
}
|
|
|
|
function validEpayUrl(value: string, label: string, env: NodeJS.ProcessEnv) {
|
|
try {
|
|
return assertConfiguredEpayUrl(value, env);
|
|
} catch {
|
|
throw new EpayConfigurationError(`${label} 无效`);
|
|
}
|
|
}
|
|
|
|
export function suggestedEpayUrls(
|
|
siteAddress = process.env.SITE_ADDRESS,
|
|
env: NodeJS.ProcessEnv = process.env,
|
|
) {
|
|
let base: URL;
|
|
try {
|
|
base = validEpayUrl(siteAddress?.trim() || "", "站点地址", env);
|
|
} catch {
|
|
const allowLoopbackHttp = (env.NODE_ENV === "development" || env.NODE_ENV === "test")
|
|
&& ["true", "1"].includes(env.EPAY_ALLOW_INSECURE_LOOPBACK_HTTP?.trim().toLowerCase() || "");
|
|
base = new URL(allowLoopbackHttp ? "http://localhost:3000" : "https://localhost:3000");
|
|
}
|
|
return {
|
|
notifyUrl: new URL("/api/payment/epay/notify", base).toString(),
|
|
returnUrl: new URL("/", base).toString(),
|
|
};
|
|
}
|
|
|
|
function environmentChatEnabled(value: string | undefined) {
|
|
const normalized = value?.trim().toLowerCase();
|
|
return normalized === "true" || normalized === "1";
|
|
}
|
|
|
|
function completeConfig(values: {
|
|
gateway: string;
|
|
pid: string;
|
|
key: string;
|
|
notifyUrl: string;
|
|
returnUrl: string;
|
|
siteName: string;
|
|
chatEnabled: boolean;
|
|
}, env: NodeJS.ProcessEnv) {
|
|
if (!values.gateway || !values.pid || !values.key || !values.notifyUrl || !values.returnUrl || !values.siteName) {
|
|
throw new EpayConfigurationError();
|
|
}
|
|
const gatewayUrl = validEpayUrl(values.gateway.replace(/\/+$/, ""), "易支付网关地址", env);
|
|
validEpayUrl(values.notifyUrl, "异步通知地址", env);
|
|
validEpayUrl(values.returnUrl, "支付返回地址", env);
|
|
return { gatewayUrl, pid: values.pid, key: values.key, notifyUrl: values.notifyUrl, returnUrl: values.returnUrl, siteName: values.siteName, chatEnabled: values.chatEnabled };
|
|
}
|
|
|
|
export async function resolveEpayConfig(
|
|
loadDatabaseRow: () => Promise<EpaySettingsRow | null>,
|
|
env: NodeJS.ProcessEnv = process.env,
|
|
) {
|
|
let row: EpaySettingsRow | null;
|
|
try {
|
|
row = await loadDatabaseRow();
|
|
} catch {
|
|
throw new EpayConfigurationError();
|
|
}
|
|
if (row) {
|
|
return completeConfig({
|
|
gateway: row.gateway_url.trim(),
|
|
pid: row.pid.trim(),
|
|
key: decryptEpayKey(row.encrypted_key, env.EPAY_CONFIG_ENCRYPTION_KEY),
|
|
notifyUrl: row.notify_url.trim(),
|
|
returnUrl: row.return_url.trim(),
|
|
siteName: row.site_name.trim(),
|
|
chatEnabled: row.chat_enabled,
|
|
}, env);
|
|
}
|
|
|
|
const defaults = suggestedEpayUrls(env.SITE_ADDRESS, env);
|
|
return completeConfig({
|
|
gateway: env.EPAY_GATEWAY_URL?.trim() || "",
|
|
pid: env.EPAY_PID?.trim() || "",
|
|
key: env.EPAY_KEY?.trim() || "",
|
|
notifyUrl: env.EPAY_NOTIFY_URL?.trim() || defaults.notifyUrl,
|
|
returnUrl: env.EPAY_RETURN_URL?.trim() || defaults.returnUrl,
|
|
siteName: env.EPAY_SITE_NAME?.trim() || "Jyotisha",
|
|
chatEnabled: environmentChatEnabled(env.EPAY_CHAT_ENABLED),
|
|
}, env);
|
|
}
|
|
|
|
export function epaySubmitUrl(gatewayUrl: URL) {
|
|
const url = new URL(gatewayUrl.toString());
|
|
url.pathname = `${url.pathname.replace(/\/$/, "")}/submit.php`;
|
|
url.search = "";
|
|
return url;
|
|
}
|