agent-skill-backend/PAYMENT_GUIDE.md

16 KiB
Raw Blame History

支付功能使用指南

📋 概述

本项目已完整接入微信支付支付宝支付,支持内容购买、账户充值等支付场景。


一、配置信息

1.1 微信支付配置

位置:src/main/resources/application-dev.ymlsrc/main/resources/application-prod.yml

payment:
  wechat:
    appId: wx7d13d99de5be3bfa          # 微信应用 ID
    mchId: 1673321732                    # 商户号
    mchKey: UDuZXDcmy5Eb6o0nTNZhu6ek4DDh4K8B  # 商户密钥
    mchSerialNo: 5EFC47D3AA59BFD1AAE548F96B5E19E1C60F067D  # 商户证书序列号
    privateKeyPath: apiclient_key.pem    # 商户私钥文件路径
    domain: https://api.mch.weixin.qq.com  # 微信服务器地址
    notifyUrl: http://127.0.0.1:19001/api/pay/wx/notify  # 支付回调地址
    returnUrl: http://127.0.0.1:19001/api/pay/success    # 支付成功跳转地址

1.2 支付宝支付配置

payment:
  alipay:
    appId: 2021004138642603              # 支付宝应用 ID
    publicKey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnakP04nUmsoFoveIvOhbLqkA1xQuYtvkrqq2AVvTsbtqpsEOTm9G095e2rBYLp89oDcf6L6BhtJPwdrhnA+qifUyVmACI9sprrsGeRYQgndK7y4c6spQcSnsnakSxlIp22j7pvBXNAZuqud2hQV+TOLKEUh1W3izTgMj/Ejoh3ZsCjgDRtTVgaytzSdHYrhNku+pIrl15/xVGJED99RYXkR8GHawxuK+vWVmxU0tiTCwTsqLz43v6TtCZ+/UfLL/luwp9B4ZvB+0qon82LILYr6oxs10kE2IAvryuDToAc1s/v/36jgt+7DXwqzfUDksHhVLHdJHChyc4ax5HmMsBwIDAQAB"  # 支付宝公钥
    privateKey: ""                       # 商户私钥(需配置)
    notifyUrl: http://127.0.0.1:19001/api/pay/alipay/trade/notify  # 支付回调地址
    returnUrl: https://shuziren.xueai.art/alipay-success  # 支付成功跳转地址
    signType: RSA2                       # 签名类型
    charset: UTF-8                       # 字符编码
    gatewayUrl: https://openapi.alipay.com/gateway.do  # 支付宝网关

二、API 接口说明

2.1 创建微信支付订单

接口地址: POST /api/pay/wx/create

请求参数:

参数 类型 必填 说明
orderNo String 订单号(不传则自动生成)
userId Long 用户 ID
userName String 用户名
amount BigDecimal 支付金额(单位:元)
productName String 商品名称
productDesc String 商品描述
businessId Long 关联业务 ID
businessType String 业务类型recharge(充值)/purchase_content(购买内容)

请求示例:

curl -X POST http://localhost:19001/api/pay/wx/create \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 123,
    "userName": "张三",
    "amount": 0.01,
    "productName": "测试商品",
    "productDesc": "商品描述",
    "businessId": 456,
    "businessType": "purchase_content"
  }'

响应示例:

{
  "code": 200,
  "message": "success",
  "data": {
    "code_url": "weixin://wxpay/bizpayurl?pr=xxx",
    "order_no": "ORDER_20260331_001"
  }
}

字段说明:

  • code_url: 微信支付二维码链接,前端可使用此链接生成二维码
  • order_no: 支付订单号,用于后续查询订单状态

2.2 创建支付宝支付订单

接口地址: POST /api/pay/alipay/create

请求参数: 与微信支付相同

请求示例:

curl -X POST http://localhost:19001/api/pay/alipay/create \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 123,
    "userName": "张三",
    "amount": 0.01,
    "productName": "测试商品",
    "businessId": 456,
    "businessType": "purchase_content"
  }'

响应示例:

{
  "code": 200,
  "message": "success",
  "data": "<form name=\"alipay\" method=\"post\" action=\"https://openapi.alipay.com/gateway.do\">...</form>"
}

使用说明:

  • 返回的是 HTML 表单字符串
  • 前端将此外壳写入页面后会自动提交跳转到支付宝支付页面

2.3 支付回调接口(系统自动处理)

微信支付回调

接口地址: POST /api/pay/wx/notify

说明:

  • 由微信支付服务器自动调用
  • 处理支付结果并更新订单状态
  • 返回 XML 格式响应给微信服务器

支付宝支付回调

异步回调: POST /api/pay/alipay/trade/notify

同步回调: GET /api/pay/alipay/trade/return

说明:

  • 异步回调由支付宝服务器自动调用
  • 同步回调用于用户支付完成后跳转回指定页面

三、使用场景示例

3.1 场景 1购买付费内容

业务流程:

  1. 用户点击购买按钮
  2. 创建购买记录(待支付状态)
  3. 创建支付订单
  4. 用户扫码或跳转支付
  5. 支付成功后更新订单和购买记录状态

前端调用示例:

