diff --git a/app/api/endpoints/wechat.py b/app/api/endpoints/wechat.py index 7e8b88b..ef4a856 100644 --- a/app/api/endpoints/wechat.py +++ b/app/api/endpoints/wechat.py @@ -51,7 +51,7 @@ async def wechat_phone_login( user_code = generate_user_code(db) user = UserDB( - username=f"user_{phone[-4:]}", + nickname=f"user_{phone[-4:]}", phone=phone, user_code=user_code, referral_code=request.referral_code, @@ -90,152 +90,4 @@ async def wechat_phone_login( ) except Exception as e: db.rollback() - return error_response(code=500, message=f"登录失败: {str(e)}") - -@router.post("/pay/order", response_model=ResponseModel) -async def create_wechat_pay_order( - orderid: str, - db: Session = Depends(get_db), - current_user: UserDB = Depends(get_current_user) -): - """创建微信支付订单""" - # 查询订单 - order = db.query(ShippingOrderDB).filter( - ShippingOrderDB.orderid == orderid, - ShippingOrderDB.userid == current_user.userid, - ShippingOrderDB.status == OrderStatus.UNPAID # 只能支付新创建的订单 - ).first() - - if not order: - return error_response(code=404, message="订单不存在或状态不正确") - - # 检查是否已经支付 - if order.pay_status: - return error_response(code=400, message="订单已支付") - - try: - # 获取微信支付客户端 - wx_pay_client = WeChatClient() - - # 创建支付订单 - resp_data = wx_pay_client.create_jsapi_payment( - orderid=orderid, - amount=int(order.final_amount * 100), - openid=current_user.openid, - description=f"蜂快到家-配送订单{orderid}" - ) - - # 更新订单支付信息 - order.prepay_id = resp_data.get("prepay_id") - db.commit() - - # 生成支付参数 - timestamp = str(int(time.time())) - nonce_str = generate_random_string() - package = f"prepay_id={resp_data.get('prepay_id')}" - - # 构建签名数据 - sign_data = f"{settings.WECHAT_APPID}\n{timestamp}\n{nonce_str}\n{package}\n" - signature = wx_pay_client.sign(sign_data.encode()) - - return success_response(data={ - "orderid": orderid, - "payment_params": { - "appId": settings.WECHAT_APPID, - "timeStamp": timestamp, - "nonceStr": nonce_str, - "package": package, - "signType": "RSA", - "paySign": signature - } - }) - - except Exception as e: - db.rollback() - return error_response(code=500, message=f"创建支付订单失败: {str(e)}") - -@router.post("/pay/notify") -async def wechat_pay_notify(request: Request): - """微信支付回调通知""" - try: - # 获取微信支付客户端 - wx_pay_client = WeChatClient() - - # 读取原始请求数据 - body = await request.body() - - # 验证签名 - headers = request.headers - signature = headers.get("Wechatpay-Signature") - timestamp = headers.get("Wechatpay-Timestamp") - nonce = headers.get("Wechatpay-Nonce") - serial_no = headers.get("Wechatpay-Serial") - - if not all([signature, timestamp, nonce, serial_no]): - return error_response(code=400, message="缺少必要的请求头") - - # 验证签名 - sign_str = f"{timestamp}\n{nonce}\n{body.decode()}\n" - if not wx_pay_client.verify_signature( - sign_str.encode(), - signature, - serial_no - ): - return error_response(code=401, message="签名验证失败") - - # 解密数据 - data = json.loads(body) - resource = data.get("resource") - if not resource: - return error_response(code=400, message="缺少资源数据") - - # 解密回调数据 - decrypted_data = wx_pay_client.decrypt_callback( - resource.get("associated_data", ""), - resource.get("nonce", ""), - resource.get("ciphertext", "") - ) - - # 解析解密后的数据 - notify_data = json.loads(decrypted_data) - - # 获取订单信息 - trade_state = notify_data.get("trade_state") - orderid = notify_data.get("out_trade_no") - transaction_id = notify_data.get("transaction_id") - - # 处理支付结果 - if trade_state == "SUCCESS": - # 获取数据库会话 - db = next(get_db()) - try: - # 查询并更新订单 - order = db.query(ShippingOrderDB).filter( - ShippingOrderDB.orderid == orderid, - ShippingOrderDB.pay_status == False # 避免重复处理 - ).first() - - if order: - # 更新订单支付状态 - order.pay_status = True - order.pay_time = datetime.now(timezone.utc) - order.transaction_id = transaction_id - - db.commit() - - return success_response(message="支付成功") - - except Exception as e: - db.rollback() - # 记录错误日志 - print(f"处理支付回调失败: {str(e)}") - return error_response(code=500, message=f"处理失败: {str(e)}") - finally: - db.close() - - return success_response(message="回调处理成功") - - except Exception as e: - # 记录错误日志 - print(f"支付回调异常: {str(e)}") - return error_response(code=500, message=f"回调处理异常: {str(e)}") \ No newline at end of file + return error_response(code=500, message=f"登录失败: {str(e)}") \ No newline at end of file diff --git a/app/core/wechat.py b/app/core/wechat.py index 10f15cc..5cb208f 100644 --- a/app/core/wechat.py +++ b/app/core/wechat.py @@ -23,60 +23,6 @@ class WeChatClient: def __init__(self): self.appid = settings.WECHAT_APPID self.secret = settings.WECHAT_SECRET - self.mchid = settings.WECHAT_MCH_ID - self.private_key = self._load_private_key() - self.cert_serial_no = settings.WECHAT_CERT_SERIAL_NO - self.access_token = None - self.api_v3_key = settings.WECHAT_API_V3_KEY - self.platform_cert = self._load_platform_cert() - - def _load_private_key(self): - """加载商户私钥""" - with open(settings.WECHAT_PRIVATE_KEY_PATH, 'rb') as f: - return serialization.load_pem_private_key( - f.read(), - password=None - ) - - def _load_platform_cert(self): - """加载微信支付平台证书""" - with open(settings.WECHAT_PLATFORM_CERT_PATH, 'rb') as f: - cert_data = f.read() - return load_pem_x509_certificate(cert_data) - - def sign(self, data: bytes) -> str: - """签名数据""" - signature = self.private_key.sign( - data, - padding.PKCS1v15(), - hashes.SHA256() - ) - return base64.b64encode(signature).decode() - - def post(self, path: str, **kwargs) -> requests.Response: - """发送 POST 请求到微信支付接口""" - url = f"https://api.mch.weixin.qq.com{path}" - timestamp = str(int(time.time())) - nonce = generate_random_string() - - # 准备签名数据 - body = kwargs.get('json', '') - body_str = json.dumps(body) if body else '' - sign_str = f"POST\n{path}\n{timestamp}\n{nonce}\n{body_str}\n" - - # 计算签名 - signature = self.sign(sign_str.encode()) - - # 设置认证信息 - auth = f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mchid}",nonce_str="{nonce}",signature="{signature}",timestamp="{timestamp}",serial_no="{self.cert_serial_no}"' - - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': auth - } - - return requests.post(url, headers=headers, **kwargs) async def get_access_token(self): """获取接口调用凭证""" @@ -112,37 +58,6 @@ class WeChatClient: return result.get("phone_info") raise Exception(result.get("errmsg", "获取手机号失败")) - def create_jsapi_payment(self, orderid: str, amount: int, openid: str, description: str) -> dict: - """创建 JSAPI 支付订单 - - Args: - orderid: 订单号 - amount: 支付金额(分) - openid: 用户openid - description: 商品描述 - """ - pay_data = { - "appid": settings.WECHAT_APPID, - "mchid": settings.WECHAT_MCH_ID, - "description": description, - "out_trade_no": orderid, - "notify_url": f"{settings.API_BASE_URL}/api/wechat/pay/notify", - "amount": { - "total": amount, - "currency": "CNY" - }, - "payer": { - "openid": openid - } - } - - # 调用微信支付API - resp = self.post("/v3/pay/transactions/jsapi", json=pay_data) - - if resp.status_code != 200: - raise Exception(f"微信支付下单失败: {resp.json().get('message')}") - - return resp.json() async def code2session(self, code: str) -> dict: """通过 code 获取用户 openid @@ -167,51 +82,4 @@ class WeChatClient: return result raise Exception(result.get("errmsg", "获取openid失败")) - def verify_signature(self, message: bytes, signature: str, serial_no: str) -> bool: - """验证微信支付回调签名 - - Args: - message: 待验证的消息 - signature: 签名字符串 - serial_no: 证书序列号 - """ - if serial_no != self.cert_serial_no: - return False - - try: - # 解码签名 - signature_bytes = base64.b64decode(signature) - - # 使用公钥验证签名 - self.platform_cert.public_key().verify( - signature_bytes, - message, - padding.PKCS1v15(), - hashes.SHA256() - ) - return True - except Exception: - return False - - def decrypt_callback(self, associated_data: str, nonce: str, ciphertext: str) -> str: - """解密回调数据 - - Args: - associated_data: 附加数据 - nonce: 随机串 - ciphertext: 密文 - Returns: - 解密后的明文 - """ - # 解码密文 - encrypted_data = base64.b64decode(ciphertext) - - # 使用 AEAD_AES_256_GCM 算法解密 - aesgcm = AESGCM(base64.b64decode(self.api_v3_key)) - data = aesgcm.decrypt( - nonce.encode(), - encrypted_data, - associated_data.encode() - ) - - return data.decode() \ No newline at end of file + \ No newline at end of file