详解Java商城项目中优惠券的处理逻辑

217 阅读3分钟

场景描述:Java商城项目,多商户平台,支付集成的是支付宝直付通和微信平台收付通。优惠券可以根据是谁负担的成本进行划分,可以划分为平台券和商家券。如果是平台券,那用户购买商品后,平台要补给商家钱,那就要定位使用了平台券的子支付订单(合并支付的情况下),再次为该商户补贴优惠券的钱,那这又是一个关联订单。这样的话用户退款时,不仅要正常退子订单的钱退给用户,还要退关联订单的钱退给平台。如果是商家券,走正常支付流程即可,不用做额外操作。退款不会退使用过的优惠券。


针对多商户平台中平台券的处理,需在支付和退款流程中设计额外的资金补偿与回收机制。以下是实现方案:

核心流程设计

1. 支付环节

  • 商家券
    用户支付时直接抵扣订单金额,无需额外操作(商户自行承担成本)。

  • 平台券
    用户支付时抵扣订单金额后,平台需额外补偿商户优惠金额:

2. 退款环节

  • 商家券
    仅退还用户实际支付金额(优惠券不退)。

  • 平台券
    需退还两部分资金:

    1. 用户实际支付金额 → 退还给用户
    2. 平台补贴金额 → 从商户回收至平台

关键实现步骤

1. 数据结构设计

// 订单表
class Order {
    String orderId;       // 主订单ID
    BigDecimal amount;    // 订单总金额
    CouponType couponType;// 优惠券类型(PLATFORM/MERCHANT)
    String couponId;      // 优惠券ID
    BigDecimal couponAmount; // 优惠券金额
    BigDecimal actualPayAmount; // 用户实际支付金额
}

// 补贴订单表(平台券专属)
class SubsidyOrder {
    String subsidyOrderId;   // 补贴订单ID
    String originalOrderId; // 关联的主订单ID
    String merchantId;      // 商户ID
    BigDecimal amount;      // 补贴金额(=优惠券金额)
    PaymentChannel channel; // 支付渠道(ALIPAY/WECHAT)
}

2. 支付流程实现

// 支付成功后处理平台券补偿
public void handlePlatformCoupon(Order order) {
    if (order.getCouponType() == CouponType.PLATFORM) {
        // 创建补贴订单
        SubsidyOrder subsidyOrder = createSubsidyOrder(
            order.getId(), 
            order.getMerchantId(),
            order.getCouponAmount()
        );
        
        // 调用支付渠道补偿商户
        if (order.getChannel() == PaymentChannel.ALIPAY) {
            alipayService.transferToMerchant(
                subsidyOrder.getId(),
                subsidyOrder.getAmount(),
                order.getMerchantAccount()
            );
        } else if (order.getChannel() == PaymentChannel.WECHAT) {
            wechatPayService.transferToMerchant(
                subsidyOrder.getId(),
                subsidyOrder.getAmount(),
                order.getMerchantAccount()
            );
        }
    }
}

3. 退款流程实现

public void refundOrder(Order order) {
    // 1. 退用户实际支付金额
    paymentService.refundOriginalOrder(order.getId(), order.getActualPayAmount());
    
    // 2. 若含平台券,回收补贴
    if (order.getCouponType() == CouponType.PLATFORM) {
        SubsidyOrder subsidyOrder = subsidyOrderService.findByOrderId(order.getId());
        
        // 从商户回收补贴金额
        if (order.getChannel() == PaymentChannel.ALIPAY) {
            alipayService.deductFromMerchant(
                subsidyOrder.getId(), 
                subsidyOrder.getAmount(),
                order.getMerchantAccount()
            );
        } else if (order.getChannel() == PaymentChannel.WECHAT) {
            wechatPayService.deductFromMerchant(
                subsidyOrder.getId(),
                subsidyOrder.getAmount(),
                order.getMerchantAccount()
            );
        }
        
        // 更新补贴订单状态为已回收
        subsidyOrder.markReversed();
    }
}

支付渠道对接关键点

支付宝直付通

  1. 补贴支付
    使用 alipay.fund.trans.uni.transfer 单笔转账到商户账户。
  2. 补贴回收
    使用 alipay.trade.order.settle 发起分账扣回(需预先签约分账关系)。

微信平台收付通

  1. 补贴支付
    调用 profitsharing.addReceiver 添加分账方,再通过 profitsharing.orders 发起分账。
  2. 补贴回收
    使用 refund 接口退款时,通过 refund_account 参数指定 REFUND_SOURCE_SUBSIDY_ACCOUNT 从二级商户回收资金。

异常处理机制

  1. 补贴支付失败
    • 记录失败状态,启动定时任务重试
    • 超过3次后触发人工干预
  2. 补贴回收失败
    • 冻结商户账户资金
    • 通过消息通知商户补足余额
    • 后续结算时自动抵扣

结算周期优化

  • 延迟补贴回收
    若实时回收失败,在商户结算周期中优先扣除待回收金额:
    public BigDecimal calculateSettleAmount(Merchant merchant) {
        BigDecimal income = merchant.getTotalIncome(); // 商户待结算收入
        BigDecimal pendingRefunds = subsidyOrderService.getPendingRefunds(merchant.getId());
        return income.subtract(pendingRefunds); // 实际结算金额
    }
    

安全与合规

  1. 资金轨迹审计
    • 为每笔补贴订单关联原支付订单号
    • 保留完整的资金进出凭证(支付宝/微信交易单号)
  2. 商户协议约束
    在商户入驻协议中明确平台券回收条款,授权平台在退款时扣回补贴。

此方案通过分离主订单与补贴订单的资金流,确保平台券的成本精准回收,同时兼容支付宝/微信双渠道的合并支付场景。