feat(all): 同步最新进度

This commit is contained in:
xingc
2025-04-03 17:21:29 +08:00
parent d6098c8b87
commit 70e0e5b034
24 changed files with 806 additions and 184 deletions

View File

@@ -11,8 +11,11 @@ import time
import httpx
import ujson
import xmltodict
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from fastapi import HTTPException
from simple_spider_tool import md5, jsonpath
from config import settings
from core.base.exceptions import WechatApiError
@@ -22,22 +25,10 @@ from core.db.engine import get_cache
logger = logging.getLogger(__name__)
class WechatApi:
def __init__(self, appid, secret):
self.appid = appid
self.secret = secret
self._access_token = None
self._access_token_expire_time = -1
class BaseClient:
def __init__(self):
self.session = httpx.AsyncClient()
async def access_token(self):
if any([
self._access_token is None,
self._access_token_expire_time < math.floor(time.time()),
]):
await self.get_access_token()
return self._access_token
async def request(self, method, url, headers=None, params=None, data=None, json=None, **kwargs):
r = await self.session.request(
method, url, headers=headers, params=params, data=data, json=json, **kwargs
@@ -62,6 +53,23 @@ class WechatApi:
'POST', url, headers=headers, params=params, data=data, json=json, **kwargs
)
class WechatApi(BaseClient):
def __init__(self, appid, secret):
self.appid = appid
self.secret = secret
self._access_token = None
self._access_token_expire_time = -1
super().__init__()
async def access_token(self):
if any([
self._access_token is None,
self._access_token_expire_time < math.floor(time.time()),
]):
await self.get_access_token()
return self._access_token
async def get_access_token(self):
redis_conn = await get_cache()
result_str = await redis_conn.get('access_token')
@@ -113,39 +121,96 @@ class WechatApi:
wechat_api = WechatApi(appid=settings.APPID, secret=settings.APP_SECRET)
async def decrypt_sensitive_data(session_key: str, encrypted_data: str, iv: str) -> dict:
def decrypt_sensitive_data(session_key: str, encrypted_data: str, iv: str) -> dict:
"""解密敏感数据"""
session_key_bytes = base64.b64decode(session_key)
iv_bytes = base64.b64decode(iv)
encrypted_data_bytes = base64.b64decode(encrypted_data)
cipher = AES.new(session_key_bytes, AES.MODE_CBC, iv_bytes)
# decrypted = cipher.decrypt(encrypted_data_bytes)
# pad = decrypted[-1]
# decrypted = decrypted[:-pad]
decrypted = unpad(cipher.decrypt(encrypted_data_bytes), AES.block_size)
return ujson.decode(decrypted.decode('utf-8'))
def decrypt_phone_number(encrypted_data: str, session_key: str, iv: str) -> dict:
try:
# Base64 解码
session_key_bytes = base64.b64decode(session_key)
encrypted_bytes = base64.b64decode(encrypted_data)
iv_bytes = base64.b64decode(iv)
class WechatPayApi(BaseClient):
def __init__(self, appid, mch_id, app_key, notify_url, debug: bool = False):
"""
:param appid: 小程序id
:param mch_id: 商户id
:param app_key: 接口密钥
:param debug:
"""
self.appid = appid
self.mch_id = mch_id
self.appi_key = app_key
self.notify_url = notify_url
self.base_url = 'https://api.mch.weixin.qq.com'
super().__init__()
# AES 解密
cipher = AES.new(session_key_bytes, AES.MODE_CBC, iv_bytes)
decrypted = unpad(cipher.decrypt(encrypted_bytes), AES.block_size)
decrypted_data = ujson.loads(decrypted.decode('utf-8'))
@staticmethod
def generate_nonce_str() -> str:
"""生成随机字符串"""
return md5(f'{time.time()}')
# 验证数据
if decrypted_data.get("watermark", {}).get("appid") != "your-appid":
raise ValueError("Invalid appid in decrypted data")
return decrypted_data
except Exception as e:
logger.info(e)
# raise HTTPException(status_code=400, detail=f"解密失败: {str(e)}")
def generate_sign(self, params: dict) -> str:
"""生成签名"""
plaintext = '&'.join([f'{k}={params[k]}' for k in sorted(params.keys())])
return md5(f'{plaintext}&key={self.appi_key}').upper()
def generate_payment_params(self, prepay_id) -> dict:
"""生成小程序支付参数"""
params = {
"appId": self.appid,
"timeStamp": f'{int(time.time())}',
"nonceStr": self.generate_nonce_str(),
"package": f"prepay_id={prepay_id}",
"signType": "MD5",
}
params["paySign"] = self.generate_sign(params)
return params
async def create_unified_order(
self,
openid: str,
body: str,
out_trade_no: str,
total_fee: int,
create_ip: str,
):
url = self.base_url + '/pay/unifiedorder'
params = {
'appid': self.appid,
'mch_id': self.mch_id,
'nonce_str': self.generate_nonce_str(),
'body': body,
'out_trade_no': out_trade_no,
'total_fee': total_fee,
'spbill_create_ip': create_ip,
'notify_url': self.notify_url,
'trade_type': 'JSAPI',
'openid': openid,
}
params['sign'] = self.generate_sign(params)
headers = {
'Content-Type': 'application/xml',
}
data = xmltodict.unparse({'xml': params}, full_document=False)
r = await self.post(url, headers=headers, data=data)
if r.status_code != 200:
raise HTTPException(status_code=500, detail='微信支付接口请求失败')
# 解析返回的 XML
result = xmltodict.parse(r.text)
return_code = jsonpath(result, '$.xml.return_code', first=True)
if return_code != 'SUCCESS':
return_msg = jsonpath(result, '$.xml.return_msg', first=True)
raise HTTPException(status_code=500, detail=return_msg)
return result['xml']['prepay_id']
wechat_pay_api = WechatPayApi(
appid=settings.APPID, mch_id=settings.PAY_MCH_ID, app_key=settings.PAY_API_KEY, notify_url=settings.PAY_NOTIFY_URL
)
if __name__ == '__main__':
import asyncio