Files
miniprogram-1/miniprogram/pages/coupon/coupon.ts
2026-01-18 11:48:03 +08:00

204 lines
6.0 KiB
TypeScript

import apiManager from '../../utils/api'
import logger from '../../utils/logger'
Page({
data: {
products: [] as Array<any>,
plans: [] as Array<any>,
userPoints: 0,
displayUserPoints: '0',
vipLevel: 0
},
async onLoad(options: Record<string, string>) {
logger.info('Coupon page loaded')
try {
const [productList, planList] = await Promise.all([
apiManager.getProductList(),
apiManager.getSubscriptionPlans()
])
const products = (productList || []).map((p: any) => ({
...p,
amountYuan: ((p.amount_cents || 0) / 100).toFixed(2)
}))
const plans = (planList || []).map((p: any) => ({
...p,
priceYuan: ((p.price || 0) / 100).toFixed(2)
}))
const ptsStr = options?.points || '0'
const lvlStr = options?.vipLevel || '0'
const ptsNum = Number(ptsStr) || 0
const lvlNum = Number(lvlStr) || 0
const fmt = (n: number) => {
const s = Math.floor(n).toString()
return s.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
this.setData({
products,
plans,
userPoints: ptsNum,
displayUserPoints: fmt(ptsNum),
vipLevel: lvlNum
})
} catch (err) {
logger.error('加载产品列表失败', err)
wx.showToast({ title: '加载失败', icon: 'none' })
}
},
async handlePlanTap(e: any) {
try {
const productId = e?.currentTarget?.dataset?.id
if (!productId) {
wx.showToast({ title: '订阅商品信息错误', icon: 'none' })
return
}
const order = await apiManager.createSubscriptionOrder(String(productId))
const prepayId = order?.prepay_id
const outTradeNo = order?.out_trade_no
if (!prepayId) {
wx.showToast({ title: '下单失败', icon: 'none' })
return
}
const pkg = `prepay_id=${prepayId}`
const timeStamp = order?.timeStamp
const nonceStr = order?.nonceStr
const paySign = order?.paySign
const signType = (order?.signType || 'RSA') as any
if (timeStamp && nonceStr && paySign) {
wx.requestPayment({
timeStamp,
nonceStr,
package: pkg,
signType,
paySign,
success: async () => {
if (outTradeNo) {
await this.queryPaymentWithRetry(outTradeNo)
} else {
wx.showToast({ title: '订阅成功', icon: 'success' })
}
},
fail: async (err) => {
logger.error('订阅支付失败', err)
if (outTradeNo) {
await this.queryPaymentWithRetry(outTradeNo)
} else {
wx.showToast({ title: '订阅支付失败', icon: 'none' })
}
}
})
} else {
wx.showToast({ title: '缺少支付签名参数', icon: 'none' })
}
} catch (error) {
logger.error('发起订阅支付失败', error)
wx.showToast({ title: '发起订阅支付失败', icon: 'none' })
}
},
async handleCouponTap(e: any) {
try {
const productId = e?.currentTarget?.dataset?.id
if (!productId) {
wx.showToast({ title: '商品信息错误', icon: 'none' })
return
}
const order = await apiManager.createJsapiOrder(productId)
const prepayId = order?.prepay_id
const outTradeNo = order?.out_trade_no
if (!prepayId) {
wx.showToast({ title: '下单失败', icon: 'none' })
return
}
const pkg = `prepay_id=${prepayId}`
const timeStamp = order?.timeStamp
const nonceStr = order?.nonceStr
const paySign = order?.paySign
const signType = (order?.signType || 'RSA') as any
if (timeStamp && nonceStr && paySign) {
wx.requestPayment({
timeStamp,
nonceStr,
package: pkg,
signType,
paySign,
success: async () => {
if (outTradeNo) {
await this.queryPaymentWithRetry(outTradeNo)
} else {
wx.showToast({ title: '支付成功', icon: 'success' })
}
},
fail: async (err) => {
logger.error('支付失败', err)
if (outTradeNo) {
await this.queryPaymentWithRetry(outTradeNo)
} else {
wx.showToast({ title: '支付失败', icon: 'none' })
}
}
})
} else {
wx.showToast({ title: '缺少支付签名参数', icon: 'none' })
}
} catch (error) {
logger.error('发起支付失败', error)
wx.showToast({ title: '发起支付失败', icon: 'none' })
}
}
,
async queryPaymentWithRetry(outTradeNo: string, maxAttempts: number = 5) {
let attempt = 0
while (attempt < maxAttempts) {
try {
const res = await apiManager.getWxpayOrder(outTradeNo)
const state = res?.trade_state || ''
if (state === 'SUCCESS') {
wx.showToast({ title: '支付成功', icon: 'success' })
this.goBackToProfile()
return
}
if (state === 'USERPAYING' || state === 'NOTPAY' || state === 'PROCESSING') {
wx.showToast({ title: '支付处理中', icon: 'none' })
await new Promise(r => setTimeout(r, 800))
attempt++
continue
}
wx.showToast({ title: '支付失败', icon: 'none' })
return
} catch (err) {
await new Promise(r => setTimeout(r, 800))
attempt++
}
}
wx.showToast({ title: '查询超时,请稍后重试', icon: 'none' })
}
,
goBackToProfile() {
try {
const pages = getCurrentPages()
let delta = 0
for (let i = pages.length - 1; i >= 0; i--) {
const route = pages[i]?.route || ''
if (route.endsWith('pages/profile/profile')) {
delta = pages.length - 1 - i
break
}
}
if (delta > 0) {
wx.navigateBack({ delta })
} else {
wx.reLaunch({ url: '/pages/profile/profile' })
}
} catch (e) {
wx.reLaunch({ url: '/pages/profile/profile' })
}
}
})