fix mini program payment bridge
This commit is contained in:
@@ -117,6 +117,7 @@
|
||||
2. 弹窗顶部标题为 `账户充值`,右上角关闭。
|
||||
3. 默认打开 `泥点充值`,可切换到 `会员卡充值`。
|
||||
4. 点击套餐后调用下单接口,按钮进入处理中状态;小程序环境走 native 支付页拉起 `wx.requestPayment`,支付页返回后刷新 `profileDashboard`。
|
||||
- 小程序 web-view 内的 H5 只负责加载微信 JS-SDK 并通过 `wx.miniProgram.navigateTo` 跳转到 `/pages/wechat-pay/index`;实际支付必须在小程序 native 页调用 `wx.requestPayment`,不要切换为 H5 支付产品。
|
||||
5. 弹窗内不写大段说明文案,只保留必要金额、泥点、会员权益和状态反馈。
|
||||
6. 会员卡充值区以套餐卡片优先展示周期、价格和处理状态;移动端单列,桌面端三列,权益表允许横向滚动,避免小屏挤压。
|
||||
|
||||
|
||||
@@ -859,6 +859,10 @@ afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
window.wx = undefined;
|
||||
document
|
||||
.querySelectorAll('script[src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"]')
|
||||
.forEach((script) => script.remove());
|
||||
mockGetRpgProfileReferralInviteCenter.mockResolvedValue(
|
||||
mockBuildReferralCenter(),
|
||||
);
|
||||
@@ -1044,6 +1048,84 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
|
||||
expect(await screen.findByText('支付已提交')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('profile recharge modal loads wechat js sdk before mini program payment bridge', async () => {
|
||||
const user = userEvent.setup();
|
||||
window.history.replaceState(null, '', '/?clientRuntime=wechat_mini_program');
|
||||
window.wx = undefined;
|
||||
const navigateTo = vi.fn((options: { url: string }) => {
|
||||
const url = new URL(`https://mini.test${options.url}`);
|
||||
const requestId = url.searchParams.get('requestId');
|
||||
window.location.hash = `wx_pay_result=${requestId}:success`;
|
||||
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
||||
});
|
||||
mockCreateRpgProfileRechargeOrder.mockResolvedValueOnce({
|
||||
order: {
|
||||
orderId: 'order-wechat-sdk-1',
|
||||
productId: 'points_60',
|
||||
productTitle: '60泥点',
|
||||
kind: 'points',
|
||||
amountCents: 600,
|
||||
status: 'pending' as const,
|
||||
paymentChannel: 'wechat_mp',
|
||||
paidAt: null as string | null,
|
||||
providerTransactionId: null,
|
||||
createdAt: '2026-04-25T10:00:00Z',
|
||||
pointsDelta: 0,
|
||||
membershipExpiresAt: null,
|
||||
},
|
||||
center: {
|
||||
walletBalance: 0,
|
||||
membership: {
|
||||
status: 'normal',
|
||||
tier: 'normal',
|
||||
startedAt: null,
|
||||
expiresAt: null,
|
||||
updatedAt: null,
|
||||
},
|
||||
pointProducts: [],
|
||||
membershipProducts: [],
|
||||
benefits: [],
|
||||
latestOrder: null,
|
||||
hasPointsRecharged: false,
|
||||
},
|
||||
wechatMiniProgramPayParams: {
|
||||
timeStamp: '1777110165',
|
||||
nonceStr: 'nonce',
|
||||
package: 'prepay_id=wx-prepay',
|
||||
signType: 'RSA',
|
||||
paySign: 'signature',
|
||||
},
|
||||
});
|
||||
|
||||
renderProfileView();
|
||||
const shortcutRegion = screen.getByRole('region', { name: '常用功能' });
|
||||
await user.click(
|
||||
within(shortcutRegion).getByRole('button', { name: /充值/u }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
const script = document.querySelector<HTMLScriptElement>(
|
||||
'script[src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"]',
|
||||
);
|
||||
expect(script).toBeTruthy();
|
||||
window.wx = {
|
||||
miniProgram: {
|
||||
navigateTo,
|
||||
},
|
||||
};
|
||||
script?.dispatchEvent(new Event('load'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(navigateTo).toHaveBeenCalledWith({
|
||||
url: expect.stringContaining('/pages/wechat-pay/index?'),
|
||||
fail: expect.any(Function),
|
||||
});
|
||||
});
|
||||
expect(await screen.findByText('支付已提交')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('profile daily task shortcut opens task center and claims reward', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onRechargeSuccess = vi.fn();
|
||||
|
||||
@@ -211,6 +211,7 @@ const RECOMMEND_ENTRY_SWIPE_THRESHOLD_PX = 36;
|
||||
const RECOMMEND_ENTRY_COMMIT_ANIMATION_MS = 180;
|
||||
const RECOMMEND_ENTRY_DRAG_LIMIT_PX = 160;
|
||||
const WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL = 'wechat_mp';
|
||||
const WECHAT_JS_SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
||||
|
||||
type ProfilePopupPanel = 'invite' | 'redeem' | 'community';
|
||||
type RechargeTab = 'points' | 'membership';
|
||||
@@ -2341,16 +2342,56 @@ function clearWechatPayResultHash() {
|
||||
window.history.replaceState(null, '', nextUrl);
|
||||
}
|
||||
|
||||
function requestWechatMiniProgramPayment(
|
||||
function loadWechatJsSdk() {
|
||||
if (typeof window === 'undefined') {
|
||||
return Promise.reject(new Error('请在微信小程序内完成支付'));
|
||||
}
|
||||
if (window.wx?.miniProgram?.navigateTo) {
|
||||
return Promise.resolve(window.wx);
|
||||
}
|
||||
|
||||
return new Promise<NonNullable<Window['wx']>>((resolve, reject) => {
|
||||
const existingScript = document.querySelector<HTMLScriptElement>(
|
||||
`script[src="${WECHAT_JS_SDK_URL}"]`,
|
||||
);
|
||||
const complete = () => {
|
||||
if (window.wx?.miniProgram?.navigateTo) {
|
||||
resolve(window.wx);
|
||||
} else {
|
||||
reject(new Error('请在微信小程序内完成支付'));
|
||||
}
|
||||
};
|
||||
|
||||
if (existingScript) {
|
||||
existingScript.addEventListener('load', complete, { once: true });
|
||||
existingScript.addEventListener(
|
||||
'error',
|
||||
() => reject(new Error('请在微信小程序内完成支付')),
|
||||
{ once: true },
|
||||
);
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = WECHAT_JS_SDK_URL;
|
||||
script.async = true;
|
||||
script.onload = complete;
|
||||
script.onerror = () => reject(new Error('请在微信小程序内完成支付'));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function requestWechatMiniProgramPayment(
|
||||
payload: WechatMiniProgramPayParams | null | undefined,
|
||||
orderId: string,
|
||||
) {
|
||||
const miniProgram = window.wx?.miniProgram;
|
||||
if (
|
||||
!payload ||
|
||||
!miniProgram ||
|
||||
typeof miniProgram.navigateTo !== 'function'
|
||||
) {
|
||||
if (!payload) {
|
||||
return Promise.reject(new Error('请在微信小程序内完成支付'));
|
||||
}
|
||||
const wxBridge = await loadWechatJsSdk();
|
||||
const miniProgram = wxBridge.miniProgram;
|
||||
if (!miniProgram || typeof miniProgram.navigateTo !== 'function') {
|
||||
return Promise.reject(new Error('请在微信小程序内完成支付'));
|
||||
}
|
||||
const navigateTo = miniProgram.navigateTo;
|
||||
|
||||
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@@ -14,4 +14,5 @@ interface Window {
|
||||
postMessage?: (message: unknown) => void;
|
||||
};
|
||||
};
|
||||
WeixinJSBridge?: unknown;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user