import datetime
import json
import os
import random
import string
import time
import OpenSSL
import base64
import requests
from urllib.parse import urlparse
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# V3版支付
class WxPayV3(object):
basedir = os.path.abspath(os.path.dirname(__file__)) # 获取当前目录
def __init__(self, ignore_resp_sign=False):
"""
:param ignore_resp_sign: 是否忽略应答验签(用于第一次缓存证书)
"""
self.app_id = 'wx14bcda174b'
self.mch_id = '1626238'
self.api_v3_key = 'Hyper_20220344566244_hyper'
self.serial_no = '4F74DF09B282ED821206A' # 证书序列号(微信支付商户平台获取)
self.ignore_resp_sign = ignore_resp_sign
self.certificates_path = self.basedir + '/certificates.json'
self.get_certificates_from_wx()
# 检查证书是否需要更新
# if not os.path.exists(self.certificates_path):
# # 如果证书文件不存在,则获取证书
# print("证书文件不存在,则获取证书")
# else:
# # 判断证书是否过期,如果过期则更新证书
# print("判断证书是否过期,如果过期则更新证书")
# with open(self.certificates_path, 'r') as f:
# content = f.read()
# print(content)
# certificates = json.loads(content)
# for certificate in certificates:
# expiry_time = certificate.get('expire_time')
# if expiry_time and datetime.datetime.now() > datetime.datetime.strptime(expiry_time,'%Y-%m-%dT%H:%M:%SZ'):
# print("证书过期,更新证书")
# self.get_certificates_from_wx() # 证书过期,更新证书
# break
# 支付签名
@staticmethod
def sign(s):
# 这里的key为示例
# api_client_key = ("-----BEGIN PRIVATE KEY-----\n"
# "MIIEvQIBADANBgkqhkiG9w0BAQEFAAIBAQDAPM45XP7PC6qe\n"
# "MIIEvQIBADANBgkqhkiG9w0BAgEAAoIBAQDAP7PC6qe\n"
# "Kkh/7UGUD/hxkPwNB2PKAms=\n"
# "-----END PRIVATE KEY-----")
current_dir = os.path.dirname(os.path.abspath(__file__))
# 构建 apiclient_key.pem 文件的完整路径
pem_file_path = os.path.join(current_dir, 'apiclient_key.pem')
# 打开文件
with open(pem_file_path, 'r') as f:
# 这里要注意的秘钥只能有三行
# -----BEGIN PRIVATE KEY-----
# ******************秘钥只能在一行,不能换行*****************
# -----END PRIVATE KEY-----
api_client_key = f.read()
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, api_client_key)
signature = base64.b64encode(OpenSSL.crypto.sign(pkey=pkey, data=s.encode(), digest='SHA256'))
return signature.decode()
# 请求签名
def _auth(self, req):
data = req.method + '\n'
parsed = urlparse(req.url)
data += parsed.path
if parsed.query:
data += "?" + parsed.query
data += '\n'
timestamp = str(int(time.time()))
data += timestamp + '\n'
nonce_str = ''.join(random.sample(string.ascii_letters + string.digits, 32))
data += nonce_str + '\n'
if req.data:
data += req.data
data += '\n'
signature = self.sign(data)
authorization = ('WECHATPAY2-SHA256-RSA2048 '
'mchid="{0}",nonce_str="{1}",'
'signature="{2}",timestamp="{3}",'
'serial_no="{4}"').format(self.mch_id,
nonce_str,
signature,
timestamp,
self.serial_no)
req.headers["Authorization"] = authorization
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json'
req.headers['User-Agent'] = 'requests ' + requests.__version__
r = req.prepare()
s = requests.Session()
resp = s.send(r, timeout=2)
# 验签
if not self.ignore_resp_sign:
nonce = resp.headers