Files
1818web-hoduan/POINTS_RECHARGE_GUIDE.md
2025-11-14 17:41:15 +08:00

16 KiB
Raw Blame History

积分充值系统使用指南

📋 功能概述

本系统实现了完整的积分直接购买功能,用户可以通过支付宝/微信支付直接购买积分,无需依赖礼品码。

核心特性

  • 多套餐选择:支持不同价格和数量的积分套餐
  • 首充奖励首次充值额外赠送10%积分
  • 赠送积分:每个套餐可配置赠送积分
  • 支付方式:支持支付宝和微信支付
  • 充值记录:完整的充值历史记录
  • 自动到账:支付成功后自动充值到账
  • 积分有效期可配置积分有效期默认365天

🗂️ 数据库结构

新增表

1. points_package - 积分套餐表

CREATE TABLE `points_package` (
  `id` bigint PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(64) NOT NULL COMMENT '套餐名称',
  `points` int NOT NULL COMMENT '基础积分',
  `bonus_points` int DEFAULT 0 COMMENT '赠送积分',
  `total_points` int NOT NULL COMMENT '总积分',
  `price` decimal(10,2) NOT NULL COMMENT '价格',
  `original_price` decimal(10,2) COMMENT '原价',
  `points_expire_days` int DEFAULT 365 COMMENT '有效期',
  `discount_label` varchar(32) COMMENT '优惠标签',
  `is_hot` tinyint(1) DEFAULT 0 COMMENT '是否热门',
  `is_active` tinyint(1) DEFAULT 1 COMMENT '是否上架'
);

默认数据

套餐名称 积分 赠送 总计 价格 原价
体验包 100 0 100 ¥10 -
标准包 500 50 550 ¥48 ¥50
超值包 1000 150 1150 ¥88 ¥100
豪华包 3000 500 3500 ¥258 ¥300
至尊包 5000 1000 6000 ¥398 ¥500
旗舰包 10000 3000 13000 ¥688 ¥1000

扩展表

2. order 表扩展

新增字段:

ALTER TABLE `order`
ADD COLUMN `order_type` tinyint DEFAULT 1 COMMENT '1-会员订单/2-积分订单',
ADD COLUMN `points_package_id` bigint COMMENT '积分套餐ID',
ADD COLUMN `points_amount` int COMMENT '积分数量';

🔌 API接口文档

用户端接口(/user/points

1. 获取积分套餐列表

接口GET /user/points/packages

权限:公开访问(无需登录)

响应示例

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "id": 2,
      "name": "标准包",
      "description": "日常使用推荐",
      "points": 500,
      "bonusPoints": 50,
      "totalPoints": 550,
      "price": 48.00,
      "originalPrice": 50.00,
      "pointsExpireDays": 365,
      "discountLabel": "赠送50积分",
      "isHot": true,
      "isActive": true
    }
  ]
}

2. 获取热门套餐

接口GET /user/points/packages/hot?limit=3

权限:公开访问

参数

  • limit数量限制默认3

3. 创建充值订单

接口POST /user/points/recharge

权限:需要登录

请求体

{
  "packageId": 2,
  "paymentMethod": 2
}

参数说明

  • packageId套餐ID必填
  • paymentMethod:支付方式(必填)
    • 1 = 支付宝
    • 2 = 微信支付

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "orderNo": "ORD20251021123456",
    "amount": 48.00,
    "pointsAmount": 605,
    "paymentMethod": 2,
    "paymentParams": "{\"prepay_id\":\"wx2025102112345678\"}",
    "createTime": "2025-10-21T12:34:56"
  }
}

注意

  • 首次充值会额外赠送10%积分
  • pointsAmount = 基础积分 + 赠送积分 + 首充奖励(如果是首次)
  • paymentParams 需要传给前端调起支付

4. 获取充值记录

接口GET /user/points/recharge/records?page=1&size=10

权限:需要登录

响应示例

