import type { WechatJsapiPrepayParams } from '@dukang/shared-types'; import { getRuntimePlatform } from './env'; import { ensureJssdkReady } from './jssdk'; import type { MiniProgramWx } from './types'; export type WechatPayInvokeConfig = { apiBase?: string; clientApp?: string; getAccessToken?: () => string | null; /** 显式指定运行端;Taro weapp 须传 mini */ platform?: 'mini' | 'wechat-h5' | 'browser'; }; /** wx.chooseWXPay 要求 timestamp 小写;服务端签名与 Bridge 使用 timeStamp */ function toChooseWxPayOptions(prepay: WechatJsapiPrepayParams) { return { timestamp: prepay.timeStamp, nonceStr: prepay.nonceStr, package: prepay.package, signType: prepay.signType, paySign: prepay.paySign, }; } function getMiniProgramWx(): MiniProgramWx | undefined { const g = globalThis as typeof globalThis & { wx?: MiniProgramWx }; if (g.wx?.requestPayment) return g.wx; if (typeof window !== 'undefined' && window.wx?.requestPayment) return window.wx; return undefined; } function invokeBridgePay(params: WechatJsapiPrepayParams): Promise { return new Promise((resolve, reject) => { const bridge = window.WeixinJSBridge; if (!bridge) { reject(new Error('WeixinJSBridge 不可用')); return; } bridge.invoke( 'getBrandWCPayRequest', { appId: params.appId, timeStamp: params.timeStamp, nonceStr: params.nonceStr, package: params.package, signType: params.signType, paySign: params.paySign, }, (res) => { const msg = res.err_msg ?? ''; if (msg.includes('ok')) resolve(); else if (msg.includes('cancel')) reject(new Error('用户取消支付')); else reject(new Error(msg || '支付失败')); }, ); }); } async function waitForWeixinBridge(): Promise { if (window.WeixinJSBridge) return; if (typeof document === 'undefined') return; await new Promise((resolve) => { if (window.WeixinJSBridge) resolve(); else document.addEventListener('WeixinJSBridgeReady', () => resolve(), { once: true }); }); } function invokeMiniProgramPay(prepay: WechatJsapiPrepayParams): Promise { const wxApi = getMiniProgramWx(); if (!wxApi?.requestPayment) { return Promise.reject(new Error('当前环境不支持微信支付')); } return new Promise((resolve, reject) => { wxApi.requestPayment!({ timeStamp: prepay.timeStamp, nonceStr: prepay.nonceStr, package: prepay.package, signType: prepay.signType, paySign: prepay.paySign, success: () => resolve(), fail: (err) => { const msg = err.errMsg || '支付失败'; if (msg.includes('cancel')) reject(new Error('用户取消支付')); else reject(new Error(msg)); }, }); }); } /** 调起微信支付 */ export async function invokeWechatPay( prepay: WechatJsapiPrepayParams, config?: WechatPayInvokeConfig, ): Promise { const platform = config?.platform ?? getRuntimePlatform(); if (platform === 'mini') { return invokeMiniProgramPay(prepay); } if (platform === 'wechat-h5') { if (config?.clientApp) { await ensureJssdkReady({ apiBase: config.apiBase ?? '/api/v1', clientApp: config.clientApp, getAccessToken: config.getAccessToken, url: typeof window !== 'undefined' ? window.location.href.split('#')[0] : undefined, jsApiList: ['chooseWXPay'], }); } // Bridge 参数与商户签名一致,优先于 chooseWXPay await waitForWeixinBridge(); if (window.WeixinJSBridge) { return invokeBridgePay(prepay); } if (window.wx?.chooseWXPay) { return new Promise((resolve, reject) => { window.wx!.chooseWXPay!({ ...toChooseWxPayOptions(prepay), success: () => resolve(), fail: (err) => reject(new Error(err.errMsg || '支付失败')), cancel: () => reject(new Error('用户取消支付')), }); }); } } throw new Error('当前环境不支持微信支付'); }