132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
if (window.WeixinJSBridge) return;
|
|
if (typeof document === 'undefined') return;
|
|
await new Promise<void>((resolve) => {
|
|
if (window.WeixinJSBridge) resolve();
|
|
else document.addEventListener('WeixinJSBridgeReady', () => resolve(), { once: true });
|
|
});
|
|
}
|
|
|
|
function invokeMiniProgramPay(prepay: WechatJsapiPrepayParams): Promise<void> {
|
|
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<void> {
|
|
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('当前环境不支持微信支付');
|
|
}
|