ai_v/blueprints/payment.py
24024 5dc2fbd0e7 fix(payment): 优化支付回调签名验证逻辑
- 取消支付回调中签名前日志冗余输出,简化验证流程
- verify_notify方法中合并签名和数据为空的校验,减少日志行
- 调整sign和sign_type参数移除逻辑,兼容不同通知类型的签名校验
- 增加验证失败时的详细日志,帮助定位公钥配置问题
- 提示可能公钥使用错误,改善错误排查体验
2026-01-14 19:57:43 +08:00

146 lines
5.3 KiB
Python
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.

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"