/** * 支付工具函数 * 封装微信支付相关逻辑 */ import Taro from '@tarojs/taro' /** * 微信支付参数类型 */ export interface WechatPaymentParams { timeStamp: string nonceStr: string package: string signType: string paySign: string } /** * 支付结果类型 */ export interface PaymentResult { success: boolean type?: 'success' | 'cancel' | 'fail' | 'error' message?: string result?: any } /** * 调用微信支付 * @param paymentData 支付参数 * @returns 支付结果 */ export const requestWechatPayment = async (paymentData: WechatPaymentParams): Promise => { try { const result = await Taro.requestPayment({ timeStamp: paymentData.timeStamp, nonceStr: paymentData.nonceStr, package: paymentData.package, signType: paymentData.signType as any, // Taro类型定义问题,使用any绕过 paySign: paymentData.paySign }) return { success: true, type: 'success', result } } catch (error: any) { console.error('微信支付调用失败:', error) // 根据错误码处理不同场景 if (error.errMsg?.includes('cancel')) { return { success: false, type: 'cancel', message: '用户取消支付' } } else if (error.errMsg?.includes('fail')) { return { success: false, type: 'fail', message: '支付失败' } } else { return { success: false, type: 'error', message: error.errMsg || '支付异常' } } } } /** * 验证支付参数 * @param paymentData 支付参数 * @returns 验证结果 */ export const validatePaymentParams = (paymentData: WechatPaymentParams): { valid: boolean; errors: string[] } => { const errors: string[] = [] if (!paymentData.timeStamp) { errors.push('时间戳不能为空') } if (!paymentData.nonceStr) { errors.push('随机字符串不能为空') } if (!paymentData.package) { errors.push('预支付ID不能为空') } if (!paymentData.signType) { errors.push('签名类型不能为空') } if (!paymentData.paySign) { errors.push('签名不能为空') } return { valid: errors.length === 0, errors } } /** * 处理支付金额 * @param amount 金额(元) * @returns 金额(分) */ export const formatPaymentAmount = (amount: number): number => { // 微信支付金额单位为分,需要乘以100 return Math.round(amount * 100) } /** * 检查支付环境 * @returns 是否支持微信支付 */ export const checkPaymentEnvironment = async (): Promise => { try { // 检查是否在微信小程序环境中 if (typeof Taro.requestPayment === 'undefined') { console.error('当前环境不支持微信支付') return false } // 可以添加更多环境检查逻辑 return true } catch (error) { console.error('检查支付环境失败:', error) return false } } /** * 支付安全验证 * @param orderId 订单ID * @param amount 支付金额 * @param paymentParams 支付参数 * @returns 验证结果 */ export const validatePaymentSecurity = ( orderId: number, amount: number, paymentParams: WechatPaymentParams ): { valid: boolean; reason?: string } => { // 验证订单ID if (!orderId || orderId <= 0) { return { valid: false, reason: '订单ID无效' } } // 验证金额 if (!amount || amount <= 0) { return { valid: false, reason: '支付金额无效' } } // 验证支付参数 const paramValidation = validatePaymentParams(paymentParams) if (!paramValidation.valid) { return { valid: false, reason: `支付参数错误: ${paramValidation.errors.join(', ')}` } } // 时间戳验证(防止重放攻击) const timestamp = parseInt(paymentParams.timeStamp) const currentTime = Math.floor(Date.now() / 1000) const timeDiff = Math.abs(currentTime - timestamp) // 时间戳应该在5分钟内有效 if (timeDiff > 300) { return { valid: false, reason: '支付参数已过期,请重新发起支付' } } // 随机字符串长度验证 - 放宽限制以适应微信支付实际返回 if (!paymentParams.nonceStr || paymentParams.nonceStr.length < 8) { return { valid: false, reason: '随机字符串长度无效' } } // 签名类型验证 - 放宽限制以适应微信支付实际返回 if (!paymentParams.signType) { return { valid: false, reason: '签名类型不能为空' } } // 验证签名类型是否支持 const supportedSignTypes = ['RSA', 'HMAC-SHA256'] if (!supportedSignTypes.includes(paymentParams.signType)) { return { valid: false, reason: '签名类型不支持' } } // 预支付ID格式验证 - 放宽限制以适应微信支付实际返回 if (!paymentParams.package) { return { valid: false, reason: '预支付ID不能为空' } } // 签名长度验证 - 放宽限制以适应微信支付实际返回 if (!paymentParams.paySign || paymentParams.paySign.length < 16) { return { valid: false, reason: '签名长度过短' } } return { valid: true } } /** * 生成支付参数哈希(用于防篡改验证) * @param paymentParams 支付参数 * @returns 参数哈希 */ export const generatePaymentParamsHash = (paymentParams: WechatPaymentParams): string => { const paramsString = [ paymentParams.timeStamp, paymentParams.nonceStr, paymentParams.package, paymentParams.signType ].join('&') // 在实际项目中,这里应该使用更安全的哈希算法 // 这里使用简单的哈希作为示例 let hash = 0 for (let i = 0; i < paramsString.length; i++) { const char = paramsString.charCodeAt(i) hash = ((hash << 5) - hash) + char hash = hash & hash // 转换为32位整数 } return Math.abs(hash).toString(16) } /** * 验证支付参数完整性 * @param originalParams 原始支付参数 * @param receivedParams 接收到的支付参数 * @returns 验证结果 */ export const verifyPaymentParamsIntegrity = ( originalParams: WechatPaymentParams, receivedParams: WechatPaymentParams ): { valid: boolean; reason?: string } => { const originalHash = generatePaymentParamsHash(originalParams) const receivedHash = generatePaymentParamsHash(receivedParams) if (originalHash !== receivedHash) { return { valid: false, reason: '支付参数被篡改' } } return { valid: true } } /** * 支付金额一致性验证 * @param expectedAmount 预期金额(元) * @param paymentAmount 支付金额(分) * @returns 验证结果 */ export const validateAmountConsistency = ( expectedAmount: number, paymentAmount: number ): { valid: boolean; reason?: string } => { const expectedInFen = Math.round(expectedAmount * 100) if (expectedInFen !== paymentAmount) { return { valid: false, reason: `金额不一致: 预期 ${expectedInFen} 分,实际 ${paymentAmount} 分` } } return { valid: true } } /** * 支付频率限制检查 */ export class PaymentRateLimiter { private static instance: PaymentRateLimiter private attempts: Map = new Map() private readonly MAX_ATTEMPTS = 5 private readonly TIME_WINDOW = 60000 // 1分钟 private constructor() {} static getInstance(): PaymentRateLimiter { if (!PaymentRateLimiter.instance) { PaymentRateLimiter.instance = new PaymentRateLimiter() } return PaymentRateLimiter.instance } /** * 检查是否超过支付频率限制 */ isRateLimited(orderId: number): { limited: boolean; remainingTime?: number } { const now = Date.now() const attempts = this.attempts.get(orderId) || [] // 清理过期的尝试记录 const recentAttempts = attempts.filter(time => now - time < this.TIME_WINDOW) this.attempts.set(orderId, recentAttempts) if (recentAttempts.length >= this.MAX_ATTEMPTS) { const oldestAttempt = Math.min(...recentAttempts) const remainingTime = this.TIME_WINDOW - (now - oldestAttempt) return { limited: true, remainingTime } } return { limited: false } } /** * 记录支付尝试 */ recordAttempt(orderId: number): void { const attempts = this.attempts.get(orderId) || [] attempts.push(Date.now()) this.attempts.set(orderId, attempts) } /** * 清除支付尝试记录 */ clearAttempts(orderId: number): void { this.attempts.delete(orderId) } } /** * 支付重试逻辑 * @param paymentFn 支付函数 * @param maxRetries 最大重试次数 * @param delay 重试延迟(毫秒) * @returns 支付结果 */ export const retryPayment = async ( paymentFn: () => Promise, maxRetries: number = 3, delay: number = 1000 ): Promise => { let lastError: any = null for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const result = await paymentFn() if (result.success) { return result } // 如果是用户取消,不重试 if (result.type === 'cancel') { return result } lastError = result if (attempt < maxRetries) { console.log(`支付失败,第${attempt}次重试...`) await new Promise(resolve => setTimeout(resolve, delay)) } } catch (error) { lastError = error if (attempt < maxRetries) { console.log(`支付异常,第${attempt}次重试...`) await new Promise(resolve => setTimeout(resolve, delay)) } } } return { success: false, type: 'error', message: `支付失败,已重试${maxRetries}次: ${lastError?.message || '未知错误'}` } } /** * 支付状态枚举 */ export enum PaymentStatus { PENDING = '待支付', PROCESSING = '支付中', SUCCESS = '支付成功', FAILED = '支付失败', REFUNDED = '已退款', CLOSED = '已关闭' } /** * 支付状态管理类 */ export class PaymentStateManager { private static instance: PaymentStateManager private state: Map = new Map() private stateHistory: Map = new Map() private constructor() {} static getInstance(): PaymentStateManager { if (!PaymentStateManager.instance) { PaymentStateManager.instance = new PaymentStateManager() } return PaymentStateManager.instance } /** * 设置支付状态 */ setPaymentState(orderId: number, status: PaymentStatus): void { const previousStatus = this.state.get(orderId) this.state.set(orderId, status) // 记录状态历史 const history = this.stateHistory.get(orderId) || [] history.push({ status, timestamp: Date.now() }) this.stateHistory.set(orderId, history) console.log(`订单 ${orderId} 支付状态流转: ${previousStatus || '初始'} -> ${status}`) } /** * 获取支付状态 */ getPaymentState(orderId: number): PaymentStatus | undefined { return this.state.get(orderId) } /** * 获取支付状态历史 */ getPaymentStateHistory(orderId: number): { status: PaymentStatus; timestamp: number }[] { return this.stateHistory.get(orderId) || [] } /** * 检查是否重复支付 */ isDuplicatePayment(orderId: number): boolean { const currentStatus = this.getPaymentState(orderId) return currentStatus === PaymentStatus.PROCESSING || currentStatus === PaymentStatus.SUCCESS } /** * 清除支付状态 */ clearPaymentState(orderId: number): void { this.state.delete(orderId) this.stateHistory.delete(orderId) } /** * 获取所有支付状态 */ getAllPaymentStates(): Map { return new Map(this.state) } /** * 验证状态流转是否合法 */ validateStateTransition(orderId: number, newStatus: PaymentStatus): { valid: boolean; reason?: string } { const currentStatus = this.getPaymentState(orderId) // 状态流转规则 const allowedTransitions: Record = { [PaymentStatus.PENDING]: [PaymentStatus.PROCESSING, PaymentStatus.CLOSED], [PaymentStatus.PROCESSING]: [PaymentStatus.SUCCESS, PaymentStatus.FAILED, PaymentStatus.CLOSED], [PaymentStatus.SUCCESS]: [PaymentStatus.REFUNDED], [PaymentStatus.FAILED]: [PaymentStatus.PROCESSING, PaymentStatus.CLOSED], [PaymentStatus.REFUNDED]: [], [PaymentStatus.CLOSED]: [] } // 初始状态允许任何流转 if (!currentStatus) { return { valid: true } } // 检查是否允许流转 const allowed = allowedTransitions[currentStatus] || [] if (!allowed.includes(newStatus)) { return { valid: false, reason: `状态流转不合法: ${currentStatus} -> ${newStatus}` } } return { valid: true } } /** * 安全设置支付状态(带验证) */ safeSetPaymentState(orderId: number, status: PaymentStatus): { success: boolean; reason?: string } { const validation = this.validateStateTransition(orderId, status) if (!validation.valid) { return { success: false, reason: validation.reason } } this.setPaymentState(orderId, status) return { success: true } } } /** * 支付超时处理 * @param orderId 订单ID * @param timeout 超时时间(毫秒) * @returns 超时Promise */ export const createPaymentTimeout = (timeout: number = 30000): Promise => { return new Promise((resolve) => { setTimeout(() => { resolve({ success: false, type: 'error', message: '支付超时,请检查网络或重试' }) }, timeout) }) } /** * 支付状态同步 * @param orderId 订单ID * @param expectedStatus 期望状态 * @param maxAttempts 最大尝试次数 * @param interval 检查间隔(毫秒) * @returns 同步结果 */ export const syncPaymentStatus = async ( orderId: number, expectedStatus: PaymentStatus, maxAttempts: number = 10, interval: number = 2000 ): Promise<{ synced: boolean; currentStatus?: PaymentStatus }> => { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { // 这里可以调用后端API查询实际支付状态 // 暂时使用状态管理器模拟 const stateManager = PaymentStateManager.getInstance() const currentStatus = stateManager.getPaymentState(orderId) if (currentStatus === expectedStatus) { return { synced: true, currentStatus } } console.log(`支付状态同步中... 第${attempt}次检查,当前状态: ${currentStatus}`) if (attempt < maxAttempts) { await new Promise(resolve => setTimeout(resolve, interval)) } } catch (error) { console.error(`支付状态同步失败,第${attempt}次尝试:`, error) } } return { synced: false } } /** * 支付状态流转管理器 */ export class PaymentStateFlowManager { private static instance: PaymentStateFlowManager private stateManager: PaymentStateManager private rateLimiter: PaymentRateLimiter private constructor() { this.stateManager = PaymentStateManager.getInstance() this.rateLimiter = PaymentRateLimiter.getInstance() } static getInstance(): PaymentStateFlowManager { if (!PaymentStateFlowManager.instance) { PaymentStateFlowManager.instance = new PaymentStateFlowManager() } return PaymentStateFlowManager.instance } /** * 开始支付流程 */ async startPaymentFlow(orderId: number): Promise<{ success: boolean; reason?: string }> { // 检查频率限制 const rateLimit = this.rateLimiter.isRateLimited(orderId) if (rateLimit.limited) { return { success: false, reason: `支付频率过高,请${Math.ceil(rateLimit.remainingTime! / 1000)}秒后重试` } } // 检查是否重复支付 if (this.stateManager.isDuplicatePayment(orderId)) { return { success: false, reason: '订单正在支付中或已支付成功,请勿重复操作' } } // 设置支付中状态 const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.PROCESSING) if (!setResult.success) { return setResult } // 记录支付尝试 this.rateLimiter.recordAttempt(orderId) return { success: true } } /** * 处理支付成功 */ handlePaymentSuccess(orderId: number): { success: boolean; reason?: string } { const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.SUCCESS) if (setResult.success) { // 清除频率限制记录 this.rateLimiter.clearAttempts(orderId) } return setResult } /** * 处理支付失败 */ handlePaymentFailure(orderId: number): { success: boolean; reason?: string } { const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.FAILED) return setResult } /** * 处理支付取消 */ handlePaymentCancel(orderId: number): { success: boolean; reason?: string } { const setResult = this.stateManager.safeSetPaymentState(orderId, PaymentStatus.CLOSED) if (setResult.success) { // 清除频率限制记录 this.rateLimiter.clearAttempts(orderId) } return setResult } /** * 处理退款 */ handleRefund(orderId: number): { success: boolean; reason?: string } { return this.stateManager.safeSetPaymentState(orderId, PaymentStatus.REFUNDED) } /** * 获取支付状态历史 */ getPaymentHistory(orderId: number): { status: PaymentStatus; timestamp: number }[] { return this.stateManager.getPaymentStateHistory(orderId) } /** * 检查是否可以重试支付 */ canRetryPayment(orderId: number): { canRetry: boolean; reason?: string } { const currentStatus = this.stateManager.getPaymentState(orderId) if (currentStatus === PaymentStatus.FAILED) { return { canRetry: true } } if (currentStatus === PaymentStatus.PROCESSING) { return { canRetry: false, reason: '订单正在支付中,请勿重复操作' } } if (currentStatus === PaymentStatus.SUCCESS) { return { canRetry: false, reason: '订单已支付成功,无需重复支付' } } if (currentStatus === PaymentStatus.CLOSED) { return { canRetry: false, reason: '订单已关闭,无法支付' } } return { canRetry: true } } /** * 重置支付状态 */ resetPaymentState(orderId: number): void { this.stateManager.clearPaymentState(orderId) this.rateLimiter.clearAttempts(orderId) } }