锁定微信支付失败响应

微信支付失败结果不再向 H5 透出原生错误

微信支付测试覆盖普通支付失败和虚拟支付取消的稳定文案

native shell 总门禁反查微信支付结果边界
This commit is contained in:
2026-06-20 13:15:27 +08:00
parent c381f9a3eb
commit 16ae7925e2
3 changed files with 64 additions and 19 deletions
+2 -15
View File
@@ -64,21 +64,8 @@ function resolvePayStatus(error) {
return errCode === -2 || /cancel/i.test(errMsg) ? 'cancel' : 'fail';
}
function normalizePayError(error) {
if (!error) {
return '';
}
if (typeof error === 'string') {
return error;
}
try {
return JSON.stringify({
errCode: error.errCode,
errMsg: error.errMsg,
});
} catch (_error) {
return String(error.errMsg || error);
}
function normalizePayError() {
return 'wechat payment unavailable';
}
function requestOrdinaryPayment(payParams) {
+24 -4
View File
@@ -125,10 +125,7 @@ describe('wechat-pay mini program payment bridge', () => {
}),
).resolves.toEqual({
status: 'cancel',
errorMessage: JSON.stringify({
errCode: -2,
errMsg: 'requestVirtualPayment:fail cancel',
}),
errorMessage: 'wechat payment unavailable',
});
expect(console.error).toHaveBeenCalledWith(
'[wechat-pay] requestVirtualPayment failed',
@@ -136,6 +133,29 @@ describe('wechat-pay mini program payment bridge', () => {
);
});
test('hides ordinary payment native failure details from H5 result', async () => {
const { requestWechatPayment } = wechatPayBridge;
globalThis.wx.requestPayment.mockImplementationOnce((options) => {
options.fail?.({
errCode: 1001,
errMsg: 'requestPayment:fail private native detail',
});
});
await expect(
requestWechatPayment({
timeStamp: '1777110165',
nonceStr: 'nonce',
package: 'prepay_id=wx-prepay',
signType: 'RSA',
paySign: 'signature',
}),
).resolves.toEqual({
status: 'fail',
errorMessage: 'wechat payment unavailable',
});
});
test('notifies previous web-view after virtual payment', () => {
const { notifyPreviousWebView } = wechatPayBridge;
const previousPage = {
+38
View File
@@ -1874,6 +1874,41 @@ function assertWechatMiniProgramCapabilityFlows() {
}
}
function assertWechatPaymentResultBoundaries() {
const paymentSource = fs.readFileSync(
'miniprogram/host-bridge/payment.js',
'utf8',
);
const paymentTestSource = fs.readFileSync(
'miniprogram/host-bridge/payment.test.js',
'utf8',
);
for (const snippet of [
"function normalizePayError()",
"return 'wechat payment unavailable'",
]) {
if (!paymentSource.includes(snippet)) {
throw new Error(`wechat payment bridge must include ${snippet}`);
}
}
if (
paymentSource.includes('JSON.stringify({\n errCode') ||
paymentSource.includes('String(error.errMsg || error)')
) {
throw new Error('wechat payment bridge must not expose native payment errors to H5');
}
for (const snippet of [
'maps virtual payment cancel errCode to cancel result',
'hides ordinary payment native failure details from H5 result',
"errorMessage: 'wechat payment unavailable'",
]) {
if (!paymentTestSource.includes(snippet)) {
throw new Error(`wechat payment bridge test must include ${snippet}`);
}
}
}
function assertHostBridgeLayerLayout() {
assertSameList(
readDirectoryFileList(
@@ -2101,6 +2136,9 @@ assertWechatMiniProgramRouteParity();
console.log('[check:native-shells] wechat-mini-program-capability-flows');
assertWechatMiniProgramCapabilityFlows();
console.log('[check:native-shells] wechat-payment-result-boundaries');
assertWechatPaymentResultBoundaries();
console.log('[check:native-shells] h5-host-bridge-event-subscription-gates');
assertH5HostBridgeEventSubscriptionGates();