// 步骤 1创建购买记录
const purchaseResponse = await fetch('/api/content/purchase', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: 123,
    contentId: 456,
    payType: 3  // 3=微信支付4=支付宝支付
  })
});

const purchaseResult = await purchaseResponse.json();

// 步骤 2创建支付订单
const payResponse = await fetch('/api/pay/wx/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: 123,
    amount: 9.99,
    productName: 'AI 技能教程',
    productDesc: '高级 AI 技能培训内容',
    businessId: 456,
    businessType: 'purchase_content'
  })
});

const payResult = await payResponse.json();

if (payResult.code === 200) {
  // 步骤 3显示支付二维码
  const codeUrl = payResult.data.code_url;
  // 使用 QRCode 库生成二维码
  new QRCode(document.getElementById('qrcode'), {
    text: codeUrl,
    width: 200,
    height: 200
  });
  
  // 步骤 4轮询查询订单状态
  const checkOrderStatus = setInterval(async () => {
    const statusResponse = await fetch(`/api/pay/order/query?orderNo=${payResult.data.order_no}`);
    const statusResult = await statusResponse.json();
    
    if (statusResult.data.status === 2) {  // 2=已支付
      clearInterval(checkOrderStatus);
      alert('支付成功!');
      window.location.reload();
    }
  }, 3000);  // 每 3 秒查询一次
  
  // 设置超时15 分钟后停止查询)
  setTimeout(() => {
    clearInterval(checkOrderStatus);
    alert('支付超时,请重新下单');
  }, 900000);
}

3.2 场景 2账户充值

业务流程:

// 创建充值订单
const rechargeResponse = await fetch('/api/pay/wx/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: 123,
    amount: 100.00,  // 充值 100 元
    productName: '账户充值',
    businessId: 123,
    businessType: 'recharge'  // 充值业务类型
  })
});

const result = await rechargeResponse.json();
if (result.code === 200) {
  // 显示支付二维码
  showQRCode(result.data.code_url);
}

说明:

  • 支付成功后,系统会自动通过回调将充值金额添加到用户账户余额

3.3 场景 3支付宝网页支付

// 创建支付宝订单
const aliPayResponse = await fetch('/api/pay/alipay/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: 123,
    amount: 0.01,
    productName: '测试商品',
    businessId: 456,
    businessType: 'purchase_content'
  })
});

const result = await aliPayResponse.json();
if (result.code === 200) {
  // 将返回的 HTML 表单写入页面
  document.body.innerHTML = result.data;
  // 表单会自动提交,跳转到支付宝支付页面
}

四、前端集成完整示例

4.1 微信支付页面

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>微信支付</title>
  <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.0/build/qrcode.min.js"></script>
  <style>
    .payment-container {
      max-width: 600px;
      margin: 50px auto;
      padding: 20px;
      text-align: center;
    }
    .qrcode-box {
      border: 1px solid #ddd;
      padding: 20px;
      display: inline-block;
      margin: 20px 0;
    }
    .status-tip {
      color: #666;
      font-size: 14px;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div class="payment-container">
    <h2>微信支付</h2>
    <div class="qrcode-box">
      <div id="qrcode"></div>
    </div>
    <p class="status-tip">请使用微信扫码支付</p>
    <p class="status-tip" id="statusTip">等待支付...</p>
  </div>

  <script>
    // 从 URL 参数获取商品信息
    const urlParams = new URLSearchParams(window.location.search);
    const productId = urlParams.get('productId');
    const amount = urlParams.get('amount');

    // 创建支付订单
    async function createPayment() {
      try {
        const response = await fetch('/api/pay/wx/create', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            userId: 123,  // 实际应从登录信息中获取
            amount: parseFloat(amount),
            productName: '商品-' + productId,
            businessId: productId,
            businessType: 'purchase_content'
          })
        });

        const result = await response.json();
        if (result.code === 200) {
          // 生成二维码
          QRCode.toCanvas(document.getElementById('qrcode'), result.data.code_url, {
            width: 200,
            height: 200
          });
          
          // 开始轮询订单状态
          checkOrderStatus(result.data.order_no);
        } else {
          alert('创建订单失败:' + result.message);
        }
      } catch (error) {
        console.error('创建订单失败:', error);
        alert('网络错误,请稍后重试');
      }
    }

    // 轮询订单状态
    async function checkOrderStatus(orderNo) {
      const maxAttempts = 300;  // 最多查询 300 次15 分钟)
      let attempts = 0;

      const timer = setInterval(async () => {
        try {
          const response = await fetch(`/api/pay/order/query?orderNo=${orderNo}`);
          const result = await response.json();
          
          if (result.code === 200 && result.data) {
            const status = result.data.status;
            const statusText = document.getElementById('statusTip');
            
            if (status === 2) {  // 已支付
              clearInterval(timer);
              statusText.textContent = '✅ 支付成功!';
              statusText.style.color = 'green';
              
              setTimeout(() => {
                window.location.href = '/pay-success.html?orderNo=' + orderNo;
              }, 1000);
              return;
            } else if (status === 3 || status === 4) {  // 支付失败或已取消
              clearInterval(timer);
              statusText.textContent = '❌ 支付失败';
              statusText.style.color = 'red';
              return;
            }
            
            attempts++;
            if (attempts >= maxAttempts) {
              clearInterval(timer);
              statusText.textContent = '⏰ 支付超时,请重新下单';
              statusText.style.color = 'orange';
            }
          }
        } catch (error) {
          console.error('查询订单状态失败:', error);
        }
      }, 3000);  // 每 3 秒查询一次
    }

    // 页面加载时创建支付订单
    createPayment();
  </script>
