feat: 小程序真实微信支付功能
新增: 1. /api/miniprogram/pay - 创建预支付订单API 2. /api/miniprogram/pay/notify - 支付回调处理 3. /api/miniprogram/login - 小程序登录获取openId 配置: - 小程序AppID: wxb8bbb2b10dec74aa - 商户号: 1318592501 - 回调地址: https://soul.cunbao.net/api/miniprogram/pay/notify 更新: - app.js: 添加getOpenId方法,支付前获取openId - read.js: processPayment调用真实支付接口 - 支持API不可用时回退到测试模式
This commit is contained in:
@@ -6,10 +6,14 @@
|
||||
App({
|
||||
globalData: {
|
||||
// API基础地址
|
||||
baseUrl: 'https://soul.ckb.fit',
|
||||
baseUrl: 'https://soul.cunbao.net',
|
||||
|
||||
// 小程序配置
|
||||
appId: 'wxb8bbb2b10dec74aa',
|
||||
|
||||
// 用户信息
|
||||
userInfo: null,
|
||||
openId: null, // 微信openId,支付必需
|
||||
isLoggedIn: false,
|
||||
|
||||
// 书籍数据
|
||||
@@ -170,7 +174,7 @@ App({
|
||||
})
|
||||
},
|
||||
|
||||
// 登录方法 - 支持模拟登录回退
|
||||
// 登录方法 - 获取openId用于支付
|
||||
async login() {
|
||||
try {
|
||||
// 获取微信登录code
|
||||
@@ -181,37 +185,80 @@ App({
|
||||
})
|
||||
})
|
||||
|
||||
console.log('[App] 获取登录code成功')
|
||||
|
||||
try {
|
||||
// 尝试发送code到服务器
|
||||
const res = await this.request('/api/wechat/login', {
|
||||
// 发送code到服务器获取openId
|
||||
const res = await this.request('/api/miniprogram/login', {
|
||||
method: 'POST',
|
||||
data: { code: loginRes.code }
|
||||
})
|
||||
|
||||
if (res.success && res.data) {
|
||||
// 保存用户信息
|
||||
this.globalData.userInfo = res.data.user
|
||||
this.globalData.isLoggedIn = true
|
||||
this.globalData.purchasedSections = res.data.user.purchasedSections || []
|
||||
this.globalData.hasFullBook = res.data.user.hasFullBook || false
|
||||
// 保存openId
|
||||
if (res.data.openId) {
|
||||
this.globalData.openId = res.data.openId
|
||||
wx.setStorageSync('openId', res.data.openId)
|
||||
console.log('[App] 获取openId成功')
|
||||
}
|
||||
|
||||
wx.setStorageSync('userInfo', res.data.user)
|
||||
wx.setStorageSync('token', res.data.token)
|
||||
// 保存用户信息
|
||||
if (res.data.user) {
|
||||
this.globalData.userInfo = res.data.user
|
||||
this.globalData.isLoggedIn = true
|
||||
this.globalData.purchasedSections = res.data.user.purchasedSections || []
|
||||
this.globalData.hasFullBook = res.data.user.hasFullBook || false
|
||||
|
||||
wx.setStorageSync('userInfo', res.data.user)
|
||||
wx.setStorageSync('token', res.data.token || '')
|
||||
}
|
||||
|
||||
return res.data
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log('API登录失败,使用模拟登录:', apiError)
|
||||
console.log('[App] API登录失败,使用模拟登录:', apiError.message)
|
||||
}
|
||||
|
||||
// API不可用时使用模拟登录
|
||||
return this.mockLogin()
|
||||
} catch (e) {
|
||||
console.error('登录失败:', e)
|
||||
console.error('[App] 登录失败:', e)
|
||||
// 最后尝试模拟登录
|
||||
return this.mockLogin()
|
||||
}
|
||||
},
|
||||
|
||||
// 获取openId (支付必需)
|
||||
async getOpenId() {
|
||||
// 先检查缓存
|
||||
const cachedOpenId = wx.getStorageSync('openId')
|
||||
if (cachedOpenId) {
|
||||
this.globalData.openId = cachedOpenId
|
||||
return cachedOpenId
|
||||
}
|
||||
|
||||
// 没有缓存则登录获取
|
||||
try {
|
||||
const loginRes = await new Promise((resolve, reject) => {
|
||||
wx.login({ success: resolve, fail: reject })
|
||||
})
|
||||
|
||||
const res = await this.request('/api/miniprogram/login', {
|
||||
method: 'POST',
|
||||
data: { code: loginRes.code }
|
||||
})
|
||||
|
||||
if (res.success && res.data?.openId) {
|
||||
this.globalData.openId = res.data.openId
|
||||
wx.setStorageSync('openId', res.data.openId)
|
||||
return res.data.openId
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[App] 获取openId失败:', e)
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
// 模拟登录(后端不可用时使用)
|
||||
mockLogin() {
|
||||
|
||||
@@ -361,65 +361,96 @@ ${id === 'preface' || id === 'epilogue' || id.startsWith('appendix') || id === '
|
||||
await this.processPayment('fullbook', null, this.data.fullBookPrice)
|
||||
},
|
||||
|
||||
// 处理支付
|
||||
// 处理支付 - 调用真实微信支付接口
|
||||
async processPayment(type, sectionId, amount) {
|
||||
this.setData({ isPaying: true })
|
||||
|
||||
try {
|
||||
// 创建订单
|
||||
// 1. 先获取openId (支付必需)
|
||||
let openId = app.globalData.openId || wx.getStorageSync('openId')
|
||||
|
||||
if (!openId) {
|
||||
console.log('[Pay] 需要先获取openId')
|
||||
openId = await app.getOpenId()
|
||||
|
||||
if (!openId) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '需要登录后才能支付,请先登录',
|
||||
showCancel: false
|
||||
})
|
||||
this.setData({ showLoginModal: true, isPaying: false })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Pay] 开始创建订单:', { type, sectionId, amount, openId: openId.slice(0, 10) + '...' })
|
||||
|
||||
// 2. 调用后端创建预支付订单
|
||||
let paymentData = null
|
||||
|
||||
try {
|
||||
const res = await app.request('/api/payment/create-order', {
|
||||
const res = await app.request('/api/miniprogram/pay', {
|
||||
method: 'POST',
|
||||
data: { type, sectionId, amount }
|
||||
data: {
|
||||
openId,
|
||||
productType: type,
|
||||
productId: sectionId,
|
||||
amount,
|
||||
description: type === 'fullbook' ? '《一场Soul的创业实验》全书' : `章节-${sectionId}`,
|
||||
userId: app.globalData.userInfo?.id || ''
|
||||
}
|
||||
})
|
||||
|
||||
if (res.success && res.data) {
|
||||
paymentData = res.data
|
||||
console.log('[Pay] 创建订单响应:', res)
|
||||
|
||||
if (res.success && res.data?.payParams) {
|
||||
paymentData = res.data.payParams
|
||||
console.log('[Pay] 获取支付参数成功')
|
||||
} else {
|
||||
throw new Error(res.error || '创建订单失败')
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.log('API创建订单失败,使用模拟支付')
|
||||
}
|
||||
|
||||
// 如果API不可用,使用模拟支付
|
||||
if (!paymentData) {
|
||||
paymentData = {
|
||||
isMock: true,
|
||||
orderId: 'mock_' + Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
// 调用微信支付或模拟支付
|
||||
if (paymentData.isMock) {
|
||||
// 模拟支付确认
|
||||
const confirmRes = await new Promise((resolve) => {
|
||||
console.log('[Pay] API创建订单失败:', apiError.message)
|
||||
|
||||
// 开发环境:API不可用时使用模拟支付
|
||||
const useMock = await new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title: '确认支付',
|
||||
content: `支付 ¥${amount} 购买${type === 'section' ? '本章' : '全书'}?\n(测试环境模拟支付)`,
|
||||
title: '支付服务暂不可用',
|
||||
content: '是否使用测试模式完成购买?',
|
||||
confirmText: '测试购买',
|
||||
cancelText: '取消',
|
||||
success: (res) => resolve(res.confirm)
|
||||
})
|
||||
})
|
||||
|
||||
if (confirmRes) {
|
||||
// 模拟支付成功,更新本地数据
|
||||
if (useMock) {
|
||||
this.mockPaymentSuccess(type, sectionId)
|
||||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||||
wx.showToast({ title: '测试购买成功', icon: 'success' })
|
||||
this.initSection(this.data.sectionId)
|
||||
}
|
||||
} else {
|
||||
// 真实微信支付
|
||||
await this.callWechatPay(paymentData)
|
||||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||||
this.setData({ isPaying: false })
|
||||
return
|
||||
}
|
||||
|
||||
// 刷新页面
|
||||
// 3. 调用微信支付
|
||||
console.log('[Pay] 调起微信支付')
|
||||
await this.callWechatPay(paymentData)
|
||||
|
||||
// 4. 支付成功,更新本地数据
|
||||
this.mockPaymentSuccess(type, sectionId)
|
||||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||||
|
||||
// 5. 刷新页面
|
||||
this.initSection(this.data.sectionId)
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Pay] 支付失败:', e)
|
||||
|
||||
if (e.errMsg && e.errMsg.includes('cancel')) {
|
||||
wx.showToast({ title: '已取消支付', icon: 'none' })
|
||||
} else {
|
||||
wx.showToast({ title: '支付失败', icon: 'none' })
|
||||
wx.showToast({ title: e.errMsg || '支付失败', icon: 'none' })
|
||||
}
|
||||
} finally {
|
||||
this.setData({ isPaying: false })
|
||||
|
||||
Reference in New Issue
Block a user