From 966ee31f351bab2895463714d806980c273a55e0 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Fri, 12 Dec 2025 15:38:46 +0800 Subject: [PATCH] update pay ui --- app_vx.py | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/app_vx.py b/app_vx.py index 89688688..a8236adc 100644 --- a/app_vx.py +++ b/app_vx.py @@ -4044,6 +4044,184 @@ def api_login_wechat(): }), 500 +# ============================================ +# 微信获取手机号接口 +# ============================================ +# 缓存微信 access_token +_wechat_access_token_cache = { + 'token': None, + 'expires_at': 0 +} + + +def get_wechat_access_token(): + """ + 获取微信小程序 access_token(带缓存) + access_token 有效期为 7200 秒,提前 5 分钟刷新 + """ + import time + global _wechat_access_token_cache + + current_time = time.time() + + # 如果缓存有效(提前5分钟刷新) + if (_wechat_access_token_cache['token'] and + _wechat_access_token_cache['expires_at'] > current_time + 300): + return _wechat_access_token_cache['token'] + + # 请求新的 access_token + url = 'https://api.weixin.qq.com/cgi-bin/token' + params = { + 'grant_type': 'client_credential', + 'appid': WECHAT_APP_ID, + 'secret': WECHAT_APP_SECRET + } + + try: + response = requests.get(url, params=params, timeout=10) + result = response.json() + + if 'access_token' in result: + _wechat_access_token_cache['token'] = result['access_token'] + _wechat_access_token_cache['expires_at'] = current_time + result.get('expires_in', 7200) + logger.info(f"获取微信 access_token 成功,有效期: {result.get('expires_in', 7200)}秒") + return result['access_token'] + else: + logger.error(f"获取微信 access_token 失败: {result}") + return None + except Exception as e: + logger.error(f"获取微信 access_token 异常: {e}") + return None + + +@app.route('/api/auth/bindphone/wechat', methods=['POST']) +@token_required +def api_bindphone_wechat(): + """ + 微信小程序绑定手机号接口 + + 前端调用 wx.getPhoneNumber 获取 code,传给后端 + 后端用 code 调用微信接口获取手机号,绑定到当前用户 + + 请求参数: + { + "code": "微信返回的动态令牌code" + } + + 返回: + { + "code": 200, + "message": "success", + "data": { + "phone": "138xxxx1234", + "bindcd": true + } + } + """ + try: + user = request.user + data = request.get_json() + code = data.get('code') if data else None + + if not code: + return jsonify({ + 'code': 400, + 'message': '缺少必要的参数 code', + 'data': None + }), 400 + + # 1. 获取 access_token + access_token = get_wechat_access_token() + if not access_token: + return jsonify({ + 'code': 500, + 'message': '获取微信凭证失败,请稍后重试', + 'data': None + }), 500 + + # 2. 调用微信接口获取手机号 + wx_phone_url = f'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}' + payload = {'code': code} + + try: + response = requests.post(wx_phone_url, json=payload, timeout=10) + result = response.json() + logger.info(f"微信获取手机号响应: {result}") + except Exception as e: + logger.error(f"调用微信获取手机号接口异常: {e}") + return jsonify({ + 'code': 500, + 'message': '调用微信接口失败,请稍后重试', + 'data': None + }), 500 + + # 3. 解析微信返回结果 + if result.get('errcode') != 0: + error_msg = result.get('errmsg', '未知错误') + logger.error(f"微信获取手机号失败: errcode={result.get('errcode')}, errmsg={error_msg}") + + # 常见错误码处理 + errcode = result.get('errcode') + if errcode == 40029: + return jsonify({'code': 400, 'message': 'code无效或已过期,请重新获取', 'data': None}), 400 + elif errcode == 40013: + return jsonify({'code': 400, 'message': 'AppID无效', 'data': None}), 400 + elif errcode == -1: + return jsonify({'code': 500, 'message': '微信服务繁忙,请稍后重试', 'data': None}), 500 + else: + return jsonify({'code': 400, 'message': f'获取手机号失败: {error_msg}', 'data': None}), 400 + + phone_info = result.get('phone_info', {}) + phone_number = phone_info.get('phoneNumber') or phone_info.get('purePhoneNumber') + + if not phone_number: + return jsonify({ + 'code': 400, + 'message': '未获取到手机号', + 'data': None + }), 400 + + # 4. 检查手机号是否已被其他用户绑定 + existing_user = User.query.filter( + User.phone == phone_number, + User.phone_confirmed == True, + User.id != user.id + ).first() + + if existing_user: + return jsonify({ + 'code': 400, + 'message': '该手机号已被其他账号绑定', + 'data': None + }), 400 + + # 5. 更新用户手机号 + user.phone = phone_number + user.phone_confirmed = True + user.phone_confirm_time = beijing_now() + db.session.commit() + + logger.info(f"用户 {user.id} 成功绑定手机号: {phone_number[:3]}****{phone_number[-4:]}") + + return jsonify({ + 'code': 200, + 'message': '绑定成功', + 'data': { + 'phone': phone_number, + 'bindcd': True + } + }) + + except Exception as e: + logger.error(f"绑定手机号异常: {e}", exc_info=True) + db.session.rollback() + return jsonify({ + 'code': 500, + 'message': '服务器内部错误', + 'data': None + }), 500 + + @app.route('/api/auth/login/email', methods=['POST']) def api_login_email(): """邮箱登录接口"""