|
|
@@ -96,6 +96,28 @@ function isInWechat(): boolean {
|
|
|
// #endif
|
|
|
}
|
|
|
|
|
|
+// 获取不含 hash 的基础URL(用于OAuth redirect_uri)
|
|
|
+function getBaseUrl(): string {
|
|
|
+ // #ifdef H5
|
|
|
+ return window.location.origin + window.location.pathname;
|
|
|
+ // #endif
|
|
|
+ return '';
|
|
|
+}
|
|
|
+
|
|
|
+// 保存当前页面上下文到 sessionStorage(OAuth 来回会丢失 hash 路由状态)
|
|
|
+function savePageContext() {
|
|
|
+ try {
|
|
|
+ sessionStorage.setItem('wx_oauth_return', window.location.href);
|
|
|
+ } catch (_) {}
|
|
|
+}
|
|
|
+
|
|
|
+// 恢复页面上下文
|
|
|
+function restorePageContext(): string | null {
|
|
|
+ try {
|
|
|
+ return sessionStorage.getItem('wx_oauth_return');
|
|
|
+ } catch (_) { return null; }
|
|
|
+}
|
|
|
+
|
|
|
const orderInfo = ref<{
|
|
|
orderNo: string;
|
|
|
amount: number;
|
|
|
@@ -173,43 +195,37 @@ async function initWxJsSdk(): Promise<boolean> {
|
|
|
// ==================== 微信 OAuth 处理 ====================
|
|
|
async function handleWechatOAuth() {
|
|
|
// #ifdef H5
|
|
|
- // 检测 URL 中是否有 OAuth 回调的 code 参数
|
|
|
- const urlParams = new URLSearchParams(window.location.search);
|
|
|
- const code = urlParams.get('code');
|
|
|
+ // OAuth 回调已在 App.vue 全局处理,这里只需读取缓存的 openid
|
|
|
+ const cachedOpenid = localStorage.getItem('wx_openid');
|
|
|
+ if (cachedOpenid) {
|
|
|
+ openid.value = cachedOpenid;
|
|
|
+ console.log('[WeChat Pay] 使用缓存的 openid');
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
+ // 双重保险:检查 URL search 中的 code(如果 App.vue 未拦截到)
|
|
|
+ const searchParams = new URLSearchParams(window.location.search);
|
|
|
+ const code = searchParams.get('code');
|
|
|
if (code) {
|
|
|
- // OAuth 回调:用 code 换取 openid
|
|
|
uni.showLoading({ title: '授权中...' });
|
|
|
try {
|
|
|
const result = await post<{ openid: string }>('/payment/wechat/openid', { code });
|
|
|
openid.value = result.openid;
|
|
|
- // 保存 openid 到 localStorage(下次不用重新授权)
|
|
|
localStorage.setItem('wx_openid', result.openid);
|
|
|
console.log('[WeChat Pay] openid 获取成功');
|
|
|
|
|
|
- // 清除 URL 中的 code 参数,避免重复处理
|
|
|
- urlParams.delete('code');
|
|
|
- urlParams.delete('state');
|
|
|
- const newSearch = urlParams.toString();
|
|
|
+ // 清除 URL 中的 code/state
|
|
|
+ searchParams.delete('code');
|
|
|
+ searchParams.delete('state');
|
|
|
+ const newSearch = searchParams.toString();
|
|
|
const newUrl = window.location.origin + window.location.pathname
|
|
|
- + (newSearch ? '?' + newSearch : '')
|
|
|
- + window.location.hash;
|
|
|
+ + (newSearch ? '?' + newSearch : '');
|
|
|
window.history.replaceState({}, '', newUrl);
|
|
|
} catch (e) {
|
|
|
console.error('[WeChat Pay] 获取 openid 失败:', e);
|
|
|
- uni.showToast({ title: '微信授权失败,请使用扫码支付', icon: 'none' });
|
|
|
- inWechat.value = false;
|
|
|
} finally {
|
|
|
uni.hideLoading();
|
|
|
}
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 没有 code 参数,检查是否有缓存的 openid
|
|
|
- const cachedOpenid = localStorage.getItem('wx_openid');
|
|
|
- if (cachedOpenid) {
|
|
|
- openid.value = cachedOpenid;
|
|
|
- console.log('[WeChat Pay] 使用缓存的 openid');
|
|
|
}
|
|
|
// #endif
|
|
|
}
|
|
|
@@ -218,9 +234,12 @@ async function handleWechatOAuth() {
|
|
|
async function redirectToWechatOAuth() {
|
|
|
// #ifdef H5
|
|
|
try {
|
|
|
- const currentUrl = window.location.href;
|
|
|
+ // 保存当前完整 URL(含 hash 路由参数),OAuth 回来时恢复
|
|
|
+ savePageContext();
|
|
|
+ // 用不含 # 的 baseUrl 作为 redirect_uri,避免微信报"非法字符"
|
|
|
+ const baseUrl = getBaseUrl();
|
|
|
const result = await get<{ oauthUrl: string; state: string }>('/payment/wechat/oauth-url', {
|
|
|
- redirect: currentUrl
|
|
|
+ redirect: baseUrl
|
|
|
});
|
|
|
window.location.href = result.oauthUrl;
|
|
|
} catch (e) {
|
|
|
@@ -260,15 +279,16 @@ onMounted(async () => {
|
|
|
paymentMethod.value = 'wechat';
|
|
|
// 处理 OAuth 回调
|
|
|
await handleWechatOAuth();
|
|
|
- // 初始化 JS-SDK(如果有 openid)
|
|
|
+ // 如果有 openid,提前初始化 JS-SDK
|
|
|
if (openid.value) {
|
|
|
initWxJsSdk(); // 异步初始化,不阻塞
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // 进入页面时直接创建订单
|
|
|
- if (productType.value === 'token-pack' || planId.value) {
|
|
|
- await createOrder();
|
|
|
+ // 微信环境不预创建订单(JSAPI 支付时另外创建)
|
|
|
+ } else {
|
|
|
+ // 非微信环境:进入页面时直接创建订单
|
|
|
+ if (productType.value === 'token-pack' || planId.value) {
|
|
|
+ await createOrder();
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
|