diff --git a/sundynix-admin/src/api.ts b/sundynix-admin/src/api.ts index 9122b0c..83b2fbc 100644 --- a/sundynix-admin/src/api.ts +++ b/sundynix-admin/src/api.ts @@ -231,15 +231,31 @@ export async function listRedeemCodes(): Promise { export interface WechatMPConfig { appid: string; token: string; // 消息推送签名校验用(与公众平台服务器配置一致) - has_app_secret: boolean; + app_secret: string; // 单管理员后台:明文回显 enabled: boolean; } +export interface WechatUserRow { + id: string; + name: string; + wechat_openid: string; + created_at: string; + tenant_name: string; + balance_micro: number; +} + +export async function adminWechatUsers(): Promise { + const res = guard(await fetch(`${ADMIN}/wechat-users`, { headers: authHeaders() })); + const d = (await res.json().catch(() => ({}))) as { users?: WechatUserRow[]; error?: string }; + if (!res.ok) throw new Error(d.error ?? `wechat users failed: ${res.status}`); + return d.users ?? []; +} + export async function getWechatMP(): Promise { const res = guard(await fetch(`${ADMIN}/wechat-mp`, { headers: authHeaders() })); const d = (await res.json().catch(() => ({}))) as Partial & { error?: string }; if (!res.ok) throw new Error(d.error ?? `wechat-mp failed: ${res.status}`); - return { appid: d.appid ?? "", token: d.token ?? "", has_app_secret: !!d.has_app_secret, enabled: !!d.enabled }; + return { appid: d.appid ?? "", token: d.token ?? "", app_secret: d.app_secret ?? "", enabled: !!d.enabled }; } // app_secret 传空串 = 沿用已保存的。 @@ -258,7 +274,7 @@ export interface WechatPayConfig { public_key_id: string; // PUB_KEY_ID_ 开头 appid: string; notify_url: string; - has_apiv3_key: boolean; + apiv3_key: string; // 单管理员后台:明文回显 } export async function getWechatPay(): Promise<{ config: WechatPayConfig; enabled: boolean; reason: string }> { diff --git a/sundynix-admin/src/components/TopupChannels.tsx b/sundynix-admin/src/components/TopupChannels.tsx index 2518c8c..1429fc7 100644 --- a/sundynix-admin/src/components/TopupChannels.tsx +++ b/sundynix-admin/src/components/TopupChannels.tsx @@ -27,10 +27,9 @@ export function TopupChannels() { } // ---- 微信支付配置:DB 存储、保存即热生效(不重启 gateway)。 -// APIv3 密钥只写不回显(密文入库,与模型 API Key 同一把密钥加密); -// 商户证书私钥文件放服务器磁盘,这里只填路径。 +// APIv3 密钥密文入库,但单管理员后台明文回显方便核对;证书私钥文件放服务器磁盘,此处只填路径。 function WechatConfigBlock() { - const empty: WechatPayConfig = { mchid: "", cert_serial: "", private_key_path: "", public_key_path: "", public_key_id: "", appid: "", notify_url: "", has_apiv3_key: false }; + const empty: WechatPayConfig = { mchid: "", cert_serial: "", private_key_path: "", public_key_path: "", public_key_id: "", appid: "", notify_url: "", apiv3_key: "" }; const [cfg, setCfg] = useState(empty); const [apiv3, setApiv3] = useState(""); // 留空=沿用已存 const [enabled, setEnabled] = useState(false); @@ -42,6 +41,7 @@ function WechatConfigBlock() { getWechatPay() .then((r) => { setCfg(r.config); + setApiv3(r.config.apiv3_key); setEnabled(r.enabled); setReason(r.reason); }) @@ -65,8 +65,6 @@ function WechatConfigBlock() { }); setEnabled(r.enabled); setReason(r.reason); - setApiv3(""); - if (apiv3) setCfg((c) => ({ ...c, has_apiv3_key: true })); } catch (e) { setErr((e as Error).message); } finally { @@ -108,12 +106,11 @@ function WechatConfigBlock() { {field("公钥 ID(PUB_KEY_ID_ 开头)", "public_key_id", "PUB_KEY_ID_01…")} {field("微信支付公钥文件路径(服务器磁盘)", "public_key_path", "/etc/sundynix/wechat/pub_key.pem", "md:col-span-2")} diff --git a/sundynix-admin/src/pages/LoginConfigPage.tsx b/sundynix-admin/src/pages/LoginConfigPage.tsx index fc0671c..077042c 100644 --- a/sundynix-admin/src/pages/LoginConfigPage.tsx +++ b/sundynix-admin/src/pages/LoginConfigPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { getWechatMP, saveWechatMP, GATEWAY, type WechatMPConfig } from "../api"; // 运维 · 登录设置:微信公众号扫码登录(带参二维码 + 关注/扫码事件)。 -// AppSecret 只写不回显(密文入库,与微信支付 APIv3 密钥同一套加密)。 +// AppSecret 明文回显(单管理员后台,方便核对/复制;密文仍加密入库)。 export function LoginConfigPage() { const [cfg, setCfg] = useState(null); const [appid, setAppid] = useState(""); @@ -18,6 +18,7 @@ export function LoginConfigPage() { setCfg(c); setAppid(c.appid); setToken(c.token); + setSecret(c.app_secret); }) .catch((e) => setErr((e as Error).message)); }, []); @@ -29,8 +30,7 @@ export function LoginConfigPage() { setOk(false); try { const r = await saveWechatMP({ appid: appid.trim(), app_secret: secret, token: token.trim() }); - setSecret(""); - setCfg((c) => (c ? { ...c, appid: appid.trim(), token: token.trim(), has_app_secret: c.has_app_secret || !!secret, enabled: r.enabled } : c)); + setCfg((c) => (c ? { ...c, appid: appid.trim(), token: token.trim(), app_secret: secret, enabled: r.enabled } : c)); setOk(true); } catch (e) { setErr((e as Error).message); @@ -47,7 +47,7 @@ export function LoginConfigPage() {

登录设置 · 微信扫码

-

公众号扫码登录(登录即引导关注)。AppSecret 加密入库、只写不回显,随时可换

+

公众号扫码登录(登录即引导关注)。AppSecret 加密入库、后台明文可见,随时可换

@@ -67,9 +67,9 @@ export function LoginConfigPage() { className="mt-1 block w-full rounded-lg border border-gray-200 px-2.5 py-1.5 font-mono text-sm text-gray-800 focus:border-violet-400 focus:outline-none" />