|
@@ -0,0 +1,500 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 支付工具函数
|
|
|
|
|
+ * 封装微信支付相关逻辑
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+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<PaymentResult> => {
|
|
|
|
|
+ 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<boolean> => {
|
|
|
|
|
+ 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<number, number[]> = 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<PaymentResult>,
|
|
|
|
|
+ maxRetries: number = 3,
|
|
|
|
|
+ delay: number = 1000
|
|
|
|
|
+): Promise<PaymentResult> => {
|
|
|
|
|
+ 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<number, PaymentStatus> = new Map()
|
|
|
|
|
+
|
|
|
|
|
+ private constructor() {}
|
|
|
|
|
+
|
|
|
|
|
+ static getInstance(): PaymentStateManager {
|
|
|
|
|
+ if (!PaymentStateManager.instance) {
|
|
|
|
|
+ PaymentStateManager.instance = new PaymentStateManager()
|
|
|
|
|
+ }
|
|
|
|
|
+ return PaymentStateManager.instance
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置支付状态
|
|
|
|
|
+ */
|
|
|
|
|
+ setPaymentState(orderId: number, status: PaymentStatus): void {
|
|
|
|
|
+ this.state.set(orderId, status)
|
|
|
|
|
+ console.log(`订单 ${orderId} 支付状态更新为: ${status}`)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取支付状态
|
|
|
|
|
+ */
|
|
|
|
|
+ getPaymentState(orderId: number): PaymentStatus | undefined {
|
|
|
|
|
+ return this.state.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)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取所有支付状态
|
|
|
|
|
+ */
|
|
|
|
|
+ getAllPaymentStates(): Map<number, PaymentStatus> {
|
|
|
|
|
+ return new Map(this.state)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 支付超时处理
|
|
|
|
|
+ * @param orderId 订单ID
|
|
|
|
|
+ * @param timeout 超时时间(毫秒)
|
|
|
|
|
+ * @returns 超时Promise
|
|
|
|
|
+ */
|
|
|
|
|
+export const createPaymentTimeout = (timeout: number = 30000): Promise<PaymentResult> => {
|
|
|
|
|
+ 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 }
|
|
|
|
|
+}
|