{
  "code": 200,
  "message": "成功",
  "data": [
    {
      "orderNo": "ORD20251021123456",
      "packageName": "标准包",
      "pointsAmount": 605,
      "amount": 48.00,
      "paymentMethodName": "微信支付",
      "statusName": "已完成",
      "createTime": "2025-10-21T12:34:56",
      "paidAt": "2025-10-21T12:35:10"
    }
  ]
}

5. 获取充值统计

接口GET /user/points/recharge/stats

权限:需要登录

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "totalRechargeCount": 5,
    "totalAmount": 240.00,
    "totalPoints": 3025,
    "isFirstRecharge": false,
    "lastRechargeTime": "2025-10-21T12:35:10"
  }
}

支付回调接口(/payment/callback

1. 支付宝回调

接口POST /payment/callback/alipay

权限:公开访问(支付宝服务器调用)

处理流程

  1. 验证支付宝签名
  2. 检查交易状态(TRADE_SUCCESSTRADE_FINISHED
  3. 调用充值处理逻辑
  4. 返回 success 给支付宝

2. 微信支付回调

接口POST /payment/callback/wechat

权限:公开访问(微信服务器调用)

处理流程

  1. 解析XML数据
  2. 验证微信签名
  3. 检查支付结果
  4. 调用充值处理逻辑
  5. 返回XML响应给微信

3. 测试回调(仅开发环境)

接口POST /payment/callback/test?orderNo=ORD20251021123456

权限:公开访问

用途:在没有真实支付的情况下测试充值流程

示例

curl -X POST "http://localhost:8080/payment/callback/test?orderNo=ORD20251021123456"

🔄 业务流程

完整充值流程

用户端                     后端                      支付平台
  |                         |                           |
  | 1. 浏览套餐列表          |                           |
  |------------------------>|                           |
  |    GET /packages        |                           |
  |<------------------------|                           |
  |                         |                           |
  | 2. 创建充值订单          |                           |
  |------------------------>|                           |
  |  POST /recharge         |                           |
  |    {packageId: 2}       |                           |
  |                         | 3. 生成订单               |
  |                         | 4. 生成支付参数           |
  |<------------------------|                           |
  |  {orderNo, paymentParams}                          |
  |                         |                           |
  | 5. 调起支付             |                           |
  |-------------------------------------------------->|
  |                         |                           |
  |                         |      6. 支付成功           |
  |                         |<---------------------------|
  |                         | POST /callback/alipay     |
  |                         |                           |
  |                         | 7. 验证签名               |
  |                         | 8. 增加用户积分           |
  |                         | 9. 更新订单状态           |
  |                         | 10. 记录变动日志          |
  |                         |-------------------------->|
  |                         |    返回 "success"         |
  |                         |                           |
  | 11. 查询充值记录         |                           |
  |------------------------>|                           |
  |<------------------------|                           |

💻 前端集成示例

1. 获取套餐列表

// 获取积分套餐
async function getPackages() {
  const response = await fetch('/user/points/packages');
  const result = await response.json();
  
  if (result.code === 200) {
    displayPackages(result.data);
  }
}

2. 创建充值订单

// 创建充值订单
async function recharge(packageId, paymentMethod) {
  const response = await fetch('/user/points/recharge', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + getToken()
    },
    body: JSON.stringify({
      packageId: packageId,
      paymentMethod: paymentMethod  // 1=支付宝, 2=微信
    })
  });
  
  const result = await response.json();
  
  if (result.code === 200) {
    const { orderNo, paymentParams } = result.data;
    
    // 调起支付
    if (paymentMethod === 1) {
      // 支付宝支付
      alipay(paymentParams);
    } else {
      // 微信支付
      wechatPay(paymentParams);
    }
  }
}

3. 支付宝支付(示例)

function alipay(paymentParams) {
  // 创建表单并提交
  const form = document.createElement('form');
  form.action = 'https://openapi.alipay.com/gateway.do';
  form.method = 'POST';
  form.innerHTML = paymentParams; // 支付宝SDK生成的表单
  document.body.appendChild(form);
  form.submit();
}

