fix: restore wechat payment oauth and jsapi flow
This commit is contained in:
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>
|
||||
Reference in New Issue
Block a user