feat(all): 同步最新进度
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user