教程:
1. 前置步骤,需要申请的内容
1. 各种支付产品
2. API密钥生成
参考:
秘钥是通过将http请求方法、请求地址、时间戳、随机生成字符串、请求内容这五个值使用\n进行组合后用证书私钥进行加密得到。
注意:
加密后得到签名值需要用utf8解码为字符串。
请求头中的认证也是有格式的:
认证类型,目前为WECHATPAY2-SHA256-RSA2048
签名信息
发起请求的商户(包括直连商户、服务商或渠道商)的商户号mchid
商户API证书序列号serial_no,用于声明所使用的证书
请求随机串nonce_str,和你上面构造签名串的随机串要保持一致
时间戳timestamp,和你上面构造签名串的时间戳要保持一致
签名值signature,上面算出来的签名值
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转换为字典。