|
|
@@ -20,7 +20,7 @@
|
|
|
| totalAmount | BigDecimal | 订单总金额(原价),设备回调时写入 |
|
|
|
| discountAmount | BigDecimal | 订单优惠金额(营销活动 + 优惠券),默认0 |
|
|
|
| paidAmount | BigDecimal | 实付总金额 = totalAmount - discountAmount |
|
|
|
-| payStatus | String | 支付状态:UNPAID / PAID / REFUND |
|
|
|
+| payStatus | String | 支付状态:UNPAID / PAID / PARTIAL_REFUND / REFUND |
|
|
|
| payType | String | 支付方式 |
|
|
|
| payChannel | String | 支付渠道:wechat / alipay / alipay_credit / balance / wechat_payscore |
|
|
|
| transactionId | String | 第三方交易号 |
|
|
|
@@ -55,8 +55,9 @@
|
|
|
| code | 枚举值 | 说明 |
|
|
|
|------|--------|------|
|
|
|
| UNPAID | 未支付 | 订单已创建,等待支付 |
|
|
|
-| PAID | 已支付 | 扣费成功 |
|
|
|
-| REFUND | 已退款 | 已完成退款 |
|
|
|
+| PAID | 已支付 | 扣费成功,未发生退款 |
|
|
|
+| PARTIAL_REFUND | 部分退款 | 累计退款 > 0 且 < 实付金额,可继续退款 |
|
|
|
+| REFUND | 全额退款 | 累计退款 ≥ 实付金额,退款入口关闭 |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -328,41 +329,71 @@ PayScoreSyncTask 每 5 分钟同步
|
|
|
| 2 | 自定义金额退款 | 传入 `refundAmount` |
|
|
|
| 3 | 全额退款 | 两者都不传,金额 = paidAmount |
|
|
|
|
|
|
-**金额校验**:退款金额 ≤ `paidAmount - 该订单历史累计已退款金额`,确保多次部分退款总额不超过实付金额。
|
|
|
+**金额校验**:
|
|
|
+- 退款金额 ≤ `paidAmount - 该订单历史累计已退款金额`
|
|
|
+- 逐商品校验:退款数量 ≤ `购买数量 - 该商品已退款数量`
|
|
|
+- 确保多次部分退款总额不超过实付金额,已全额退款的商品不可重复选择
|
|
|
|
|
|
-#### 退款入口
|
|
|
+#### 退款流程
|
|
|
+
|
|
|
+用户端和管理后台走不同的退款路径:
|
|
|
+
|
|
|
+**用户端(小程序)**:提交申请 → 等待审核
|
|
|
+
|
|
|
+```
|
|
|
+用户提交退款(refund.vue)
|
|
|
+ → POST /payment/refund
|
|
|
+ → PaymentServiceImpl.refund(Map) // immediate=false(默认)
|
|
|
+ → RefundService.createRefundRecord()
|
|
|
+ ├── 创建 t_refund(status=PENDING)
|
|
|
+ ├── 创建 t_refund_item(商品明细)
|
|
|
+ ├── 更新 t_order.refundStatus = PENDING
|
|
|
+ └── 返回”退款申请已提交,请等待审核”
|
|
|
+ → 管理员在运营平台审核
|
|
|
+ ├── 通过 → approveRefund()
|
|
|
+ │ → 执行第三方退款 API
|
|
|
+ │ → t_refund.status = REFUNDED
|
|
|
+ │ → t_order.refundAmount 累加
|
|
|
+ │ → 累计 ≥ 实付 → payStatus = REFUND
|
|
|
+ │ → 累计 < 实付 → payStatus = PARTIAL_REFUND
|
|
|
+ └── 拒绝 → rejectRefund()
|
|
|
+ → t_refund.status = REJECTED
|
|
|
+ → 清除 t_order.refundStatus / currentRefundId
|
|
|
+ → 保留 t_order.refundAmount(累计已退金额)
|
|
|
+ → 用户可重新提交申请
|
|
|
+```
|
|
|
+
|
|
|
+**管理后台直接退款**:即时执行
|
|
|
+
|
|
|
+```
|
|
|
+管理员在订单列表点击退款(hook.tsx handleRefund)
|
|
|
+ → POST /order/{id}/refund
|
|
|
+ → PaymentServiceImpl.refund(Map) // immediate=true
|
|
|
+ → RefundService.createRefundRecord()
|
|
|
+ → 立即执行第三方退款 API
|
|
|
+ → t_refund.status = REFUNDED
|
|
|
+```
|
|
|
+
|
|
|
+**多次退款**:
|
|
|
|
|
|
```
|
|
|
-┌─────────────────────────────────────────────────────┐
|
|
|
-│ 退款发起入口 │
|
|
|
-├──────────────┬──────────────────┬───────────────────┤
|
|
|
-│ 小程序端 │ 管理后台直接退款 │ 管理后台审核退款 │
|
|
|
-│ refund.vue │ 订单列表→退款按钮 │ 退款申请管理页面 │
|
|
|
-│ POST │ POST │ POST │
|
|
|
-│ /payment │ /order/{id} │ /refund/{id} │
|
|
|
-│ /refund │ /refund │ /review │
|
|
|
-├──────────────┴──────────────────┴───────────────────┤
|
|
|
-│ PaymentServiceImpl.refund() │
|
|
|
-│ ┌─────────────────────────────────────────────┐ │
|
|
|
-│ │ 有 products/refundAmount? │ │
|
|
|
-│ │ ├── 是 → RefundService.createRefundRecord() │ │
|
|
|
-│ │ │ 创建 t_refund + t_refund_item │ │
|
|
|
-│ │ │ → 执行第三方退款(金额=计算后的退款金额) │ │
|
|
|
-│ │ │ → markRefunded() + 更新订单汇总字段 │ │
|
|
|
-│ │ └── 否 → 全额退款(兼容旧行为) │ │
|
|
|
-│ └─────────────────────────────────────────────┘ │
|
|
|
-└─────────────────────────────────────────────────────┘
|
|
|
+订单实付 100 元,购 3 件商品:
|
|
|
+ 第一次退 2 件,金额 60 元 → payStatus = PARTIAL_REFUND,refundAmount = 60
|
|
|
+ 第二次退 1 件,金额 40 元 → 累计 100 ≥ 100 → payStatus = REFUND
|
|
|
+ 退款入口关闭,已退商品不可再选
|
|
|
```
|
|
|
|
|
|
#### 退款追溯
|
|
|
|
|
|
-每笔退款在 `t_refund_item` 中记录了退款商品明细(`order_goods_id`、`product_id`、`product_name`、`quantity`、`price`),可通过 `refund_id` 关联查询。管理后台退款详情页展示的是实际退款商品,而非订单全部商品。
|
|
|
+- 每笔退款在 `t_refund_item` 中记录了退款商品明细(`order_goods_id`、`product_id`、`product_name`、`quantity`、`price`),可通过 `refund_id` 关联查询
|
|
|
+- 管理后台退款详情页展示的是实际退款商品,而非订单全部商品
|
|
|
+- 小程序订单详情页展示退款记录时间线(绿色=已退款,红色=已拒绝,橙色=审核中),含每次申请的金额、原因、商品明细和拒绝原因
|
|
|
|
|
|
#### 退款特殊处理
|
|
|
|
|
|
-- **支付分退款**:支付分订单完结后走微信退款接口,需传入原始支付分订单号
|
|
|
+- **支付分退款**:支付分订单完结后走微信退款接口,需传入原始支付分订单号。每次退款生成唯一 `out_refund_no`(含时间戳),避免重试时参数不一致导致微信 `INVALID_REQUEST`
|
|
|
- **优惠券不可退回**:订单退款时,已使用的优惠券状态不会回滚为”未使用”,这是当前的业务规则
|
|
|
-- **部分退款**:支持选择商品退款(金额由商品自动计算)或自定义金额退款,多次退款累计不超过实付金额
|
|
|
+- **部分退款**:支持选择商品退款(金额由商品自动计算)或自定义金额退款,多次退款累计不超过实付金额。已全额退款的商品前端禁选、后端校验
|
|
|
- **异物放入订单**(orderType=IN)不展示给用户端,退款需在运营平台操作
|
|
|
|
|
|
---
|