Administrator
发布于 2025-02-22 / 4 阅读
0

使用python编写微信native支付

教程:

1. 前置步骤,需要申请的内容

2. API密钥生成

参考:

秘钥是通过将http请求方法、请求地址、时间戳、随机生成字符串、请求内容这五个值使用\n进行组合后用证书私钥进行加密得到。

注意:

  1. 加密后得到签名值需要用utf8解码为字符串。

  2. 请求头中的认证也是有格式的:

    1. 认证类型,目前为WECHATPAY2-SHA256-RSA2048

    2. 签名信息

      • 发起请求的商户(包括直连商户、服务商或渠道商)的商户号mchid

      • 商户API证书序列号serial_no,用于声明所使用的证书

      • 请求随机串nonce_str,和你上面构造签名串的随机串要保持一致

      • 时间戳timestamp,和你上面构造签名串的时间戳要保持一致

      • 签名值signature,上面算出来的签名值

    3. body参数必须在一行,不能换行!!,如果你的body是字典,你需要使用json.dumps将字典序列化为json格式的字符串。注意!!!而不是直接用str(body)将字典转换为字符串。

2.1 构建加密内容

    sign_str = '\n'.join([
        HTTP_METHOD.upper(),  # HTTP请求方法
        url.split(urlparse(url).netloc)[-1],  # path+args
        TIME,  # 时间戳
        RANDOM_STRING,  # 请求随机串
        REQUEST_BODY, ''  # 请求报文主体
    ])  # 结尾空窜仅用于让后面多一个\n

2.2 使用秘钥加密

首先安装包:pip install pycryptodomex

然后导入包:

from base64 import b64encode
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import pkcs1_15
from Cryptodome.Hash import SHA256

最后加密:

函数参数:

  • key_path: 秘钥地址。

  • sign_str:2.1中创建的加密内容。

def get_sign(key_path, sign_str):
  rsa_key = RSA.importKey(open(key_path).read())
  signer = pkcs1_15.new(rsa_key)
  digest = SHA256.new(sign_str.encode('utf8'))
  sign = b64encode(signer.sign(digest)).decode('utf-8')
  return sign

3. 发送的请求内容

3.1 订单请求体

    # 订单请求体
    data = {
        "appid": APP_ID, # 小程序AppID
        "mchid": MCH_ID, # 商户号
        "description": "测试",  # 商品描述
        "out_trade_no": order_id,  # 商户订单号(自行随机生成)
        "notify_url": NOTIFY_URL, # 微信支付回调地址
        "support_fapiao": False,  # 不支持开票
        "amount": {
            "total": amount,  # 订单金额,单位为“分”
            "currency": "CNY"  # CNY:人民币
        },
        "settle_info": {
            "profit_sharing": False  # 不支持分账
        }
    }

3.2 订单请求头

    # 构建请求头
    headers = {
        "Content-Type": "application/json",
        "Authorization": f'WECHATPAY2-SHA256-RSA2048 mchid="{MCH_ID}",nonce_str="{nonce_str}",signature="{signature}",timestamp="{timestamp}",serial_no="{MERCHANT_SERIAL_NO}"'
    }

4. 解密成功支付后微信回调报文

参考:

4.1 加密报文格式

AES-GCM是一种NIST标准的认证加密算法, 是一种能够同时保证数据的保密性、 完整性和真实性的一种加密模式。它最广泛的应用是在TLS中。

证书和回调报文使用的加密密钥为APIv3密钥,请参考什么是APIv3密钥?如何获取APIv3密钥

对于加密的数据,我们使用了一个独立的JSON对象来表示。为了方便阅读,示例做了Pretty格式化,并加入了注释。

注意native的response如下:

所以首先要将加密内容从resource中提取出来。

{
  "original_type": "transaction", // 加密前的对象类型
  "algorithm": "AEAD_AES_256_GCM", // 加密算法
  // Base64编码后的密文
  "ciphertext": "...",
  // 加密使用的随机串初始化向量)
  "nonce": "...",
  // 附加数据包(可能为空)
  "associated_data": ""
}

4.2 解密

安装cryptography包。 pip install cryptography 

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)

注意得到的结果是bytes形式的json,所以需要用decode,然后用json将str转换为字典。