ai_v/blueprints/payment.py
24024 925da47118 fix(timezone): 调整订单时间显示为北京时间
- 在订单创建时间和支付时间上统一加上8小时的时差偏移
- 修改get_orders和api_payment_history中时间的格式化逻辑,确保时间正确转换
- buy.html和recharge_history.html模板中调整paid_at时间显示,增加时差处理
- auth.py中buy_page接口支持支付成功提示和订单信息传递
- payment.py中同步支付回调改为重定向至充值页并传递成功参数
2026-01-14 20:29:31 +08:00

180 lines
6.6 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}")
# 重定向到充值页面,并带上成功参数
return redirect(url_for('auth.buy_page', success='true', out_trade_no=out_trade_no))
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('/history', methods=['GET'])
def payment_history():
"""获取当前用户的充值历史记录"""
if 'user_id' not in session:
return redirect(url_for('auth.login'))
user_id = session['user_id']
orders = Order.query.filter_by(user_id=user_id).order_by(Order.created_at.desc()).all()
import datetime
return render_template('recharge_history.html', orders=orders, modules={'datetime': datetime})
@payment_bp.route('/api/history', methods=['GET'])
def api_payment_history():
"""API 获取当前用户的充值历史记录"""
if 'user_id' not in session:
return jsonify({'code': 401, 'msg': '请先登录'}), 401
user_id = session['user_id']
orders = Order.query.filter_by(user_id=user_id).order_by(Order.created_at.desc()).all()
from datetime import timedelta
return jsonify({
"orders": [{
"id": o.id,
"out_trade_no": o.out_trade_no,
"amount": float(o.amount),
"points": o.points,
"status": o.status,
"trade_no": o.trade_no,
"created_at": (o.created_at + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'),
"paid_at": (o.paid_at + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S') if o.paid_at else None
} for o in orders]
})
@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"