ai_v/blueprints/payment.py
24024 acaa5d4fd8 fix(payment): 增加支付回调签名验证日志及异常处理
- 在支付宝同步回调(payment_return)中添加详细日志记录
- 添加同步回调缺少签名参数的错误处理
- 同步回调中移除sign和sign_type再进行签名验证
- 同步回调异常捕获,返回500错误并记录详细异常信息
- 在支付宝异步通知(payment_notify)中添加详细日志记录
- 异步通知缺少签名参数时返回fail并记录错误日志
- 异步通知验证签名失败或交易状态异常时返回fail并记录错误日志
- 异步通知中增加已处理订单状态跳过处理并返回success的逻辑
- 异步通知异常捕获时回滚数据库事务并返回fail
- 支付宝服务(AlipayService)的签名验证方法增加日志记录和异常捕获
- 签名验证时移除不参与验证的sign和sign_type字段,防止验证错误
2026-01-14 19:48:00 +08:00

155 lines
5.7 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
# 验证签名前,先记录关键信息
logger.info(f"验证订单号: {data.get('out_trade_no')}")
logger.info(f"支付宝交易号: {data.get('trade_no')}")
logger.info(f"支付金额: {data.get('total_amount')}")
# 从数据中移除sign参数以进行验证
verify_data = data.copy()
verify_data.pop('sign', None)
verify_data.pop('sign_type', None) # 也要移除sign_type
alipay_service = AlipayService()
success = alipay_service.verify_notify(verify_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"