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

699 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 积分充值系统使用指南
## 📋 功能概述
本系统实现了完整的积分直接购买功能,用户可以通过支付宝/微信支付直接购买积分,无需依赖礼品码。
### ✨ 核心特性
-**多套餐选择**:支持不同价格和数量的积分套餐
-**首充奖励**首次充值额外赠送10%积分
-**赠送积分**:每个套餐可配置赠送积分
-**支付方式**:支持支付宝和微信支付
-**充值记录**:完整的充值历史记录
-**自动到账**:支付成功后自动充值到账
-**积分有效期**可配置积分有效期默认365天
---
## 🗂️ 数据库结构
### 新增表
#### 1. `points_package` - 积分套餐表
```sql
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` 表扩展
新增字段:
```sql
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`
**权限**:公开访问(无需登录)
**响应示例**
```json
{
"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`
**权限**:需要登录
**请求体**
```json
{
"packageId": 2,
"paymentMethod": 2
}
```
**参数说明**
- `packageId`套餐ID必填
- `paymentMethod`:支付方式(必填)
- `1` = 支付宝
- `2` = 微信支付
**响应示例**
```json
{
"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`
**权限**:需要登录
**响应示例**
```json
{
"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`
**权限**:需要登录
**响应示例**
```json
{
"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_SUCCESS``TRADE_FINISHED`
3. 调用充值处理逻辑
4. 返回 `success` 给支付宝
---
#### 2. 微信支付回调
**接口**`POST /payment/callback/wechat`
**权限**:公开访问(微信服务器调用)
**处理流程**
1. 解析XML数据
2. 验证微信签名
3. 检查支付结果
4. 调用充值处理逻辑
5. 返回XML响应给微信
---
#### 3. 测试回调(仅开发环境)
**接口**`POST /payment/callback/test?orderNo=ORD20251021123456`
**权限**:公开访问
**用途**:在没有真实支付的情况下测试充值流程
**示例**
```bash
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. 获取套餐列表
```javascript
// 获取积分套餐
async function getPackages() {
const response = await fetch('/user/points/packages');
const result = await response.json();
if (result.code === 200) {
displayPackages(result.data);
}
}
```
---
### 2. 创建充值订单
```javascript
// 创建充值订单
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. 支付宝支付(示例)
```javascript
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. 微信支付(示例)
```javascript
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集成
```xml
<!-- pom.xml -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.38.0.ALL</version>
</dependency>
```
```java
// 生成支付宝支付参数
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集成
```xml
<!-- pom.xml -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
```
```java
// 生成微信支付参数
private String generateWechatPayParams(Order order) {
// 使用微信支付SDK创建预支付订单
// 返回prepay_id等参数
}
```
---
### 2. 回调签名验证
#### 支付宝签名验证
```java
@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";
}
```
#### 微信支付签名验证
```java
@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();
}
```
---
## 📊 数据库迁移
### 执行迁移脚本
```bash
# 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. 开发环境测试
```bash
# 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 配置说明
```java
// 公开接口(无需登录)
"/user/points/packages/**" // 套餐浏览
"/payment/callback/**" // 支付回调
// 需要登录的接口
"/user/points/recharge" // 创建充值订单
"/user/points/recharge/**" // 充值记录、统计
```
---
## 📈 运营建议
### 1. 套餐定价策略
- **体验包**:吸引新用户尝试
- **标准包**:日常充值主力
- **超值包**:性价比标杆,设置为热门
- **豪华包/至尊包**:满足重度用户
- **旗舰包**:年度大额充值,最高性价比
### 2. 营销活动
- **首充奖励**已自动实现首次充值额外赠送10%
- **限时优惠**:通过 `discount_label` 标签展示
- **热门推荐**:将主推套餐设置为 `is_hot=1`
- **节日活动**:临时调整 `bonus_points` 赠送比例
### 3. 数据分析
查看充值统计视图:
```sql
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;`
**手动补单**
```bash
curl -X POST "http://localhost:8080/payment/callback/test?orderNo=xxx"
```
---
### Q2: 如何修改套餐价格?
```sql
UPDATE points_package
SET price = 45.00,
update_time = NOW()
WHERE id = 2;
```
---
### Q3: 如何下架某个套餐?
```sql
UPDATE points_package
SET is_active = 0,
update_time = NOW()
WHERE id = 6;
```
---
### Q4: 如何查看用户充值历史?
```sql
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认证、订单防重、支付签名验证
**易于扩展**:支持新增支付方式、调整套餐策略
**数据完整**:充值记录、变动日志、统计分析
现在用户可以直接购买积分,不再依赖礼品码!🎉