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)

 

posted @ 2023-08-01 15:02  Wchime  阅读(183)  评论(0编辑  收藏  举报