diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/pay/PayConsumeDeposit.java b/mall-common/src/main/java/com/suisung/mall/common/modules/pay/PayConsumeDeposit.java index 37d0b0c8..a8ceea50 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/pay/PayConsumeDeposit.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/pay/PayConsumeDeposit.java @@ -39,9 +39,15 @@ public class PayConsumeDeposit implements Serializable { @ApiModelProperty(value = "商户网站唯一订单号(DOT):合并支付则为多个订单号, 没有创建联合支付交易号") private String order_id; - @ApiModelProperty(value = "交易号:支付宝etc") + @ApiModelProperty(value = "重要:一般是微信支付宝等的(非合单)主交易流水号") private String deposit_trade_no; + @ApiModelProperty(value = "(合单)商品子交易流水号") + private String ord_sub_trade_no; + + @ApiModelProperty(value = "(合单)运费子交易流水号") + private String df_sub_trade_no; + @ApiModelProperty(value = "支付渠道") private Integer payment_channel_id; diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java index 1ddd7fe8..f9e42fb4 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/LakalaPayService.java @@ -64,9 +64,11 @@ public interface LakalaPayService { * @param originTradeNo 原拉卡拉交易流水号 * @param refundAmount 退款金额(单位:分) * @param refundReason 退款原因 + * @param lklMerchantNo 拉卡拉商户号(可选参数),不填默认选择店铺的商户号 + * @param lklTermNo 拉卡拉终端号(可选参数),不填默认选择店铺的终端号 * @return Pair,包含退款是否成功以及消息 */ - Pair innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason); + Pair innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason, String lklMerchantNo, String lklTermNo); /** * 文件上传 diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java index 9bd26a8e..87ca69f9 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/PayUserPayService.java @@ -51,13 +51,13 @@ public interface PayUserPayService extends IBaseService { */ ShopOrderReturn wxPayRefund(ShopOrderReturn orderReturn); - /** - * 拉卡拉 退款接口 - * - * @param orderReturn - * @return - */ - ShopOrderReturn lklPayRefund(ShopOrderReturn orderReturn); +// /** +// * 拉卡拉 退款接口 +// * +// * @param orderReturn +// * @return +// */ +// ShopOrderReturn lklPayRefund(ShopOrderReturn orderReturn); void wxRefundNotify(HttpServletRequest request); diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java index 306c1458..8943dd01 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/LakalaPayServiceImpl.java @@ -341,10 +341,12 @@ public class LakalaPayServiceImpl implements LakalaPayService { * @param originTradeNo 原拉卡拉交易流水号 * @param refundAmount 退款金额(单位:分) * @param refundReason 退款原因 + * @param lklMerchantNo 拉卡拉商户号(可选参数),不填默认选择店铺的商户号 + * @param lklTermNo 拉卡拉终端号(可选参数),不填默认选择店铺的终端号 * @return Pair,包含退款是否成功以及消息 */ @Override - public Pair innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason) { + public Pair innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason, String lklMerchantNo, String lklTermNo) { try { log.info("开始执行拉卡拉内部退款,参数: storeId={}, outTradeNo={}, originTradeNo={}, refundAmount={}, refundReason={}", storeId, outTradeNo, originTradeNo, refundAmount, refundReason); @@ -382,8 +384,10 @@ public class LakalaPayServiceImpl implements LakalaPayService { V3LabsRelationRefundRequest refundRequest = new V3LabsRelationRefundRequest(); refundRequest.setOutTradeNo(outTradeNo); refundRequest.setOriginTradeNo(originTradeNo); - refundRequest.setMerchantNo(shopStoreBase.getLkl_merchant_no()); - refundRequest.setTermNo(shopStoreBase.getLkl_term_no()); + lklMerchantNo = StrUtil.isBlank(lklMerchantNo) ? shopStoreBase.getLkl_merchant_no() : lklMerchantNo; + lklTermNo = StrUtil.isBlank(lklTermNo) ? shopStoreBase.getLkl_term_no() : lklTermNo; + refundRequest.setMerchantNo(lklMerchantNo); + refundRequest.setTermNo(lklTermNo); refundRequest.setRefundAmount(refundAmount); refundRequest.setRefundReason(refundReason); diff --git a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java index 44607fb8..f82046ac 100644 --- a/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java +++ b/mall-pay/src/main/java/com/suisung/mall/pay/service/impl/PayConsumeTradeServiceImpl.java @@ -7,10 +7,14 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.api.*; +import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.constant.MqConstant; import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.exception.ApiException; @@ -24,6 +28,8 @@ import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.pay.mapper.PayConsumeTradeMapper; import com.suisung.mall.pay.service.*; import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.data.util.Pair; @@ -49,6 +55,8 @@ import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser; @Service public class PayConsumeTradeServiceImpl extends BaseServiceImpl implements PayConsumeTradeService { + private static final Logger logger = LoggerFactory.getLogger(PayConsumeTradeServiceImpl.class); + @Autowired private PayUserResourceService payUserResourceService; @@ -181,7 +189,7 @@ public class PayConsumeTradeServiceImpl extends BaseServiceImpl depositQueryWrapper = new QueryWrapper<>(); depositQueryWrapper.apply(orderId != null, "FIND_IN_SET ('" + orderId + "', order_id )"); + + // 重要:充值记录 PayConsumeDeposit consumeRow = payConsumeDepositService.findOne(depositQueryWrapper); // log.debug("查询到的在线支付信息: {}", consumeRow); @@ -1353,12 +1363,42 @@ public class PayConsumeTradeServiceImpl extends BaseServiceImpl refundResult = lakalaPayService.innerLklRefund( storeId, returnRow.getReturn_id(), // 退货单ID - depositTradeNo, + outTradeNo, String.valueOf(moneyDifference.multiply(BigDecimal.valueOf(100)).intValue()), // 单位:分 - returnRow.getReturn_buyer_message() + returnRow.getReturn_buyer_message(), + lklMerchantNo, + lklTermNo ); if (!refundResult.getFirst()) { @@ -2049,4 +2091,43 @@ public class PayConsumeTradeServiceImpl extends BaseServiceImpl subTradeNos = getLklSubTradeNo(outSplitRspInfos); + if (subTradeNos != null) { + payConsumeDeposit.setOrd_sub_trade_no(subTradeNos.getFirst()); + payConsumeDeposit.setDf_sub_trade_no(subTradeNos.getSecond()); + } } // 判断是否联合支付 @@ -1752,51 +1760,51 @@ public class PayUserPayServiceImpl extends BaseServiceImpl resp = lakalaPayService.innerLklRefund( - orderReturn.getStore_id(), - orderReturn.getReturn_id(), - deposit_trade_no, - Integer.toString(NumberUtil.mul(orderReturn.getTrade_payment_amount(), 100).intValue()), - orderReturn.getReturn_buyer_message() - ); - - if (!resp.getFirst()) { - LogUtil.error("拉卡拉退款异常: " + resp.getSecond()); - return null; - } - - // 解析响应并获取退单号 - String lklTradeNo = ""; - try { - JSONObject respData = StrUtil.isNotBlank(resp.getSecond()) ? JSONObject.parseObject(resp.getSecond()) : null; - lklTradeNo = respData != null ? respData.getString("trade_no") : ""; - } catch (Exception e) { - LogUtil.error("解析拉卡拉退款响应数据失败: " + e.getMessage()); - return null; - } - - // 构造并返回结果 - String finalLklTradeNo = lklTradeNo; - return new ShopOrderReturn() {{ - setReturn_channel_flag(1); - setDeposit_trade_no(deposit_trade_no); - setOrder_id(orderReturn.getOrder_id()); - setReturn_channel_trans_id(finalLklTradeNo); - setReturn_channel_time(new Date()); - }}; - } +// /** +// * 拉卡拉退款 +// * +// * @param orderReturn +// * @return ShopOrderReturn 退款结果信息 +// */ +// @Override +// public ShopOrderReturn lklPayRefund(ShopOrderReturn orderReturn) { +// // 获取并处理交易号 +// String deposit_trade_no = CheckUtil.isEmpty(orderReturn.getDeposit_trade_no()) ? "" : orderReturn.getDeposit_trade_no(); +// +// // 调用拉卡拉退款接口 +// Pair resp = lakalaPayService.innerLklRefund( +// orderReturn.getStore_id(), +// orderReturn.getReturn_id(), +// deposit_trade_no, +// Integer.toString(NumberUtil.mul(orderReturn.getTrade_payment_amount(), 100).intValue()), +// orderReturn.getReturn_buyer_message() +// ); +// +// if (!resp.getFirst()) { +// LogUtil.error("拉卡拉退款异常: " + resp.getSecond()); +// return null; +// } +// +// // 解析响应并获取退单号 +// String lklTradeNo = ""; +// try { +// JSONObject respData = StrUtil.isNotBlank(resp.getSecond()) ? JSONObject.parseObject(resp.getSecond()) : null; +// lklTradeNo = respData != null ? respData.getString("trade_no") : ""; +// } catch (Exception e) { +// LogUtil.error("解析拉卡拉退款响应数据失败: " + e.getMessage()); +// return null; +// } +// +// // 构造并返回结果 +// String finalLklTradeNo = lklTradeNo; +// return new ShopOrderReturn() {{ +// setReturn_channel_flag(1); +// setDeposit_trade_no(deposit_trade_no); +// setOrder_id(orderReturn.getOrder_id()); +// setReturn_channel_trans_id(finalLklTradeNo); +// setReturn_channel_time(new Date()); +// }}; +// } /** * 微信退款成功回调 @@ -1823,7 +1831,7 @@ public class PayUserPayServiceImpl extends BaseServiceImpl 返回包含商品订单子流水号和运费子流水号的二元组 + * 若没有找到对应类型,返回的两个元素可能为null + * 若出现异常或输入无效,返回null + */ + protected Pair getLklSubTradeNo(String outSplitRspInfos) { + // 1. 输入参数校验 + if (StrUtil.isBlank(outSplitRspInfos)) { + logger.warn("获取子流水号失败:输入的分账信息为空"); + return null; + } + + // 2. 解析JSON数组 + JSONArray outSplitRspInfoArray; + try { + outSplitRspInfoArray = JSONUtil.parseArray(outSplitRspInfos); + } catch (Exception e) { + logger.error("JSON解析失败:{}", outSplitRspInfos, e); + return null; + } + + if (outSplitRspInfoArray.isEmpty()) { + logger.warn("获取子流水号失败:分账信息数组为空"); + return null; + } + + // 3. 初始化结果变量 + String productSubTradeNo = null; // 商品订单子流水号 + String shippingSubTradeNo = null; // 运费子流水号 + + // 4. 遍历JSON数组 + for (cn.hutool.json.JSONObject item : outSplitRspInfoArray.jsonIter()) { + // 5. 获取out_sub_trade_no字段 + String outSubTradeNo = item.getStr("out_sub_trade_no"); + if (StrUtil.isBlank(outSubTradeNo)) { + logger.warn("分账信息缺少out_sub_trade_no字段:{}", item); + continue; + } + + // 6. 根据前缀分类处理 + if (outSubTradeNo.startsWith("ORD-")) { + // 处理商品订单 + if (productSubTradeNo != null) { + logger.warn("检测到重复的商品订单子流水号:{}", outSubTradeNo); + } + productSubTradeNo = item.getStr("sub_trade_no"); + } else if (outSubTradeNo.startsWith("DF-")) { + // 处理运费订单 + if (shippingSubTradeNo != null) { + logger.warn("检测到重复的运费子流水号:{}", outSubTradeNo); + } + shippingSubTradeNo = item.getStr("sub_trade_no"); + } else { + logger.warn("未知前缀的out_sub_trade_no:{}", outSubTradeNo); + } + } + + // 7. 严格校验:必须同时存在商品和运费子流水号 + if (StrUtil.isNotBlank(productSubTradeNo) && StrUtil.isNotBlank(shippingSubTradeNo)) { + return Pair.of(productSubTradeNo, shippingSubTradeNo); + } else { + logger.warn("必须同时存在商品订单和运费子流水号,当前结果:商品={}, 运费={}", + productSubTradeNo, shippingSubTradeNo); + return null; + } + } + } diff --git a/mall-pay/src/main/resources/mapper/pay/PayConsumeDepositMapper.xml b/mall-pay/src/main/resources/mapper/pay/PayConsumeDepositMapper.xml index 5c150976..551b70b3 100644 --- a/mall-pay/src/main/resources/mapper/pay/PayConsumeDepositMapper.xml +++ b/mall-pay/src/main/resources/mapper/pay/PayConsumeDepositMapper.xml @@ -4,7 +4,7 @@ deposit_id - , order_id, deposit_trade_no, payment_channel_id, app_id, server_id, deposit_app_id_channel, deposit_subject, deposit_payment_type, deposit_trade_status, deposit_seller_id, deposit_seller_email, deposit_buyer_id, deposit_buyer_email, currency_id, currency_symbol_left, deposit_total_fee, deposit_quantity, deposit_price, deposit_body, deposit_gmt_create, deposit_gmt_payment, deposit_gmt_close, deposit_is_total_fee_adjust, deposit_use_coupon, deposit_discount, deposit_notify_time, deposit_notify_type, deposit_notify_id, deposit_sign_type, deposit_sign, deposit_extra_param, lkl_combine_params, deposit_service, deposit_state, deposit_async, deposit_review, deposit_enable, store_id, user_id, chain_id + , order_id, deposit_trade_no, ord_sub_trade_no, df_sub_trade_no, payment_channel_id, app_id, server_id, deposit_app_id_channel, deposit_subject, deposit_payment_type, deposit_trade_status, deposit_seller_id, deposit_seller_email, deposit_buyer_id, deposit_buyer_email, currency_id, currency_symbol_left, deposit_total_fee, deposit_quantity, deposit_price, deposit_body, deposit_gmt_create, deposit_gmt_payment, deposit_gmt_close, deposit_is_total_fee_adjust, deposit_use_coupon, deposit_discount, deposit_notify_time, deposit_notify_type, deposit_notify_id, deposit_sign_type, deposit_sign, deposit_extra_param, lkl_combine_params, deposit_service, deposit_state, deposit_async, deposit_review, deposit_enable, store_id, user_id, chain_id