This commit is contained in:
aaron 2025-01-22 13:41:01 +08:00
parent 1e22bc4587
commit 66db8f0a1e
2 changed files with 3 additions and 283 deletions

View File

@ -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,
@ -91,151 +91,3 @@ 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)}")

View File

@ -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()