forked from wangziqi/gongxue-base
feat: add commerce refund workflow
This commit is contained in:
@@ -18,7 +18,7 @@ apps/api/src/
|
||||
tenant/ 租户解析、品牌配置、域名识别
|
||||
catalog/ 公开题库、内容入口、分类树、题目集合、练习蓝图、手册、商城、资料资源只读接口
|
||||
learning/ 组卷 session、答题、错题、收藏、练习进度、排行榜
|
||||
commerce/ 订单、支付确认、激活码、优惠券、权益
|
||||
commerce/ 订单、支付确认、退款、激活码、优惠券、权益
|
||||
referral/ 销售/代理客资追踪、首绑保护、团队关系、CRM 队列
|
||||
storage/ 对象存储签名 provider
|
||||
video/ 题目视频列表、搜索、SVIP/次数校验和签名播放
|
||||
|
||||
@@ -108,7 +108,8 @@
|
||||
| 激活码预检查/兑换 | 可联调 | `/api/commerce/activation-codes/check`、`redeem`;支持地区校验、自用码拒绝、已用码稳定 reasonCode |
|
||||
| 优惠券后台配置 | 可联调 | `/api/tenant-admin/coupons` |
|
||||
| 优惠券前台领取/下单抵扣 | 可联调 | `/api/commerce/coupons/claim`;支持同用户同券幂等领取、下单绑定、负数订单项、全额优惠自动开通权益 |
|
||||
| 退款/补偿/对账 | 待补齐 | 需退款接口、支付补偿任务、对账、异常订单处理 |
|
||||
| 退款状态机 | 可联调 | `/api/commerce/refunds`、`/api/commerce/refunds/status`;支持退款申请、审核、处理中、成功/失败/拒绝/取消、退款金额累计、部分退款、全额退款权益撤销、退款事件和审计 |
|
||||
| 补偿/对账/真实退款 provider | 待补齐 | 真实微信/支付宝退款 API、支付补偿任务、对账、异常订单自动处理和退款 worker |
|
||||
|
||||
## 租户后台与平台后台
|
||||
|
||||
|
||||
@@ -228,6 +228,7 @@ GET /api/tenant-admin/audit-logs
|
||||
- 激活码兑换、支付成功和零元优惠订单都走同一套 `grantSvipEntitlement` 权益开通逻辑。
|
||||
- 优惠券领取同用户同券幂等;下单后优惠券 redemption 会绑定订单并进入 `used`,订单明细会写入负数 `coupon_discount` 项。
|
||||
- `/api/commerce/payments/manual-confirm` 是线下收款/迁移期能力,只允许租户后台具备 `tenant:payment:write` 的成员调用,普通学生不能伪造手工支付成功。
|
||||
- `/api/commerce/refunds` 和 `/api/commerce/refunds/status` 已提供内部退款状态机;退款权限拆分为 `tenant:refund:read/write/review`,全额退款成功会撤销订单来源权益,真实微信/支付宝退款 API 和对账 worker 后续接入。
|
||||
- 租户支付账户、短信、OAuth 登录配置接口只保存公开配置;密钥进入 `app_private.tenant_secrets` 或生产 KMS/Vault,API 只返回 `secretRef` 和掩码状态。
|
||||
- `tenant-admin` 采用角色默认权限 + `tenant_memberships.permissions` 覆盖的权限矩阵。成员可进入后台,但每个接口会校验具体权限点;学生和跨租户成员会被拒绝。
|
||||
- 当前默认角色:`tenant_owner`/`tenant_admin` 全权限,`tenant_operator` 可维护内容和活动,`teacher` 可维护内容并按班级范围查看学生,`sales` 可维护激活码和优惠券,`agent` 只读部分兑换码/优惠券。
|
||||
@@ -245,7 +246,7 @@ GET /api/tenant-admin/audit-logs
|
||||
1. 完善内容导入和文件上传:Excel/CSV、分数线、视频导入,对象存储上传后校验、PDF 预览、防盗链和视频水印。
|
||||
2. 接入真实短信 provider:阿里云/腾讯云,密钥放 `app_private.tenant_secrets` 或生产 Vault。
|
||||
3. 接入真实 OAuth provider:微信网页、微信小程序、QQ,并处理旧 PocketBase 身份映射。
|
||||
4. 补退款、支付补偿任务、对账、异常订单处理和优惠券核销报表。
|
||||
4. 补真实微信/支付宝退款 provider、支付补偿任务、对账、异常订单自动处理和优惠券核销报表。
|
||||
5. 扩展 `apps/worker`:支付补偿、日报统计、导入后检查、CRM 死信告警和公共题库同步。
|
||||
6. 开始 Taro scaffold,把 `supabaseApi` 抽到跨端包或适配层。
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
| 背单词 | `VocabularyPage.tsx`、`VocabularyQuiz.tsx` | 部分覆盖 | 单词列表、进度、收藏、统计、每日计划和后端复习调度已覆盖;后续补收藏练习体验、发音/音频策略、排行榜和更精细的间隔算法参数 |
|
||||
| 知识手册 | `Handbook*.tsx` | 已覆盖 | 前端需做好 Markdown/公式/图片渲染和搜索体验 |
|
||||
| 分数线 | `ScorelinePage.tsx` | 已覆盖 | 动态字段/趋势已有;缺批量导入和复杂筛选优化 |
|
||||
| 商城/SVIP | `Store.tsx`、`SvipModal.tsx` | 部分覆盖 | 套餐、订单、订单详情/状态轮询、权益、激活码预检查/兑换、优惠券领取/下单抵扣、微信支付/支付宝 provider 主链路已有;缺退款/对账/补偿任务和前端收银台体验 |
|
||||
| 商城/SVIP | `Store.tsx`、`SvipModal.tsx` | 部分覆盖 | 套餐、订单、订单详情/状态轮询、权益、激活码预检查/兑换、优惠券领取/下单抵扣、微信支付/支付宝 provider 主链路、内部退款状态机和全额退款权益撤销已有;缺真实退款 provider、对账/补偿任务和前端收银台/售后体验 |
|
||||
| 个人中心 | `Profile.tsx` | 部分覆盖 | 基本资料、权益、订单统计、练习历史、学习统计、签到积分、考试倒计时和趋势已有;缺勋章 API、账号绑定/换绑、学习报告可视化 |
|
||||
| 资料下载 | `QuestionExporterPublishModal.tsx` 等 | 部分覆盖 | 资源台账、上传确认、签名下载和 PDF/图片预览基础已有;缺水印、防盗链、杀毒扫描和 worker 复检 |
|
||||
| AI 择校推荐 | 业务规划新增 | 未覆盖 | 需设计学生输入 schema、地区数据上下文、AI JSON 输出、PDF 报告 |
|
||||
|
||||
@@ -59,7 +59,8 @@
|
||||
|
||||
1. 支付
|
||||
- 已完成微信支付 JSAPI、支付宝 WAP/H5 的创建支付参数和 webhook 幂等开通权益。
|
||||
- 继续补退款、支付补偿任务、对账、异常订单处理。
|
||||
- 已完成内部退款状态机、退款申请/审核/处理接口、部分/全额退款状态、全额退款权益撤销和审计事件。
|
||||
- 继续补真实微信/支付宝退款 provider、支付补偿任务、对账和异常订单自动处理。
|
||||
- 租户自有商户收款和平台代收/服务商模式。
|
||||
|
||||
2. 国内登录和短信
|
||||
@@ -91,8 +92,8 @@
|
||||
- 继续补断点续练和复盘体验。
|
||||
|
||||
7. 订单和营销体验
|
||||
- 已完成订单详情、订单状态轮询、激活码预检查、优惠券前台领取和下单抵扣计算。
|
||||
- 继续补退款、支付补偿任务、对账、异常订单处理、优惠券核销报表和复杂活动规则。
|
||||
- 已完成订单详情、订单状态轮询、激活码预检查、优惠券前台领取、下单抵扣计算和内部退款状态机。
|
||||
- 继续补真实 provider 退款、支付补偿任务、对账、异常订单自动处理、优惠券核销报表和复杂活动规则。
|
||||
|
||||
8. 积分和反馈增强
|
||||
- 已完成每日签到、积分流水、反馈提交、租户后台处理、奖励积分幂等。
|
||||
@@ -200,5 +201,5 @@
|
||||
2. 云服务器部署 Supabase/PostgreSQL 和 API,配置对象存储生产环境变量,跑 `check:refactor` 的远程等价测试。
|
||||
3. 导出现有 PocketBase 数据,做完整 dry-run 迁移。
|
||||
4. 开始 `apps/taro`,先接租户解析、首页、题库、背单词、知识手册。
|
||||
5. 并行补对象存储、真实登录、退款对账、CRM worker 和公共题库版本同步 worker。
|
||||
5. 并行补对象存储、真实登录、真实退款 provider、支付对账和公共题库版本同步 worker。
|
||||
6. 前后端联调通过后,再做支付、权限、数据导入、资料下载、视频播放的商用验收。
|
||||
|
||||
@@ -872,6 +872,60 @@ GET /api/commerce/entitlements
|
||||
|
||||
订单详情会返回 `pricing`、`payments`、`items`、`couponRedemptions`,可用于收银台、订单详情页和售后排查。订单状态轮询页只需消费 `status/payment`,避免频繁拉取全量明细。
|
||||
|
||||
### 退款和售后
|
||||
|
||||
学生端不直接发起后台退款命令。普通用户订单页只展示 `GET /api/commerce/orders/status` 和 `GET /api/commerce/orders/detail` 返回的订单状态、支付状态、`refundedAmountCents`,并提供客服/工单入口。租户后台或运营后台才接退款接口。
|
||||
|
||||
租户后台退款列表:
|
||||
|
||||
```text
|
||||
GET /api/commerce/refunds?status=requested&orderNo=<orderNo>
|
||||
权限:tenant:refund:read
|
||||
```
|
||||
|
||||
创建退款申请:
|
||||
|
||||
```text
|
||||
POST /api/commerce/refunds
|
||||
权限:tenant:refund:write
|
||||
body: {
|
||||
"orderNo": "<orderNo>",
|
||||
"refundNo": "<可选,前端幂等键>",
|
||||
"amountCents": 500,
|
||||
"reason": "用户协商退款",
|
||||
"entitlementAction": "revoke_on_success | none"
|
||||
}
|
||||
```
|
||||
|
||||
退款状态流转:
|
||||
|
||||
```text
|
||||
POST /api/commerce/refunds/status
|
||||
body: {
|
||||
"refundId": "<refundId>",
|
||||
"action": "approve | reject | mark_processing | mark_succeeded | mark_failed | cancel",
|
||||
"providerRefundNo": "<支付平台退款单号,可选>",
|
||||
"note": "<处理备注>"
|
||||
}
|
||||
```
|
||||
|
||||
状态说明:
|
||||
|
||||
```text
|
||||
requested -> approved -> processing -> succeeded
|
||||
requested/approved -> rejected
|
||||
requested/approved -> cancelled
|
||||
approved/processing -> failed
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- 金额单位一律是分,前端不要传元。
|
||||
- `refundNo` 是幂等键;同一订单同一金额重复提交会返回原退款申请。
|
||||
- 后端会限制累计退款金额不能超过实付金额。
|
||||
- 全额退款成功后订单和支付会进入 `refunded`,相关订单权益会被置为 `revoked`;部分退款进入 `partially_refunded`,默认不撤销权益。
|
||||
- 当前接口完成内部退款状态机和人工成功登记;真实微信/支付宝退款 API、自动对账和补偿 worker 后续接入。前端不要假设点击退款后已经实时调用支付平台。
|
||||
|
||||
### 激活码预检查与兑换
|
||||
|
||||
兑换前建议先调用:
|
||||
|
||||
Reference in New Issue
Block a user