修复小程序内充值设备识别
为 API 请求和刷新请求补充宿主运行时请求头。 在小程序充值设备身份异常时清理旧 token 并重新拉起小程序登录。 补充小程序充值回归测试并隔离测试 spy。
This commit is contained in:
@@ -10,9 +10,16 @@ import {
|
||||
type RedeemProfileRewardCodeResponse,
|
||||
type WechatNativePayment,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import { refreshStoredAccessToken } from '../../services/apiClient';
|
||||
import {
|
||||
clearStoredAccessToken,
|
||||
refreshStoredAccessToken,
|
||||
} from '../../services/apiClient';
|
||||
import { startWechatBind, type AuthUser } from '../../services/authService';
|
||||
import { requestHostPayment } from '../../services/host-bridge/hostBridge';
|
||||
import {
|
||||
getHostRuntime,
|
||||
requestHostLogin,
|
||||
requestHostPayment,
|
||||
} from '../../services/host-bridge/hostBridge';
|
||||
import {
|
||||
resolveProfileRechargeProductPaymentChannel,
|
||||
WECHAT_H5_PAYMENT_CHANNEL,
|
||||
@@ -47,6 +54,7 @@ const WECHAT_NATIVE_CONFIRM_RETRY_DELAYS_MS = [800, 1600] as const;
|
||||
const WECHAT_PAY_CONFIRM_RETRY_DELAYS_MS = [800, 1600, 3000] as const;
|
||||
const WECHAT_PAY_RESULT_RECHECK_INTERVAL_MS = 250;
|
||||
const WECHAT_PAY_RESULT_RECHECK_TIMEOUT_MS = 10000;
|
||||
const WECHAT_RECHARGE_UNSUPPORTED_DEVICE_TEXT = '当前登录设备不支持充值';
|
||||
const WECHAT_JSAPI_MISSING_IDENTITY_TEXT = '缺少微信';
|
||||
|
||||
export type ProfileReferralPanel = 'invite' | 'redeem' | 'community';
|
||||
@@ -87,6 +95,13 @@ function isWechatJsapiMissingIdentityError(error: unknown) {
|
||||
);
|
||||
}
|
||||
|
||||
function isWechatRechargeUnsupportedDeviceError(error: unknown) {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
error.message.includes(WECHAT_RECHARGE_UNSUPPORTED_DEVICE_TEXT)
|
||||
);
|
||||
}
|
||||
|
||||
type UsePlatformProfileCenterControllerArgs = {
|
||||
activeTab: string;
|
||||
isAuthenticated: boolean;
|
||||
@@ -642,15 +657,16 @@ export function usePlatformProfileCenterController({
|
||||
return;
|
||||
}
|
||||
if (paymentChannel === WECHAT_NATIVE_PAYMENT_CHANNEL) {
|
||||
const codeUrl = response.wechatNativePayment?.codeUrl?.trim();
|
||||
const expiresAt = response.wechatNativePayment?.expiresAt?.trim();
|
||||
if (!codeUrl || !expiresAt) {
|
||||
const wechatNativePayment = response.wechatNativePayment;
|
||||
const codeUrl = wechatNativePayment?.codeUrl?.trim();
|
||||
const expiresAt = wechatNativePayment?.expiresAt?.trim();
|
||||
if (!wechatNativePayment || !codeUrl || !expiresAt) {
|
||||
throw new Error('微信 Native 支付链接生成失败');
|
||||
}
|
||||
pendingWechatRechargeOrderIdRef.current = response.order.orderId;
|
||||
setRechargeCenter(response.center);
|
||||
setNativeWechatPayment({
|
||||
...response.wechatNativePayment,
|
||||
...wechatNativePayment,
|
||||
codeUrl,
|
||||
expiresAt,
|
||||
orderId: response.order.orderId,
|
||||
@@ -667,6 +683,29 @@ export function usePlatformProfileCenterController({
|
||||
.catch((error: unknown) => {
|
||||
pendingWechatRechargeOrderIdRef.current = null;
|
||||
setNativeWechatPayment(null);
|
||||
if (
|
||||
paymentChannel === WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_CHANNEL &&
|
||||
getHostRuntime().kind === 'wechat_mini_program' &&
|
||||
isWechatRechargeUnsupportedDeviceError(error)
|
||||
) {
|
||||
clearStoredAccessToken();
|
||||
setSubmittingRechargeProductId(null);
|
||||
setRechargeError(null);
|
||||
setRechargePaymentResult({
|
||||
kind: 'pending',
|
||||
title: '需要重新登录',
|
||||
message: '正在打开微信小程序登录,请重新登录后再支付。',
|
||||
});
|
||||
void requestHostLogin().catch((miniProgramLoginError: unknown) => {
|
||||
setRechargePaymentResult(null);
|
||||
setRechargeError(
|
||||
miniProgramLoginError instanceof Error
|
||||
? miniProgramLoginError.message
|
||||
: '请在微信小程序内重新登录后再支付',
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
paymentChannel === WECHAT_JSAPI_PAYMENT_CHANNEL &&
|
||||
isWechatJsapiMissingIdentityError(error)
|
||||
|
||||
@@ -381,6 +381,7 @@ const {
|
||||
const {
|
||||
mockGetPublicAuthUserByCode,
|
||||
mockGetPublicAuthUserById,
|
||||
mockClearStoredAccessToken,
|
||||
mockRefreshStoredAccessToken,
|
||||
mockRequestJson,
|
||||
mockUpdateAuthProfile,
|
||||
@@ -403,6 +404,7 @@ const {
|
||||
avatarUrl: null,
|
||||
}),
|
||||
),
|
||||
mockClearStoredAccessToken: vi.fn(),
|
||||
mockRefreshStoredAccessToken: vi.fn(async () => 'jwt-refreshed-token'),
|
||||
mockRequestJson: vi.fn(async () => ({
|
||||
read: {
|
||||
@@ -418,6 +420,7 @@ const mockStartWechatBind = vi.hoisted(() => vi.fn(async () => undefined));
|
||||
|
||||
vi.mock('../../services/apiClient', () => ({
|
||||
BACKGROUND_AUTH_REQUEST_OPTIONS: {},
|
||||
clearStoredAccessToken: mockClearStoredAccessToken,
|
||||
refreshStoredAccessToken: mockRefreshStoredAccessToken,
|
||||
requestJson: mockRequestJson,
|
||||
}));
|
||||
@@ -1272,6 +1275,7 @@ function renderStatefulLoggedOutHomeView(
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
vi.clearAllMocks();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
@@ -1752,6 +1756,41 @@ test('profile recharge modal posts virtual payment params in mini program web-vi
|
||||
expect(onRechargeSuccess).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('profile recharge modal requests mini program login when stored token is not a mini program device', async () => {
|
||||
const user = userEvent.setup();
|
||||
window.history.replaceState(null, '', '/?clientRuntime=wechat_mini_program');
|
||||
window.wx = {
|
||||
miniProgram: {
|
||||
navigateTo: vi.fn((options: { success?: () => void }) => {
|
||||
options.success?.();
|
||||
}),
|
||||
},
|
||||
};
|
||||
const requestHostLogin = vi
|
||||
.spyOn(hostBridgeServices, 'requestHostLogin')
|
||||
.mockResolvedValueOnce(true);
|
||||
mockCreateRpgProfileRechargeOrder.mockRejectedValueOnce(
|
||||
new Error('当前登录设备不支持充值,请在微信环境内登录后重试'),
|
||||
);
|
||||
|
||||
renderProfileView();
|
||||
await openRechargeModal(user);
|
||||
await user.click(await screen.findByRole('button', { name: /60泥点/u }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCreateRpgProfileRechargeOrder).toHaveBeenCalledWith(
|
||||
'points_60',
|
||||
'wechat_mp_virtual',
|
||||
);
|
||||
});
|
||||
expect(mockClearStoredAccessToken).toHaveBeenCalledTimes(1);
|
||||
expect(requestHostLogin).toHaveBeenCalledTimes(1);
|
||||
expect(await screen.findByRole('dialog', { name: '需要重新登录' })).toBeTruthy();
|
||||
expect(screen.getByText('正在打开微信小程序登录,请重新登录后再支付。')).toBeTruthy();
|
||||
expect(mockRedirectToPaymentUrl).not.toHaveBeenCalled();
|
||||
expect(mockRequestWechatJsapiPayment).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('profile recharge modal posts membership goods virtual payment params in mini program web-view', async () => {
|
||||
const user = userEvent.setup();
|
||||
window.history.replaceState(null, '', '/?clientRuntime=wechat_mini_program');
|
||||
|
||||
Reference in New Issue
Block a user