Files
soul-yongping/miniprogram/app.js
卡若 0a5d470fef 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不可用时回退到测试模式
2026-01-23 05:44:21 +08:00

372 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Soul创业实验 - 小程序入口
* 开发: 卡若
*/
App({
globalData: {
// API基础地址
baseUrl: 'https://soul.cunbao.net',
// 小程序配置
appId: 'wxb8bbb2b10dec74aa',
// 用户信息
userInfo: null,
openId: null, // 微信openId支付必需
isLoggedIn: false,
// 书籍数据
bookData: null,
totalSections: 62,
// 购买记录
purchasedSections: [],
hasFullBook: false,
// 主题配置
theme: {
brandColor: '#00CED1',
brandSecondary: '#20B2AA',
goldColor: '#FFD700',
bgColor: '#000000',
cardBg: '#1c1c1e'
},
// 系统信息
systemInfo: null,
statusBarHeight: 44,
navBarHeight: 88,
// TabBar相关
currentTab: 0
},
onLaunch() {
// 获取系统信息
this.getSystemInfo()
// 检查登录状态
this.checkLoginStatus()
// 加载书籍数据
this.loadBookData()
// 检查更新
this.checkUpdate()
},
// 获取系统信息
getSystemInfo() {
try {
const systemInfo = wx.getSystemInfoSync()
this.globalData.systemInfo = systemInfo
this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44
// 计算导航栏高度
const menuButton = wx.getMenuButtonBoundingClientRect()
if (menuButton) {
this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight
}
} catch (e) {
console.error('获取系统信息失败:', e)
}
},
// 检查登录状态
checkLoginStatus() {
try {
const userInfo = wx.getStorageSync('userInfo')
const token = wx.getStorageSync('token')
if (userInfo && token) {
this.globalData.userInfo = userInfo
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = userInfo.purchasedSections || []
this.globalData.hasFullBook = userInfo.hasFullBook || false
}
} catch (e) {
console.error('检查登录状态失败:', e)
}
},
// 加载书籍数据
async loadBookData() {
try {
// 先从缓存加载
const cachedData = wx.getStorageSync('bookData')
if (cachedData) {
this.globalData.bookData = cachedData
}
// 从服务器获取最新数据
const res = await this.request('/api/book/all-chapters')
if (res && res.data) {
this.globalData.bookData = res.data
wx.setStorageSync('bookData', res.data)
}
} catch (e) {
console.error('加载书籍数据失败:', e)
}
},
// 检查更新
checkUpdate() {
if (wx.canIUse('getUpdateManager')) {
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate((res) => {
if (res.hasUpdate) {
console.log('发现新版本')
}
})
updateManager.onUpdateReady(() => {
wx.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
success: (res) => {
if (res.confirm) {
updateManager.applyUpdate()
}
}
})
})
updateManager.onUpdateFailed(() => {
wx.showToast({
title: '更新失败,请稍后重试',
icon: 'none'
})
})
}
},
// 统一请求方法
request(url, options = {}) {
return new Promise((resolve, reject) => {
const token = wx.getStorageSync('token')
wx.request({
url: this.globalData.baseUrl + url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
...options.header
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
// 未授权,清除登录状态
this.logout()
reject(new Error('未授权'))
} else {
reject(new Error(res.data?.message || '请求失败'))
}
},
fail: (err) => {
reject(err)
}
})
})
},
// 登录方法 - 获取openId用于支付
async login() {
try {
// 获取微信登录code
const loginRes = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject
})
})
console.log('[App] 获取登录code成功')
try {
// 发送code到服务器获取openId
const res = await this.request('/api/miniprogram/login', {
method: 'POST',
data: { code: loginRes.code }
})
if (res.success && res.data) {
// 保存openId
if (res.data.openId) {
this.globalData.openId = res.data.openId
wx.setStorageSync('openId', res.data.openId)
console.log('[App] 获取openId成功')
}
// 保存用户信息
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('[App] API登录失败使用模拟登录:', apiError.message)
}
// API不可用时使用模拟登录
return this.mockLogin()
} catch (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() {
const mockUser = {
id: 'user_' + Date.now(),
nickname: '卡若',
phone: '15880802661',
avatar: '',
referralCode: 'SOUL' + Date.now().toString(36).toUpperCase().slice(-6),
purchasedSections: [],
hasFullBook: false,
earnings: 0,
pendingEarnings: 0,
referralCount: 0,
createdAt: new Date().toISOString()
}
const mockToken = 'mock_token_' + Date.now()
// 保存用户信息
this.globalData.userInfo = mockUser
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = mockUser.purchasedSections
this.globalData.hasFullBook = mockUser.hasFullBook
wx.setStorageSync('userInfo', mockUser)
wx.setStorageSync('token', mockToken)
console.log('模拟登录成功:', mockUser)
return { user: mockUser, token: mockToken }
},
// 手机号登录
async loginWithPhone(phoneCode) {
try {
// 尝试API登录
const res = await this.request('/api/wechat/phone-login', {
method: 'POST',
data: { code: phoneCode }
})
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
wx.setStorageSync('userInfo', res.data.user)
wx.setStorageSync('token', res.data.token)
return res.data
}
} catch (e) {
console.log('手机号API登录失败使用模拟登录:', e)
}
// 回退到模拟登录
return this.mockLogin()
},
// 退出登录
logout() {
this.globalData.userInfo = null
this.globalData.isLoggedIn = false
this.globalData.purchasedSections = []
this.globalData.hasFullBook = false
wx.removeStorageSync('userInfo')
wx.removeStorageSync('token')
},
// 检查是否已购买章节
hasPurchased(sectionId) {
if (this.globalData.hasFullBook) return true
return this.globalData.purchasedSections.includes(sectionId)
},
// 获取章节总数
getTotalSections() {
return this.globalData.totalSections
},
// 切换TabBar
switchTab(index) {
this.globalData.currentTab = index
},
// 显示Toast
showToast(title, icon = 'none') {
wx.showToast({
title,
icon,
duration: 2000
})
},
// 显示Loading
showLoading(title = '加载中...') {
wx.showLoading({
title,
mask: true
})
},
// 隐藏Loading
hideLoading() {
wx.hideLoading()
}
})