- 在支付宝同步回调(payment_return)中添加详细日志记录 - 添加同步回调缺少签名参数的错误处理 - 同步回调中移除sign和sign_type再进行签名验证 - 同步回调异常捕获,返回500错误并记录详细异常信息 - 在支付宝异步通知(payment_notify)中添加详细日志记录 - 异步通知缺少签名参数时返回fail并记录错误日志 - 异步通知验证签名失败或交易状态异常时返回fail并记录错误日志 - 异步通知中增加已处理订单状态跳过处理并返回success的逻辑 - 异步通知异常捕获时回滚数据库事务并返回fail - 支付宝服务(AlipayService)的签名验证方法增加日志记录和异常捕获 - 签名验证时移除不参与验证的sign和sign_type字段,防止验证错误
155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
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"
|