4. 微信支付(示例)

function wechatPay(paymentParams) {
  const params = JSON.parse(paymentParams);
  
  // 调起微信支付
  WeixinJSBridge.invoke('getBrandWCPayRequest', {
    appId: params.appId,
    timeStamp: params.timeStamp,
    nonceStr: params.nonceStr,
    package: params.package,
    signType: params.signType,
    paySign: params.paySign
  }, function(res) {
    if (res.err_msg === 'get_brand_wcpay_request:ok') {
      // 支付成功,跳转到充值记录页面
      window.location.href = '/points/records';
    }
  });
}

🔧 后端开发说明

1. 支付接口对接

目前 generatePaymentParams() 方法返回的是模拟数据,需要对接真实的支付宝/微信SDK

支付宝SDK集成

<!-- pom.xml -->
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.38.0.ALL</version>
</dependency>
// 生成支付宝支付参数
private String generateAlipayParams(Order order) {
    AlipayClient alipayClient = new DefaultAlipayClient(
        "https://openapi.alipay.com/gateway.do",
        APP_ID,
        PRIVATE_KEY,
        "json",
        "UTF-8",
        ALIPAY_PUBLIC_KEY,
        "RSA2"
    );
    
    AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
    request.setNotifyUrl("https://yourdomain.com/payment/callback/alipay");
    
    JSONObject bizContent = new JSONObject();
    bizContent.put("out_trade_no", order.getOrderNo());
    bizContent.put("total_amount", order.getAmount());
    bizContent.put("subject", "积分充值");
    bizContent.put("product_code", "QUICK_MSECURITY_PAY");
    
    request.setBizContent(bizContent.toString());
    
    AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
    return response.getBody();
}

微信支付SDK集成

<!-- pom.xml -->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.12</version>
</dependency>
// 生成微信支付参数
private String generateWechatPayParams(Order order) {
    // 使用微信支付SDK创建预支付订单
    // 返回prepay_id等参数
}

2. 回调签名验证

支付宝签名验证

@PostMapping("/alipay")
public String alipayCallback(HttpServletRequest request) {
    Map<String, String> params = new HashMap<>();
    Map<String, String[]> requestParams = request.getParameterMap();
    
    for (String name : requestParams.keySet()) {
        params.put(name, request.getParameter(name));
    }
    
    // 验证签名
    boolean signVerified = AlipaySignature.rsaCheckV1(
        params,
        ALIPAY_PUBLIC_KEY,
        "UTF-8",
        "RSA2"
    );
    
    if (!signVerified) {
        return "fail";
    }
    
    // 验证通过,处理业务
    String orderNo = params.get("out_trade_no");
    String tradeStatus = params.get("trade_status");
    
    if ("TRADE_SUCCESS".equals(tradeStatus)) {
        pointsRechargeService.handleRechargePaymentSuccess(orderNo);
    }
    
    return "success";
}

微信支付签名验证

@PostMapping("/wechat")
public String wechatCallback(@RequestBody String xmlData) {
    // 解析XML
    Map<String, String> params = WXPayUtil.xmlToMap(xmlData);
    
    // 验证签名
    boolean signVerified = WXPayUtil.isSignatureValid(
        params,
        WECHAT_API_KEY
    );
    
    if (!signVerified) {
        return errorXml();
    }
    
    // 验证通过,处理业务
    String orderNo = params.get("out_trade_no");
    String resultCode = params.get("result_code");
    
    if ("SUCCESS".equals(resultCode)) {
        pointsRechargeService.handleRechargePaymentSuccess(orderNo);
    }
    
    return successXml();
}

📊 数据库迁移

执行迁移脚本

# 1. 备份数据库
mysqldump -u root -p 1818ai > backup_$(date +%Y%m%d).sql

# 2. 执行V6迁移脚本
mysql -u root -p 1818ai < V6__add_points_recharge_system.sql

