feat:第三方扣款、退款

This commit is contained in:
dzc committed 2025-05-06 15:42:12 +08:00
1 parent 68419fe9ac
commit e695b288d0
27 files changed
+705 -86

No files matched your search

@@ -20,6 +20,7 @@ public enum HardTypeEnum {
READ_CARD("3", "读卡器"),
DOOR("4", "生鲜柜"),
MERCHANT_QR_CODE("5", "商户二维码扣款牌"),
OTHER("6","其他支付方式(第三方接口对接)")
;
private final String type;
private final String description;
@@ -18,6 +18,7 @@ public enum HardOrderTypeEnum {
FOREIGN_TICKET("3", "外来人员"),
STRIDE_DEPT("4", "跨单位"),
ZY("5", "正元公务餐"),
LB("5", "宁波银行(美好生活)"),
;
private final String type;
private final String description;
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.hard.api.feign;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import com.bsd.pjcloud.thirdparty.dto.pay.RefundDto;
import com.bsd.pjcloud.thirdparty.vo.pay.PayBaseVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayVo;
import com.bsd.pjcloud.thirdparty.vo.pay.RefundVo;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(contextId = "remoteHardThirdPayService", value = ServiceNameConstants.HARDWARE_SERVICE)
public interface RemoteHardThirdPayService {
/**
* 第三方支付
*/
@PostMapping("/hard-third-pay/wallet-refund")
PayBaseVo<PayVo> walletPay(@RequestBody PayDto dto, WalletTypeEnum walletType);
/**
* 第三方退款
*/
@PostMapping("/hard-third-pay/wallet-refund")
PayBaseVo<RefundVo> walletRefund(@RequestBody RefundDto dto);
}
@@ -1,4 +1,5 @@
com.bsd.pjcloud.hard.api.feign.RemoteHardDoorService
com.bsd.pjcloud.hard.api.feign.RemoteHardOrderService
com.bsd.pjcloud.hard.api.feign.RemoteHardSpuController
com.bsd.pjcloud.hard.api.feign.RemoteHardThirdPayService
com.bsd.pjcloud.hard.api.feign.RemoteHardThirdSelfZyService
@@ -0,0 +1,14 @@
package com.bsd.pjcloud.hard.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@ConfigurationProperties(prefix = "lb-callback")
@Component
public class LbCallbackConfig {
private Long mchId;
}
@@ -1,4 +1,4 @@
package com.bsd.pjcloud.hard.zy.config;
package com.bsd.pjcloud.hard.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -0,0 +1,47 @@
package com.bsd.pjcloud.hard.controller.third;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.hard.service.HardThirdPayService;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import com.bsd.pjcloud.thirdparty.dto.pay.RefundDto;
import com.bsd.pjcloud.thirdparty.vo.pay.PayBaseVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayVo;
import com.bsd.pjcloud.thirdparty.vo.pay.RefundVo;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/hard-third-pay")
@Tag(description = "hard-third-pay", name = "第三方支付调用")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class HardThirdPayController {
private final HardThirdPayService hardThirdPayService;
@Inner
@PostMapping("/wallet-pay")
@SysLog("第三方支付")
@Operation(summary = "第三方支付")
public PayBaseVo<PayVo> walletPay(@RequestBody PayDto dto, WalletTypeEnum walletType) {
return hardThirdPayService.walletPay(dto, walletType);
}
@Inner
@PostMapping("/wallet-refund")
@SysLog("第三方退款")
@Operation(summary = "第三方退款")
public PayBaseVo<RefundVo> walletRefund(@RequestBody RefundDto dto) {
return hardThirdPayService.walletRefund(dto);
}
}
@@ -10,6 +10,7 @@ import com.bsd.pjcloud.hard.api.dto.order.HardCreateCarDto;
import com.bsd.pjcloud.hard.api.entity.order.HardCarEntity;
import com.bsd.pjcloud.hard.api.entity.order.HardParentCarEntity;
import com.bsd.pjcloud.hard.api.vo.order.HardCarVo;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@@ -39,6 +40,10 @@ public interface HardCarService extends IService<HardCarEntity> {
*/
Long createCard(Long userId, Long merchantId, HardTypeEnum sysType, HardOrderTypeEnum orderTYpe, List<HardCreateCarDto> list);
/**
* 创建第三方支付购物车
*/
Long createThiredPayCard(Long userId, Long merchantId, List<PayDto.Product> list);
/**
* 生成购买商品的描述
@@ -0,0 +1,21 @@
package com.bsd.pjcloud.hard.service;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import com.bsd.pjcloud.thirdparty.dto.pay.RefundDto;
import com.bsd.pjcloud.thirdparty.vo.pay.PayBaseVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayVo;
import com.bsd.pjcloud.thirdparty.vo.pay.RefundVo;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
public interface HardThirdPayService {
/**
* 第三方支付
*/
PayBaseVo<PayVo> walletPay(PayDto dto, WalletTypeEnum walletType);
/**
* 第三方退款
*/
PayBaseVo<RefundVo> walletRefund(RefundDto dto);
}
@@ -349,7 +349,7 @@ public class HardDoorFlowServiceImpl extends ServiceImpl<HardDoorFlowMapper, Har
HardDoorFlowEntity updateObj = HardDoorFlowEntity.builder().id(flowDetail.getId()).flowStatus(HardDoorFlowEnum.FLOW_CALLBACK_RECOGNITION.getType()).skuCallbackVo(JSONUtil.toJsonStr(dto)).skuCallbackTime(LocalDateTime.now()).build();
String ksFlowCode = dto.getState().getCode();
String ksRecognitionCode = dto.getResult().getStatus().getCode();
if (ObjectUtil.notEqual(ksFlowCode, "2") || ObjectUtil.notEqual(ksRecognitionCode, "2")) {
if (ObjectUtil.notEqual(ksFlowCode, "2") || ObjectUtil.notEqual(ksFlowCode, "5") || ObjectUtil.notEqual(ksRecognitionCode, "2")) {
if (ObjectUtil.equals(ksFlowCode, "2") && ObjectUtil.equals(ksRecognitionCode, "3")) {
// 未拿商品取消订单
hardOrderService.cancelHardDoorOrder(flowDetail.getOrderId());
@@ -6,6 +6,7 @@ import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderStatusEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderTypeEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardPayTypeEnum;
@@ -26,7 +27,7 @@ import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleLogMapper;
import com.bsd.pjcloud.hard.service.HardAfterSaleService;
import com.bsd.pjcloud.hard.service.HardOrderService;
import com.bsd.pjcloud.hard.service.HardThirdSelfZyService;
import com.bsd.pjcloud.hard.zy.config.ZyCallbackConfig;
import com.bsd.pjcloud.hard.config.ZyCallbackConfig;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.feign.RemoteMchService;
@@ -68,13 +69,10 @@ public class HardThirdSelfZyServiceImpl implements HardThirdSelfZyService {
try {
// 查询用户
UserInfo userDetail = remoteUserService.getInnerByUserId(dto.getUserId()).getData();
if (ObjectUtil.isEmpty(userDetail)) throw new RuntimeException("用户不存在不存在");
if (ObjectUtil.isEmpty(userDetail)) throw new RuntimeException("用户不存在");
// 钱包
WalletManageEntity wallet = remoteWalletService.getWallet(dto.getUserId(), WalletTypeEnum.EAT).getData();
if (ObjectUtil.isEmpty(wallet)) throw new RuntimeException("用户钱包不存在");
// 用户
UserInfo user = remoteUserService.getInnerByUserId(wallet.getUserId()).getData();
if (ObjectUtil.isEmpty(user)) throw new RuntimeException("用户不存在");
// 查询正元默认商户
MchManageVo zyDefaultMch = getZyDefaultMch();
if (ObjectUtil.isEmpty(zyDefaultMch)) throw new RuntimeException("默认商户不存在");
@@ -95,7 +93,7 @@ public class HardThirdSelfZyServiceImpl implements HardThirdSelfZyService {
.merchantDeptId(zyDefaultMch.getDeptId())
.userDeptId(userDetail.getSysUser().getDeptId())
.parentCarId(null)
.sysType(null)
.sysType(HardTypeEnum.OTHER.getType())
.orderStatus(HardOrderStatusEnum.CREATE.getType())
.orderType(HardOrderTypeEnum.ZY.getType())
.payType(HardPayTypeEnum.WALLET_CARD.getType())
@@ -114,8 +112,8 @@ public class HardThirdSelfZyServiceImpl implements HardThirdSelfZyService {
HardCallbackPayVo result = new HardCallbackPayVo();
result.setCode(HardCallbackPayStatus.SUCCESS);
result.setOrderId(orderObj.getId());
result.setUserId(user.getSysUser().getUserId());
result.setUsername(user.getSysUser().getName());
result.setUserId(userDetail.getSysUser().getUserId());
result.setUsername(userDetail.getSysUser().getName());
result.setPayTime(LocalDateTime.now());
return result;
} catch (Exception e) {
@@ -11,6 +11,7 @@ import com.bsd.pjcloud.canteen.api.feign.RemoteOnlineCanteenSpuService;
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
import com.bsd.pjcloud.canteen.vo.app.CanteenOnlineSpuAppVO;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.hard.api.constant.HardProductNatureEnum;
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderStatusEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderTypeEnum;
@@ -29,6 +30,7 @@ import com.bsd.pjcloud.hard.service.HardCarService;
import com.bsd.pjcloud.hard.service.HardSpuService;
import com.bsd.pjcloud.supermarket.api.feign.RemoteSupermarketUnitSkuService;
import com.bsd.pjcloud.supermarket.vo.erp.SmErpUnitWithSpuVo;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -208,6 +210,9 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
};
}
/**
* 创建购物车
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Long createCard(Long userId, Long merchantId, HardTypeEnum sysType, HardOrderTypeEnum orderTYpe, List<HardCreateCarDto> list) {
@@ -218,6 +223,25 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
return parentCarObj.getId();
}
/**
* 创建第三方支付购物车
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Long createThiredPayCard(Long userId, Long merchantId, List<PayDto.Product> list) {
// 创建父订单
HardParentCarEntity parentCarObj = HardParentCarEntity.builder().userId(userId).merchantId(merchantId).hardType(HardTypeEnum.OTHER.getType()).orderStatus(HardOrderStatusEnum.PAY.getType()).build();
hardParentCarMapper.insert(parentCarObj);
List<HardCarEntity> carObjectList = new ArrayList<>();
for (PayDto.Product v : list) {
Long totalPrice = BigDecimal.valueOf(v.getCount()).multiply(BigDecimal.valueOf(v.getPrice())).setScale(0, RoundingMode.CEILING).longValue();
HardCarEntity item = HardCarEntity.builder().parentCarId(parentCarObj.getId()).productName(v.getName()).productPic(v.getPic()).productPrice(v.getPrice()).productNature(HardProductNatureEnum.DEFAULT.getType()).productCount(v.getCount()).totalPrice(totalPrice).build();
carObjectList.add(item);
}
baseMapper.insert(carObjectList);
return parentCarObj.getId();
}
@Transactional(rollbackFor = Exception.class)
public void appCreateCard(Long parentCarId, HardTypeEnum sysType, HardOrderTypeEnum orderType, List<HardCreateCarDto> productList) {
List<Long> productIds = productList.stream().map(HardCreateCarDto::getProductId).toList();
@@ -0,0 +1,251 @@
package com.bsd.pjcloud.hard.service.impl.third;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderStatusEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardOrderTypeEnum;
import com.bsd.pjcloud.hard.api.constant.order.HardPayTypeEnum;
import com.bsd.pjcloud.hard.api.constant.refund.HardAfterSaleOperateTypeEnum;
import com.bsd.pjcloud.hard.api.constant.refund.HardSaleStatusEnum;
import com.bsd.pjcloud.hard.api.constant.refund.HardSaleTypeEnum;
import com.bsd.pjcloud.hard.api.constant.third.HardCallbackPayStatus;
import com.bsd.pjcloud.hard.api.dto.order.HardCreateCarDto;
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleEntity;
import com.bsd.pjcloud.hard.api.entity.order.HardAfterSaleLogEntity;
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
import com.bsd.pjcloud.hard.api.vo.order.HardOrderVo;
import com.bsd.pjcloud.hard.api.vo.third.HardCallbackPayVo;
import com.bsd.pjcloud.hard.config.LbCallbackConfig;
import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleLogMapper;
import com.bsd.pjcloud.hard.service.HardAfterSaleService;
import com.bsd.pjcloud.hard.service.HardCarService;
import com.bsd.pjcloud.hard.service.HardOrderService;
import com.bsd.pjcloud.hard.service.HardThirdPayService;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayVoStatusEnum;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import com.bsd.pjcloud.thirdparty.dto.pay.RefundDto;
import com.bsd.pjcloud.thirdparty.vo.pay.PayBaseVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayVo;
import com.bsd.pjcloud.thirdparty.vo.pay.RefundVo;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.feign.RemoteMchService;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletService;
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTimeUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
@Validated
@Slf4j
@RequiredArgsConstructor
@Service
public class HardThirdPayServiceImpl implements HardThirdPayService {
private static final String TAG = "第三方支付";
private final LbCallbackConfig lbCallbackConfig;
private final HardOrderService hardOrderService;
private final HardCarService hardCarService;
private final HardAfterSaleService hardAfterSaleService;
private final HardAfterSaleLogMapper hardAfterSaleLogMapper;
private final RemoteMchService mchService;
private final RemoteUserService userService;
private final RemoteWalletService walletService;
/**
* 第三方支付
*/
@Override
@Transactional(rollbackFor = Exception.class)
public PayBaseVo<PayVo> walletPay(PayDto dto, WalletTypeEnum walletType) {
PayBaseVo baseVo = new PayBaseVo<PayVo>();
try {
// 默认商户
MchManageVo defaultMch = getLbDefaultMch();
if (ObjectUtil.isEmpty(defaultMch)) {
baseVo.set(ThirdPayVoStatusEnum.MCH_EXIST);
return baseVo;
}
// 查询用户
UserInfo userDetail = userService.getInnerByUserId(dto.getUserId()).getData();
if (ObjectUtil.isEmpty(userDetail)) {
baseVo.set(ThirdPayVoStatusEnum.USER_EXIST);
return baseVo;
}
// 钱包
WalletManageEntity wallet = walletService.getWallet(dto.getUserId(), walletType).getData();
if (ObjectUtil.isEmpty(wallet)) {
baseVo.set(ThirdPayVoStatusEnum.USER_EXIST);
return baseVo;
}
// 判断余额
Integer balance = wallet.getBalance();
int totalPrice = dto.getPrice();
if (balance < totalPrice) {
baseVo.set(ThirdPayVoStatusEnum.BALANCE_NOT_ENOUGH);
return baseVo;
}
List<PayDto.Product> products = dto.getProducts();
for (PayDto.Product v : products) {
if (!ObjectUtil.isAllNotEmpty(v.getName(), v.getCount(), v.getPrice())) continue;
products.add(v);
}
String subject = "美好生活物品 * ?";
Integer orderTotalPrice = dto.getPrice();
Long parentCarId = null;
if (ObjectUtil.isNotEmpty(products)) {
subject = generateSubDesc(products);
orderTotalPrice = computeCartTotalPrice(products);
// 添加购物车
parentCarId = hardCarService.createThiredPayCard(dto.getUserId(), defaultMch.getId(), products);
}
// 添加硬件订单
HardOrderEntity orderObj = HardOrderEntity.builder()
.orderNo(dto.getThirdOrderId())
.parentCarId(parentCarId)
.userId(dto.getUserId())
.merchantId(defaultMch.getId())
.merchantType(defaultMch.getTypes())
.merchantDeptId(defaultMch.getDeptId())
.userDeptId(userDetail.getSysUser().getDeptId())
.sysType(HardTypeEnum.OTHER.getType())
.orderStatus(HardOrderStatusEnum.CREATE.getType())
.orderType(HardOrderTypeEnum.LB.getType())
.payType(HardPayTypeEnum.WALLET_CARD.getType())
.orderSourceTypeId(null)
.totalPrice(orderTotalPrice)
.extraPrice(0)
.payPrice(dto.getPrice())
.isOfflineOrder(YesNoEnum.NO.getCode())
.productDesc(subject)
.walletId(wallet.getId())
.build();
log.info("|{}|第三方支付订单参数|{}", TAG, JSONUtil.toJsonStr(orderObj));
hardOrderService.save(orderObj);
hardOrderService.fastReduceBalance(orderObj);
// 返回
baseVo.set(ThirdPayVoStatusEnum.SUCCESS);
PayVo payVo = new PayVo();
payVo.setPayTime(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
payVo.setThirdOrderId(dto.getThirdOrderId());
payVo.setPlatformOrderId(String.valueOf(orderObj.getId()));
baseVo.setData(payVo);
return baseVo;
} catch (Exception e) {
log.error("|{}|第三方支付异常dto|{}|walletType|{}|error|{}|", TAG, JSONUtil.toJsonStr(dto), walletType, e.getMessage());
baseVo.set(ThirdPayVoStatusEnum.UNKNOWN_ERROR);
return baseVo;
}
}
/**
* 第三方退款
*/
@Override
@Transactional(rollbackFor = Exception.class)
public PayBaseVo<RefundVo> walletRefund(RefundDto dto) {
PayBaseVo baseVo = new PayBaseVo<RefundVo>();
try {
// 查询用户
UserInfo userDetail = userService.getInnerByUserId(dto.getUserId()).getData();
if (ObjectUtil.isEmpty(userDetail)) {
baseVo.set(ThirdPayVoStatusEnum.USER_EXIST);
return baseVo;
}
HardOrderVo order = hardAfterSaleService.validateOrderCanRefund(Long.valueOf(dto.getPlatformOrderId()));
if (ObjectUtil.isEmpty(userDetail)) {
baseVo.set(ThirdPayVoStatusEnum.ORDER_EXIST);
return baseVo;
}
// 添加售后订单
HardAfterSaleEntity saveObj = BeanUtils.toBean(dto, HardAfterSaleEntity.class);
saveObj.setType(HardSaleTypeEnum.AFTER_SALE.getType());
saveObj.setStatus(HardSaleStatusEnum.COMPLETE.getStatus());
saveObj.setNo(dto.getThirdRefundId());
saveObj.setWay("10");
saveObj.setOrderId(order.getId());
saveObj.setRefundPrice(order.getPayPrice());
saveObj.setUserId(order.getUserId());
saveObj.setAuditUserId(null);
saveObj.setAuditTime(LocalDateTime.now());
saveObj.setApplyReason("宁波银行(美好生活)api退款");
saveObj.setAuditReason("宁波银行(美好生活)api退款");
saveObj.setApplyDescription("宁波银行(美好生活)api退款");
hardAfterSaleService.save(saveObj);
// 添加售后进度
HardAfterSaleLogEntity saveLogObj = HardAfterSaleLogEntity.builder()
.afterSaleId(saveObj.getId())
.userId(order.getUserId())
.operateType(HardAfterSaleOperateTypeEnum.ADMIN_REFUND.getType())
.beforeStatus(order.getOrderStatus())
.afterStatus(HardSaleStatusEnum.COMPLETE.getStatus())
.content(HardSaleStatusEnum.COMPLETE.getContent())
.build();
hardAfterSaleLogMapper.insert(saveLogObj);
// 执行退款
hardAfterSaleService.refund(saveObj.getId(), "zy");
// 返回
RefundVo refundVo = new RefundVo();
refundVo.setRefundTime(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
refundVo.setThirdOrderId(order.getOrderNo());
refundVo.setThirdRefundId(dto.getThirdRefundId());
refundVo.setPlatformOrderId(dto.getPlatformOrderId());
refundVo.setPlatformRefundId(String.valueOf(saveObj.getId()));
baseVo.set(ThirdPayVoStatusEnum.SUCCESS);
baseVo.setData(refundVo);
return baseVo;
} catch (Exception e) {
log.error("|{}|第三方退款dto|{}|error|{}|", TAG, JSONUtil.toJsonStr(dto), e.getMessage());
baseVo.set(ThirdPayVoStatusEnum.UNKNOWN_ERROR);
return baseVo;
}
}
/**
* 查询美好生活默认商户信息
*/
private MchManageVo getLbDefaultMch() {
MchManageVo defaultMch = mchService.getById(lbCallbackConfig.getMchId()).getData();
log.info("查询美好生活默认商户信息 mchId |{}|defaultMch{}", lbCallbackConfig.getMchId(), JSONUtil.toJsonStr(defaultMch));
return defaultMch;
}
/**
* 生成商品简介
*/
private String generateSubDesc(List<PayDto.Product> list) {
StringBuilder str = new StringBuilder();
for (PayDto.Product v : list) {
str.append(v.getName()).append("x").append(v.getCount()).append(";");
}
return str.toString();
}
/**
* 计算购物车总价
*/
private int computeCartTotalPrice(List<PayDto.Product> list) {
int totalPrice = 0;
for (PayDto.Product v : list) {
totalPrice += v.getPrice() * v.getCount();
}
return totalPrice;
}
}
@@ -12,9 +12,9 @@ import java.util.Arrays;
@AllArgsConstructor
@Getter
public enum ThirdPayApiBizTypeEnum {
PAY("10", "支付"),
REFUND("20", "退款"),
WALLET_INFO("30", "钱包详情"),
;
private final String type;
@@ -18,7 +18,9 @@ public enum ThirdPayVoStatusEnum {
PARAMS_ERROR(500, "参数错误"),
USER_EXIST(501, "用户不存在"),
ORDER_EXIST(502, "订单不存在"),
MCH_EXIST(503, "第三方商户未配置"),
BALANCE_NOT_ENOUGH(505, "用户余额不足"),
UNKNOWN_ERROR(599, "未知错误,请联系管理员"),
;
private final int type;
@@ -3,6 +3,8 @@ package com.bsd.pjcloud.thirdparty.dto.pay;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPaySoureBizTypeEnum;
import lombok.Data;
import java.util.List;
@Data
public class PayDto {
@@ -12,18 +14,43 @@ public class PayDto {
private Long userId;
/**
* 第三方唯一id
* 第三方订单唯一id
*/
private String thirdOrderId;
/**
* 第三方统一支付类型
*/
private ThirdPaySoureBizTypeEnum bizType;
/**
* 扣款金额(分)
*/
private Integer money;
private Integer price;
/**
* 商品列表
*/
private List<Product> products;
@Data
public static class Product {
/**
* 商品名
*/
private String name;
/**
* 商品图片(网络可访问)
*/
private String pic;
/**
* 商品价格(分)
*/
private Integer price;
/**
* 商品数量
*/
private Integer count;
}
}
@@ -15,4 +15,14 @@ public class RefundDto {
*/
private String platformOrderId;
/**
* 第三方退款单号
*/
private String thirdRefundId;
/**
* 退款金额(单位分)
*/
private Integer price;
}
@@ -18,8 +18,8 @@ public class PayVo {
private String thirdOrderId;
/**
* 平台支付时间
* 平台支付时间(yyyy-MM-dd HH:mm:ss格式)
*/
private LocalDateTime payTime;
private String payTime;
}
@@ -5,6 +5,11 @@ import lombok.Data;
@Data
public class PayWalletInfoVo {
/**
* 用户id
*/
private String userId;
/**
* sm4加密后的手机号
*/
@@ -2,7 +2,6 @@ package com.bsd.pjcloud.thirdparty.vo.pay;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class RefundVo {
@@ -22,9 +21,19 @@ public class RefundVo {
*/
private String thirdOrderId;
/**
* 第三方唯一退款单号
*/
private String thirdRefundId;
/**
* 退款金额(单位分)
*/
private Integer price;
/**
* 退款时间
*/
private LocalDateTime refundTime;
private String refundTime;
}
@@ -7,7 +7,9 @@ import com.bsd.pjcloud.thirdparty.dto.pay.PayGetWalletInfoDto;
import com.bsd.pjcloud.thirdparty.service.ThirdPayApiService;
import com.bsd.pjcloud.thirdparty.service.ThirdPayLogService;
import com.bsd.pjcloud.thirdparty.vo.pay.PayBaseVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayVo;
import com.bsd.pjcloud.thirdparty.vo.pay.PayWalletInfoVo;
import com.bsd.pjcloud.thirdparty.vo.pay.RefundVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -39,8 +41,21 @@ public class ThirdPayApiController {
return thirdPayApiService.getWalletInfo(body, headers);
}
@Inner(value = false)
@Operation(summary = "通过userId扣款", description = "通过userId扣款")
@PostMapping("/wallet-pay")
public PayBaseVo<PayVo> walletPay(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
String body = httpEntity.getBody();
return thirdPayApiService.walletPay(body, headers);
}
@Inner(value = false)
@Operation(summary = "退款", description = "退款")
@PostMapping("/wallet-refund")
public PayBaseVo<RefundVo> walletRefund(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers) {
String body = httpEntity.getBody();
return thirdPayApiService.walletRefund(body, headers);
}
}
@@ -11,14 +11,14 @@ import java.util.Map;
public interface ThirdPayApiService {
/**
* 扣款
* 通过userId扣款
*/
PayVo pay(PayDto dto);
PayBaseVo<PayVo> walletPay(String json, Map<String, String> headers);
/**
* 退款
*/
RefundVo refund(RefundDto dto);
PayBaseVo<RefundVo> walletRefund(String json, Map<String, String> headers);
/**
* 根据加密的手机号查询用户钱包信息
@@ -1,6 +1,7 @@
package com.bsd.pjcloud.thirdparty.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.thirdparty.constant.ThirdKeyTypeEnum;
@@ -19,13 +20,14 @@ import java.util.Map;
/**
* 第三方密钥管理
*/
@Service
@Validated
@Slf4j
@RequiredArgsConstructor
public class ThirdKeyManageServiceImpl extends ServiceImpl<ThirdKeyManageMapper, ThirdKeyManageEntity> implements ThirdKeyManageService {
private static final String TAG = "第三方密钥管理";
/**
* 新增
*/
@@ -61,38 +63,43 @@ public class ThirdKeyManageServiceImpl extends ServiceImpl<ThirdKeyManageMapper,
*/
@Override
public ThirdKeyManageEntity checkSignAndReturnConfig(String json, Map<String, String> headers) {
Map<String, String> transitionHeader = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : headers.entrySet()) {
transitionHeader.put(entry.getKey().toUpperCase(), entry.getValue());
}
// 验签
String appKey = transitionHeader.get("APP-KEY");
String bizType = transitionHeader.get("BIZ-TYPE");
String timestamp = transitionHeader.get("TIMESTAMP");
String sign = transitionHeader.get("SIGN");
if (!ObjectUtil.isAllNotEmpty(appKey, bizType, timestamp, sign)) {
try {
Map<String, String> transitionHeader = new LinkedHashMap<>();
for (Map.Entry<String, String> entry : headers.entrySet()) {
transitionHeader.put(entry.getKey().toUpperCase(), entry.getValue());
}
// 验签
String appKey = transitionHeader.get("APP-KEY");
String bizType = transitionHeader.get("BIZ-TYPE");
String timestamp = transitionHeader.get("TIMESTAMP");
String sign = transitionHeader.get("SIGN");
if (!ObjectUtil.isAllNotEmpty(appKey, bizType, timestamp, sign)) {
return null;
}
// 判断bizType
ThirdKeyTypeEnum keyType = ThirdKeyTypeEnum.valueOfByType(bizType);
if (ObjectUtil.isEmpty(keyType)) {
return null;
}
// 查询配置
ThirdKeyManageEntity keyManage = get(keyType);
if (ObjectUtil.isEmpty(keyManage)) {
return null;
}
if (ObjectUtil.notEqual(appKey, keyManage.getAppKey())) {
return null;
}
// 计算签名
String signStr = String.format("%s%s%s%s%s", keyManage.getAppKey(), keyManage.getAppSecret(), keyManage.getBizType(), timestamp, json);
String successSignMd5 = DigestUtils.md5Hex(signStr);
if (ObjectUtil.notEqual(successSignMd5.toLowerCase(), sign.toLowerCase())) {
return null;
}
return keyManage;
} catch (Exception e) {
log.error("|{}|checkSign意外的错误,加密参数|{}|", TAG, JSONUtil.toJsonStr(json), e);
return null;
}
// 判断bizType
ThirdKeyTypeEnum keyType = ThirdKeyTypeEnum.valueOfByType(bizType);
if (ObjectUtil.isEmpty(keyType)) {
return null;
}
// 查询配置
ThirdKeyManageEntity keyManage = get(keyType);
if (ObjectUtil.isEmpty(keyManage)) {
return null;
}
if (ObjectUtil.notEqual(appKey, keyManage.getAppKey())) {
return null;
}
// 计算签名
String signStr = String.format("%s%s%s%s%s", keyManage.getAppKey(), keyManage.getAppSecret(), keyManage.getBizType(), timestamp, json);
String successSignMd5 = DigestUtils.md5Hex(signStr);
if (ObjectUtil.notEqual(successSignMd5.toLowerCase(), sign.toLowerCase())) {
return null;
}
return keyManage;
}
/**
@@ -2,8 +2,10 @@ package com.bsd.pjcloud.thirdparty.service.impl.pay;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.hard.api.feign.RemoteHardOrderService;
import com.bsd.pjcloud.hard.api.feign.RemoteHardThirdPayService;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayApiBizTypeEnum;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPaySoureBizTypeEnum;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayVoStatusEnum;
import com.bsd.pjcloud.thirdparty.dto.pay.PayDto;
import com.bsd.pjcloud.thirdparty.dto.pay.PayGetWalletInfoDto;
@@ -23,7 +25,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Arrays;
import java.util.Map;
/**
@@ -40,48 +41,106 @@ public class ThirdPayApiServiceImpl implements ThirdPayApiService {
private final ThirdKeyManageService keyManageService;
private final ThirdPayLogService payLogService;
private final RemoteWalletService remoteWalletService;
private final RemoteWalletService walletService;
private final RemoteHardOrderService hardOrderService;
private final RemoteHardThirdPayService hardThirdPayService;
/**
* 扣款
* 通过userId扣款
*/
@Override
public PayVo pay(PayDto dto) {
public PayBaseVo<PayVo> walletPay(String json, Map<String, String> headers) {
long startTime = System.currentTimeMillis();
ThirdPayLogEntity logObj = ThirdPayLogEntity.builder().apiUrl("/pay").bizType(ThirdPayApiBizTypeEnum.PAY.getType()).req(JSONUtil.toJsonStr(dto)).build();
PayVo vo = new PayVo();
try {
} catch (Exception e) {
String error = String.format("|%s||pay|意外的错误error|%s|stack|%s|", TAG, e.getMessage(), Arrays.toString(e.getStackTrace()));
log.error(error);
logObj.setError(error);
} finally {
ThirdPayLogEntity logObj = ThirdPayLogEntity.builder().apiUrl("/third/third-pay-api/wallet-pay").bizType(ThirdPayApiBizTypeEnum.PAY.getType()).paySourceType(ThirdPaySoureBizTypeEnum.LB.getType()).req(json).build();
// 开始处理
PayBaseVo baseVo = new PayBaseVo<PayVo>();
ThirdKeyManageEntity keySetting = keyManageService.checkSignAndReturnConfig(json, headers);
if (ObjectUtil.isEmpty(keySetting)) {
baseVo.set(ThirdPayVoStatusEnum.SIGN_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
PayDto dto = JSONUtil.toBean(json, PayDto.class);
if (ObjectUtil.isEmpty(dto)) {
baseVo.set(ThirdPayVoStatusEnum.PARAMS_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
// 开始支付
try {
PayBaseVo<PayVo> payVoPayBaseVo = hardThirdPayService.walletPay(dto, WalletTypeEnum.PAY);
if (ObjectUtil.isEmpty(payVoPayBaseVo)) {
throw new RuntimeException("支付失败");
}
// 日志
logObj.setRes(JSONUtil.toJsonStr(payVoPayBaseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return payVoPayBaseVo;
} catch (Exception e) {
log.error("|{}|通过userId扣款错误,json|{}|headers|{}|", TAG, json, headers, e);
baseVo.set(ThirdPayVoStatusEnum.UNKNOWN_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
return vo;
}
/**
* 退款
*/
@Override
public RefundVo refund(RefundDto dto) {
public PayBaseVo<RefundVo> walletRefund(String json, Map<String, String> headers) {
long startTime = System.currentTimeMillis();
ThirdPayLogEntity logObj = ThirdPayLogEntity.builder().apiUrl("/refund").bizType(ThirdPayApiBizTypeEnum.REFUND.getType()).req(JSONUtil.toJsonStr(dto)).build();
RefundVo vo = new RefundVo();
try {
} catch (Exception e) {
String error = String.format("|%s||refund|意外的错误error|%s|stack|%s|", TAG, e.getMessage(), Arrays.toString(e.getStackTrace()));
log.error(error);
logObj.setError(error);
} finally {
ThirdPayLogEntity logObj = ThirdPayLogEntity.builder().apiUrl("/third/third-pay-api/wallet-refund").bizType(ThirdPayApiBizTypeEnum.REFUND.getType()).paySourceType(ThirdPaySoureBizTypeEnum.LB.getType()).req(json).build();
// 开始处理
PayBaseVo baseVo = new PayBaseVo<RefundVo>();
ThirdKeyManageEntity keySetting = keyManageService.checkSignAndReturnConfig(json, headers);
if (ObjectUtil.isEmpty(keySetting)) {
baseVo.set(ThirdPayVoStatusEnum.SIGN_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
RefundDto dto = JSONUtil.toBean(json, RefundDto.class);
if (ObjectUtil.isEmpty(dto)) {
baseVo.set(ThirdPayVoStatusEnum.PARAMS_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
// 开始退款
try {
PayBaseVo<RefundVo> payVoPayBaseVo = hardThirdPayService.walletRefund(dto);
if (ObjectUtil.isEmpty(payVoPayBaseVo)) {
throw new RuntimeException("退款失败");
}
// 日志
logObj.setRes(JSONUtil.toJsonStr(payVoPayBaseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return payVoPayBaseVo;
} catch (Exception e) {
log.error("|{}|退款,json|{}|headers|{}|", TAG, json, headers, e);
baseVo.set(ThirdPayVoStatusEnum.UNKNOWN_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
return vo;
}
/**
@@ -89,27 +148,47 @@ public class ThirdPayApiServiceImpl implements ThirdPayApiService {
*/
@Override
public PayBaseVo<PayWalletInfoVo> getWalletInfo(String json, Map<String, String> headers) {
// 日志
long startTime = System.currentTimeMillis();
ThirdPayLogEntity logObj = ThirdPayLogEntity.builder().apiUrl("/third/third-pay-api/user-wallet").bizType(ThirdPayApiBizTypeEnum.WALLET_INFO.getType()).req(json).paySourceType(ThirdPaySoureBizTypeEnum.LB.getType()).build();
// 开始处理
PayBaseVo baseVo = new PayBaseVo<PayWalletInfoVo>();
ThirdKeyManageEntity keySetting = keyManageService.checkSignAndReturnConfig(json, headers);
if (ObjectUtil.isEmpty(keySetting)) {
baseVo.set(ThirdPayVoStatusEnum.SIGN_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
PayGetWalletInfoDto dto = JSONUtil.toBean(json, PayGetWalletInfoDto.class);
if (ObjectUtil.isEmpty(dto)) {
baseVo.set(ThirdPayVoStatusEnum.PARAMS_ERROR);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
// 手机号解密
String phone = LbRetailSignUtils.sm4Decrypt(dto.getSignPhone(), keySetting.getFieldKey());
if (ObjectUtil.isEmpty(phone)) {
baseVo.set(ThirdPayVoStatusEnum.PARAMS_ERROR);
baseVo.setMessage("手机号解迷失败");
baseVo.setMessage("手机号解析失败");
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
WalletManageVo userWallet = remoteWalletService.getWalletByUserPhone(phone, WalletTypeEnum.EAT).getData();
WalletManageVo userWallet = walletService.getWalletByUserPhone(phone, WalletTypeEnum.EAT).getData();
if (ObjectUtil.isEmpty(userWallet)) {
baseVo.set(ThirdPayVoStatusEnum.USER_EXIST);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
PayGetWalletInfoVo result = new PayGetWalletInfoVo();
@@ -119,6 +198,10 @@ public class ThirdPayApiServiceImpl implements ThirdPayApiService {
// 返回成功的参数
baseVo.set(ThirdPayVoStatusEnum.SUCCESS);
baseVo.setData(result);
// 日志
logObj.setRes(JSONUtil.toJsonStr(baseVo));
logObj.setTimeConsuming(Long.toString(System.currentTimeMillis() - startTime));
payLogService.save(logObj);
return baseVo;
}
@@ -0,0 +1,69 @@
package com.bsd.pjcloud.admin;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.thirdparty.ThirdPartyApp;
import com.bsd.pjcloud.thirdparty.constant.ThirdKeyTypeEnum;
import com.bsd.pjcloud.thirdparty.entity.ThirdKeyManageEntity;
import com.bsd.pjcloud.thirdparty.service.ThirdKeyManageService;
import com.bsd.pjcloud.thirdparty.utils.lb.tool.LbRetailSignUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@SpringBootTest(classes = ThirdPartyApp.class)
public class PayTest {
private final String baseUrl = "127.0.0.1:9999";
private final String walletInfoUrl = "/third/third-pay-api/user-wallet";
private final String walletPayUrl = "/third/third-pay-api/wallet-pay";
private final String walletRefundUrl = "/third/third-pay-api/wallet-refund";
@Resource
private ThirdKeyManageService keyManageService;
@Test
public void walletInfo() throws Exception {
ThirdKeyManageEntity config = getLbConfig();
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("signPhone", LbRetailSignUtils.sm4Encrypt("17683843144", config.getFieldKey()));
String body = JSONUtil.toJsonStr(paramsMap);
// 发送请求
String res = HttpRequest.post(baseUrl + walletInfoUrl).contentType("application/json").header(apiCountersign(body)).body(body).execute().body();
log.info(res);
}
private ThirdKeyManageEntity getLbConfig() {
return keyManageService.get(ThirdKeyTypeEnum.LB);
}
private Map<String, List<String>> apiCountersign(String body) {
ThirdKeyManageEntity config = getLbConfig();
String timestamp = String.valueOf(Instant.now().getEpochSecond());
Map<String, List<String>> headers = new HashMap<>();
headers.put("timestamp", List.of(timestamp));
headers.put("app-key", List.of(config.getAppKey()));
headers.put("biz-type", List.of(config.getBizType()));
headers.put("sign", List.of());
String signStr = String.format("%s%s%s%s%s", config.getAppKey(), config.getAppSecret(), config.getBizType(), timestamp, body);
String successSignMd5 = DigestUtils.md5Hex(signStr);
headers.put("sign", List.of(successSignMd5));
return headers;
}
}
@@ -124,7 +124,8 @@ public class SysUserController {
/**
* 根据手机号查询用户
*/
@GetMapping("/user/inner/phone")
@Inner
@GetMapping("/inner/phone")
R<UserInfo> getInnerByPhone(@RequestParam String phone) {
SysUser user = userService.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getPhone, phone));
if (user == null) return R.failed(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, phone));