</body>
</html>

4.2 支付宝支付页面

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>支付宝支付</title>
</head>
<body>
  <div style="text-align: center; padding: 50px;">
    <h2>正在跳转到支付宝支付页面...</h2>
    <p>请稍候</p>
  </div>

  <script>
    // 从 URL 获取商品信息
    const urlParams = new URLSearchParams(window.location.search);
    const productId = urlParams.get('productId');
    const amount = urlParams.get('amount');

    // 创建支付订单
    async function createAlipay() {
      try {
        const response = await fetch('/api/pay/alipay/create', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            userId: 123,
            amount: parseFloat(amount),
            productName: '商品-' + productId,
            businessId: productId,
            businessType: 'purchase_content'
          })
        });

        const result = await response.json();
        if (result.code === 200) {
          // 将返回的 HTML 表单写入页面并提交
          document.body.innerHTML = result.data;
          document.forms[0].submit();
        } else {
          alert('创建订单失败:' + result.message);
          setTimeout(() => {
            window.location.href = '/index.html';
          }, 2000);
        }
      } catch (error) {
        console.error('创建订单失败:', error);
        alert('网络错误,请稍后重试');
      }
    }

    createAlipay();
  </script>
</body>
</html>

五、核心代码文件

文件路径 说明
src/main/java/com/kexue/skills/controller/PayController.java 支付接口控制器
src/main/java/com/kexue/skills/service/PayService.java 支付服务接口
src/main/java/com/kexue/skills/service/impl/PayServiceImpl.java 支付服务实现类
src/main/java/com/kexue/skills/config/PaymentConfig.java 支付配置类
src/main/java/com/kexue/skills/entity/PaymentOrder.java 支付订单实体类
src/main/java/com/kexue/skills/service/PaymentOrderService.java 支付订单服务接口
src/main/java/com/kexue/skills/service/impl/PaymentOrderServiceImpl.java 支付订单服务实现类

六、注意事项

6.1 开发环境测试

⚠️ 重要提示: 本地开发环境下,微信和支付宝无法直接访问到你的 localhost 回调地址。

解决方案:

  1. 使用内网穿透工具(推荐)

    # 使用 ngrok
    ngrok http 19001
    
    # 将生成的域名配置到回调地址
    # 例如https://abc123.ngrok.io/api/pay/wx/notify
    
  2. 部署到测试服务器

    • 直接部署到具有公网 IP 的服务器进行测试
  3. 修改配置文件

    payment:
      wechat:
        notifyUrl: https://your-domain.ngrok.io/api/pay/wx/notify
      alipay:
        notifyUrl: https://your-domain.ngrok.io/api/pay/alipay/trade/notify
    

6.2 金额单位

  • 前端传入金额单位:
  • 微信支付内部转换:(代码自动处理)
  • 建议最小金额0.01 元

6.3 业务类型说明

业务类型 说明 支付成功后操作
recharge 账户充值 增加用户账户余额
purchase_content 购买内容 更新内容购买记录状态

6.4 支付状态码

状态码 说明
1 待支付
2 已支付
3 支付失败
4 已取消
5 已退款

6.5 安全建议

  1. 权限验证:所有支付接口都应添加权限验证(已部分实现)
  2. 签名验证:确保支付回调的签名验证正确
  3. 幂等性处理:支付回调应支持重复通知(已实现)
  4. 日志记录:完整的支付日志便于问题排查

七、常见问题

Q1: 如何查询订单状态?

// 通过订单号查询
PaymentOrder order = paymentOrderService.queryByOrderNo(orderNo);

// 或通过主键查询
PaymentOrder order = paymentOrderService.queryById(orderId);

Q2: 如何处理支付回调失败?

系统已实现幂等性处理,同一订单多次回调不会重复处理。如果回调失败,微信/支付宝会按一定频率重试。

Q3: 如何申请退款?

当前版本暂未实现退款功能,如需退款,需要:

  1. 调用微信/支付宝退款 API
  2. 更新支付订单状态为已退款5
  3. 恢复用户余额或购买权限

Q4: 测试时使用真实资金吗?

是的,对接的是正式环境。如需测试环境,需要:

  • 微信:申请沙箱环境
  • 支付宝:使用测试账号

八、技术支持

如遇问题,请检查以下内容:

  1. 配置文件中的商户号、密钥是否正确
  2. 回调地址是否可被外网访问
  3. 商户私钥文件是否存在且路径正确
  4. 查看日志文件中的详细错误信息
  5. 确认微信/支付宝商户号状态正常

文档版本: v1.0
更新时间: 2026-03-31
维护人员: 系统开发团队