MBE 微信支付集成指南

更新日期: 2026-02-02
版本: 1.0.0

目录

  1. 概述
  2. 前置准备
  3. 商户号申请
  4. 环境配置
  5. API接口说明
  6. 前端集成
  7. 支付流程
  8. 回调处理
  9. 退款处理
  10. 常见问题

概述

MBE系统支持以下微信支付方式:

支付方式 场景 API类型
Native支付 PC端扫码支付 pay_type: native
H5支付 手机浏览器支付 pay_type: h5
JSAPI支付 微信内H5/小程序 pay_type: jsapi

架构图

┌─────────────────────────────────────────────────────────────┐
│                      MBE 支付系统架构                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   前端应用                                                   │
│   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐      │
│   │ PC Web      │   │ Mobile Web  │   │ 微信内H5    │      │
│   │ (扫码支付)   │   │ (H5支付)    │   │ (JSAPI)    │      │
│   └──────┬──────┘   └──────┬──────┘   └──────┬──────┘      │
│          │                 │                  │             │
│          └────────────────┬┴─────────────────┘             │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              MBE Payment API (/api/payment)          │  │
│   │                                                      │  │
│   │  POST /create          创建支付订单                   │  │
│   │  GET  /status/{id}     查询支付状态                   │  │
│   │  POST /close/{id}      关闭订单                       │  │
│   │  POST /refund          申请退款                       │  │
│   │  GET  /methods         获取支付方式                   │  │
│   │  GET  /plans           获取订阅计划                   │  │
│   └──────────────────────────┬──────────────────────────┘  │
│                              │                              │
│                              ▼                              │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              WeChatPayService                        │  │
│   │              (src/payments/wechat_pay.py)            │  │
│   └──────────────────────────┬──────────────────────────┘  │
│                              │                              │
│                              ▼                              │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              微信支付平台 API V3                      │  │
│   │              api.mch.weixin.qq.com                   │  │
│   └─────────────────────────────────────────────────────┘  │
│                                                             │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              Webhook回调处理                         │  │
│   │              POST /webhook/wechat/pay                │  │
│   │                                                      │  │
│   │  1. 验证签名                                         │  │
│   │  2. 解密数据                                         │  │
│   │  3. 更新订阅状态                                     │  │
│   │  4. 增加Token额度                                    │  │
│   └─────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

前置准备

所需材料

材料 说明
营业执照 企业或个体工商户
法人身份证 正反面照片
对公银行账户 用于结算和验证
联系人手机号 接收验证码
已备案域名 用于H5支付和回调

依赖安装

# 安装微信支付SDK
pip install wechatpayv3

# 或者在 requirements.txt 中添加
echo "wechatpayv3>=1.2.0" >> requirements.txt
pip install -r requirements.txt

商户号申请

申请流程

  1. 访问商户平台

  2. 填写资料

    • 选择主体类型(企业/个体工商户)
    • 上传营业执照
    • 填写法人信息
    • 填写结算账户
  3. 账户验证

    • 微信支付向结算账户打入随机验证款(0.01-1元)
    • 登录商户平台填写验证金额
  4. 签署协议

    • 在线签署《微信支付商户服务协议》
  5. 获取配置信息

    配置项 获取位置
    商户号(mch_id) 账户中心 → 账户信息
    API V3密钥 账户中心 → API安全 → 设置API V3密钥
    商户证书序列号 账户中心 → API安全 → 申请API证书
    商户私钥 申请API证书时下载 apiclient_key.pem

开通支付产品

在商户平台 → 产品中心 开通:

  • Native支付(默认开通)
  • H5支付(需单独申请,设置H5支付域名)
  • JSAPI支付(需绑定公众号/小程序AppID)

环境配置

配置文件

.env.development.env.production 中添加:

# 支付环境
PAYMENT_ENV=sandbox  # sandbox(测试)或 production(生产)

# 支付回调URL(需公网可访问)
PAYMENT_CALLBACK_URL=https://mbe.hi-maker.com

