from flask import Blueprint, request, redirect, url_for, session, jsonify, render_template from extensions import db from models import Order, User from services.alipay_service import AlipayService import uuid from datetime import datetime import logging logger = logging.getLogger(__name__) payment_bp = Blueprint('payment', __name__, url_prefix='/payment') # 积分价格配置 POINTS_PACKAGES = { '50': {'points': 50, 'amount': 5.00}, '200': {'points': 200, 'amount': 20.00}, '1000': {'points': 1000, 'amount': 100.00}, '5000': {'points': 5000, 'amount': 500.00}, } @payment_bp.route('/create', methods=['POST']) def create_payment(): if 'user_id' not in session: return jsonify({'code': 401, 'msg': '请先登录'}), 401 package_id = request.form.get('package_id') if package_id not in POINTS_PACKAGES: return jsonify({'code': 400, 'msg': '无效的套餐'}), 400 package = POINTS_PACKAGES[package_id] user_id = session['user_id'] # 生成唯一订单号 (时间戳 + 随机位) out_trade_no = datetime.now().strftime('%Y%m%d%H%M%S') + str(uuid.uuid4().hex[:6]) # 创建订单记录 try: order = Order( out_trade_no=out_trade_no, user_id=user_id, amount=package['amount'], points=package['points'], status='PENDING' ) db.session.add(order) db.session.commit() except Exception as e: db.session.rollback() return f"订单创建失败: {str(e)}", 500 # 获取支付链接 try: alipay_service = AlipayService() pay_url = alipay_service.create_order_url( out_trade_no=out_trade_no, total_amount=package['amount'], subject=f"购买{package['points']}积分" ) return redirect(pay_url) except Exception as e: return f"支付链接生成失败: {str(e)}", 500 @payment_bp.route('/return') def payment_return(): """支付成功后的同步跳转页面""" try: logger.info(f"收到支付宝同步回调,参数: {dict(request.args)}") data = request.args.to_dict() signature = data.get("sign") if not signature: logger.error("同步回调缺少签名参数") return "参数错误:缺少签名", 400 alipay_service = AlipayService() # 直接传递原始字典,由 verify_notify 处理 success = alipay_service.verify_notify(data, signature) out_trade_no = data.get('out_trade_no') order = Order.query.filter_by(out_trade_no=out_trade_no).first() if success: logger.info(f"同步回调验证成功,订单号: {out_trade_no}") # 同步通知仅用于页面展示,实际业务逻辑在异步通知 notify 中处理 return render_template('buy.html', success=True, order=order) else: logger.error(f"同步回调验证失败,订单号: {out_trade_no}") return "支付验证失败", 400 except Exception as e: logger.error(f"处理同步回调时发生异常: {str(e)}", exc_info=True) return f"处理支付回调失败: {str(e)}", 500 @payment_bp.route('/notify', methods=['POST']) def payment_notify(): """支付宝异步通知""" try: logger.info(f"收到支付宝异步通知,参数: {request.form.to_dict()}") data = request.form.to_dict() signature = data.get("sign") # 不要pop,保留原始数据 if not signature: logger.error("异步通知缺少签名参数") return "fail" alipay_service = AlipayService() success = alipay_service.verify_notify(data, signature) if success and data.get('trade_status') in ['TRADE_SUCCESS', 'TRADE_FINISHED']: out_trade_no = data.get('out_trade_no') trade_no = data.get('trade_no') logger.info(f"异步通知验证成功,订单号: {out_trade_no}, 支付宝交易号: {trade_no}") order = Order.query.filter_by(out_trade_no=out_trade_no).first() if order and order.status == 'PENDING': order.status = 'PAID' order.trade_no = trade_no order.paid_at = datetime.utcnow() # 给用户加积分 user = User.query.get(order.user_id) if user: user.points += order.points logger.info(f"用户 {user.id} 充值 {order.points} 积分") db.session.commit() logger.info(f"订单 {out_trade_no} 处理成功") return "success" elif order: logger.warning(f"订单 {out_trade_no} 状态为 {order.status},跳过处理") return "success" # 已处理过的订单也返回success else: logger.error(f"未找到订单: {out_trade_no}") return "fail" else: logger.error(f"异步通知验证失败或交易状态异常: {data.get('trade_status')}") return "fail" except Exception as e: logger.error(f"处理异步通知时发生异常: {str(e)}", exc_info=True) db.session.rollback() return "fail"