本教程将指导你使用最小依赖实现 FastAPI 后端 + Flutter 前端 的微信支付 API v3 接入流程,适用于 App 场景(APP支付,非小程序),支持完整的下单、签名、回调解密与验证。
📌 一、基础准备
微信商户平台设置
-
登录 微信商户平台
-
申请商户号
-
下载:
- API v3 密钥
- 商户私钥(
apiclient_key.pem
) - 平台证书(可后续自动获取)
获取必要参数
参数名 | 说明 |
---|---|
appid | 微信开放平台生成的 AppID |
mchid | 商户号 |
api_v3_key | 商户平台设置的 APIv3 密钥 |
cert_serial_no | 商户证书序列号 |
apiclient_key.pem | 商户私钥 |
🧱 二、系统架构流程图(Mermaid)
🧩 三、FastAPI 后端实现
3.1 依赖安装
最小依赖:
pip install cryptography pycryptodome fastapi uvicorn requests
3.2 私钥签名函数
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import base64
def sign(message: str, private_key_path: str):
with open(private_key_path, "r") as f:
key = RSA.import_key(f.read())
h = SHA256.new(message.encode())
signature = PKCS1_v1_5.new(key).sign(h)
return base64.b64encode(signature).decode()
3.3 构建 Authorization 请求头
import time, uuid
def build_authorization(method, url_path, body, mchid, cert_serial_no, private_key_path):
timestamp = str(int(time.time()))
nonce_str = uuid.uuid4().hex
message = f"{method}\n{url_path}\n{timestamp}\n{nonce_str}\n{body}\n"
signature = sign(message, private_key_path)
token = (
f'mchid="{mchid}",' +
f'nonce_str="{nonce_str}",' +
f'timestamp="{timestamp}",' +
f'serial_no="{cert_serial_no}",' +
f'signature="{signature}"'
)
return f"WECHATPAY2-SHA256-RSA2048 {token}"
3.4 统一下单接口(/v3/pay/transactions/app)
from fastapi import FastAPI, HTTPException
import requests, json
app = FastAPI()
@app.post("/pay/app")
def create_order():
mchid = "your_mchid"
appid = "your_appid"
cert_serial_no = "your_cert_serial"
private_key_path = "./apiclient_key.pem"
url_path = "/v3/pay/transactions/app"
url = f"https://api.mch.weixin.qq.com{url_path}"
out_trade_no = uuid.uuid4().hex[:32]
body_data = {
"mchid": mchid,
"appid": appid,
"description": "测试商品",
"out_trade_no": out_trade_no,
"notify_url": "https://yourdomain.com/pay/callback",
"amount": {"total": 1, "currency": "CNY"}
}
body = json.dumps(body_data, separators=(",", ":"))
headers = {
"Authorization": build_authorization("POST", url_path, body, mchid, cert_serial_no, private_key_path),
"Content-Type": "application/json",
"Accept": "application/json"
}
resp = requests.post(url, headers=headers, data=body)
if resp.status_code != 200:
raise HTTPException(status_code=resp.status_code, detail=resp.text)
prepay_id = resp.json()["prepay_id"]
# 返回给 Flutter 的支付参数
timestamp = str(int(time.time()))
nonce_str = uuid.uuid4().hex
package = "Sign=WXPay"
pay_sign_str = f"{appid}\n{timestamp}\n{nonce_str}\n{prepay_id}\n"
pay_sign = sign(pay_sign_str, private_key_path)
return {
"appid": appid,
"partnerid": mchid,
"prepayid": prepay_id,
"package": package,
"noncestr": nonce_str,
"timestamp": timestamp,
"sign": pay_sign
}
📩 四、支付回调验签 + 解密
4.1 微信回调内容结构
微信支付成功后,POST 请求到你的 notify_url
,其 headers 包含签名字段:
Wechatpay-Signature
Wechatpay-Timestamp
Wechatpay-Nonce
Wechatpay-Serial
4.2 验签函数
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes, serialization
from base64 import b64decode
def verify_signature(timestamp, nonce, body, signature, platform_cert_path):
message = f"{timestamp}\n{nonce}\n{body}\n".encode()
with open(platform_cert_path, "rb") as f:
cert = serialization.load_pem_public_key(f.read())
cert.verify(
b64decode(signature),
message,
padding.PKCS1v15(),
hashes.SHA256()
)
4.3 解密回调报文(AES-GCM)
from Crypto.Cipher import AES
def decrypt_notify(resource, api_v3_key):
ciphertext = base64.b64decode(resource["ciphertext"])
nonce = resource["nonce"].encode()
associated_data = resource["associated_data"].encode()
cipher = AES.new(api_v3_key.encode(), AES.MODE_GCM, nonce=nonce)
cipher.update(associated_data)
return json.loads(cipher.decrypt_and_verify(ciphertext[:-16], ciphertext[-16:]))
4.4 完整回调处理接口
from fastapi import Request
@app.post("/pay/callback")
async def wechat_notify(request: Request):
headers = request.headers
timestamp = headers.get("Wechatpay-Timestamp")
nonce = headers.get("Wechatpay-Nonce")
signature = headers.get("Wechatpay-Signature")
serial = headers.get("Wechatpay-Serial")
body = await request.body()
body_str = body.decode()
# 验签
verify_signature(timestamp, nonce, body_str, signature, "./wechat_platform_cert.pem")
event = json.loads(body_str)
decrypted = decrypt_notify(event["resource"], api_v3_key="your_apiv3_key")
print("✅ 支付成功:", decrypted)
# TODO: 检查 out_trade_no 与订单状态,更新业务
return {"code": "SUCCESS", "message": "成功"}
📱 五、Flutter 端调用示例
使用 wechat_kit
或 fluwx
final result = await wechat.pay(
appId: payParams['appid'],
partnerId: payParams['partnerid'],
prepayId: payParams['prepayid'],
packageValue: payParams['package'],
nonceStr: payParams['noncestr'],
timeStamp: int.parse(payParams['timestamp']),
sign: payParams['sign'],
);
实用小工具
App Store 截图生成器、应用图标生成器 、在线图片压缩和 Chrome插件-强制开启复制-护眼模式-网页乱码设置编码
乖猫记账,AI智能分类的聊天记账。