# 微信支付配置
WECHAT_PAY_APP_ID=wx1234567890abcdef
WECHAT_PAY_MCH_ID=1234567890
WECHAT_PAY_API_V3_KEY=your_32_chars_api_v3_key_here_xx

# 证书配置(二选一)
# 方式A: 文件路径
WECHAT_PAY_PRIVATE_KEY_PATH=/path/to/apiclient_key.pem
WECHAT_PAY_CERT_SERIAL=1234567890ABCDEF1234567890ABCDEF12345678

# 方式B: 直接配置内容(Docker/云环境推荐)
WECHAT_PAY_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvg...

配置验证

# 测试配置是否正确
from src.payments.config import get_payment_config

config = get_payment_config()
print(f"WeChat configured: {config.is_wechat_enabled()}")
print(f"Environment: {config.environment}")

API接口说明

1. 创建支付订单

POST /api/payment/create

请求参数

参数 类型 必填 说明
amount float 金额(元)
provider string 支付方式:wechat / alipay
pay_type string 支付类型:native / h5 / jsapi,默认native
plan string 订阅计划:personal / pro / enterprise
description string 商品描述
openid string 条件 JSAPI支付必填,用户OpenID
return_url string H5支付完成跳转URL

请求示例

Native支付(PC扫码)

{
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "native",
    "plan": "personal",
    "description": "MBE个人订阅-1个月"
}

H5支付(手机浏览器)

{
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "h5",
    "plan": "personal",
    "return_url": "https://edu.hi-maker.com/payment/result"
}

JSAPI支付(微信内)

{
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "jsapi",
    "plan": "personal",
    "openid": "oUpF8uN95-Ptaags6E_roPHg7AG0"
}

响应示例

Native支付响应

{
    "success": true,
    "order_id": "user123_personal_20260202120000_abc123",
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "native",
    "code_url": "weixin://wxpay/bizpayurl?pr=xxx",
    "expire_time": "2026-02-02T14:00:00Z",
    "sandbox": false
}

H5支付响应

{
    "success": true,
    "order_id": "user123_personal_20260202120000_abc123",
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "h5",
    "h5_url": "https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=xxx",
    "pay_url": "https://wx.tenpay.com/...",
    "expire_time": "2026-02-02T12:05:00Z"
}

JSAPI支付响应

{
    "success": true,
    "order_id": "user123_personal_20260202120000_abc123",
    "amount": 29.00,
    "provider": "wechat",
    "pay_type": "jsapi",
    "pay_params": {
        "appId": "wx1234567890",
        "timeStamp": "1706860800",
        "nonceStr": "abc123def456",
        "package": "prepay_id=wx...",
        "signType": "RSA",
        "paySign": "xxxxx"
    }
}

2. 查询支付状态

GET /api/payment/status/{order_id}

{
    "success": true,
    "order_id": "user123_personal_20260202120000_abc123",
    "status": "paid",  // pending / paid / failed / closed
    "data": {
        "transaction_id": "4200001234567890",
        "amount": 2900,
        "success_time": "2026-02-02T12:01:00Z"
    }
}

3. 关闭订单

POST /api/payment/close/{order_id}

4. 申请退款

POST /api/payment/refund

{
    "order_id": "user123_personal_20260202120000_abc123",
    "refund_amount": 29.00,
    "reason": "用户申请退款"
}

5. 获取支付方式

GET /api/payment/methods

6. 获取订阅计划

GET /api/payment/plans


前端集成

React/Next.js 示例

1. 支付组件

// components/PaymentButton.tsx
import { useState } from 'react';
import QRCode from 'qrcode.react';

interface PaymentButtonProps {
  plan: string;
  amount: number;
  onSuccess?: () => void;
}

