阶段性修改

This commit is contained in:
tianmo committed 2026-09-29 19:34:24 +08:00
1 parent 4833f7fa6a
commit 5a92bab835
65 files changed
+346 -178

No files matched your search

+5
View File
@@ -34,7 +34,12 @@ const envSchema = z.object({
/** 虚拟支付,未开通时留空 */
WX_OFFER_ID: z.string().default(''),
/** 现网支付密钥 */
WX_PAY_APPKEY: z.string().default(''),
/** 沙箱(测试)支付密钥 */
WX_PAY_APPKEY_SANDBOX: z.string().default(''),
/** 虚拟支付环境:0 现网 / 1 沙箱。切换后需重启服务端生效。 */
WX_PAY_ENV: z.coerce.number().int().min(0).max(1).default(0),
/** 播放地址签名有效期(秒) */
PLAY_URL_TTL_SECONDS: z.coerce.number().int().positive().default(1800),
@@ -83,6 +83,7 @@ export class AdminService {
refunded,
revenueFen: revenue._sum.amount_fen ?? 0,
payEnabled: this.wxpay.enabled,
payEnv: this.wxpay.env,
};
}
@@ -61,7 +61,7 @@ export class OrdersService {
quantity,
amount_fen: amountFen,
status: OrderStatus.CREATED,
env: 0,
env: this.wxpay.env,
attach: `course:${course.code}`,
},
});
@@ -70,7 +70,7 @@ export class OrdersService {
const signData = JSON.stringify({
offerId: this.wxpay.offerId,
buyQuantity: quantity,
env: 0,
env: this.wxpay.env,
currencyType: 'CNY',
productId: product.wx_product_id,
goodsPrice: product.price_fen,
@@ -143,4 +143,3 @@ export const STATUS_TEXT: Record<number, string> = {
[OrderStatus.REFUNDED]: '已退款',
[OrderStatus.CLOSED]: '已关闭',
};
@@ -12,6 +12,9 @@ const XPAY_BASE = 'https://api.weixin.qq.com';
* paySig 服务端身份,密钥是后台的「现网 AppKey」:HMAC_SHA256(appkey, uri + '&' + postBody)
* signature 用户态身份,密钥是该用户的 sessionKey:HMAC_SHA256(sessionKey, postBody)
*
* 支持现网与沙箱两套密钥,由 WX_PAY_ENV 决定当前使用哪一套(0 现网 / 1 沙箱)。
* 沙箱用于上线前试跑,不产生真实扣款。
*
* 注意:postBody 必须与实际发出的字符串逐字节一致,不能格式化、不能改键顺序。
*/
@Injectable()
@@ -20,8 +23,18 @@ export class WxPayService {
constructor(@Inject(APP_CONFIG) private readonly cfg: AppConfig) {}
/** 当前支付环境:0 现网 / 1 沙箱。 */
get env(): number {
return this.cfg.WX_PAY_ENV;
}
/** 当前环境对应的支付密钥。 */
private get appkey(): string {
return this.env === 1 ? this.cfg.WX_PAY_APPKEY_SANDBOX : this.cfg.WX_PAY_APPKEY;
}
get enabled(): boolean {
return this.cfg.WX_OFFER_ID !== '' && this.cfg.WX_PAY_APPKEY !== '';
return this.cfg.WX_OFFER_ID !== '' && this.appkey !== '';
}
get offerId(): string {
@@ -30,7 +43,7 @@ export class WxPayService {
/** 服务端身份签名。uri 不带问号参数。 */
calcPaySig(uri: string, postBody: string): string {
return createHmac('sha256', this.cfg.WX_PAY_APPKEY).update(`${uri}&${postBody}`).digest('hex');
return createHmac('sha256', this.appkey).update(`${uri}&${postBody}`).digest('hex');
}
/** 用户态签名。 */
@@ -44,7 +57,7 @@ export class WxPayService {
*/
async queryOrder(outTradeNo: string, openid: string): Promise<XpayQueryResult> {
const uri = '/xpay/query_order';
const body = JSON.stringify({ openid, env: 0, order_id: outTradeNo });
const body = JSON.stringify({ openid, env: this.env, order_id: outTradeNo });
const paySig = this.calcPaySig(uri, body);
const res = await fetch(`${XPAY_BASE}${uri}?pay_sig=${paySig}`, {
@@ -77,7 +90,7 @@ export class WxPayService {
const uri = '/xpay/refund_order';
const body = JSON.stringify({
openid: input.openid,
env: 0,
env: this.env,
order_id: input.outTradeNo,
refund_order_id: input.refundOrderId,
refund_amount: input.refundAmountFen,
@@ -106,4 +119,3 @@ export interface XpayQueryResult {
status?: number;
wx_order_id?: string;
}