# 3. 验证结果
mysql -u root -p 1818ai -e "SELECT * FROM points_package;"
mysql -u root -p 1818ai -e "DESC \`order\`;"

🧪 测试流程

1. 开发环境测试

# 1. 获取套餐列表
curl -X GET "http://localhost:8080/user/points/packages"

# 2. 创建充值订单需要登录token
curl -X POST "http://localhost:8080/user/points/recharge" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"packageId":2,"paymentMethod":2}'

# 3. 模拟支付成功(测试用)
curl -X POST "http://localhost:8080/payment/callback/test?orderNo=ORD20251021123456"

# 4. 查看充值记录
curl -X GET "http://localhost:8080/user/points/recharge/records?page=1&size=10" \
  -H "Authorization: Bearer YOUR_TOKEN"

# 5. 查看用户积分
curl -X GET "http://localhost:8080/user/info" \
  -H "Authorization: Bearer YOUR_TOKEN"

2. 生产环境验证

  1. 确认支付宝/微信支付配置正确
  2. 确认回调URL可以被外网访问
  3. 小额充值测试¥0.01
  4. 验证积分到账是否正确
  5. 验证首充奖励是否生效
  6. 验证充值记录是否正确

🛡️ 安全配置

SecurityConfig 配置说明

// 公开接口(无需登录)
"/user/points/packages/**"  // 套餐浏览
"/payment/callback/**"      // 支付回调

// 需要登录的接口
"/user/points/recharge"     // 创建充值订单
"/user/points/recharge/**"  // 充值记录、统计

📈 运营建议

1. 套餐定价策略

  • 体验包:吸引新用户尝试
  • 标准包:日常充值主力
  • 超值包:性价比标杆,设置为热门
  • 豪华包/至尊包:满足重度用户
  • 旗舰包:年度大额充值,最高性价比

2. 营销活动

  • 首充奖励已自动实现首次充值额外赠送10%
  • 限时优惠:通过 discount_label 标签展示
  • 热门推荐:将主推套餐设置为 is_hot=1
  • 节日活动:临时调整 bonus_points 赠送比例

3. 数据分析

查看充值统计视图:

SELECT * FROM v_points_recharge_stats 
ORDER BY recharge_date DESC 
LIMIT 30;

常见问题

Q1: 支付成功但积分没到账?

检查步骤

  1. 查看订单状态:SELECT * FROM order WHERE order_no = 'xxx';
  2. 查看支付回调日志:检查 /payment/callback/alipay 日志
  3. 查看用户积分:SELECT points FROM user WHERE id = xxx;
  4. 查看积分变动日志:SELECT * FROM points_consumption_log WHERE user_id = xxx;

手动补单

curl -X POST "http://localhost:8080/payment/callback/test?orderNo=xxx"

Q2: 如何修改套餐价格?

UPDATE points_package 
SET price = 45.00, 
    update_time = NOW() 
WHERE id = 2;

Q3: 如何下架某个套餐?

UPDATE points_package 
SET is_active = 0, 
    update_time = NOW() 
WHERE id = 6;

Q4: 如何查看用户充值历史?

SELECT 
    o.order_no,
    o.amount,
    o.points_amount,
    pp.name as package_name,
    o.create_time,
    o.paid_at,
    CASE o.status
        WHEN 0 THEN '待支付'
        WHEN 1 THEN '已完成'
        WHEN 2 THEN '已取消'
        WHEN 3 THEN '支付失败'
    END as status_name
FROM `order` o
LEFT JOIN points_package pp ON o.points_package_id = pp.id
WHERE o.user_id = 123 
  AND o.order_type = 2
ORDER BY o.create_time DESC;

🎯 总结

功能完整:支持套餐管理、订单创建、支付回调、充值到账全流程

安全可靠JWT认证、订单防重、支付签名验证

易于扩展:支持新增支付方式、调整套餐策略

数据完整:充值记录、变动日志、统计分析

现在用户可以直接购买积分,不再依赖礼品码!🎉