fix: restore wechat payment oauth and jsapi flow
This commit is contained in:
@@ -105,6 +105,50 @@ describe('decidePaymentLaunch', () => {
|
||||
expect(decision.recovery.paymentMode).toBe('popup')
|
||||
expect(decision.recovery.resumeToken).toBe('resume-2')
|
||||
})
|
||||
|
||||
it('returns wechat oauth launch when backend requires in-app authorization', () => {
|
||||
const decision = decidePaymentLaunch(createOrderResult({
|
||||
result_type: 'oauth_required',
|
||||
payment_type: 'wxpay',
|
||||
oauth: {
|
||||
authorize_url: '/api/v1/auth/oauth/wechat/payment/start?payment_type=wxpay',
|
||||
appid: 'wx123',
|
||||
scope: 'snsapi_base',
|
||||
redirect_url: '/auth/wechat/payment/callback',
|
||||
},
|
||||
}), {
|
||||
visibleMethod: 'wxpay',
|
||||
orderType: 'balance',
|
||||
isMobile: true,
|
||||
})
|
||||
|
||||
expect(decision.kind).toBe('wechat_oauth')
|
||||
expect(decision.oauth?.authorize_url).toContain('/api/v1/auth/oauth/wechat/payment/start')
|
||||
expect(decision.paymentState.paymentType).toBe('wxpay')
|
||||
})
|
||||
|
||||
it('returns wechat jsapi launch when backend has a jsapi payload ready', () => {
|
||||
const decision = decidePaymentLaunch(createOrderResult({
|
||||
result_type: 'jsapi_ready',
|
||||
payment_type: 'wxpay',
|
||||
jsapi: {
|
||||
appId: 'wx123',
|
||||
timeStamp: '1712345678',
|
||||
nonceStr: 'nonce-123',
|
||||
package: 'prepay_id=wx123',
|
||||
signType: 'RSA',
|
||||
paySign: 'signed-payload',
|
||||
},
|
||||
}), {
|
||||
visibleMethod: 'wxpay',
|
||||
orderType: 'subscription',
|
||||
isMobile: true,
|
||||
})
|
||||
|
||||
expect(decision.kind).toBe('wechat_jsapi')
|
||||
expect(decision.jsapi?.appId).toBe('wx123')
|
||||
expect(decision.paymentState.orderType).toBe('subscription')
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildCreateOrderPayload', () => {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import type { CreateOrderRequest, CreateOrderResult, MethodLimit, OrderType } from '@/types/payment'
|
||||
import type {
|
||||
CreateOrderRequest,
|
||||
CreateOrderResult,
|
||||
MethodLimit,
|
||||
OrderType,
|
||||
WechatJSAPIPayload,
|
||||
WechatOAuthInfo,
|
||||
} from '@/types/payment'
|
||||
|
||||
export const PAYMENT_RECOVERY_STORAGE_KEY = 'payment.recovery.current'
|
||||
|
||||
@@ -16,6 +23,8 @@ export type PaymentLaunchKind =
|
||||
| 'redirect_waiting'
|
||||
| 'stripe_popup'
|
||||
| 'stripe_route'
|
||||
| 'wechat_oauth'
|
||||
| 'wechat_jsapi'
|
||||
| 'unhandled'
|
||||
|
||||
export interface PaymentRecoverySnapshot {
|
||||
@@ -47,6 +56,8 @@ export interface PaymentLaunchDecision {
|
||||
paymentState: PaymentRecoverySnapshot
|
||||
recovery: PaymentRecoverySnapshot
|
||||
stripeMethod?: StripeVisibleMethod
|
||||
oauth?: WechatOAuthInfo
|
||||
jsapi?: WechatJSAPIPayload
|
||||
}
|
||||
|
||||
export interface BuildCreateOrderPayloadInput {
|
||||
@@ -139,6 +150,15 @@ export function decidePaymentLaunch(
|
||||
return { kind, paymentState, recovery: paymentState, stripeMethod }
|
||||
}
|
||||
|
||||
if (result.result_type === 'oauth_required' && result.oauth?.authorize_url) {
|
||||
return { kind: 'wechat_oauth', paymentState: baseState, recovery: baseState, oauth: result.oauth }
|
||||
}
|
||||
|
||||
const jsapiPayload = result.jsapi ?? result.jsapi_payload
|
||||
if (result.result_type === 'jsapi_ready' && jsapiPayload) {
|
||||
return { kind: 'wechat_jsapi', paymentState: baseState, recovery: baseState, jsapi: jsapiPayload }
|
||||
}
|
||||
|
||||
if (baseState.qrCode) {
|
||||
return { kind: 'qr_waiting', paymentState: baseState, recovery: baseState }
|
||||
}
|
||||
|
||||
@@ -92,6 +92,15 @@ const routes: RouteRecordRaw[] = [
|
||||
title: 'WeChat OAuth Callback'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/auth/wechat/payment/callback',
|
||||
name: 'WeChatPaymentOAuthCallback',
|
||||
component: () => import('@/views/auth/WechatPaymentCallbackView.vue'),
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
title: 'WeChat Payment Callback'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/auth/oidc/callback',
|
||||
name: 'OIDCOAuthCallback',
|
||||
|
||||
@@ -156,6 +156,28 @@ export interface CreateOrderRequest {
|
||||
plan_id?: number
|
||||
return_url?: string
|
||||
payment_source?: string
|
||||
openid?: string
|
||||
is_mobile?: boolean
|
||||
}
|
||||
|
||||
export type CreateOrderResultType = 'order_created' | 'oauth_required' | 'jsapi_ready'
|
||||
|
||||
export interface WechatOAuthInfo {
|
||||
authorize_url?: string
|
||||
appid?: string
|
||||
openid?: string
|
||||
scope?: string
|
||||
state?: string
|
||||
redirect_url?: string
|
||||
}
|
||||
|
||||
export interface WechatJSAPIPayload {
|
||||
appId?: string
|
||||
timeStamp?: string
|
||||
nonceStr?: string
|
||||
package?: string
|
||||
signType?: string
|
||||
paySign?: string
|
||||
}
|
||||
|
||||
export interface CreateOrderResult {
|
||||
@@ -167,8 +189,14 @@ export interface CreateOrderResult {
|
||||
pay_amount: number
|
||||
fee_rate: number
|
||||
expires_at: string
|
||||
result_type?: CreateOrderResultType
|
||||
payment_type?: string
|
||||
out_trade_no?: string
|
||||
payment_mode?: string
|
||||
resume_token?: string
|
||||
oauth?: WechatOAuthInfo
|
||||
jsapi?: WechatJSAPIPayload
|
||||
jsapi_payload?: WechatJSAPIPayload
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
|
||||
155
frontend/src/views/auth/WechatPaymentCallbackView.vue
Normal file
155
frontend/src/views/auth/WechatPaymentCallbackView.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="min-h-screen bg-gray-50 px-4 py-10 dark:bg-dark-900">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<div class="card p-6">
|
||||
<h1 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ callbackTitleText }}
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ errorMessage || callbackProcessingText }}
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-if="!errorMessage"
|
||||
class="mt-6 flex items-center justify-center py-10"
|
||||
>
|
||||
<div
|
||||
class="h-8 w-8 animate-spin rounded-full border-4 border-primary-500 border-t-transparent"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="mt-6 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-700/50 dark:bg-red-900/20"
|
||||
>
|
||||
<p class="text-sm text-red-700 dark:text-red-400">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<button
|
||||
class="btn btn-primary mt-4"
|
||||
type="button"
|
||||
@click="goBackToPayment"
|
||||
>
|
||||
{{ backToPaymentText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const errorMessage = ref('')
|
||||
|
||||
function textWithFallback(key: string, zh: string, en: string): string {
|
||||
const translated = t(key)
|
||||
if (translated !== key) return translated
|
||||
return String(locale.value).toLowerCase().startsWith('zh') ? zh : en
|
||||
}
|
||||
|
||||
const callbackProcessingText = computed(() =>
|
||||
textWithFallback(
|
||||
'auth.wechatPayment.callbackProcessing',
|
||||
'正在恢复微信支付...',
|
||||
'Resuming WeChat payment...',
|
||||
))
|
||||
const callbackTitleText = computed(() =>
|
||||
textWithFallback(
|
||||
'auth.wechatPayment.callbackTitle',
|
||||
'正在恢复微信支付',
|
||||
'Resuming WeChat payment',
|
||||
))
|
||||
const backToPaymentText = computed(() =>
|
||||
textWithFallback(
|
||||
'auth.wechatPayment.backToPayment',
|
||||
'返回支付页',
|
||||
'Back to payment',
|
||||
))
|
||||
|
||||
function readQueryString(key: string): string {
|
||||
const value = route.query[key]
|
||||
if (Array.isArray(value)) {
|
||||
return typeof value[0] === 'string' ? value[0] : ''
|
||||
}
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function parseFragmentParams(): URLSearchParams {
|
||||
const raw = typeof window !== 'undefined' ? window.location.hash : ''
|
||||
const hash = raw.startsWith('#') ? raw.slice(1) : raw
|
||||
return new URLSearchParams(hash)
|
||||
}
|
||||
|
||||
function normalizeRedirectPath(path: string | null | undefined): string {
|
||||
const value = (path || '').trim()
|
||||
if (!value) return '/purchase'
|
||||
if (!value.startsWith('/')) return '/purchase'
|
||||
if (value.startsWith('//') || value.includes('://')) return '/purchase'
|
||||
if (value === '/payment') return '/purchase'
|
||||
if (value.startsWith('/payment?')) return '/purchase' + value.slice('/payment'.length)
|
||||
return value
|
||||
}
|
||||
|
||||
function goBackToPayment() {
|
||||
void router.replace('/purchase')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const fragment = parseFragmentParams()
|
||||
const readParam = (key: string) => fragment.get(key) || readQueryString(key)
|
||||
|
||||
const error = readParam('error') || readParam('err_msg') || readParam('errmsg')
|
||||
const errorDescription = readParam('error_description') || readParam('message')
|
||||
|
||||
if (error) {
|
||||
errorMessage.value = errorDescription || error
|
||||
return
|
||||
}
|
||||
|
||||
const openid = readParam('openid')
|
||||
const state = readParam('state')
|
||||
const scope = readParam('scope')
|
||||
const paymentType = readParam('payment_type')
|
||||
const amount = readParam('amount')
|
||||
const orderType = readParam('order_type')
|
||||
const planId = readParam('plan_id')
|
||||
const redirectURL = new URL(
|
||||
normalizeRedirectPath(readParam('redirect')),
|
||||
window.location.origin,
|
||||
)
|
||||
|
||||
if (!openid) {
|
||||
errorMessage.value = textWithFallback(
|
||||
'auth.wechatPayment.callbackMissingOpenId',
|
||||
'微信支付回调缺少 openid。',
|
||||
'The WeChat payment callback is missing the openid.',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const query: Record<string, string> = {
|
||||
...Object.fromEntries(redirectURL.searchParams.entries()),
|
||||
wechat_resume: '1',
|
||||
openid,
|
||||
}
|
||||
if (state) query.state = state
|
||||
if (scope) query.scope = scope
|
||||
if (paymentType) query.payment_type = paymentType
|
||||
if (amount) query.amount = amount
|
||||
if (orderType) query.order_type = orderType
|
||||
if (planId) query.plan_id = planId
|
||||
|
||||
await router.replace({
|
||||
path: redirectURL.pathname,
|
||||
query,
|
||||
})
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import WechatPaymentCallbackView from '@/views/auth/WechatPaymentCallbackView.vue'
|
||||
|
||||
const { replaceMock, routeState, locationState } = vi.hoisted(() => ({
|
||||
replaceMock: vi.fn(),
|
||||
routeState: {
|
||||
query: {} as Record<string, unknown>,
|
||||
},
|
||||
locationState: {
|
||||
current: {
|
||||
href: 'http://localhost/auth/wechat/payment/callback',
|
||||
hash: '',
|
||||
search: '',
|
||||
pathname: '/auth/wechat/payment/callback',
|
||||
origin: 'http://localhost',
|
||||
} as Location & { origin: string },
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: () => routeState,
|
||||
useRouter: () => ({
|
||||
replace: replaceMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
locale: { value: 'zh-CN' },
|
||||
}),
|
||||
}))
|
||||
|
||||
describe('WechatPaymentCallbackView', () => {
|
||||
beforeEach(() => {
|
||||
replaceMock.mockReset()
|
||||
routeState.query = {}
|
||||
locationState.current = {
|
||||
href: 'http://localhost/auth/wechat/payment/callback',
|
||||
hash: '',
|
||||
search: '',
|
||||
pathname: '/auth/wechat/payment/callback',
|
||||
origin: 'http://localhost',
|
||||
} as Location & { origin: string }
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
value: locationState.current,
|
||||
})
|
||||
})
|
||||
|
||||
it('redirects back to purchase with openid and payment context from hash fragment', async () => {
|
||||
locationState.current.hash = '#openid=openid-123&payment_type=wxpay&amount=12.5&order_type=balance&redirect=%2Fpurchase%3Ffrom%3Dwechat'
|
||||
|
||||
mount(WechatPaymentCallbackView)
|
||||
await flushPromises()
|
||||
|
||||
expect(replaceMock).toHaveBeenCalledWith({
|
||||
path: '/purchase',
|
||||
query: {
|
||||
from: 'wechat',
|
||||
wechat_resume: '1',
|
||||
openid: 'openid-123',
|
||||
payment_type: 'wxpay',
|
||||
amount: '12.5',
|
||||
order_type: 'balance',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows an error when the callback payload is missing openid', async () => {
|
||||
locationState.current.hash = '#payment_type=wxpay'
|
||||
|
||||
const wrapper = mount(WechatPaymentCallbackView)
|
||||
await flushPromises()
|
||||
|
||||
expect(replaceMock).not.toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('微信支付回调缺少 openid。')
|
||||
})
|
||||
})
|
||||
@@ -309,6 +309,20 @@ const previewImage = ref('')
|
||||
|
||||
const paymentPhase = ref<'select' | 'paying'>('select')
|
||||
|
||||
interface CreateOrderOptions {
|
||||
openid?: string
|
||||
paymentType?: string
|
||||
isResume?: boolean
|
||||
}
|
||||
|
||||
interface WeixinJSBridgeLike {
|
||||
invoke(
|
||||
action: string,
|
||||
payload: Record<string, unknown>,
|
||||
callback: (result: Record<string, unknown>) => void,
|
||||
): void
|
||||
}
|
||||
|
||||
function emptyPaymentState(): PaymentRecoverySnapshot {
|
||||
return {
|
||||
orderId: 0,
|
||||
@@ -326,6 +340,48 @@ function emptyPaymentState(): PaymentRecoverySnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
function readRouteQueryValue(value: unknown): string {
|
||||
if (Array.isArray(value)) {
|
||||
return typeof value[0] === 'string' ? value[0] : ''
|
||||
}
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function getWeixinJSBridge(): WeixinJSBridgeLike | undefined {
|
||||
return (window as Window & { WeixinJSBridge?: WeixinJSBridgeLike }).WeixinJSBridge
|
||||
}
|
||||
|
||||
function waitForWeixinJSBridge(timeoutMs = 4000): Promise<WeixinJSBridgeLike | null> {
|
||||
const existing = getWeixinJSBridge()
|
||||
if (existing) return Promise.resolve(existing)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let settled = false
|
||||
const finish = (bridge: WeixinJSBridgeLike | null) => {
|
||||
if (settled) return
|
||||
settled = true
|
||||
document.removeEventListener('WeixinJSBridgeReady', handleReady)
|
||||
document.removeEventListener('onWeixinJSBridgeReady', handleReady)
|
||||
window.clearTimeout(timer)
|
||||
resolve(bridge)
|
||||
}
|
||||
const handleReady = () => finish(getWeixinJSBridge() ?? null)
|
||||
const timer = window.setTimeout(() => finish(getWeixinJSBridge() ?? null), timeoutMs)
|
||||
document.addEventListener('WeixinJSBridgeReady', handleReady, false)
|
||||
document.addEventListener('onWeixinJSBridgeReady', handleReady, false)
|
||||
})
|
||||
}
|
||||
|
||||
async function invokeWechatJsapiPayment(payload: Record<string, unknown>): Promise<Record<string, unknown>> {
|
||||
const bridge = await waitForWeixinJSBridge()
|
||||
if (!bridge) {
|
||||
throw new Error('WeixinJSBridge is unavailable')
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
bridge.invoke('getBrandWCPayRequest', payload, (result) => resolve(result || {}))
|
||||
})
|
||||
}
|
||||
|
||||
const paymentState = ref<PaymentRecoverySnapshot>(emptyPaymentState())
|
||||
|
||||
function persistRecoverySnapshot(snapshot: PaymentRecoverySnapshot) {
|
||||
@@ -560,25 +616,32 @@ async function confirmSubscribe() {
|
||||
await createOrder(selectedPlan.value.price, 'subscription', selectedPlan.value.id)
|
||||
}
|
||||
|
||||
async function createOrder(orderAmount: number, orderType: OrderType, planId?: number) {
|
||||
async function createOrder(orderAmount: number, orderType: OrderType, planId?: number, options: CreateOrderOptions = {}) {
|
||||
submitting.value = true
|
||||
errorMessage.value = ''
|
||||
try {
|
||||
const result = await paymentStore.createOrder(buildCreateOrderPayload({
|
||||
const requestType = normalizeVisibleMethod(options.paymentType || selectedMethod.value) || options.paymentType || selectedMethod.value
|
||||
const payload = buildCreateOrderPayload({
|
||||
amount: orderAmount,
|
||||
paymentType: selectedMethod.value,
|
||||
paymentType: requestType,
|
||||
orderType,
|
||||
planId,
|
||||
origin: typeof window !== 'undefined' ? window.location.origin : '',
|
||||
isWechatBrowser: typeof window !== 'undefined' && /MicroMessenger/i.test(window.navigator.userAgent),
|
||||
})) as CreateOrderResult & { resume_token?: string }
|
||||
})
|
||||
if (options.openid) {
|
||||
payload.openid = options.openid
|
||||
}
|
||||
payload.is_mobile = isMobileDevice()
|
||||
|
||||
const result = await paymentStore.createOrder(payload) as CreateOrderResult & { resume_token?: string }
|
||||
const openWindow = (url: string, features = POPUP_WINDOW_FEATURES) => {
|
||||
const win = window.open(url, 'paymentPopup', features)
|
||||
if (!win || win.closed) {
|
||||
window.location.href = url
|
||||
}
|
||||
}
|
||||
const visibleMethod = normalizeVisibleMethod(selectedMethod.value) || selectedMethod.value
|
||||
const visibleMethod = normalizeVisibleMethod(requestType) || requestType
|
||||
const stripeMethod = visibleMethod === 'wxpay' ? 'wechat_pay' : 'alipay'
|
||||
const stripeRouteUrl = result.client_secret
|
||||
? router.resolve({
|
||||
@@ -599,6 +662,11 @@ async function createOrder(orderAmount: number, orderType: OrderType, planId?: n
|
||||
stripeRouteUrl,
|
||||
})
|
||||
|
||||
if (decision.kind === 'wechat_oauth' && decision.oauth?.authorize_url) {
|
||||
window.location.href = decision.oauth.authorize_url
|
||||
return
|
||||
}
|
||||
|
||||
if (decision.kind === 'unhandled') {
|
||||
errorMessage.value = t('payment.result.failed')
|
||||
appStore.showError(errorMessage.value)
|
||||
@@ -617,6 +685,16 @@ async function createOrder(orderAmount: number, orderType: OrderType, planId?: n
|
||||
window.location.href = decision.paymentState.payUrl
|
||||
return
|
||||
}
|
||||
if (decision.kind === 'wechat_jsapi' && decision.jsapi) {
|
||||
const jsapiResult = await invokeWechatJsapiPayment(decision.jsapi as Record<string, unknown>)
|
||||
const errMsg = String(jsapiResult.err_msg || '').toLowerCase()
|
||||
if (errMsg.includes('cancel')) {
|
||||
appStore.showInfo(t('payment.qr.cancelled'))
|
||||
} else if (errMsg && !errMsg.includes('ok')) {
|
||||
appStore.showError(t('payment.result.failed'))
|
||||
}
|
||||
return
|
||||
}
|
||||
if (decision.kind === 'redirect_waiting' && decision.paymentState.payUrl) {
|
||||
if (isMobileDevice()) {
|
||||
window.location.href = decision.paymentState.payUrl
|
||||
@@ -640,6 +718,50 @@ async function createOrder(orderAmount: number, orderType: OrderType, planId?: n
|
||||
}
|
||||
}
|
||||
|
||||
async function resumeWechatPaymentFromQuery() {
|
||||
const openid = readRouteQueryValue(route.query.openid)
|
||||
if (readRouteQueryValue(route.query.wechat_resume) !== '1' || !openid) {
|
||||
return
|
||||
}
|
||||
|
||||
const paymentType = normalizeVisibleMethod(readRouteQueryValue(route.query.payment_type)) || 'wxpay'
|
||||
const orderType = readRouteQueryValue(route.query.order_type) === 'subscription' ? 'subscription' : 'balance'
|
||||
const planId = Number.parseInt(readRouteQueryValue(route.query.plan_id), 10)
|
||||
const rawAmount = Number.parseFloat(readRouteQueryValue(route.query.amount))
|
||||
const orderAmount = Number.isFinite(rawAmount) && rawAmount > 0
|
||||
? rawAmount
|
||||
: (orderType === 'subscription'
|
||||
? (checkout.value.plans.find(plan => plan.id === planId)?.price ?? 0)
|
||||
: validAmount.value)
|
||||
|
||||
selectedMethod.value = paymentType
|
||||
if (orderType === 'balance' && orderAmount > 0) {
|
||||
amount.value = orderAmount
|
||||
}
|
||||
if (orderType === 'subscription' && Number.isFinite(planId) && planId > 0) {
|
||||
selectedPlan.value = checkout.value.plans.find(plan => plan.id === planId) ?? null
|
||||
}
|
||||
|
||||
const nextQuery = { ...route.query }
|
||||
delete nextQuery.wechat_resume
|
||||
delete nextQuery.openid
|
||||
delete nextQuery.state
|
||||
delete nextQuery.scope
|
||||
delete nextQuery.payment_type
|
||||
delete nextQuery.amount
|
||||
delete nextQuery.order_type
|
||||
delete nextQuery.plan_id
|
||||
await router.replace({ path: route.path, query: nextQuery })
|
||||
|
||||
if (orderAmount > 0) {
|
||||
await createOrder(orderAmount, orderType, Number.isFinite(planId) && planId > 0 ? planId : undefined, {
|
||||
openid,
|
||||
paymentType,
|
||||
isResume: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await paymentAPI.getCheckoutInfo()
|
||||
@@ -672,6 +794,7 @@ onMounted(async () => {
|
||||
removeRecoverySnapshot()
|
||||
}
|
||||
}
|
||||
await resumeWechatPaymentFromQuery()
|
||||
if (checkout.value.balance_disabled) {
|
||||
activeTab.value = 'subscription'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user