export function PaymentButton({ plan, amount, onSuccess }: PaymentButtonProps) {
  const [loading, setLoading] = useState(false);
  const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);
  const [orderId, setOrderId] = useState<string | null>(null);

  // 检测设备类型
  const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
  const isWechat = /MicroMessenger/i.test(navigator.userAgent);

  // 创建支付订单
  const createPayment = async () => {
    setLoading(true);
    try {
      // 根据环境选择支付类型
      let payType = 'native';
      if (isWechat) {
        payType = 'jsapi';
      } else if (isMobile) {
        payType = 'h5';
      }

      const response = await fetch('/api/payment/create', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-User-ID': getUserId(), // 从登录状态获取
        },
        body: JSON.stringify({
          amount,
          provider: 'wechat',
          pay_type: payType,
          plan,
          return_url: `${window.location.origin}/payment/result`,
        }),
      });

      const data = await response.json();

      if (data.success) {
        setOrderId(data.order_id);

        if (payType === 'native') {
          // PC端显示二维码
          setQrCodeUrl(data.code_url);
          startPolling(data.order_id);
        } else if (payType === 'h5') {
          // H5支付跳转
          window.location.href = data.h5_url;
        } else if (payType === 'jsapi') {
          // 微信内调起支付
          invokeWechatPay(data.pay_params);
        }
      } else {
        alert(data.error || '创建订单失败');
      }
    } catch (error) {
      console.error('Payment error:', error);
      alert('支付请求失败');
    } finally {
      setLoading(false);
    }
  };

  // 轮询支付状态
  const startPolling = (orderId: string) => {
    const interval = setInterval(async () => {
      const res = await fetch(`/api/payment/status/${orderId}`);
      const data = await res.json();
      
      if (data.status === 'paid') {
        clearInterval(interval);
        setQrCodeUrl(null);
        onSuccess?.();
      }
    }, 2000);

    // 5分钟后停止轮询
    setTimeout(() => clearInterval(interval), 5 * 60 * 1000);
  };

  // 微信JSAPI支付
  const invokeWechatPay = (params: any) => {
    if (typeof WeixinJSBridge !== 'undefined') {
      WeixinJSBridge.invoke('getBrandWCPayRequest', params, (res: any) => {
        if (res.err_msg === 'get_brand_wcpay_request:ok') {
          onSuccess?.();
        }
      });
    }
  };

  return (
    <div>
      <button 
        onClick={createPayment} 
        disabled={loading}
        className="bg-green-500 text-white px-6 py-3 rounded-lg"
      >
        {loading ? '处理中...' : `微信支付 ¥${amount}`}
      </button>

      {/* 二维码弹窗 */}
      {qrCodeUrl && (
        <div className="fixed inset-0 bg-black/50 flex items-center justify-center">
          <div className="bg-white p-8 rounded-xl text-center">
            <h3 className="text-lg font-bold mb-4">微信扫码支付</h3>
            <QRCode value={qrCodeUrl} size={200} />
            <p className="mt-4 text-gray-500">请使用微信扫描二维码完成支付</p>
            <button 
              onClick={() => setQrCodeUrl(null)}
              className="mt-4 text-gray-400"
            >
              取消支付
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

2. 支付结果页

// pages/payment/result.tsx
import { useEffect, useState } from 'react';
import { useSearchParams } from 'next/navigation';

export default function PaymentResult() {
  const searchParams = useSearchParams();
  const [status, setStatus] = useState<'loading' | 'success' | 'fail'>('loading');

  useEffect(() => {
    // H5支付返回后查询状态
    const orderId = searchParams.get('order_id');
    if (orderId) {
      checkPaymentStatus(orderId);
    }
  }, [searchParams]);

  const checkPaymentStatus = async (orderId: string) => {
    try {
      const res = await fetch(`/api/payment/status/${orderId}`);
      const data = await res.json();
      setStatus(data.status === 'paid' ? 'success' : 'fail');
    } catch {
      setStatus('fail');
    }
  };

  return (
    <div className="text-center py-20">
      {status === 'loading' && <p>正在确认支付结果...</p>}
      {status === 'success' && (
        <div>
          <h1 className="text-2xl text-green-500">支付成功!</h1>
          <p className="mt-4">您的订阅已生效</p>
        </div>
      )}
      {status === 'fail' && (
        <div>
          <h1 className="text-2xl text-red-500">支付未完成</h1>
          <p className="mt-4">请重试或联系客服</p>
        </div>
      )}
    </div>
  );
}

支付流程

Native支付流程(PC扫码)

用户         前端         MBE API      微信支付
 │           │             │            │
 │─选择计划──▶│             │            │
 │           │─创建订单────▶│            │
 │           │             │─下单请求───▶│
 │           │             │◀──code_url──│
 │           │◀─返回code_url│            │
 │◀─显示二维码│             │            │
 │           │             │            │
 │─扫码支付──────────────────────────────▶│
 │           │             │            │
 │           │◀──轮询状态───│            │
 │           │             │◀──回调通知──│
 │           │             │─验证签名    │
 │           │             │─更新订阅    │
 │           │◀──支付成功───│            │
 │◀─显示成功─│             │            │

H5支付流程(手机浏览器)

用户         前端         MBE API      微信支付      微信APP
 │           │             │            │            │
 │─点击支付──▶│             │            │            │
 │           │─创建订单────▶│            │            │
 │           │             │─下单请求───▶│            │
 │           │             │◀──h5_url────│            │
 │           │◀─返回h5_url─│            │            │
 │◀─跳转URL──│             │            │            │
 │───────────────────────────────────────▶│           │
 │           │             │            │◀──打开微信──│
 │           │             │            │            │◀─支付
 │◀────────────────────────────────return_url────────│
 │─跳回结果页▶│             │            │            │
 │           │─查询状态────▶│            │            │

回调处理

回调URL配置

在微信商户平台配置回调URL:

https://mbe.hi-maker.com/webhook/wechat/pay

回调处理逻辑

MBE系统会自动处理支付回调,执行以下操作:

  1. 验证签名 - 确保回调来自微信
  2. 解密数据 - 解密resource字段获取支付信息
  3. 更新订阅 - 根据订单类型更新用户订阅状态
  4. 增加Token - 根据计划增加Token额度
  5. 记录日志 - 记录支付日志

支付成功后自动处理

# 订单ID格式决定处理方式
# {user_id}_{plan}_{timestamp}_{random} -> 订阅购买
# {user_id}_token_{amount}_{timestamp}_{random} -> Token充值
# {user_id}_expert_{expert_id}_{timestamp}_{random} -> 专家购买
产品类型 处理逻辑
personal/pro/enterprise 升级订阅 + 增加Token额度
token 增加Token额度
expert 开通专家访问权限

退款处理

发起退款

# 通过API发起退款
POST /api/payment/refund
{
    "order_id": "user123_personal_20260202120000_abc123",
    "refund_amount": 29.00,
    "reason": "用户申请退款"
}

退款注意事项

  • 退款金额不能超过原订单金额
  • 退款会原路退回用户支付账户
  • 退款可能需要1-3个工作日到账
  • 退款成功后需要相应扣除用户权益

常见问题

1. 沙箱模式如何测试?

# 设置环境变量
PAYMENT_ENV=sandbox

# 使用模拟支付接口
POST /webhook/payment/simulate/{order_id}?status=success

2. H5支付报"商户未配置域名"?

在微信商户平台 → 产品中心 → H5支付 → 设置H5支付域名

3. 回调收不到通知?

  1. 确保回调URL公网可访问
  2. 检查防火墙是否放行微信IP
  3. 查看服务器日志排查错误

4. 签名验证失败?

  1. 检查API V3密钥是否正确(32位)
  2. 检查商户证书序列号是否正确
  3. 检查私钥文件是否完整

5. 如何区分沙箱和生产环境?

from src.payments.config import get_payment_config
config = get_payment_config()

if config.is_sandbox:
    print("当前是沙箱环境")
else:
    print("当前是生产环境")

相关文档


文档版本: 1.0.0
更新时间: 2026-02-02