From 8a107ee57574e6d64e210eb76720e40bb5b27164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E5=8F=B8git?= <240241002@qq.com> Date: Fri, 23 Jan 2026 15:00:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(payment):=20=E5=90=8C=E6=AD=A5=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E4=B8=AD=E5=A4=84=E7=90=86=E8=AE=A2=E5=8D=95=E7=8A=B6?= =?UTF-8?q?=E6=80=81=EF=BC=8C=E9=98=B2=E6=AD=A2=E5=BC=82=E6=AD=A5=E5=BB=B6?= =?UTF-8?q?=E8=BF=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 同步回调中加锁查询订单,防止并发导致重复发放 - 将订单状态从PENDING更新为PAID,并记录支付交易号和支付时间 - 支付成功时增加用户积分,记录日志 - 异常时回滚事务并记录错误日志 - 异步通知加锁查询订单,确保并发安全 - 更新支付宝支付跳转和通知地址配置 --- blueprints/payment.py | 34 +++++++++++++++++++++++++++++++++- config.py | 4 ++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/blueprints/payment.py b/blueprints/payment.py index 2bc2910..ef74597 100644 --- a/blueprints/payment.py +++ b/blueprints/payment.py @@ -76,6 +76,37 @@ def payment_return(): out_trade_no = data.get('out_trade_no') if success: + # 同步回调也进行订单处理,防止异步回调延迟或失败 + out_trade_no = data.get('out_trade_no') + trade_no = data.get('trade_no') + + try: + # 查询订单 (加锁防止并发导致双重发放) + order = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first() + + # 如果订单存在且状态为PENDING,则更新为PAID + if order and order.status == 'PENDING': + order.status = 'PAID' + order.trade_no = trade_no + order.paid_at = get_bj_now() + + # 增加用户积分 + user = db.session.get(User, order.user_id) + if user: + user.points += order.points + system_logger.info(f"同步回调-订单支付成功", order_id=out_trade_no, points=order.points, user_id=user.id) + + db.session.commit() + elif order: + # 订单已经是完成状态,不做处理 + pass + else: + system_logger.warning(f"同步回调-未找到订单", order_id=out_trade_no) + + except Exception as e: + db.session.rollback() + system_logger.error(f"同步回调-订单状态更新失败: {str(e)}") + return redirect(url_for('auth.buy_page', success='true', out_trade_no=out_trade_no)) else: system_logger.warning(f"支付同步回调验证失败", order_id=out_trade_no) @@ -150,7 +181,8 @@ def payment_notify(): out_trade_no = data.get('out_trade_no') trade_no = data.get('trade_no') - order = Order.query.filter_by(out_trade_no=out_trade_no).first() + # 加锁查询,确保并发安全 + order = Order.query.filter_by(out_trade_no=out_trade_no).with_for_update().first() if order and order.status == 'PENDING': order.status = 'PAID' order.trade_no = trade_no diff --git a/config.py b/config.py index 1421ee6..754f485 100644 --- a/config.py +++ b/config.py @@ -59,8 +59,8 @@ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+BMpGTJMzDoOnjyGh69rDLbV/8rlB ALIPAY_PUBLIC_KEY = """-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlDx4KdOtOQE+tBq6jHKKFenRaRe2gbBnleBk++5gki9IQuxVyZUGTJixstf2gELFHWrGanpnwmGggXsqG+Rm5ZLJOlmFM1k0XeAIDvi6tP/rM+ZDFSu1bMBYtT5vzgVZC7mzIvOp9gsT/puqd3aNZmlviLD0R6OYN0zvFX+5qADZV7A9ziA+nXPFSHreBh7yY/q9ophVZNeHGPoYkDVI5++RrF1cALKOdit0giN5vxpe3ch9z3E6+FZg3LiP+1RW3tMiDQfp/SlVs6bNhLUtmlI5r7+mtFCKDUCEpnQ3S9e0II6rzyVXRyKCFs7qi5YzyhhmO3tJJoe9ilEFyNzfRQIDAQAB -----END PUBLIC KEY-----""" # 支付宝公钥 - ALIPAY_RETURN_URL = "http://331002.xyz:2010/payment/return" # 支付成功跳转地址 - ALIPAY_NOTIFY_URL = "http://331002.xyz:2010/payment/notify" # 支付异步通知地址 + ALIPAY_RETURN_URL = "http://860576.xyz:2010/payment/return" # 支付成功跳转地址 + ALIPAY_NOTIFY_URL = "http://860576.xyz:2010/payment/notify" # 支付异步通知地址 ALIPAY_DEBUG = False # 是否使用沙箱环境 # 开发模式配置