|

Aimee

Write the Code. Change the World.

积分 + 现金混合支付,退款的时候怎么拆

· 分享镜

积分 + 现金混合支付,退款的时候怎么拆

用 200 积分 + 30 元现金买了一件 50 元的商品(积分按 1:0.1 换算,200 积分 = 20 元)。

退款的时候,退多少积分、退多少现金?这个问题没有唯一答案,但要提前设计好,不然退款时会很乱。


先定清楚积分能不能退

混合支付退款的第一个决策不是"怎么算",是"积分退不退"。

常见的业务规则有三种:

  1. 积分不退:积分是平台让利,消费即消耗,退货只退现金部分
  2. 积分全退:退货恢复到下单前状态,积分和现金都退
  3. 积分延迟退:退货成功后积分进"待确认"状态,物流确认签收/无异议后才释放

三种规则没有对错,取决于业务目标。但规则一旦定了,就要在订单、退款、积分三个模块都落实一致,不能各自理解各自写。


退款金额怎么拆

假设业务规定积分全退,退款金额拆分有两种常见方案:

方案 A:按支付比例分摊

订单总价 50 元 = 积分抵扣 20 元 + 现金 30 元,现金占比 60%。

退款 50 元 → 现金退 30 元,积分退回 200 分。

这种方案直觉友好,"退回原来付的",用户容易理解。

方案 B:先退现金,不够再退积分

退款优先用现金覆盖,现金不够了才动积分。

退款 50 元 → 先退现金 30 元,差 20 元换算成 200 积分退回。

两种方案在全额退款时结果一致,在部分退款时会有差异。


部分退款时要特别小心

用户只退一件(共买了两件),退款金额是 25 元。

按比例分摊:25 元中现金占 60%,退现金 15 元,退积分 100 分。

先退现金:25 元 ≤ 现金 30 元,全额退现金 25 元,积分不退(退完了剩 5 元现金、200 积分在账上)。

哪种对用户更友好?要看平台积分是否有有效期。积分快过期的情况下,先退现金对用户更不利;积分长期有效则无所谓。

这个决策得产品定,定完写进退款规则文档,开发按规则实现。


实现:退款记录要拆开存

无论用哪种规则,退款记录都要把现金退款和积分退款分开记:

CREATE TABLE refund_detail (
    id            BIGINT PRIMARY KEY,
    refund_id     BIGINT,
    type          ENUM('CASH','POINT'),
    amount        DECIMAL(10,2),   -- 现金金额,或积分数量(转换成元)
    point_count   INT,             -- 积分数量(type=POINT 时有效)
    status        ENUM('PENDING','SUCCESS','FAILED'),
    created_at    DATETIME
);

退款执行时两个动作独立进行,各有自己的状态:

// 1. 发起现金退款(调支付渠道)
alipay.refund(outTradeNo, cashAmount);
refundDetailDao.updateStatus(cashDetailId, SUCCESS);

// 2. 返还积分(调积分服务)
pointService.returnPoints(userId, pointCount, refundId);
refundDetailDao.updateStatus(pointDetailId, SUCCESS);

两个动作都要做幂等。积分服务如果失败了,需要重试机制,不能让现金退了但积分没退(或者反过来)。


积分退款的幂等

积分服务通常在平台内部,比调外部支付接口可靠,但网络问题或服务重启仍然可能导致重复调用。

// 用 refund_id + type 做唯一键,防止重复退积分
boolean success = pointService.returnPoints(userId, pointCount, "refund:" + refundId);

积分服务侧记录 dedup_key,相同的 key 直接返回上次结果。


混合支付的退款逻辑,技术实现本身不难,难在规则没定清楚就开始写代码。积分退不退、按什么顺序退、部分退款怎么分摊——这些问题在产品文档里没有答案,开发就会各自理解各自写,退款逻辑最终一定乱。先把规则写成文档,确认过了再动代码。

评论0

登录后参与评论。

还没有评论,来抢沙发吧。

回到顶部