Flask路由间Session值无法持久化问题排查求助
问题描述
开发Flask应用时,在payment_callback路由中设置了session['response_status'],但在payment_result路由中读取该值时始终为None。日志显示payment_callback中session已正确设置,但跳转后无法获取。
代码片段
@app.route('/payment/callback', methods=['POST']) def payment_callback(): global processed_transactions try: data = get_request_data() logger.info(f"Received data: {data}") response_status = decode_if_bytes(data.get('payment_result', {}).get('response_status')) if response_status: session['response_status'] = response_status logger.info(f"Callback session data: {dict(session)}") else: logger.warning("Response status not found in the received data.") return redirect(url_for('payment_result')) except Exception as e: logger.error(f"Error querying transaction: {e}") flash('Error querying transaction', 'danger') return redirect(url_for('index')) @app.route('/payment/return', methods=['GET', 'POST']) def payment_return(): time.sleep(3) logger.info('------return--------') return redirect(url_for('payment_result')) @app.route('/payment/result') def payment_result(): print('I want to get response status') response_status = session.get('response_status') print(response_status) # Output is NONE----------------- here is the problem return render_template('Payment_Result.html')
日志输出
INFO:main:Received data: {'tran_ref': 'TST2417201883637', 'merchant_id': 78035, 'profile_id': 137790, 'cart_id': 'card_123', 'cart_description': 'Sample Payment', 'cart_currency': 'EGP', 'cart_amount': '10.00', 'tran_currency': 'EGP', 'tran_total': '10.00', 'tran_type': 'Sale', 'tran_class': 'ECom', 'customer_details': {'name': 'shady Henawy wardy', 'email': 'shadywardy@gmail.com', 'street1': 'Cairo', 'city': 'Cairo', 'state': 'GZ', 'country': 'EG', 'zip': '11637', 'ip': '197.49.115.176'}, 'payment_result': {'response_status': 'A', 'response_code': 'G01271', 'response_message': 'Authorised', 'acquirer_ref': 'TRAN0001.66745A17.0003D57B', 'cvv_result': ' ', 'avs_result': ' ', 'transaction_time': '2024-06-20T16:34:31Z'}, 'payment_info': {'payment_method': 'Visa', 'card_type': 'Credit', 'card_scheme': 'Visa', 'payment_description': '4000 00## #### 0002', 'expiryMonth': 6, 'expiryYear': 2029}, 'ipn_trace': 'IPNS0001.66745A17.00005B19'} INFO:main:Callback session data: {'response_status': 'A'} INFO:werkzeug:127.0.0.1 - - [20/Jun/2024 19:34:38] "POST /payment/callback HTTP/1.1" 302 - INFO:main:------return-------- INFO:werkzeug:127.0.0.1 - - [20/Jun/2024 19:34:41] "GET /payment/return HTTP/1.1" 302 - INFO:werkzeug:127.0.0.1 - - [20/Jun/2024 19:34:41] "GET /payment/result HTTP/1.1" 200 - I want to get response status None
核心原因
问题出在会话隔离:payment_callback是支付网关服务器发起的POST请求,这个请求的session属于网关的会话,和用户浏览器的会话完全无关。你在callback里设置的session是网关请求的session,而用户访问payment_result时用的是自己浏览器的session,自然读不到之前设置的值。
修复方案
不能用session传递跨会话的数据,改用以下两种常用方案:
方案1:用交易标识存储状态到数据库/缓存
- 在
payment_callback中,将response_status和交易ID(比如tran_ref)一起存入数据库或Redis缓存 - 在
payment_result中,通过用户会话里的交易ID(发起支付时应该已存在用户session)去查询状态
示例代码:
# 假设用SQLAlchemy存储交易状态 from models import Transaction # 自定义的交易模型 @app.route('/payment/callback', methods=['POST']) def payment_callback(): global processed_transactions try: data = get_request_data() logger.info(f"Received data: {data}") tran_ref = data.get('tran_ref') response_status = decode_if_bytes(data.get('payment_result', {}).get('response_status')) if response_status and tran_ref: # 存入数据库 transaction = Transaction.query.filter_by(tran_ref=tran_ref).first() if transaction: transaction.response_status = response_status db.session.commit() logger.info(f"Updated transaction {tran_ref} status to {response_status}") else: logger.warning("Response status or tran_ref not found in the received data.") return redirect(url_for('payment_result')) except Exception as e: logger.error(f"Error querying transaction: {e}") flash('Error querying transaction', 'danger') return redirect(url_for('index')) @app.route('/payment/result') def payment_result(): print('I want to get response status') # 从用户session获取之前存储的交易ID tran_ref = session.get('current_tran_ref') response_status = None if tran_ref: transaction = Transaction.query.filter_by(tran_ref=tran_ref).first() if transaction: response_status = transaction.response_status print(response_status) return render_template('Payment_Result.html', response_status=response_status)
方案2:通过redirect传递参数(适合非敏感状态)
如果response_status不是敏感信息,可以在payment_callback重定向时将其作为参数传递,然后payment_result从请求参数中获取:
@app.route('/payment/callback', methods=['POST']) def payment_callback(): global processed_transactions try: data = get_request_data() logger.info(f"Received data: {data}") response_status = decode_if_bytes(data.get('payment_result', {}).get('response_status')) if response_status: # 重定向时携带参数 return redirect(url_for('payment_result', status=response_status)) else: logger.warning("Response status not found in the received data.") return redirect(url_for('payment_result')) except Exception as e: logger.error(f"Error querying transaction: {e}") flash('Error querying transaction', 'danger') return redirect(url_for('index')) @app.route('/payment/result') def payment_result(): print('I want to get response status') # 从请求参数获取状态 response_status = request.args.get('status') print(response_status) return render_template('Payment_Result.html', response_status=response_status)
注意:方案2的参数会暴露在URL中,若状态包含敏感信息不建议使用,优先选方案1。
内容的提问来源于stack exchange,提问作者shady
相关产品推荐
相关产品推荐

