微信支付需要用到微信公众平台账号、微信商户账号。
注册完成后,我们需要在平台找到 appid、mchid、证书等
具体操作:微信支付接入前准备(Native支付)
这里仅演示 签名的生成方法、Authorization的传递以及出现401 Unauthorized
的解决方案。
# _*_ coding: utf-8 _*_
"""
Time: 2022/2/28 14:33
Author: Jyun
Version: V 0.1
File: wxpay.py
Blog: https://ctrlcv.blog.youkuaiyun.com
"""
import time
import json
import random
import string
import requests
from base64 import b64encode
from urllib.parse import urlparse
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import pkcs1_15
from Cryptodome.Hash import SHA256
# 生成二维码
def to_qr(text):
import qrcode
img = qrcode.make(text)
with open('pay.png', 'wb') as f:
img.save(f)
class WXPay:
""" 微信 Native支付
"""
def __init__(self):
self.appid = "wx1fxxxxxc0eeexxx" # APPID
self.mchid = "100000001" # 商户号
self.payment_url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native' # Native支付下单接口
self.refund_url = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds' # 退款接口
self.notify_url = "https://weixin.qq.com/" # 通知url
self.serial_no = '48757XXXX9D9CD835841B45969B0XXXXXXXXXXXX' # 商户证书序列号
self.apiclient_key = './apiclient_key.pem' # 本地证书路径
# 生成签名
def get_sign(self, sign_str):
rsa_key = RSA.importKey(open(self.apiclient_key).read())
signer = pkcs1_15.new(rsa_key)
digest = SHA256.new(sign_str.encode('utf8'))
sign = b64encode(signer.sign(digest)).decode('utf-8')
return sign
def request(self, url: str, method: str, data: dict = None):
data = json.dumps(data) if data else ''
random_str = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32))
timestamp = str(int(time.time()))
sign_str = '\n'.join([
method.upper(), # HTTP请求方法
url.split(urlparse(url).netloc)[-1], # path+args
timestamp, # 时间戳
random_str, # 请求随机串
data, '' # 请求报文主体
]) # 结尾空窜仅用于让后面多一个\n
sign = self.get_sign(sign_str)
headers = {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mchid}",nonce_str="{random_str}",signature="{sign}",timestamp="{timestamp}",serial_no="{self.serial_no}"'
}
response = requests.request(url=url, method=method, data=data, headers=headers)
return response
# 支付
def payment(self, order_no, total, description):
data = {
"mchid": self.mchid,
"out_trade_no": order_no, # 订单号
"appid": self.appid,
"description": description, # 商品描述
"notify_url": self.notify_url,
"amount": {
"total": total, # 总金额(分)
"currency": "CNY"
}
}
return self.request(self.payment_url, 'POST', data)
# 退款
def refund(self, transaction_id, out_refund_no, refund, reason):
data = {
"transaction_id": transaction_id, # 微信支付订单号(交易单号)
"out_refund_no": out_refund_no, # 商户退款单号(商户单号)
"reason": reason, # 退款原因
"notify_url": self.notify_url, # 通知Url
"amount": {
"total": refund, # 订单金额
"refund": refund, # 退款金额(分)
"currency": "CNY"
}
}
return self.request(self.refund_url, 'POST', data)
def application_bill(self):
url = 'https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=2022-02-28&bill_type=ALL'
return self.request(url, 'GET')
if __name__ == "__main__":
wx = WXPay()
resp = wx.payment("80000000018", 1, "订单支付-测试")
print(resp.text)
to_qr(resp.json()['code_url'])
# resp = wx.refund('4200002222222222000000000000', 'T80000000018', 1, '商品已售完')
# print(resp.text)