2025 年最新 Python 服务器端实现微信小程序微信支付详细教程(更新中)

微信支付流程流程图

在这里插入图片描述

微信商户平台 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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

唤醒手腕

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

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

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

打赏作者

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

抵扣说明:

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

余额充值