import io
import json
import os
import time
from urllib.parse import quote
import requests
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from django.conf import settings
from django.utils import timezone
from django_redis import get_redis_connection
from requests_toolbelt import MultipartEncoder
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
APPID = ""
MCHID = ""
NOTIFY_URL = ""
SERIAL_NO = ""
class Pay():
def __init__(self):
# =======【基本信息设置】=====================================
self.appid = APPID
self.mchid = MCHID
self.notify_url = NOTIFY_URL
self.serial_no = SERIAL_NO
def get_head(self, method, uri, data=None):
"""
请求头加密
"""
timestamp = int(time.time())
nonce_str = self.get_nonce_str()
sign_str = f"{method}\n{uri}\n{timestamp}\n{nonce_str}\n"
if data:
sign_str += f'{data}\n'
else:
sign_str += f'\n'
sign = self.get_sign(sign_str)
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': '*/*',
"Authorization": f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mchid}",nonce_str="{nonce_str}",signature="{sign}",timestamp="{timestamp}",serial_no="{self.serial_no}"'
}
return headers
def get_nonce_str(self, length=32):
"""生成随机字符串"""
import random
chars = "abcdefghijklmnopqrstuvwxyz0123456789"
strs = []
for x in range(length):
strs.append(chars[random.randrange(0, len(chars))])
return "".join(strs)
def make_order(self, trade_no, total_amount, openid, desc):
url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi'
data = {
'appid': self.appid, # appid
'mchid': self.mchid, # 商户号
'description': desc, # 商品描述
'out_trade_no': str(trade_no), # 商户订单号
'notify_url': self.notify_url, # 微信支付结果异步通知地址
'amount': {
'total': int(total_amount),
},
"payer": {
"openid": openid
}
}
try:
res = requests.post(url, json=data,
headers=self.get_head(data=json.dumps(data), method='POST',
uri='/v3/pay/transactions/jsapi')).json()
except:
return False, '微信下单失败'
if 'prepay_id' in res:
return True, res['prepay_id']
else:
return False, res['message']
def close_order(self, trade_no):
# 关闭订单
url = f'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{trade_no}/close'
data = {
"mchid": self.mchid
}
res = requests.post(url, json=data,
headers=self.get_head(data=json.dumps(data), method='POST',
uri=f'/v3/pay/transactions/out-trade-no/{trade_no}/close'))
return res
def get_order(self, trade_no):
# 查询订单
url = f'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{trade_no}?mchid={self.mchid}'
res = requests.get(
url, headers=self.get_head(
method='GET',
uri=f'/v3/pay/transactions/out-trade-no/{trade_no}?mchid={self.mchid}'
)
)
print(res.json())
def key_value_url(self, value, urlencode):
"""
将键值对转为 key1=value1&key2=value2
对参数按照key=value的格式,并按照参数名ASCII字典序排序
"""
slist = sorted(value)
buff = []
for k in slist:
v = quote(value[k]) if urlencode else value[k]
buff.append("{0}={1}".format(k, v))
return "&".join(buff)
def get_sign(self, sign_str):
if not settings.DEBUG:
rsa_key = RSA.importKey(open(os.path.join(settings.BASE_DIR, 'shishi/wx_cert/apiclient_key.pem')).read())
else:
rsa_key = RSA.importKey(open(os.path.join(settings.BASE_DIR, 'wx_cert/apiclient_key.pem')).read())
signer = pkcs1_15.new(rsa_key)
digest = SHA256.new(sign_str.encode('utf8'))
sign = base64.b64encode(signer.sign(digest)).decode('utf-8')
return sign
def format(self, prepay_id):
# 给前端格式化
timestamp = int(time.time())
nonce_str = self.get_nonce_str()
package = f'prepay_id={prepay_id}'
sign_type = 'RSA'
data = {
'timeStamp': timestamp,
'nonceStr': nonce_str,
'package': package,
'signType': sign_type,
}
sign_str = f'{self.appid}\n{timestamp}\n{nonce_str}\n{package}\n'
sign = self.get_sign(sign_str)
data['paySign'] = sign
return data
@staticmethod
def decrypt(nonce, ciphertext, associated_data):
key = MY
key_bytes = str.encode(key)
nonce_bytes = str.encode(nonce)
ad_bytes = str.encode(associated_data)
data = base64.b64decode(ciphertext)
aesgcm = AESGCM(key_bytes)
return aesgcm.decrypt(nonce_bytes, data, ad_bytes)
def refunds(self, trade_no, refund_no, money):
# 退款
url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds'
data = {
"out_trade_no": trade_no,
"out_refund_no": refund_no,
"reason": "押金退款",
"notify_url": "https://weixin.qq.com",
"funds_account": "AVAILABLE",
"amount": {
"refund": money,
"total": money,
"currency": "CNY"
}
}
try:
res = requests.post(url, json=data,
headers=self.get_head(data=json.dumps(data), method='POST',
uri='/v3/refund/domestic/refunds')).json()
except Exception as e:
print(e)
return False, '微信退款失败'
if 'message' in res:
return False, res['message']
return True, ''