FastAPI + Flutter 微信支付 API v3 集成教程(App 支付)

本教程将指导你使用最小依赖实现 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)

Flutter FastAPI WeChat 数据库 请求创建支付订单 v3 下单接口(App 场景) 返回 prepay_id 返回签名参数(App 调用) 拉起支付(App内唤起) 支付回调通知 返回 200 确认 更新订单状态 Flutter FastAPI WeChat 数据库

🧩 三、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_kitfluwx

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智能分类的聊天记账。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@井九

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值