微信支付流程流程图
微信商户平台 API 证书
链接地址:https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage
获取 Authorization 签名案例
问题:请求参数里带Body参数(包体参数)如何计算签名?
使用商户 API 证书私钥生成签名
Authorization 签名获取文档:https://pay.weixin.qq.com/doc/v3/merchant/4012365336
商户号:1900007291
证书序列号:408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB
测试私钥 ./certificates/wechat_private_key_template.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAptpm+qvIDCh/9wjU26SQCK26ogYkBhDrYxnAaw2JbbBsp1oD
bHKk+1r381NeBUG2HEFAuU+Fr72u5ot3yKdzoF/FajAzQNKnm569/D3upKoi8mYB
aST15Uig8j8qoUW1U217LL0jEHlSnHV3lcaDTXqDpTRR4Bfz9IqOgJgFZ8/oTfEo
mSrjrLYef81Eyxr7ZIMQXEKKEK7V4UXKS0+/fDsiG/cXidhzt8UbTL9vqXqxM2+I
DImyO+FAc/tkBG55LmzxPto1Nq0WbnZzRM/wTzrd0I/8NlevxtFbphg4evlHjFNI
7+GrqR87ViEwuAJJ9Je5QQjct5YJfFRWiZ5CMQIDAQABAoIBAGBi/GhEgezcHIg1
ltlHaFlLGuxsRbUnYwM9phVxnXk7GJlYe2/TjpERjPkIqOC6hBwwadZjJORP3FCc
Mtc8PKRhjuZ377O7vU0915x2nnyLOGL1IE2AJ3iLi0ZFzTea0FPgg+5lWHM00s9F
YI6qPcGtS41M+xtMWwZiYE3TBBRibHiY8ugGyaNAhiMKehyW05uApjlIF55wwCGx
BkyESJpGRR/6853iHke6Ge+xVcMa9QmQdoH0QqL/8kT28PL568mJJr0Ow/83t4+d
Pe70YPzKAxgUnaDsHJqO+b8qH69AEs8rTI5h2Mon6pH+bJT66KUoiXhn+Kf+4LSs
henRP10CgYEA1QJSfuFOWVRjrg3N/rAIc/Ak84BTZavbyrkqBSuoTs9i/nMI/hOz
VxpDntg7Bx2Tctl6sZO3GioTxKdc/YYaTKci1TKBbeginpsqEQVgwkMCy8HpvUmR
fyAMqLwZC4h9+j+NiZtuoFJDTCgv+WYbasX+kWYEUM21bnSYuO7yEQsCgYEAyIdP
r9uzqPgzN34Tmx+CNTa16VjhBh+zkBtXRLDLhWBeIYxoYNJARD98Pb1XZdvpkZZW
Sk7MfaKo2/DomzyyyB/MbHWwAdFi3yb4y7uMJfyC1MzdUSNN3Vp579hJxHkJ+nN4
Ys76yfcEeVOLnvUT1Z0KKCdIWRdT1Lgi+X1itzMCgYBJUXlPzwGG4fNFj97d0X23
Wmt9nSgXkOYgi0eZbAOMzPmIF9R6kBFk49dur4Lx2g5Ms+r1gKC/0sfnIqxxX11i
EQ1+UNoYGJUB/uql3TIG68XkmKR50P7RwRhaZBRC0gJ6xrFTMjsL2ATuC88niyvY
vrn3FiRaI9RVZrDCxwxvLQKBgEXW4okEAqGBuAzGqztmkOnJoTehDdYdKmOxMgap
cGiGdKJIjX3THDDoz3ONQyglnEZpTqpYoV3MTfU0BT8zt6x9bqwDnQY1D7NalmIW
cqw0Mri8lQQSQKcsQLWo5aA466G/n5kCL1Qx5OwAjesRvhOyuvvbGpZ0ymyWqQ+t
fLkDAoGATcul1L8y5D/wNVP1GXbXMZfBsFP3bbqy8c+Ashm6g8OLm2mGNntd5Z6h
1KkID7Yksh+dZ6t7XaPBtGACXX5Eryr537JVvdX8hAVCp5HVtaN/9VBVP8Ka2e4s
VS/xeNgOMQ7uzhRPBJ8HiTmdI1nHhDnYQpGiBgQn0Z5RAkSvFMk=
-----END RSA PRIVATE KEY-----
Python 实现测试源码
import json
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_sign_string():
# 构建待签名的字符串
# 将字典转化为 JSON 字符串
body = {
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"description": "Image形象店-深圳腾大-QQ公仔",
"out_trade_no": "1217752501201407033233368018",
"notify_url": "https://www.weixin.qq.com/wxpay/pay.php",
"amount": {
"total": 100,
"currency": "CNY"
},
"payer": {
"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
}
}
json_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
print(json_str)
# 构建签名字符串
sign_string = (
"POST\n"
"/v3/pay/transactions/jsapi\n"
"1554208460\n"
"593BEC0C930BF1AFEB40B4A08C8FB242\n"
f"{json_str}\n"
)
return sign_string
def sign_with_private_key(sign_string, private_key_path):
# 载入私钥
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file.read(), password=None)
# 对待签名字符串进行 SHA256 签名
signature = private_key.sign(
sign_string.encode("utf-8"),
padding.PKCS1v15(), # 修改为从 padding 模块中导入
algorithm=hashes.SHA256()
)
# 对签名结果进行 Base64 编码
base64_signature = base64.b64encode(signature).decode('utf-8')
return base64_signature
def signature_base64():
# 获取待签名字符串
sign_string = generate_sign_string()
# 私钥路径(替换为你本地的私钥路径)
private_key_path = "./certificates/wechat_private_key_template.pem"
# 获取签名并进行 Base64 编码
signature_base64 = sign_with_private_key(sign_string, private_key_path)
print("Base64编码后的签名:", signature_base64)
return signature_base64
print(signature_base64())
运行结果
mI35pfNEQV6777ke/1T+LJLQDNTm7yeoUJH+j/adPGhmCCi0PbgkvYQTRcXH0uibcLVtvFLdGLpmoYO9FV6lBBsTAjuhh5YOvQi0e2g3e0yytitiNET9FEuqM0pjnKfRW4K6LIZDdbWJv9KhZUx3DrJa5TL7OJ7VdADVivxVySIlPVKjGwuCXzuXSJes0UcILgWQUMyha5/3nYofuHtS7r+KYyMuxD+oJ9VM1Qdxk4UIG59CP5Y3wtYIFybyF3bdu1caHTRRX+DLyMXyYA/IrTmiW01c4RPjpHBX5Dk1sZyY1zVsWNsvMHr2e1NTWtBxKJ+qk5N61J7caYoepHFaxw==
微信小程序下单 Python 实现
Authorization 签名获取文档:https://pay.weixin.qq.com/doc/v3/merchant/4012365336
signature_base64 函数实现(authorization.py)
import json
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_sign_string(nonce_str: str, timestamp: str, body: str):
# 构建待签名的字符串
# 将字典转化为 JSON 字符串
json_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
print(json_str)
# 构建签名字符串
sign_string = (
"POST\n"
"/v3/pay/transactions/jsapi\n"
f"{timestamp}\n"
f"{nonce_str}\n"
f"{json_str}\n"
)
return sign_string
def sign_with_private_key(sign_string, private_key_path):
# 载入私钥
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file.read(), password=None)
# 对待签名字符串进行 SHA256 签名
signature = private_key.sign(
sign_string.encode("utf-8"),
padding.PKCS1v15(), # 修改为从 padding 模块中导入
algorithm=hashes.SHA256()
)
# 对签名结果进行 Base64 编码
base64_signature = base64.b64encode(signature).decode('utf-8')
return base64_signature
def signature_base64(nonce_str: str, timestamp: str, body: str):
# 获取待签名字符串
sign_string = generate_sign_string(nonce_str, timestamp, body)
# 私钥路径(替换为你本地的私钥路径)
private_key_path = "../certificates/wechat_private_key.pem"
# 获取签名并进行 Base64 编码
signature_base64 = sign_with_private_key(sign_string, private_key_path)
print("Base64编码后的签名:", signature_base64)
return signature_base64
获取 prepay_id Python 实现(wechat_pay.py)
import random
import time
import aiohttp
import asyncio
import json
import authorization
import os
import struct
def hexdump_random_bytes(num_bytes=16, chunk_size=4):
# 从系统获取 num_bytes 个随机字节
random_bytes = os.urandom(num_bytes)
result = []
# 按照 chunk_size(每组大小)分割字节,逐个解析
for i in range(0, len(random_bytes), chunk_size):
# 将 4 个字节解包为一个无符号 32 位整数
value = struct.unpack('>I', random_bytes[i:i + chunk_size])[0]
# 将整数格式化为 8 位大写十六进制字符串并加入结果列表
result.append(f'{value:08X}')
# 将结果列表中的每个十六进制值用换行符连接成一个字符串并返回
return ''.join(result)
async def post_payment():
url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
body = {
"appid": "【微信小程序 APPID】",
"mchid": "【微信支付 商户号】",
"description": "Image形象店-深圳腾大-QQ公仔",
"out_trade_no": "1217752501201407033233368018",
"time_expire": "2025-06-08T10:34:56+08:00",
"attach": "自定义数据说明",
"notify_url": "https://www.weixin.qq.com/wxpay/pay.php",
"goods_tag": "WXG",
"support_fapiao": True,
"amount": {
"total": 100,
"currency": "CNY"
},
"payer": {
"openid": "【支付者OPENID】"
},
"detail": {
"cost_price": 608800,
"invoice_id": "微信123",
"goods_detail": [
{
"merchant_goods_id": "1246464644",
"wechatpay_goods_id": "1001",
"goods_name": "iPhoneX 256G",
"quantity": 1,
"unit_price": 528800
}
]
},
"scene_info": {
"payer_client_ip": "14.23.150.211",
"device_id": "013467007045764",
"store_info": {
"id": "0001",
"name": "腾讯大厦分店",
"area_code": "440305",
"address": "广东省深圳市南山区科技中一道10000号"
}
},
"settle_info": {
"profit_sharing": False
}
}
timestamp = int(time.time())
nonce_str = hexdump_random_bytes()
signature = authorization.signature_base64(nonce_str=nonce_str, timestamp=timestamp, body=body)
Authorization = f"WECHATPAY2-SHA256-RSA2048 mchid=\"1663832872\",nonce_str=\"{nonce_str}\",signature=\"{signature}\",timestamp=\"{timestamp}\",serial_no=\"469895E514E558B32E9BE3967EA74D15B0934E4B\""
headers = {
"Authorization": Authorization, # 你的授权信息
"Accept": "application/json",
"Content-Type": "application/json"
}
json_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, data=json_str) as response:
if response.status == 200:
result = await response.json()
print("支付请求成功:", result)
else:
print(f"支付请求失败,状态码: {response.status}")
result = await response.text()
print("失败响应:", result)
# 运行异步函数
loop = asyncio.get_event_loop()
loop.run_until_complete(post_payment())
请求成功
{'prepay_id': 'wx11232905584572a33772f17f75d92e······'}
Authorization 签名常见问题
微信支付商户API v3要求请求通过HTTP Authorization头来传递签名。Authorization由认证类型和签名信息两个部分组成。
Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900007291",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="gEuexJ547PHFV77TQ6eiE4tphVYfWfUe1Wc2dBmVnoMYU2rl/M4zhw+b3vBhuMw6AC7pteNkryLA7UWU2h+umo0OdSuuLm1++O3NckQPCSfm6dypsjn4GYm84KMqXWFrhFmyxEwIdEJDr3w1UYfxOcu55OQupfLkrt/ZzuOspnliJFrPzGQFUk7lGqMMtpz3EfbDUNxnVsHblORg3hVmuYNmbGWnS2ovU30Y2Q+iKFDxzkaXBk8LTy6HzvxizRo6Q+J4SVM7O0hKXfgo1QdI68kpzNULb3EVBXlhTyPUzhkHzzLxECL1qHl3HH2hEv8++C+4wBlsagF3j/O6PABojA==",timestamp="1554208460",serial_no="408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB"
常见问题:Http头Authorization认证类型不正确
失败响应: {"code":"SIGN_ERROR","message":"Http头Authorization认证类型不正确"}
常见问题:签名错误,请检查后再试
失败响应: {"code":"SIGN_ERROR","detail":{"detail":{"issue":"sign not match"},"field":"signature","location":"authorization","sign_information":{"method":"POST","sign_message_length":1058,"truncated_sign_message":"···","url":"/v3/pay/transactions/jsapi"}},"message":"签名错误,请检查后再试"}
常见问题:appid和openid不匹配
失败响应: {"code":"PARAM_ERROR","message":"appid和openid不匹配"}
常见问题:该订单已支付
失败响应: {"code":"ORDERPAID","message":"该订单已支付"}
获取 paySign 签名案例
问题:如何创建小程序调起支付签名(获取 paySign)?
官方文档:https://pay.weixin.qq.com/doc/v3/merchant/4012365341
商户号:1900007291
证书序列号:408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB
测试私钥 ./certificates/wechat_private_key_template.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAptpm+qvIDCh/9wjU26SQCK26ogYkBhDrYxnAaw2JbbBsp1oD
bHKk+1r381NeBUG2HEFAuU+Fr72u5ot3yKdzoF/FajAzQNKnm569/D3upKoi8mYB
aST15Uig8j8qoUW1U217LL0jEHlSnHV3lcaDTXqDpTRR4Bfz9IqOgJgFZ8/oTfEo
mSrjrLYef81Eyxr7ZIMQXEKKEK7V4UXKS0+/fDsiG/cXidhzt8UbTL9vqXqxM2+I
DImyO+FAc/tkBG55LmzxPto1Nq0WbnZzRM/wTzrd0I/8NlevxtFbphg4evlHjFNI
7+GrqR87ViEwuAJJ9Je5QQjct5YJfFRWiZ5CMQIDAQABAoIBAGBi/GhEgezcHIg1
ltlHaFlLGuxsRbUnYwM9phVxnXk7GJlYe2/TjpERjPkIqOC6hBwwadZjJORP3FCc
Mtc8PKRhjuZ377O7vU0915x2nnyLOGL1IE2AJ3iLi0ZFzTea0FPgg+5lWHM00s9F
YI6qPcGtS41M+xtMWwZiYE3TBBRibHiY8ugGyaNAhiMKehyW05uApjlIF55wwCGx
BkyESJpGRR/6853iHke6Ge+xVcMa9QmQdoH0QqL/8kT28PL568mJJr0Ow/83t4+d
Pe70YPzKAxgUnaDsHJqO+b8qH69AEs8rTI5h2Mon6pH+bJT66KUoiXhn+Kf+4LSs
henRP10CgYEA1QJSfuFOWVRjrg3N/rAIc/Ak84BTZavbyrkqBSuoTs9i/nMI/hOz
VxpDntg7Bx2Tctl6sZO3GioTxKdc/YYaTKci1TKBbeginpsqEQVgwkMCy8HpvUmR
fyAMqLwZC4h9+j+NiZtuoFJDTCgv+WYbasX+kWYEUM21bnSYuO7yEQsCgYEAyIdP
r9uzqPgzN34Tmx+CNTa16VjhBh+zkBtXRLDLhWBeIYxoYNJARD98Pb1XZdvpkZZW
Sk7MfaKo2/DomzyyyB/MbHWwAdFi3yb4y7uMJfyC1MzdUSNN3Vp579hJxHkJ+nN4
Ys76yfcEeVOLnvUT1Z0KKCdIWRdT1Lgi+X1itzMCgYBJUXlPzwGG4fNFj97d0X23
Wmt9nSgXkOYgi0eZbAOMzPmIF9R6kBFk49dur4Lx2g5Ms+r1gKC/0sfnIqxxX11i
EQ1+UNoYGJUB/uql3TIG68XkmKR50P7RwRhaZBRC0gJ6xrFTMjsL2ATuC88niyvY
vrn3FiRaI9RVZrDCxwxvLQKBgEXW4okEAqGBuAzGqztmkOnJoTehDdYdKmOxMgap
cGiGdKJIjX3THDDoz3ONQyglnEZpTqpYoV3MTfU0BT8zt6x9bqwDnQY1D7NalmIW
cqw0Mri8lQQSQKcsQLWo5aA466G/n5kCL1Qx5OwAjesRvhOyuvvbGpZ0ymyWqQ+t
fLkDAoGATcul1L8y5D/wNVP1GXbXMZfBsFP3bbqy8c+Ashm6g8OLm2mGNntd5Z6h
1KkID7Yksh+dZ6t7XaPBtGACXX5Eryr537JVvdX8hAVCp5HVtaN/9VBVP8Ka2e4s
VS/xeNgOMQ7uzhRPBJ8HiTmdI1nHhDnYQpGiBgQn0Z5RAkSvFMk=
-----END RSA PRIVATE KEY-----
Python 实现测试源码
import json
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_sign_string():
# 构建签名字符串
sign_string = (
"wx2421b1c4370ec43b\n"
"1554208460\n"
"593BEC0C930BF1AFEB40B4A08C8FB242\n"
"prepay_id=wx201410272009395522657a690389285100\n"
)
return sign_string
def sign_with_private_key(sign_string, private_key_path):
# 载入私钥
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file.read(), password=None)
# 对待签名字符串进行 SHA256 签名
signature = private_key.sign(
sign_string.encode("utf-8"),
padding.PKCS1v15(), # 修改为从 padding 模块中导入
algorithm=hashes.SHA256()
)
# 对签名结果进行 Base64 编码
base64_signature = base64.b64encode(signature).decode('utf-8')
return base64_signature
def signature_base64():
# 获取待签名字符串
sign_string = generate_sign_string()
# 私钥路径(替换为你本地的私钥路径)
private_key_path = "./certificates/wechat_private_key_template.pem"
# 获取签名并进行 Base64 编码
signature_base64 = sign_with_private_key(sign_string, private_key_path)
print("Base64编码后的签名:", signature_base64)
return signature_base64
print(signature_base64())
运行结果
mI35pfNEQV6777ke/1T+LJLQDNTm7yeoUJH+j/adPGhmCCi0PbgkvYQTRcXH0uibcLVtvFLdGLpmoYO9FV6lBBsTAjuhh5YOvQi0e2g3e0yytitiNET9FEuqM0pjnKfRW4K6LIZDdbWJv9KhZUx3DrJa5TL7OJ7VdADVivxVySIlPVKjGwuCXzuXSJes0UcILgWQUMyha5/3nYofuHtS7r+KYyMuxD+oJ9VM1Qdxk4UIG59CP5Y3wtYIFybyF3bdu1caHTRRX+DLyMXyYA/IrTmiW01c4RPjpHBX5Dk1sZyY1zVsWNsvMHr2e1NTWtBxKJ+qk5N61J7caYoepHFaxw==
微信小程序端 请求示例
wx.requestPayment
(
{
"timeStamp": "1554208460",
"nonceStr": "593BEC0C930BF1AFEB40B4A08C8FB242",
"package": "prepay_id=wx201410272009395522657a690389285100",
"signType": "RSA",
"paySign": "mI35pfNEQV6777ke/1T+LJLQDNTm7yeoUJH+j/adPGhmCCi0PbgkvYQTRcXH0uibcLVtvFLdGLpmoYO9FV6lBBsTAjuhh5YOvQi0e2g3e0yytitiNET9FEuqM0pjnKfRW4K6LIZDdbWJv9KhZUx3DrJa5TL7OJ7VdADVivxVySIlPVKjGwuCXzuXSJes0UcILgWQUMyha5/3nYofuHtS7r+KYyMuxD+oJ9VM1Qdxk4UIG59CP5Y3wtYIFybyF3bdu1caHTRRX+DLyMXyYA/IrTmiW01c4RPjpHBX5Dk1sZyY1zVsWNsvMHr2e1NTWtBxKJ+qk5N61J7caYoepHFaxw==%",
"success": function(res){},
"fail": function(res){},
"complete": function(res){}
}
)
fastapi 实现微信支付案例
服务器路由:./controller/basic_controller.py
import aiohttp
from fastapi import APIRouter, Request
from fastapi.params import Body
from open import wechat_pay
from service.response import success, error
# 创建路由器实例
router = APIRouter()
@router.post("/login")
async def login(request: Request, params: dict = Body(...)):
code = params.get("code")
params = {
"appid": "【微信小程序 APPID】",
"certificates": "【微信小程序 APP SECRET】",
"js_code": code,
"grant_type": "authorization_code"
}
async with aiohttp.ClientSession() as session:
# 使用 GET 方法获取响应
async with session.get("https://api.weixin.qq.com/sns/jscode2session", params=params) as response:
# 返回响应的文本内容
data = await response.json()
return success(code=200, message="登录成功", data=data)
@router.post("/wechat/pay")
async def login(request: Request, params: dict = Body(...)):
openid = params.get("openid")
amount_total = params.get("amount_total")
description = params.get("description")
data = await wechat_pay.create_wechat_pay(openid=openid, description=description, amount_total=amount_total)
return success(code=200, message="请求成功", data=data)
签名函数:./open/authorization.py
import json
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_sign_string(nonce_str: str, timestamp: str, body: str):
# 构建待签名的字符串
# 将字典转化为 JSON 字符串
json_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
# 构建签名字符串
sign_string = (
"POST\n"
"/v3/pay/transactions/jsapi\n"
f"{timestamp}\n"
f"{nonce_str}\n"
f"{json_str}\n"
)
return sign_string
def sign_with_private_key(sign_string, private_key_path):
# 载入私钥
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file.read(), password=None)
# 对待签名字符串进行 SHA256 签名
signature = private_key.sign(
sign_string.encode("utf-8"),
padding.PKCS1v15(), # 修改为从 padding 模块中导入
algorithm=hashes.SHA256()
)
# 对签名结果进行 Base64 编码
base64_signature = base64.b64encode(signature).decode('utf-8')
return base64_signature
def signature_base64(nonce_str: str, timestamp: str, body: str):
# 获取待签名字符串
sign_string = generate_sign_string(nonce_str, timestamp, body)
# 私钥路径(替换为你本地的私钥路径)
private_key_path = "./certificates/wechat_private_key_template.pem"
# 获取签名并进行 Base64 编码
signature_base64 = sign_with_private_key(sign_string, private_key_path)
print("Base64编码后的签名:", signature_base64)
return signature_base64
签名函数:./open/paysign.py
import json
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generate_sign_string(nonce_str: str, timestamp: str, prepay_id: str):
# 构建签名字符串
sign_string = (
"【微信小程序 APPID】\n"
f"{timestamp}\n"
f"{nonce_str}\n"
f"prepay_id={prepay_id}\n"
)
return sign_string
def sign_with_private_key(sign_string, private_key_path):
# 载入私钥
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file.read(), password=None)
# 对待签名字符串进行 SHA256 签名
signature = private_key.sign(
sign_string.encode("utf-8"),
padding.PKCS1v15(), # 修改为从 padding 模块中导入
algorithm=hashes.SHA256()
)
# 对签名结果进行 Base64 编码
base64_signature = base64.b64encode(signature).decode('utf-8')
return base64_signature
def signature_base64(nonce_str: str, timestamp: str, prepay_id: str):
# 获取待签名字符串
sign_string = generate_sign_string(nonce_str, timestamp, prepay_id)
# 私钥路径(替换为你本地的私钥路径)
private_key_path = "./certificates/wechat_private_key_template.pem"
# 获取签名并进行 Base64 编码
signature_base64 = sign_with_private_key(sign_string, private_key_path)
print("Base64编码后的签名:", signature_base64)
return signature_base64
./open/wechat_pay.py
import time
import aiohttp
import asyncio
import json
from open import authorization, paysign
import os
import struct
def hexdump_random_bytes(num_bytes=16, chunk_size=4):
random_bytes = os.urandom(num_bytes)
result = []
for i in range(0, len(random_bytes), chunk_size):
value = struct.unpack('>I', random_bytes[i:i + chunk_size])[0]
result.append(f'{value:08X}')
return ''.join(result)
async def post_payment(params):
url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
body = {
"appid": params['appid'],
"mchid": params['mchid'],
"description": params['description'],
"out_trade_no": params['out_trade_no'],
"notify_url": params['notify_url'],
"support_fapiao": True,
"amount": {
"total": params['amount_total'],
"currency": "CNY"
},
"payer": {
"openid": params['openid']
},
"settle_info": {
"profit_sharing": False
}
}
signature = authorization.signature_base64(
nonce_str=params['nonce_str'],
timestamp=params['timestamp'],
body=body
)
Authorization = f"WECHATPAY2-SHA256-RSA2048 mchid=\"{params['mchid']}\",nonce_str=\"{params['nonce_str']}\",signature=\"{signature}\",timestamp=\"{params['timestamp']}\",serial_no=\"{params['serial_no']}\""
headers = {
"Authorization": Authorization,
"Accept": "application/json",
"Content-Type": "application/json"
}
json_str = json.dumps(body, ensure_ascii=False, separators=(',', ':'))
async with aiohttp.ClientSession() as session:
try:
async with session.post(url, headers=headers, data=json_str) as response:
if response.status == 200:
result = await response.json()
print("支付请求成功:", result)
return result.get("prepay_id")
else:
result = await response.text()
print(f"支付请求失败,状态码: {response.status}, 错误信息: {result}")
return None
except Exception as e:
print(f"请求发生异常: {str(e)}")
return None
def get_payment_params(params):
return {
"timeStamp": params['timestamp'],
"nonceStr": params['nonce_str'],
"package": f"prepay_id={params['prepay_id']}",
"signType": "RSA",
"paySign": params['pay_sign']
}
async def create_wechat_pay(openid: str, description: str, amount_total: int):
out_trade_no = hexdump_random_bytes()
nonce_str = hexdump_random_bytes()
params = {
'appid': "【微信小程序 APPID】",
'mchid': "【微信商户 ID】",
'description': description,
'out_trade_no': out_trade_no,
'notify_url': "https://www.weixin.qq.com/wxpay/pay.php",
'openid': openid,
'serial_no': "【微信支付证书序列号】",
'amount_total': amount_total,
'nonce_str': nonce_str,
'timestamp': str(int(time.time()))
}
prepay_id = await post_payment(params)
if prepay_id:
pay_sign = paysign.signature_base64(timestamp=params['timestamp'], nonce_str=params['nonce_str'],
prepay_id=prepay_id)
params['prepay_id'] = prepay_id
params['pay_sign'] = pay_sign
return get_payment_params(params)
return None
# 在 __main__ 块中
if __name__ == "__main__":
description = "测试标题"
amount_total = 1000 # 示例金额(单位:分)
loop = asyncio.get_event_loop()
loop.run_until_complete(create_wechat_pay(description=description, amount_total=amount_total))
服务器统一响应:./service/response.py
from typing import Any
from pydantic import BaseModel
# 定义统一的响应格式
class ResponseModel(BaseModel):
code: int
message: str
data: Any
# 创建一个统一的成功响应函数
def success(code: int = 200, data: Any = None, message: str = "请求成功") -> ResponseModel:
return ResponseModel(code=code, message=message, data=data)
# 创建一个统一的失败响应函数
def error(code: int = 400, message: str = "请求失败", data: Any = None) -> ResponseModel:
return ResponseModel(code=code, message=message, data=data)
服务器主程序:app.py
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from controller.basic_controller import router as basic_router
# 创建 FastAPI 实例
app = FastAPI()
# 添加 CORS 中间件,允许跨域访问
app.add_middleware(
CORSMiddleware,
allow_origins="*", # 允许跨域的源
allow_credentials=True, # 允许发送 cookies 等凭证
allow_methods=["*"], # 允许所有 HTTP 方法(GET, POST, PUT 等)
allow_headers=["*"], # 允许所有请求头
)
# 引入控制器中的路由
app.include_router(basic_router)
启动服务器程序
uvicorn app:app --host 0.0.0.0 --port 5000
GitHub 测试案例项目
链接地址:https://github.com/wristwaking/wechat-miniprogram-payment