feat: 接入微信H5与Native充值支付

This commit is contained in:
2026-05-15 06:40:40 +08:00
parent 73424f958a
commit 5b70ec6af7
18 changed files with 1890 additions and 122 deletions

View File

@@ -0,0 +1,76 @@
export const WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL = 'wechat_mp';
export const WECHAT_H5_PAYMENT_CHANNEL = 'wechat_h5';
export const WECHAT_NATIVE_PAYMENT_CHANNEL = 'wechat_native';
export const MOCK_PAYMENT_CHANNEL = 'mock';
export type ProfileRechargeWechatPaymentChannel =
| typeof WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL
| typeof WECHAT_H5_PAYMENT_CHANNEL
| typeof WECHAT_NATIVE_PAYMENT_CHANNEL;
type PaymentPlatformNavigator = Pick<Navigator, 'userAgent' | 'maxTouchPoints'>;
export type PaymentPlatformContext = {
location?: Pick<Location, 'search'> | null;
navigator?: Partial<PaymentPlatformNavigator> | null;
matchMedia?: Window['matchMedia'] | null;
};
export function resolveProfileRechargePaymentChannel(
context: PaymentPlatformContext = {},
): ProfileRechargeWechatPaymentChannel {
const location =
context.location ??
(typeof window !== 'undefined' ? window.location : null);
const navigatorLike =
context.navigator ?? (typeof navigator !== 'undefined' ? navigator : null);
const matchMedia =
context.matchMedia ??
(typeof window !== 'undefined' && typeof window.matchMedia === 'function'
? window.matchMedia.bind(window)
: null);
if (isWechatMiniProgramRuntime(location)) {
return WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL;
}
if (isMobileWebRuntime(navigatorLike, matchMedia)) {
return WECHAT_H5_PAYMENT_CHANNEL;
}
return WECHAT_NATIVE_PAYMENT_CHANNEL;
}
export function isManualMockPaymentChannel(paymentChannel: string) {
return paymentChannel.trim() === MOCK_PAYMENT_CHANNEL;
}
function isWechatMiniProgramRuntime(
location: Pick<Location, 'search'> | null | undefined,
) {
const params = new URLSearchParams(location?.search ?? '');
return (
params.get('clientRuntime') === 'wechat_mini_program' ||
params.get('clientType') === 'mini_program'
);
}
function isMobileWebRuntime(
navigatorLike: Partial<PaymentPlatformNavigator> | null | undefined,
matchMedia: Window['matchMedia'] | null | undefined,
) {
const userAgent = navigatorLike?.userAgent?.toLowerCase() ?? '';
if (
/android|iphone|ipad|ipod|mobile|micromessenger|windows phone/u.test(
userAgent,
)
) {
return true;
}
if ((navigatorLike?.maxTouchPoints ?? 0) > 1) {
return true;
}
return Boolean(matchMedia?.('(max-width: 767px)').matches);
}