• 微信支付V3版本的Python使用方法


    微信支付自推出V3版本接口以来,体验下来确实比V2有了很大的提升,但介于其官方没有提供Python版本的SDK,故在此记录通过Python3来使用微信支付V3的主要过程。

    1.搞清楚自己的身份

    这一步看似可以忽略,但也需要仔细,笔者之前就犯了一个错,看了不对应的文档。 微信支付的商家有两种身份模式:普通商户、服务商。

    一般的大部分商户都是属于普通商户,只有品牌商等才为服务商,并且两者身份不能互换。 普通商户的API文档: 服务商API文档:

     

    2.配置阶段

    需要在商户端界面申请API证书和配置APIv3密钥,次过程可以参考每一个步骤右侧的查看指引按钮。

    3.构造签名

    V3版本的接口调用需要对请求进行签名,参考 根据官方的文档可以按此步骤生成请求签名。

    class WeixinPayUtil(object):
        @staticmethod
        def signature(private_key_path, sign_str):
            """
            生成签名值
            private_key_path 私钥路径
            sign_str 签名字符串
            https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
            """
            with open(private_key_path) as file:
                private_key = file.read()
            try:
                rsa_key = RSA.import_key(private_key)
                signer = PKCS1_v1_5.new(rsa_key)
                digest = SHA256.new(sign_str.encode('utf-8'))
                return base64.b64encode(signer.sign(digest)).decode('utf-8')
            except Exception:
                raise "WeixinPaySignIError"

    统一下单完整方法

    import time
    import requests
    import string
    import random
    import json
    from Crypto.PublicKey import RSA
    import random
    import base64
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    
    
    class WeixinPayUtil(object):
        @staticmethod
        def signature(private_key_path, sign_str):
            """
            生成签名值
            private_key_path 私钥路径
            sign_str 签名字符串
            https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
            """
            with open(private_key_path) as file:
                private_key = file.read()
            try:
                rsa_key = RSA.import_key(private_key)
                signer = PKCS1_v1_5.new(rsa_key)
                digest = SHA256.new(sign_str.encode('utf-8'))
                return base64.b64encode(signer.sign(digest)).decode('utf-8')
            except Exception:
                raise "WeixinPaySignIError"
    
    
    class WeixinPay(object):
        """
        微信支付
        """
        base_url = 'https://api.mch.weixin.qq.com'
    
        def __init__(self, mch_id, app_id, mch_serial_no, cert_dir, notify_url):
            self.mch_id = mch_id  # 直连商户号
            self.app_id = app_id  # 微信应用ID
            self.mch_serial_no = mch_serial_no  # # 商户API证书序列号
            self.cert_dir = cert_dir  # apiclient_key.pem 地址
            self.notify_url = notify_url  # 回调地址
    
            self.timestamp = str(int(time.time()))
            self.nonce_str = ''.join(random.sample(string.ascii_letters + string.digits, 16))
    
        def _generate_request_sign(self, url_path, data, method='POST'):
            """
            生成请求签名
            """
            sign_list = [method, url_path, self.timestamp, self.nonce_str]
            if data is not None:
                sign_list.append(data)
            else:
                sign_list.append('')
            sign_str = '\n'.join(sign_list) + '\n'
    
            print("sign_str:", sign_str)
            return WeixinPayUtil.signature(private_key_path=self.cert_dir, sign_str=sign_str)
    
        def _generate_pay_sign(self, app_id, package):
            """
            生成支付签名
            """
            sign_list = [app_id, self.timestamp, self.nonce_str, package]
            sign_str = '\n'.join(sign_list) + '\n'
            return WeixinPayUtil.signature(private_key_path=self.cert_dir, sign_str=sign_str)
    
        def _generate_auth_header(self, signature):
            """
            生成授权请求头
            """
            return f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mch_id}",nonce_str="{self.nonce_str}",' \
                   f'signature="{signature}",timestamp="{self.timestamp}",serial_no="{self.mch_serial_no}"'
    
        def unified_order(self, order_id, openid, amount, desc, mch_id=None, notify_url=None, profit_sharing=False,
                          expire_time=None, attach=None, goods_tag=None, detail=None, scene_info=None, currency='CNY'):
            """
            统一下单
            https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
            """
            url_path = '/v3/pay/transactions/jsapi'
            url = self.base_url + url_path
    
            data = {
                'appid': self.app_id,
                'mchid': mch_id if mch_id is not None else self.mch_id,
                'description': desc,
                'out_trade_no': order_id,
                'notify_url': notify_url if notify_url is not None else self.notify_url,
                'settle_info': {
                    'profit_sharing': profit_sharing
                },
                'amount': {
                    'total': amount,
                    'currency': currency
                },
                'payer': {
                    'openid': openid
                }
            }
    
            if attach:
                data.update({'attach': attach})
            if expire_time:
                data.update({'time_expire': expire_time})
            if goods_tag:
                data.update({'goods_tag': goods_tag})
            if detail:
                data.update({'detail': detail})
            if scene_info:
                data.update({'scene_info': scene_info})
    
            data = json.dumps(data)
            signature = self._generate_request_sign(url_path=url_path, data=data)
            print("signature:", signature)
    
            print("Authorization signature:", self._generate_auth_header(signature))
            headers = {'Authorization': self._generate_auth_header(signature), 'Content-Type': 'application/json'}
    
            res = requests.post(url=url, data=data, headers=headers, timeout=10)
    
            print("res:", json.loads(res.content))
    
            # 支付签名
            # pay_sign = self._generate_pay_sign(app_id=self.app_id, package='prepay_id=' + res['prepay_id'])
    
            # return {
            #     'timestamp': self.timestamp,
            #     'nonce_str': self.nonce_str,
            #     'prepay_id': res['prepay_id'],
            #     'sign_type': 'RSA',
            #     'pay_sign': pay_sign
            # }
    
    
    weixinpay = WeixinPay(mch_id="111111111", app_id="wxda22111111111111",
                          mch_serial_no="2906811111111111111111111111111111111111", cert_dir="/tmp/p1/apiclient_key.pem",
                          notify_url="http://www.baidu.com/")
    
    weixinpay.unified_order(order_id="20220623170034664189", openid="ofJ2B1q6_pU11111111111111111", amount=100,
                            desc="test")

    解密

    算法接口的细节,可以参考RFC 5116

    大部分编程语言(较新版本)都支持了AEAD_AES_256_GCM 。开发者可以参考下列的示例,了解如何使用您的编程语言实现解密。

    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    import base64
    
    def decrypt(nonce, ciphertext, associated_data):
        key = "Your32Apiv3Key"
    
        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)
  • 相关阅读:
    Docker部署nginx
    解决网页在手机端适配问题
    记一次Jenkins+Docker+gitlab自动化部署vue
    Docker部署jenkins
    备案
    Jenkins插件使用--Publish Over SSH
    打开root用户ssh登陆
    gitlab配置git
    Dokcer容器内无法域名解析
    vue开发环境搭建
  • 原文地址:https://www.cnblogs.com/zhaoyingjie/p/16408968.html
Copyright © 2020-2023  润新知