python实现微信扫码支付
import datetime import hashlib import time import json import random import string import requests from base64 import b64encode from urllib.parse import urlparse from Crypto.PublicKey import RSA from Crypto.Signature import pkcs1_15 from Crypto.Hash import SHA256 from cryptography.hazmat.primitives.ciphers.aead import AESGCM import base64 from io import BytesIO # 生成二维码 def to_qr(text): import qrcode img = qrcode.make(text) bIo = BytesIO() img.save(bIo, format='PNG') ig = bIo.getvalue() data = base64.b64encode(ig) data = data.decode('utf-8') # return data # print(data) with open('pay.png', 'wb') as f: img.save(f) return data class WXPay: """ 微信 Native支付 """ def __init__(self): self.appid = "xxxxxxxxxxxxxxxxxx" # APPID self.mchid = "xxxxxxx" # 商户号 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.transfer_url = 'https://api.mch.weixin.qq.com/v3/transfer/batches' # 转账接口 self.notify_url = "https://xxxxxxxxxxxxxxxxxx.com" # 通知url self.serial_no = 'xxxxxxxxxxxxx' # 商户证书序列号 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=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())) # print("---------", url.split(urlparse(url).netloc)[-1]) 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 = { 'Accept': 'application/json;', '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}"', 'Wechatpay-Serial': self.serial_no } response = requests.request(url=url, method=method, data=data, headers=headers) return response def selectorder(self, order_id): url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{}?mchid={}'.format(order_id, self.mchid) # print(url) return self.request(url, 'GET') # 支付 def payment(self, order_no, total, description): now_time = datetime.datetime.now() + datetime.timedelta(days=1) now_time_str = now_time.strftime("%Y-%m-%dT%H:%M:%S+08:00") data = { "mchid": self.mchid, "out_trade_no": order_no, # 订单号 "appid": self.appid, "description": description, # 商品描述 "notify_url": self.notify_url, "time_expire": now_time_str, "amount": { "total": total, # 总金额(分) "currency": "CNY" } } return self.request(self.payment_url, 'POST', data) # 退款 def refund(self, out_trade_no, out_refund_no, refund, reason): data = { "out_trade_no": out_trade_no, # 微信支付订单号(交易单号) "out_refund_no": out_refund_no, # 商户退款单号(商户单号) "reason": reason, # 退款原因 "notify_url": self.notify_url, # 通知Url "amount": { "total": refund, # 订单金额 "refund": refund, # 退款金额(分) "currency": "CNY" } } # print(data) 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') def transfer(self, out_batch_no, batch_name, batch_remark, total_amount, total_num, transfer_detail_list): ''' 商家转账到零钱 :param out_batch_no: 订单号 :param batch_name: 批次名字 :param batch_remark: 批次描述 :param total_amount: 总金额 :param total_num: 总笔数 :param transfer_detail_list: 详情列表 { out_detail_no: 详情订单号 transfer_amount: 金额 transfer_remark': 描述 openid': 用户openid } :return: Response ''' data = { 'appid': self.appid, 'out_batch_no': out_batch_no, 'batch_name': batch_name, 'batch_remark': batch_remark, 'total_amount': total_amount, 'total_num': total_num, 'transfer_detail_list': transfer_detail_list } return self.request(self.transfer_url, 'POST', data) def pay_decrypt(nonce, ciphertext, associated_data): # 支付结果解密 key = "xxxxxxxxxxxxxxxxx" key_bytes = str.encode(key) nonce_bytes = str.encode(nonce) ad_bytes = str.encode(associated_data) data = base64.b64decode(ciphertext) # print(nonce_bytes, data, ad_bytes) aesgcm = AESGCM(key_bytes) return aesgcm.decrypt(nonce_bytes, data, ad_bytes) def order_uuid(): """ """ order_id = str(datetime.datetime.fromtimestamp(time.time())).replace("-", "").replace(" ", "").replace(":","").replace(".", "") + str(random.randint(100, 999)) return order_id if __name__ == "__main__": wx = WXPay() order_uid = order_uuid() print(order_uid) resp = wx.payment(order_uid, 1, "订单支付-测试") print(resp.text) to_qr(resp.json()['code_url']) # --------------------------------------------------------------------------------------------------------- out_batch_no = order_uuid() batch_name = '提现' batch_remark = '用户提现' total_amount = 1 total_num = 1 transfer_detail_list = [ { 'out_detail_no': order_uuid(), 'transfer_amount': 1, 'transfer_remark': '用户提现', 'openid': 'odDp41UknzC5pD37d9EZWkyKCJk0', } ] wx = WXPay() ret = wx.transfer(out_batch_no, batch_name, batch_remark, total_amount, total_num, transfer_detail_list) print(ret.text)