feat(avatar): route token recharge through Huihui org orders
This commit is contained in:
@@ -156,7 +156,8 @@ export interface TokenPaymentOrder {
|
||||
pointsAmount: number
|
||||
price: number
|
||||
status: 'pending' | 'paid' | 'failed' | 'closed' | 'refunded'
|
||||
provider: 'huihui' | 'wechat_virtual'
|
||||
provider: 'huihui' | 'wechat_virtual' | 'huihui_org'
|
||||
providerOrderNo?: string
|
||||
providerStatus: string
|
||||
payMessage: string
|
||||
failureReason: string
|
||||
@@ -196,6 +197,12 @@ export const chargeToken = (
|
||||
payScene: 'APP' | 'H5' | 'LITE' | 'JSAPI'
|
||||
) => request.post<TokenPaymentOrder>('/token/charge', { planId, paymentMethod, payScene })
|
||||
|
||||
export const chargeTokenOrg = (
|
||||
planId: string,
|
||||
paymentMethod: 'wechat' | 'alipay',
|
||||
payScene: 'APP' | 'H5' | 'LITE' | 'JSAPI'
|
||||
) => request.post<TokenPaymentOrder>('/token/charge/org', { planId, paymentMethod, payScene })
|
||||
|
||||
export const getTokenPaymentStatus = (orderId: string) =>
|
||||
request.get<TokenPaymentOrder>(`/token/payment/${orderId}`)
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ app.use(pinia)
|
||||
|
||||
// —— 混合架构:在挂载前注入 uniapp 壳传入的认证与会会资料 ——
|
||||
const params = getLaunchParams()
|
||||
if (params.evn !== undefined) sessionStorage.setItem('hh_launch_evn', params.evn)
|
||||
const avatarStore = useAvatarStore(pinia)
|
||||
const userStore = useUserStore(pinia)
|
||||
|
||||
|
||||
@@ -12,15 +12,16 @@ export interface UniLaunchParams {
|
||||
nickname?: string
|
||||
avatar?: string
|
||||
ts?: string
|
||||
evn?: string
|
||||
}
|
||||
|
||||
const PARAM_KEYS: (keyof UniLaunchParams)[] = ['token', 'userId', 'nickname', 'avatar', 'ts']
|
||||
const PARAM_KEYS: (keyof UniLaunchParams)[] = ['token', 'userId', 'nickname', 'avatar', 'ts', 'evn']
|
||||
|
||||
function readParams(search: string, target: UniLaunchParams): void {
|
||||
const sp = new URLSearchParams(search)
|
||||
for (const key of PARAM_KEYS) {
|
||||
const value = sp.get(key)
|
||||
if (value) target[key] = value
|
||||
if (value || (key === 'evn' && sp.has(key))) target[key] = value ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
chargeToken,
|
||||
chargeTokenOrg,
|
||||
applyTokenInvoice,
|
||||
getRechargePlans,
|
||||
getTokenBalance,
|
||||
@@ -118,7 +118,7 @@ import {
|
||||
getTokenPaymentStatus,
|
||||
type TokenPaymentOrder
|
||||
} from '@/api'
|
||||
import { isInUniWebView, onNativeMessage, UniEvents } from '@/utils/uniapp-bridge'
|
||||
import { isInUniWebView, onNativeMessage } from '@/utils/uniapp-bridge'
|
||||
import wechatPayIcon from '@/assets/wechat-pay.svg'
|
||||
import alipayIcon from '@/assets/alipay.svg'
|
||||
|
||||
@@ -235,71 +235,12 @@ const checkoutLabel = computed(() => {
|
||||
})
|
||||
|
||||
const payScene = () => {
|
||||
if (paymentMethod.value === 'wechat' && /miniProgram/i.test(navigator.userAgent)) return 'LITE' as const
|
||||
if (isInUniWebView()) return 'APP' as const
|
||||
if (paymentMethod.value === 'wechat' && /MicroMessenger/i.test(navigator.userAgent)) return 'JSAPI' as const
|
||||
return 'H5' as const
|
||||
}
|
||||
|
||||
const parsePayMessage = (message: string) => {
|
||||
const text = (message || '').trim()
|
||||
if (!text) return null
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
const findPaymentUrl = (value: any): string => {
|
||||
if (typeof value === 'string' && /^https?:\/\//i.test(value)) return value
|
||||
if (!value || typeof value !== 'object') return ''
|
||||
for (const key of ['url', 'payUrl', 'mwebUrl', 'mweb_url', 'redirectUrl']) {
|
||||
if (typeof value[key] === 'string' && /^https?:\/\//i.test(value[key])) return value[key]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const invokeWechatJsapi = (params: Record<string, any>) => new Promise<void>((resolve, reject) => {
|
||||
const invoke = () => {
|
||||
const bridge = (window as any).WeixinJSBridge
|
||||
if (!bridge?.invoke) {
|
||||
reject(new Error('当前微信环境无法拉起支付'))
|
||||
return
|
||||
}
|
||||
bridge.invoke('getBrandWCPayRequest', params, (result: any) => {
|
||||
const message = String(result?.err_msg || result?.errMsg || '')
|
||||
if (/\bok\b/i.test(message)) resolve()
|
||||
else reject(new Error(/cancel/i.test(message) ? '支付已取消' : '微信支付未完成'))
|
||||
})
|
||||
}
|
||||
if ((window as any).WeixinJSBridge) invoke()
|
||||
else document.addEventListener('WeixinJSBridgeReady', invoke, { once: true })
|
||||
})
|
||||
|
||||
const launchPayment = async (order: TokenPaymentOrder) => {
|
||||
const parsed = parsePayMessage(order.payMessage)
|
||||
if (order.payWay === 'JSAPI' && order.paymentMethod === 'wechat' && parsed && typeof parsed === 'object') {
|
||||
await invokeWechatJsapi(parsed)
|
||||
return
|
||||
}
|
||||
if (isInUniWebView() && UniEvents.payment({
|
||||
orderId: order.id,
|
||||
orderNo: order.orderNo,
|
||||
paymentMethod: order.paymentMethod,
|
||||
payType: order.payType,
|
||||
payWay: order.payWay,
|
||||
payMessage: order.payMessage,
|
||||
paymentParams: parsed
|
||||
})) return
|
||||
|
||||
const paymentUrl = findPaymentUrl(parsed)
|
||||
if (paymentUrl) {
|
||||
window.location.assign(paymentUrl)
|
||||
return
|
||||
}
|
||||
throw new Error('请在会会 App 内完成支付')
|
||||
}
|
||||
|
||||
const clearPendingOrder = () => {
|
||||
pendingOrderId.value = ''
|
||||
sessionStorage.removeItem('hh_pending_payment_order')
|
||||
@@ -349,12 +290,18 @@ const doCharge = async () => {
|
||||
charging.value = true
|
||||
paymentNotice.value = ''
|
||||
try {
|
||||
const order = await chargeToken(selectedPlan.value.id, paymentMethod.value, payScene())
|
||||
const order = await chargeTokenOrg(selectedPlan.value.id, paymentMethod.value, payScene())
|
||||
if (order.status === 'failed') throw new Error(order.failureReason || '支付订单创建失败')
|
||||
if (!order.providerOrderNo || !/^\d+$/.test(order.providerOrderNo)) throw new Error('会会充值订单号缺失')
|
||||
paymentNoticeTone.value = 'pending'
|
||||
paymentNotice.value = '支付订单已创建,正在等待付款结果'
|
||||
startPolling(order.id)
|
||||
await launchPayment(order)
|
||||
const checkoutBase = window.location.hostname === 'digital.99hui.com'
|
||||
? 'https://99hui.com/huihui-h5/'
|
||||
: 'https://fat-open.99hui.com/huihui-h5/'
|
||||
const evn = sessionStorage.getItem('hh_launch_evn')
|
||||
const evnPart = evn ? `&evn=${encodeURIComponent(evn)}` : '&evn'
|
||||
window.location.assign(`${checkoutBase}#/payOrder?orderNo=${encodeURIComponent(order.providerOrderNo)}&isOrg=true${evnPart}`)
|
||||
} catch (error: any) {
|
||||
paymentNoticeTone.value = 'error'
|
||||
paymentNotice.value = error?.message || '充值失败,请重试'
|
||||
|
||||
Reference in New Issue
Block a user