## Feat - 京东支付

This commit is contained in:
sangelxiu1 2025-07-16 09:11:21 +08:00
parent e308911271
commit 7105312311
97 changed files with 4986 additions and 364 deletions

View File

@ -141,4 +141,12 @@ public interface ISaOrderServiceApi {
*/
R<BigDecimal> getRetailPayOrder(Long pkMember, String orderCode);
/**
* 根据订单编号查询订单信息
*
* @param orderCode 订单编号
* @param pkMember 会员主键
* @return
*/
R<SaOrderExt> queryOrderByCode(String orderCode, Long pkMember);
}

View File

@ -0,0 +1,9 @@
package com.hzs.third.pay;
import com.hzs.common.core.domain.R;
import com.hzs.common.domain.third.pay.TOnlinePayment;
public interface ITOnlinePaymentServiceApi {
R<TOnlinePayment> getOnlinePayment(String orderNo);
R<String> refund(String orderNo, Long userId);
}

View File

@ -123,11 +123,11 @@ public class ApiCuMemberWithdrawController extends BaseController {
return AjaxResult.error(TransactionUtils.getContent(FinanceMsgConstants.RECHAGER_USER_NOT_EXIST));
}
//获取银行卡信息
Long pkBank = cuMemberWithdrawParam.getPkBank();
CuMemberBankVO memberBank = cuMemberBankService.selectCuMemberBankById(pkBank);
if (null == memberBank) {
return AjaxResult.error(TransactionUtils.getContent(FinanceMsgConstants.BIND_BANK_CARD));
}
// Long pkBank = cuMemberWithdrawParam.getPkBank();
// CuMemberBankVO memberBank = cuMemberBankService.selectCuMemberBankById(pkBank);
// if (null == memberBank) {
// return AjaxResult.error(TransactionUtils.getContent(FinanceMsgConstants.BIND_BANK_CARD));
// }
//校验支付密码
String payPwd = cuMemberLoginExt.getPayPwd();
if (StringUtils.isEmpty(payPwd) || !SecurityUtils.matchesPassword(cuMemberWithdrawParam.getPayPwd(), payPwd)) {
@ -232,7 +232,8 @@ public class ApiCuMemberWithdrawController extends BaseController {
CurrencyDTO currencyDTO = baseService.getCurrency(pkCountry);
CuMemberWithdraw cuMemberWithdraw = CuMemberWithdraw.builder().pkMember(pkMember)
.pkAccount(pkAccount).pkBank(pkBank).remarks(cuMemberWithdrawParam.getRemarks())
// .pkAccount(pkAccount).pkBank(pkBank).remarks(cuMemberWithdrawParam.getRemarks())
.pkAccount(pkAccount).pkBank(0L).remarks(cuMemberWithdrawParam.getRemarks())
.pkRate(currencyDTO.getPkId()).cashAmount(cashAmount)
.serviceCharge(serviceChargeAmount).incomeTax(payIncomeTax)
.issuedAmount(issuedAmount).withdrawCode(code).build();

View File

@ -18,6 +18,7 @@ import com.hzs.common.security.service.UserTokenService;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.common.service.ITransactionCommonService;
import com.hzs.common.util.TransactionUtils;
import com.hzs.member.account.param.CuMemberWithdrawRejectParam;
import com.hzs.member.account.service.ICuMemberBaseService;
import com.hzs.member.account.service.ICuMemberWithdrawService;
import com.hzs.member.account.vo.CuMemberPetitionVO;
@ -209,4 +210,11 @@ public class CuMemberWithdrawController extends BaseController {
return toAjax(cuMemberWithdrawService.withdrawPaymentPositiveByPkId(memberWithdrawVO));
}
@Log(module = EOperationModule.WITHDRAWAL_DETAIL, business = EOperationBusiness.WITHDRAWAL_DETAIL, method = EOperationMethod.REJECT)
@PostMapping("/reject")
public AjaxResult reject(@RequestBody CuMemberWithdrawRejectParam param) {
cuMemberWithdrawService.withdrawReject(param);
return AjaxResult.success();
}
}

View File

@ -0,0 +1,13 @@
package com.hzs.member.account.param;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class CuMemberWithdrawRejectParam implements Serializable {
private static final long serialVersionUID = 1L;
private List<Long> idList;
private String rejectRemark;
}

View File

@ -5,6 +5,7 @@ import com.hzs.common.domain.member.account.CuMemberAccount;
import com.hzs.common.domain.member.account.CuMemberWithdraw;
import com.hzs.common.domain.system.config.BdWithdrawal;
import com.hzs.member.account.dto.MemberAccountAudit;
import com.hzs.member.account.param.CuMemberWithdrawRejectParam;
import com.hzs.member.account.vo.CuMemberPetitionVO;
import com.hzs.member.account.vo.CuMemberWithdrawVO;
@ -192,4 +193,10 @@ public interface ICuMemberWithdrawService extends IService<CuMemberWithdraw> {
*/
List<Long> selectWithdrawMember(Integer pkCountry, Long pkMember);
/**
* 提现驳回
* @param param
*/
void withdrawReject(CuMemberWithdrawRejectParam param);
}

View File

@ -4,12 +4,16 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.core.constant.MemberFieldConstants;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.exception.ServiceException;
import com.hzs.common.core.utils.CommonUtil;
import com.hzs.common.core.utils.reflect.ReflectUtils;
import com.hzs.common.domain.member.account.CuMemberAccount;
@ -20,9 +24,12 @@ import com.hzs.common.domain.member.base.CuMember;
import com.hzs.common.domain.member.ext.CuMemberAccountExt;
import com.hzs.common.domain.system.config.*;
import com.hzs.common.domain.system.config.ext.BdWithdrawalTaxExt;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.member.account.dto.BusinessCommissionDTO;
import com.hzs.member.account.dto.CuMemberAccountParam;
import com.hzs.member.account.dto.MemberAccountAudit;
import com.hzs.member.account.mapper.CuMemberWithdrawMapper;
import com.hzs.member.account.param.CuMemberWithdrawRejectParam;
import com.hzs.member.account.service.*;
import com.hzs.member.account.vo.CuMemberBankVO;
import com.hzs.member.account.vo.CuMemberPetitionVO;
@ -47,10 +54,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -727,4 +731,36 @@ public class CuMemberWithdrawServiceImpl extends ServiceImpl<CuMemberWithdrawMap
public List<Long> selectWithdrawMember(Integer pkCountry, Long pkMember) {
return baseMapper.selectWithdrawMember(pkCountry, pkMember);
}
@Override
public void withdrawReject(CuMemberWithdrawRejectParam param) {
if(ObjectUtil.isEmpty(param)){
throw new ServiceException("参数异常!");
}
if(CollUtil.isEmpty(param.getIdList())){
throw new ServiceException("请选择需要驳回的数据!");
}
if(StrUtil.isEmpty(param.getRejectRemark())){
throw new ServiceException("请录入驳回原因!");
}
LambdaQueryWrapper<CuMemberWithdraw> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CuMemberWithdraw::getPkId, param.getIdList());
CuMemberWithdraw withdraw = CuMemberWithdraw.builder().build();
withdraw.setApproveRemarks(param.getRejectRemark());
withdraw.setApproveState(EApproveStatus.REJECTED.getValue());
withdraw.setApprover(SecurityUtils.getUserId());
withdraw.setApproveTime(new Date());
baseMapper.update(withdraw, wrapper);
List<CuMemberWithdraw> cuMemberWithdrawList = baseMapper.selectList(wrapper);
Integer pkCountry = SecurityUtils.getPkCountry();
if(CollUtil.isNotEmpty(cuMemberWithdrawList)){
for (CuMemberWithdraw cuMemberWithdraw : cuMemberWithdrawList) {
BusinessCommissionDTO businessCommissionDTO = BusinessCommissionDTO.builder()
.tradeCode(cuMemberWithdraw.getWithdrawCode()).pkCountry(pkCountry)
.tradeType(EApprovalBusiness.WITHDRAWAL.getValue())
.pkCreator(SecurityUtils.getUserId()).build();
cuMemberTradeService.businessCommissionReject(businessCommissionDTO);
}
}
}
}

View File

@ -2,6 +2,7 @@ package com.hzs.member.base.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -33,6 +34,7 @@ import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.system.config.BdAccount;
import com.hzs.common.domain.system.config.BdAwards;
import com.hzs.common.domain.system.config.BdGrade;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.util.TransactionUtils;
import com.hzs.member.account.dto.BusinessCommissionDTO;
import com.hzs.member.account.service.ICuMemberAccountService;
@ -56,6 +58,7 @@ import com.hzs.system.config.IAwardsServiceApi;
import com.hzs.system.config.IGradeServiceApi;
import com.hzs.system.config.dto.AreaCurrencyDTO;
import com.hzs.system.config.dto.GradeDTO;
import com.hzs.third.pay.ITOnlinePaymentServiceApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
@ -86,6 +89,9 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
@DubboReference
ISaOrderServiceApi iSaOrderServiceApi;
@DubboReference
ITOnlinePaymentServiceApi itOnlinePaymentServiceApi;
@Autowired
private MemberToolsHandler memberToolsHandler;
@Autowired
@ -583,7 +589,14 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
.set(CuMemberRetailRegion::getModifiedTime, currentDateTime)
);
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
if (saOrder.getPayType().equals(EOrderPayType.WALLET.getValue())) {
// 钱包支付则需要回退金额
// 默认回退给创建人
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
} else {
// 在线支付处理退款
this.handleOnlineRefund(saOrder);
}
return true;
}
@ -623,8 +636,13 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
.set(CuMemberRetailRegion::getDelFlag, EDelFlag.DELETE.getValue())
.set(CuMemberRetailRegion::getModifiedTime, currentDateTime)
);
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
if (saOrder.getPayType().equals(EOrderPayType.WALLET.getValue())) {
// 钱包支付则需要回退金额
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
} else {
// 在线支付处理退款
this.handleOnlineRefund(saOrder);
}
return true;
}
@ -845,8 +863,20 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
cuMemberRegister.setPkCountry(saOrder.getPkCountry());
cuMemberRegister.setPkModified(saOrder.getPkCreator());
iCuMemberRegisterService.updateCuMemberRegister(cuMemberRegister);
CuMemberAccount payMemberAccount = cuMemberAccountMap.get(saOrder.getPkCreator());
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
if (saOrder.getPayType().equals(EOrderPayType.WALLET.getValue())) {
// 钱包支付则需要回退金额
CuMemberAccount payMemberAccount = cuMemberAccountMap.get(saOrder.getPkCreator());
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
}
}
for (int i = memberList.size() - 1; i >= 0; i--) {
Long pkMember = memberList.get(i);
SaOrderExt saOrder = saOrderMap.get(pkMember);
if (saOrder.getPayType().equals(EOrderPayType.ONLINE.getValue())) {
// 在线支付处理退款
this.handleOnlineRefund(saOrder);
}
}
return Boolean.TRUE;
}
@ -889,12 +919,19 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
this.handleParentLevel(saOrder, currentDateTime);
// 部分撤单 todo 发货后邮费不退未发货退邮费
if (saOrder.getBackOrderAmount() != null && ComputeUtil.compareValue(saOrder.getBackOrderAmount())) {
cancelPartOrderBackAmount(pkApprove, saOrder);
if (saOrder.getPayType().equals(EOrderPayType.WALLET.getValue())) {
// 钱包支付则需要回退金额
// 部分撤单
if (saOrder.getBackOrderAmount() != null && ComputeUtil.compareValue(saOrder.getBackOrderAmount())) {
cancelPartOrderBackAmount(pkApprove, saOrder);
} else {
CuMemberAccount payMemberAccount = iCuMemberAccountService.queryCuMemberAccountByPkMember(saOrder.getPkCreator());
// 回退账号余额
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
}
} else {
CuMemberAccount payMemberAccount = iCuMemberAccountService.queryCuMemberAccountByPkMember(saOrder.getPkCreator());
// 回退账号余额
cancelOrderBackAccount(pkApprove, saOrder, payMemberAccount);
// 在线支付处理退款
this.handleOnlineRefund(saOrder);
}
return Boolean.TRUE;
}
@ -1647,4 +1684,23 @@ public class CuMemberBusinessServiceImpl implements ICuMemberBusinessService {
}
/**
* 在线支付退款
*
* @param saOrder
*/
private void handleOnlineRefund(SaOrderExt saOrder) {
R<TOnlinePayment> onlinePaymentR = itOnlinePaymentServiceApi.getOnlinePayment(saOrder.getOrderCode());
if (ObjectUtil.isNotEmpty(onlinePaymentR)) {
TOnlinePayment onlinePayment = onlinePaymentR.getData();
if (ObjectUtil.isNotEmpty(onlinePayment)) {
R<String> result = itOnlinePaymentServiceApi.refund(saOrder.getOrderCode(), saOrder.getPkCreator());
if (!result.isSuccess()) {
throw new ServiceException(result.getMsg());
}
}
}
}
}

View File

@ -18,6 +18,7 @@ import com.hzs.common.domain.sale.ext.SaOrderExt;
import com.hzs.common.domain.sale.ext.SaOrderItemsExt;
import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.sale.order.SaOrderItems;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.common.security.auth.AuthMemberUtil;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.common.util.TransactionUtils;
@ -120,6 +121,16 @@ public class ApiOrderController extends BaseController {
return tableDataInfo;
}
@GetMapping("/refund-list")
public TableDataInfo refundList() {
// 返回结果
startPage();
List<TOnlineRefundExt> list = iSaOrderService.queryRefundList();
TableDataInfo tableDataInfo = getDataTable(list);
tableDataInfo.setRows(list);
return tableDataInfo;
}
/**
* 订单详情
*

View File

@ -6,6 +6,7 @@ import com.hzs.common.domain.sale.ext.SaDeliverItemsExt;
import com.hzs.common.domain.sale.ext.SaOrderExt;
import com.hzs.common.domain.sale.ext.SaOrderItemsExt;
import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.sale.order.param.*;
import com.hzs.sale.order.vo.*;
import org.apache.ibatis.annotations.Param;
@ -110,6 +111,8 @@ public interface SaOrderMapper extends BaseMapper<SaOrder> {
@Param("memberId") Long memberId,
@Param("pkCountry") Integer pkCountry);
List<TOnlineRefundExt> queryRefundList(@Param("userId") Long userId);
/**
* 自助撤单列表
**/

View File

@ -161,4 +161,13 @@ public class SaOrderServiceProvider implements ISaOrderServiceApi {
return R.ok(ComputeUtil.computeAdd(saOrder.getOrderAmount(), saOrder.getPostage()));
}
@Override
public R<SaOrderExt> queryOrderByCode(String orderCode, Long pkMember) {
SaOrderExt saOrder = redisService.getCacheObject(CacheConstants.TEMP_ORDER + pkMember + orderCode);
if (null == saOrder) {
// 直销订单
saOrder = iSaOrderService.queryOrderByCode(orderCode, null);
}
return R.ok(saOrder);
}
}

View File

@ -10,6 +10,7 @@ import com.hzs.common.domain.sale.ext.*;
import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.sale.order.SaOrderItems;
import com.hzs.common.domain.system.config.BdGrade;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.sale.order.param.*;
import com.hzs.sale.order.vo.*;
import com.hzs.system.config.dto.GradeDTO;
@ -379,6 +380,8 @@ public interface ISaOrderService extends IService<SaOrder> {
*/
List<SaOrderExt> myOrderList(MyOrderParam param, Long memberId, Integer pkCountry);
List<TOnlineRefundExt> queryRefundList();
/**
* 自助撤单列表
**/

View File

@ -34,6 +34,7 @@ import com.hzs.common.domain.system.base.BdCountry;
import com.hzs.common.domain.system.base.ext.BdProductStorehouseExt;
import com.hzs.common.domain.system.config.BdAwards;
import com.hzs.common.domain.system.config.BdGrade;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.common.security.service.UserTokenService;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.common.util.TransactionUtils;
@ -1501,6 +1502,11 @@ public class SaOrderServiceImpl extends ServiceImpl<SaOrderMapper, SaOrder> impl
return baseMapper.myOrderList(param, memberId, pkCountry);
}
@Override
public List<TOnlineRefundExt> queryRefundList() {
return baseMapper.queryRefundList(SecurityUtils.getUserId());
}
@Override
public List<SaOrderExt> selfRevokeOrderList(MyOrderParam param, Long memberId, Integer pkCountry) {
return baseMapper.selfRevokeOrderList(param, memberId, pkCountry);

View File

@ -2,6 +2,7 @@ package com.hzs.sale.shopping.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.hzs.common.core.constant.CacheConstants;
import com.hzs.common.core.domain.R;
@ -133,7 +134,7 @@ public class ShoppingCartServiceImpl implements IShoppingCartService {
for (String innkey : innerShopMap.keySet()) {
String mapValue = innerShopMap.get(innkey);
ShoppingCartRedis scr = JSONUtil.toBean(mapValue, ShoppingCartRedis.class);
if (scr.getSpecialArea() == value.getValue()) {
if (ObjectUtil.isNotEmpty(scr.getSpecialArea()) && scr.getSpecialArea() == value.getValue()) {
scv.setSpecialArea(value.getValue());
scv.setSpecialAreaVal(value.getLabel());
if (pkCountry.equals(scr.getPkCountry())) {

View File

@ -1414,4 +1414,29 @@
group by cmrr.province, cmrr.city, cmrr.county) cmrr
</select>
<select id="queryRefundList" resultType="com.hzs.common.domain.third.pay.ext.TOnlineRefundExt">
select cm.member_code,
cm.member_name,
top.business_type pay_business_type,
top.business_code pay_business_code,
tor.business_code,
tor.refund_status,
tor.refund_money,
tor.finish_money,
tor.creation_time,
tor.finish_time,
tor.refund_channel,
tor.refund_code,
tor.error_msg
from T_ONLINE_REFUND tor
inner join T_ONLINE_PAYMENT top
on top.pk_id = tor.pk_online_payment
left join cu_member cm
on cm.pk_id = top.pk_creator
where tor.del_flag = 0
<if test="userId != null">
and tor.PK_CREATOR = #{userId}
</if>
order by creation_time desc
</select>
</mapper>

View File

@ -321,6 +321,11 @@ public class CacheConstants {
*/
public static final String RETAIL_TEMP_OTHER_PARAM = CACHE_PREFIX + "retail:temp_other:param:";
/**
* 在线支付回调上锁
*/
public final static String ONLINE_PAY_KEY = CACHE_PREFIX + "online:payment:";
/**
* 手机每天获取短信缓存 -- Hashsms:count: 年月日, 登录名
*/

View File

@ -27,6 +27,12 @@ public class SysConstants {
*/
public static final String PAY_PASSWORD = "222222";
/**
* 特殊处理编号对外开放使用主要用于支付接入审核
*/
public static final String SPECIAL_CODE = "BD68880628";
/**
* 开放使用会员编号用于支付登录查看数据
*/

View File

@ -121,6 +121,15 @@ public enum EOrderType {
resultList.add(EOrderType.REPURCHASE_ORDER);
return resultList;
}
public static String getLabelByValue(Integer value) {
if (null == value) {
return "";
}
for (EOrderType enums : EOrderType.values()) {
if (enums.getValue() == value) {
return enums.getLabel();
}
}
return "";
}
}

View File

@ -13,6 +13,11 @@ import lombok.Getter;
@Getter
public enum EPayChannel {
/**
* 通联
*/
ALLIN(3, "通联", 0, EnumsPrefixConstants.PAY_CHANNEL + "3"),
/**
* 京东
*/

View File

@ -27,6 +27,12 @@ public enum EPayType {
* 银行卡
*/
BANK_CARD(3, "银行卡", 0, EnumsPrefixConstants.PAY_TYPE + "3"),
// , 5=(京东)小金库, 6=(京东)白条, 7=(京东)超级白条, 8=(京东)钱包余额, 9=(京东)云闪付
MINI_TREASURY(5, "小金库", 0, EnumsPrefixConstants.PAY_TYPE + "5"),
CREDIT_LINE(6, "白条", 0, EnumsPrefixConstants.PAY_TYPE + "6"),
SUPER_CREDIT_LINE(7, "超级白条", 0, EnumsPrefixConstants.PAY_TYPE + "7"),
WALLET_BALANCE(8, "钱包余额", 0, EnumsPrefixConstants.PAY_TYPE + "8"),
CLOUD_PAY(9, "云闪付", 0, EnumsPrefixConstants.PAY_TYPE + "9"),
;

View File

@ -15,9 +15,6 @@ import org.apache.ibatis.type.JdbcType;
/**
* 在线支付信息
*
* @author hzs
* @since 2022-07-28
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ -59,7 +56,7 @@ public class TOnlinePayment extends BaseEntity {
private BigDecimal businessMoney;
/**
* 支付渠道5=宝付,6=汇付,8=汇付
* 支付渠道0=杉德,1=支付宝,2=微信,3=通联,4=京东,5=宝付,6=汇付
*/
@TableField("PAY_CHANNEL")
private Integer payChannel;
@ -125,4 +122,21 @@ public class TOnlinePayment extends BaseEntity {
@TableField("PAY_SOURCE")
private Integer paySource;
/**
* 主账户金额
*/
@TableField("MAIN_ACCOUNT_MONEY")
private BigDecimal mainAccountMoney;
/**
* 子账户金额
*/
@TableField("SUB_ACCOUNT_MONEY")
private BigDecimal subAccountMoney;
/**
* 支付-前端回调页面的扩展参数
*/
@TableField(exist = false)
private String extParam;
}

View File

@ -0,0 +1,75 @@
package com.hzs.common.domain.third.pay;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hzs.common.core.web.domain.BaseEntity;
import lombok.*;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.util.List;
/**
* <p>
* 支付请求主表
* </p>
*
* @author bd
* @since 2025-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("T_ONLINE_PAYMENT_SEP_ACC")
@KeySequence("T_ONLINE_PAYMENT_SEP_ACC_SEQ")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TOnlinePaymentSepAcc extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId("PK_ID")
private Long pkId;
/**
* 在线支付信息ID
*/
@TableField("PK_PAYMENT")
private Long pkPayment;
/**
* 版本号
*/
@TableField("VERSION")
private String version;
/**
* 支付请求信息
*/
@TableField("REQ_CONTENT")
private String reqContent;
/**
* 分账请求信息
*/
@TableField("REQ_CONTENT_SEP_ACC")
private String reqContentSepAcc;
/**
* 支付响应信息
*/
@TableField("RESP_CONTENT")
private String respContent;
@TableField("TRADE_AMOUNT")
private BigDecimal tradeAmount;
@TableField("OUT_TRADE_NO")
private String outTradeNo;
@TableField(exist = false)
private List<TOnlinePaymentSepAccD> sepAccDList;
}

View File

@ -0,0 +1,59 @@
package com.hzs.common.domain.third.pay;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hzs.common.core.web.domain.BaseEntity;
import lombok.*;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
* <p>
* 支付请求子表(分账)
* </p>
*
* @author bd
* @since 2025-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("T_ONLINE_PAYMENT_SEP_ACC_D")
@KeySequence("T_ONLINE_PAYMENT_SEP_ACC_D_SEQ")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TOnlinePaymentSepAccD extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId("PK_ID")
private Long pkId;
/**
* 支付请求主表ID
*/
@TableField("PK_SEP_ACC")
private Long pkSepAcc;
/**
* 分账账号
*/
@TableField("ACCOUNT")
private String account;
/**
* 分账比例
*/
@TableField("PROPORTION")
private BigDecimal proportion;
@TableField("TRADE_AMOUNT")
private BigDecimal tradeAmount;
@TableField("OUT_TRADE_NO")
private String outTradeNo;
}

View File

@ -1,5 +1,7 @@
package com.hzs.common.domain.third.pay.ext;
import com.hzs.common.core.annotation.Transaction;
import com.hzs.common.core.constant.EnumsPrefixConstants;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -32,4 +34,8 @@ public class TOnlinePaymentExt extends TOnlinePayment {
*/
private String phone;
private Integer orderType;
private String orderTypeVal;
}

View File

@ -148,7 +148,7 @@ public class PayRefundListener {
switch (EPayChannel.getEnumByValue(tOnlinePayment.getPayChannel())) {
case HUIFU:
// 新汇付
str = iRefundService.huifuRefundHandle(tOnlinePayment);
// str = iRefundService.huifuRefundHandle(tOnlinePayment);
break;
default:
}

View File

@ -0,0 +1,191 @@
package com.hzs.third.mqcall.listener;
import com.hzs.common.core.constant.RabbitMqConstants;
import com.hzs.common.core.enums.ECallbackStatus;
import com.hzs.common.core.enums.EPayBusinessType;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.core.enums.EPayStatus;
import com.hzs.common.domain.sale.ext.SaOrderExt;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.sale.order.ISaOrderServiceApi;
import com.hzs.third.mq.dto.RefundOrderDTO;
import com.hzs.third.pay.dto.RefundDTO;
import com.hzs.third.pay.service.IPayService;
import com.hzs.third.pay.service.IRefundService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 订单退款处理监听
*/
@Slf4j
@Component
public class RefundOrderListener {
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@Autowired
private IRefundService iRefundService;
@Autowired
private IPayService iPayService;
@Autowired
private RabbitTemplate rabbitTemplate;
@DubboReference
ISaOrderServiceApi iSaOrderServiceApi;
/**
* 此监听只处理在线支付支付成功但是订单未成功的处理
*
* @param message
* @param channel
* @throws Exception
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = RabbitMqConstants.REFUND_ORDER_EXCHANGE, type = "topic"),
value = @Queue(value = RabbitMqConstants.REFUND_ORDER_QUEUE, durable = "true", autoDelete = "false"),
key = RabbitMqConstants.REFUND_ORDER_KEY))
@RabbitHandler
public void onMessage(Message<RefundOrderDTO> message, Channel channel) throws Exception {
// 手动应答
Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
channel.basicAck(deliveryTag, false);
RefundOrderDTO refundOrderDTO = message.getPayload();
log.info("订单退款MQ处理. 参数:{}", refundOrderDTO);
// 防止MQ推送比实际更新数据库快此处停1.5秒再处理
Thread.sleep(1500);
// 重试次数
int retryNum = null != refundOrderDTO.getRetryNum() ? refundOrderDTO.getRetryNum() : 0;
if (retryNum > 2) {
log.error("订单退款MQ处理. 订单退款已重试3次仍未成功不进行再次重试");
return;
}
// 支付会员ID
Long pkMember = refundOrderDTO.getPkMember();
// 支付订单编号
String orderCode = refundOrderDTO.getOrderCode();
// 支付业务类型
Integer payBusinessType = refundOrderDTO.getPayBusinessType();
// 业务类型默认为直销订单
EPayBusinessType eBusinessType = EPayBusinessType.MEMBER_ORDER;
if (null != payBusinessType) {
// 传入支付业务类型不为空则进行转换
EPayBusinessType ePayBusinessType = EPayBusinessType.getEnumByValue(payBusinessType);
if (null != ePayBusinessType) {
eBusinessType = ePayBusinessType;
}
}
// 校验在线支付信息是否可以进行退款
TOnlinePayment tOnlinePayment = itOnlinePaymentService.queryByBusiness(eBusinessType.getValue(), orderCode, null);
if (null == tOnlinePayment) {
// 在线支付信息不存在
log.error("订单退款MQ处理. 在线支付信息不存在");
return;
}
if (EPayStatus.PAID.getValue() == tOnlinePayment.getPayStatus()
&& ECallbackStatus.FAIL.getValue() == tOnlinePayment.getCallbackStatus()) {
// 已支付 并且 回调业务失败可以进行退款
// 业务重试次数
int businessRetryNum = null != refundOrderDTO.getBusinessRetryNum() ? refundOrderDTO.getBusinessRetryNum() : 0;
if (businessRetryNum > 2) {
log.error("订单退款MQ处理. 直销订单业务已重试3次仍未成功不进行再次重试。开始进行退款");
if (EPayBusinessType.MEMBER_ORDER.equals(eBusinessType)) {
// 直销订单
// 业务处理重试三次都失败则进行退款流程进行退款处理
SaOrderExt saOrderExt = iSaOrderServiceApi.queryOrderByCode(orderCode, pkMember).getData();
if (null == saOrderExt) {
log.error("订单退款MQ处理. 直销订单不存在");
return;
}
log.info("订单退款MQ处理订单数据{}", saOrderExt);
if (null != saOrderExt.getOrderStatus() && EPayStatus.PAID.getValue() == saOrderExt.getOrderStatus()) {
log.error("订单退款MQ处理. 直销订单已支付成功,不能进行退款");
return;
}
}
// 退款处理
String str = this.handleRefund(tOnlinePayment, eBusinessType, orderCode, pkMember);
if (null == str) {
log.info("订单退款MQ处理. 申请三方支付退款成功");
return;
} else {
log.error("订单退款MQ处理. 申请三方支付退款失败:{}", str);
}
// 退款失败重试次数+1推送MQ
refundOrderDTO.setRetryNum(++retryNum);
rabbitTemplate.convertAndSend(RabbitMqConstants.REFUND_ORDER_EXCHANGE, RabbitMqConstants.REFUND_ORDER_KEY, refundOrderDTO);
return;
}
// 直销订单缓冲5秒后进行重试
Thread.sleep(5000);
tOnlinePayment.setPkModified(SecurityUtils.getUserId());
tOnlinePayment.setModifiedTime(new Date());
// 业务处理重试
if (iPayService.retryHandle(tOnlinePayment, false)) {
// 业务处理重试成功直接结束
return;
}
// 业务处理重试失败次数+1继续推送MQ
refundOrderDTO.setBusinessRetryNum(++businessRetryNum);
rabbitTemplate.convertAndSend(RabbitMqConstants.REFUND_ORDER_EXCHANGE, RabbitMqConstants.REFUND_ORDER_KEY, refundOrderDTO);
return;
}
log.info("订单退款MQ处理. 订单未支付或业务已经处理成功");
}
/**
* 处理退款
*
* @param tOnlinePayment 支付信息
* @param eBusinessType 支付业务类型
* @param orderCode 订单编号
* @param pkMember 会员ID
* @return
*/
private String handleRefund(TOnlinePayment tOnlinePayment, EPayBusinessType eBusinessType, String orderCode, Long pkMember) {
String str = null;
RefundDTO refundDTO = RefundDTO.builder()
.businessType(eBusinessType.getValue())
.businessCode(orderCode)
.refundAmount(tOnlinePayment.getPayMoney())
.userId(pkMember)
.pkCountry(tOnlinePayment.getPkCountry())
.build();
// 订单存在并且没有支付成功代表着钱收到了但是单没有下成所以需要进行退款
switch (EPayChannel.getEnumByValue(tOnlinePayment.getPayChannel())) {
case JD:
// 京东
str = iRefundService.jdRefundDivision(tOnlinePayment.getBusinessCode(), tOnlinePayment.getPkCreator());
break;
default:
}
return str;
}
}

View File

@ -0,0 +1,27 @@
package com.hzs.third.pay.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "jd.bank") // 绑定jd.pay前缀的配置
public class JdPayBankProperties {
private String merchantNo;
private String signKey;
private String priCertPwd;
private String priCert;
private String pubCert;
private String apiDomain;
private String pageBackUrl;
private String notifyUrl;
private String refundNotifyUrl;
private List<JdPaySeparateAccountConfig> separateAccounts;
}

View File

@ -0,0 +1,50 @@
package com.hzs.third.pay.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 京东支付配置
*/
@Data
@Component
@ConfigurationProperties(prefix = "jdpay")
public class JdPayConfig {
/**
* 商户编号
*/
private String customerNum;
/**
* 店铺编号
*/
private String shopNum;
/**
* 公钥
*/
private String accessKey;
/**
* 私钥
*/
private String secretKey;
/**
* 回调地址
*/
private String callbackUrl;
/**
* 代付回调地址
*/
private String agentCallbackUrl;
/**
* 退款回调地址
*/
private String refundCallbackUrl;
}

View File

@ -0,0 +1,14 @@
package com.hzs.third.pay.config;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class JdPaySeparateAccountConfig implements Serializable {
private String account;
@JsonFormat(shape = JsonFormat.Shape.STRING)
private BigDecimal proportion;
}

View File

@ -0,0 +1,27 @@
package com.hzs.third.pay.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "jd.wechat-alipay") // 绑定jd.pay前缀的配置
public class JdPayWechatAlipayProperties {
private String merchantNo;
private String signKey;
private String priCertPwd;
private String priCert;
private String pubCert;
private String apiDomain;
private String pageBackUrl;
private String notifyUrl;
private String refundNotifyUrl;
private List<JdPaySeparateAccountConfig> separateAccounts;
}

View File

@ -0,0 +1,154 @@
package com.hzs.third.pay.constants;
/**
* 京东支付常量类
*/
public class JdPayConstants {
/**
* 终端标记暂用京东注册手机号
*/
public static final String TERMINAL_ID = "18246334501";
/**
* 用户注册账号暂用京东注册手机号
*/
public static final String USER_ACCOUNT = "18246334501";
/**
* 应用名称
*/
public static final String APP_NAME = "盛美源";
// TODO 上面3项考试动态化配置
/**
* 订单状态 - 交易处理中
*/
public static final String ORDER_INT = "INT";
/**
* 订单状态 - 成功
*/
public static final String ORDER_SUCCESS = "SUCCESS";
/**
* 查询状态 - 交易处理中
*/
public static final String QUERY_INIT = "INIT";
/**
* 查询状态 - 成功
*/
public static final String QUERY_SUCCESS = "SUCCESS";
/**
* 查询状态 - 失败
*/
public static final String QUERY_FAIL = "FAIL";
/**
* 处理成功
*/
public static final String RESULT_SUCCESS = "success";
/**
* 处理成功业务响应码
*/
public static final String RESULT_SUCCESS_CODE = "C000000";
/**
* 银行卡交易不支持错误编码
*/
public static final String RESULT_FAIL_CODE_6 = "C000006";
/**
* 请求成功
*/
public static final String SUCCESS = "true";
/**
* 返回成功
*/
public static final String RETURN_SUCCESS = "200";
/**
* 返回失败
*/
public static final String RETURN_FAIL = "500";
/**
* 支付接口版本
*/
public static final String PAY_VERSION = "V4.0";
/**
* 绑卡类型 身份证
*/
public static final String ID_CARD_TYPE = "IDCARD";
/**
* 快捷支付标准业务场景
*/
public static final String FAST_TRADESCENE_QUICKPAY = "QUICKPAY";
/**
* 代付业务类型
*/
public static final String AGENT_BUSINESS_TYPE = "DEFY";
/**
* 代付支付方式
*/
public static final String AGENT_BANK_TYPE = "DEFY";
/**
* 支付地址
*/
public static final String PAY_URL = "https://openapi.duolabao.com";
/**
* 扫码支付调用方法
*/
public static final String METHOD_SCAN = "/v3/order/payurl/create";
/**
* 快捷支付绑卡方法
*/
public static final String METHOD_BIND_CARD = "/api/applyBindCard";
/**
* 快捷支付绑卡确认方法
*/
public static final String METHOD_CONFIRM_BIND = "/api/confirmBindCard";
/**
* 快捷支付解绑方法
*/
public static final String METHOD_UNBIND = "/api/unBindCard";
/**
* 快捷支付预下单
*/
public static final String METHOD_PRE_ORDER = "/api/applyQuickPayWithCheck";
/**
* 快捷支付确认订单
*/
public static final String METHOD_CONFIRM_ORDER = "/api/confirmQuickPay";
/**
* 京东代付余额查询方法
*/
public static final String METHOD_QUERY_BALANCE = "/api/queryBalance";
/**
* 京东代付调用方法
*/
public static final String METHOD_AGENT = "/api/payWithCheck";
/**
* 京东查询订单路径
*/
public static final String QUERY_ORDER = "/v3/order/query";
/**
* 京东退款路径
*/
public static final String REFUND_ORDER = "/v3/order/refund/part";
}

View File

@ -5,13 +5,19 @@ package com.hzs.third.pay.constants;
*/
public class PayConfigConstants {
// 新汇付PC微信扫码支付
// 京东收银台H5
public static final String PAY_CONFIG_5 = "PAY:CONFIG:%s:5";
// 京东收银台PC
public static final String PAY_CONFIG_6 = "PAY:CONFIG:%s:6";
// 通联微信
public static final String PAY_CONFIG_32 = "PAY:CONFIG:%s:32";
// 通联银行卡
public static final String PAY_CONFIG_33 = "PAY:CONFIG:%s:33";
// 新汇付PC微信扫码
public static final String PAY_CONFIG_81 = "PAY:CONFIG:%s:81";
// 新汇付PC支付宝扫码支付
// 新汇付PC支付宝扫码
public static final String PAY_CONFIG_82 = "PAY:CONFIG:%s:82";
// 新汇付H5微信支付
public static final String PAY_CONFIG_83 = "PAY:CONFIG:%s:83";
// 新汇付H5支付宝支付
public static final String PAY_CONFIG_84 = "PAY:CONFIG:%s:84";
}

View File

@ -0,0 +1,100 @@
package com.hzs.third.pay.controller.api;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.core.enums.EPayStatus;
import com.hzs.common.core.utils.StringUtils;
import com.hzs.common.core.web.domain.AjaxResult;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.third.pay.controller.base.PayBaseController;
import com.hzs.third.pay.dto.RefundDTO;
import com.hzs.third.pay.param.PayParam;
import com.hzs.third.pay.service.IRefundService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @Description: 京东退款控制器
* @Author: jiang chao
* @Time: 2023/8/17 15:11
* @Classname: JdRefundController
* @PackageName: com.hzs.third.pay.controller.api
*/
@Slf4j
@RestController
@RequestMapping("/jd-refund")
public class JdRefundController extends PayBaseController {
@Autowired
private IRefundService iRefundService;
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@GetMapping("/test/{orderCode}")
public AjaxResult test(@PathVariable String orderCode) {
iRefundService.jdRefundDivision(orderCode, SecurityUtils.getUserId());
return AjaxResult.success();
}
@GetMapping("/test2")
public AjaxResult test2() {
iRefundService.jdQueryEnterpriseWalletBalance();
return AjaxResult.success();
}
/**
* 京东退款接口
*
* @param param
* @return
*/
@PostMapping("/refund")
public AjaxResult refund(@RequestBody PayParam param) {
if (null == param.getBusinessType() || StringUtils.isEmpty(param.getBusinessCode())) {
// 缺少参数
return AjaxResult.error("缺少参数");
}
// 查询在线支付明细
TOnlinePayment tOnlinePayment = itOnlinePaymentService.queryByBusiness(param.getBusinessType(), param.getBusinessCode(), SecurityUtils.getPkCountry());
if (null == tOnlinePayment || EPayStatus.UNPAID.getValue() == tOnlinePayment.getPayStatus()) {
// 在线支付明细为空 或者 未支付成功则不能进行退款
return AjaxResult.error("订单不存在或未支付成功,不能进行退款");
}
// 获取支付渠道
EPayChannel ePayChannel = EPayChannel.getEnumByValue(tOnlinePayment.getPayChannel());
if (null == ePayChannel) {
return AjaxResult.error("订单支付信息有误,不能进行退款");
}
try {
// 京东收银台退款处理
String str = iRefundService.jdCashRefundHandle(RefundDTO.builder()
.businessType(param.getBusinessType())
.businessCode(param.getBusinessCode())
.refundAmount(tOnlinePayment.getPayMoney())
.userId(SecurityUtils.getUserId())
.pkCountry(SecurityUtils.getPkCountry())
.build(),
tOnlinePayment);
// 京东银行卡退款处理 --
// String str = iRefundService.jdRefundHandle(RefundDTO.builder()
// .businessType(param.getBusinessType())
// .businessCode(param.getBusinessCode())
// .refundAmount(tOnlinePayment.getPayMoney())
// .userId(SecurityUtils.getUserId())
// .pkCountry(SecurityUtils.getPkCountry())
// .build(),
// tOnlinePayment);
if (null == str) {
return AjaxResult.success();
}
return AjaxResult.error("退款失败:" + str);
} catch (Exception e) {
log.error("调用京东退款处理返回异常!", e);
return AjaxResult.error("退款异常,请刷新后重试");
}
}
}

View File

@ -1,10 +1,13 @@
package com.hzs.third.pay.controller.api;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzs.activity.base.IActivityServiceApi;
import com.hzs.common.core.annotation.RepeatSubmitSimple;
import com.hzs.common.core.constant.SysConstants;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.utils.CommonUtil;
import com.hzs.common.core.utils.StringUtils;
import com.hzs.common.core.web.domain.AjaxResult;
import com.hzs.common.domain.member.account.CuMemberRecharge;
import com.hzs.common.domain.third.pay.TOnlinePayment;
@ -13,9 +16,11 @@ import com.hzs.member.account.IMemberTradeServiceApi;
import com.hzs.sale.order.ISaOrderServiceApi;
import com.hzs.sale.ticket.ITicketServiceApi;
import com.hzs.third.pay.constants.PayConfigConstants;
import com.hzs.third.pay.param.StatusParam;
import com.hzs.third.pay.param.PayParam;
import com.hzs.third.pay.param.UnifiedOrderParam;
import com.hzs.third.pay.service.*;
import com.hzs.third.pay.service.IHuiFuPayService;
import com.hzs.third.pay.service.IJdPayService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import com.hzs.third.pay.util.PayUtil;
import com.hzs.third.pay.vo.OnlinePayConfigVO;
import lombok.extern.slf4j.Slf4j;
@ -24,7 +29,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Date;
@ -39,9 +43,9 @@ public class PayController {
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@Autowired
private IHuiFuPayService iHuiFuPayService;
@Autowired
private IJdPayService iJdPayService;
@Autowired
private IHuiFuPayService iHuiFuPayService;
@Autowired
private RedisTemplate redisTemplate;
@ -52,18 +56,24 @@ public class PayController {
IMemberTradeServiceApi iMemberTradeServiceApi;
@DubboReference
ITicketServiceApi iTicketServiceApi;
@DubboReference
IActivityServiceApi iActivityServiceApi;
/**
* 统一下单
*
* @param source 请求来源EDataSource
* @param source 请求来源EDataSource
* @param param
* @return
*/
@RepeatSubmitSimple
@PostMapping("/unifiedorder")
public AjaxResult unifiedOrder(@RequestHeader("Source") String source,
@Valid @RequestBody UnifiedOrderParam param) {
@RequestBody UnifiedOrderParam param) {
if (null == param.getBusinessType() || StringUtils.isEmpty(param.getBusinessCode())
|| null == param.getPayChannel() || null == param.getPayType()) {
return AjaxResult.error("缺少参数");
}
EPayBusinessType payBusinessType = EPayBusinessType.getEnumByValue(param.getBusinessType());
if (null == payBusinessType) {
return AjaxResult.error("业务类型不存在");
@ -85,6 +95,8 @@ public class PayController {
Long userId = SecurityUtils.getUserId();
// 登录用户国家
Integer pkCountry = SecurityUtils.getPkCountry();
// 微信 openId用于微信小程序支付
String openId = null;
// 1. 根据业务类型业务单号进行校验如果已经支付成功直接返回其它情况则继续执行
TOnlinePayment onlinePayment = itOnlinePaymentService.queryByBusiness(payBusinessType.getValue(), param.getBusinessCode(), pkCountry);
@ -116,9 +128,12 @@ public class PayController {
onlinePayment.setOriginalOrder(onlinePayment.getBusinessCode());
onlinePayment.setPaySource(dataSource.getValue());
}
onlinePayment.setExtParam(param.getExtParam());
// 业务金额
BigDecimal businessMoney = null;
// 实际支付金额随机立减之后
BigDecimal amount = null;
// 部分业务金额随机立减
switch (payBusinessType) {
@ -151,17 +166,11 @@ public class PayController {
break;
default:
}
if (null == businessMoney) {
return AjaxResult.error("当前支付金额有误");
}
if (businessMoney.compareTo(new BigDecimal("10000")) >= 0) {
return AjaxResult.error("当前支付方式单笔金额不能超过10000元");
}
// 更新业务金额
onlinePayment.setBusinessMoney(businessMoney);
// 暂存于实际支付金额业务进行随机立减后的
onlinePayment.setPayMoney(PayUtil.handleAmountRandom(businessMoney));
amount = PayUtil.handleAmountRandom(businessMoney);
// 暂存于实际支付金额部分业务进行随机立减
onlinePayment.setPayMoney(amount);
// 结果实体
R payResult = null;
@ -171,53 +180,14 @@ public class PayController {
// 京东
switch (payType) {
case WECHAT:
// 微信
break;
case ALIPAY:
// 支付宝
// 微信支付宝 扫码支付
// payResult = iJdPayService.scanPay(onlinePayment);
payResult = iJdPayService.cashRegister(onlinePayment, dataSource);
break;
default:
// 默认京东收银台
payResult = iJdPayService.cashDeskPay(onlinePayment);
}
break;
case HUIFU:
// 新汇付
switch (payType) {
case WECHAT:
// 微信
switch (dataSource) {
case H5:
// H5浏览器使用微信小程序支付
if (null != param.getAppletFlag() && EYesNo.YES.getIntValue() == param.getAppletFlag()) {
// 小程序支付
payResult = iHuiFuPayService.wechatPrePayUrl(onlinePayment, false);
break;
}
default:
// PC以及默认按扫码支付
// 非小程序支付微信内置浏览器
payResult = iHuiFuPayService.wechatPrePayUrl(onlinePayment, true);
break;
}
break;
case ALIPAY:
// 支付宝
switch (dataSource) {
case H5:
case APP:
// H5浏览器 或者 APP
payResult = iHuiFuPayService.aliPrePayUrl(onlinePayment);
default:
// PC以及默认按扫码支付
}
break;
case BANK_CARD:
// 银行卡
default:
}
default:
}
@ -225,8 +195,8 @@ public class PayController {
if (null != payResult) {
if (payResult.isSuccess()) {
// 实际支付金额在支付之前不需要入库实际以取支付回调为准
onlinePayment.setPayMoney(null);
itOnlinePaymentService.saveOrUpdate(onlinePayment);
// onlinePayment.setPayMoney(null);
// itOnlinePaymentService.saveOrUpdate(onlinePayment);
return AjaxResult.success(payResult.getData());
}
@ -242,16 +212,23 @@ public class PayController {
* @return
*/
@GetMapping("/status")
public AjaxResult status(@Valid StatusParam param) {
// 业务类型校验业务类型
EPayBusinessType ePayBusinessType = EPayBusinessType.getEnumByValue(param.getBusinessType());
if (null == ePayBusinessType) {
return AjaxResult.success("支付业务类型错误", 0);
public AjaxResult status(PayParam param) {
// if (null == param.getBusinessType() || StringUtils.isEmpty(param.getBusinessCode())) {
// return AjaxResult.success("缺少参数");
// }
if (StringUtils.isEmpty(param.getBusinessCode())) {
return AjaxResult.success("缺少参数");
}
LambdaQueryWrapper<TOnlinePayment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TOnlinePayment::getBusinessType, param.getBusinessType());
queryWrapper.eq(TOnlinePayment::getBusinessCode, param.getBusinessCode());
// 业务类型校验业务类型
// EPayBusinessType ePayBusinessType = EPayBusinessType.getEnumByValue(param.getBusinessType());
// if (null == ePayBusinessType) {
// return AjaxResult.success("支付业务类型错误", 0);
// }
QueryWrapper<TOnlinePayment> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("BUSINESS_TYPE", param.getBusinessType());
queryWrapper.eq("BUSINESS_CODE", param.getBusinessCode());
TOnlinePayment onlinePayment = itOnlinePaymentService.getOne(queryWrapper);
if (null == onlinePayment) {
return AjaxResult.success("支付信息不存在", 0);
@ -277,6 +254,28 @@ public class PayController {
// 各支付方式 true=显示false=隐藏
// 京东收银台H5
String pay5 = String.format(PayConfigConstants.PAY_CONFIG_5, pkCountry);
if (redisTemplate.hasKey(pay5)) {
payConfigVO.setPay5((Boolean) redisTemplate.opsForValue().get(pay5));
}
// 京东收银台PC
String pay6 = String.format(PayConfigConstants.PAY_CONFIG_6, pkCountry);
if (redisTemplate.hasKey(pay6)) {
payConfigVO.setPay6((Boolean) redisTemplate.opsForValue().get(pay6));
}
// 通联微信
String pay32 = String.format(PayConfigConstants.PAY_CONFIG_32, pkCountry);
if (redisTemplate.hasKey(pay32)) {
payConfigVO.setPay32((Boolean) redisTemplate.opsForValue().get(pay32));
}
// 通联银行卡
String pay33 = String.format(PayConfigConstants.PAY_CONFIG_33, pkCountry);
if (redisTemplate.hasKey(pay33)) {
payConfigVO.setPay33((Boolean) redisTemplate.opsForValue().get(pay33));
}
// 新汇付PC微信扫码支付
String pay81 = String.format(PayConfigConstants.PAY_CONFIG_81, pkCountry);
if (redisTemplate.hasKey(pay81)) {
@ -287,16 +286,12 @@ public class PayController {
if (redisTemplate.hasKey(pay82)) {
payConfigVO.setPay82((Boolean) redisTemplate.opsForValue().get(pay82));
}
// 新汇付H5微信支付
String pay83 = String.format(PayConfigConstants.PAY_CONFIG_83, pkCountry);
if (redisTemplate.hasKey(pay83)) {
payConfigVO.setPay83((Boolean) redisTemplate.opsForValue().get(pay83));
}
// 新汇付H5支付宝支付
String pay84 = String.format(PayConfigConstants.PAY_CONFIG_84, pkCountry);
if (redisTemplate.hasKey(pay84)) {
payConfigVO.setPay84((Boolean) redisTemplate.opsForValue().get(pay84));
if (SysConstants.SPECIAL_CODE.equals(SecurityUtils.getMemberCode())) {
// 在线支付测试账号强制显示支付方式
payConfigVO.setPay5(true);
}
return AjaxResult.success(payConfigVO);
}

View File

@ -0,0 +1,64 @@
package com.hzs.third.pay.controller.base;
import cn.hutool.crypto.SecureUtil;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @Description: 京东基础控制器
* @Author: jiang chao
* @Time: 2022/12/27 17:47
* @Classname: JdBaseController
* @PackageName: com.hzs.third.pay.controller.base
*/
public class JdBaseController {
/**
* 获取请求体
*
* @param request
* @return
* @throws Exception
*/
protected String getRequestBody(HttpServletRequest request) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} finally {
if (null != br) {
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 校验token
*
* @param request
* @param reqBody
* @param secretKey
* @return
*/
protected boolean checkToken(HttpServletRequest request, String reqBody, String secretKey) {
// SHA1 加密串
String shaStr = "secretKey=" + secretKey +
"&timestamp=" + request.getHeader("timestamp") +
"&body=" + reqBody;
String tokenCheck = SecureUtil.sha1(shaStr).toUpperCase();
return tokenCheck.equals(request.getHeader("token"));
}
}

View File

@ -52,15 +52,15 @@ public class OnlinePayConfigController {
payConfigVO.setPay82((Boolean) redisTemplate.opsForValue().get(pay82));
}
// 新汇付H5微信支付
String pay83 = String.format(PayConfigConstants.PAY_CONFIG_83, pkCountry);
if (redisTemplate.hasKey(pay83)) {
payConfigVO.setPay83((Boolean) redisTemplate.opsForValue().get(pay83));
}
// 新汇付H5支付宝支付
String pay84 = String.format(PayConfigConstants.PAY_CONFIG_84, pkCountry);
if (redisTemplate.hasKey(pay84)) {
payConfigVO.setPay84((Boolean) redisTemplate.opsForValue().get(pay84));
}
// String pay83 = String.format(PayConfigConstants.PAY_CONFIG_83, pkCountry);
// if (redisTemplate.hasKey(pay83)) {
// payConfigVO.setPay83((Boolean) redisTemplate.opsForValue().get(pay83));
// }
// // 新汇付H5支付宝支付
// String pay84 = String.format(PayConfigConstants.PAY_CONFIG_84, pkCountry);
// if (redisTemplate.hasKey(pay84)) {
// payConfigVO.setPay84((Boolean) redisTemplate.opsForValue().get(pay84));
// }
return AjaxResult.success(payConfigVO);
}
@ -76,10 +76,16 @@ public class OnlinePayConfigController {
public AjaxResult saveConfig(@RequestBody OnlinePayConfigParam param) {
Integer pkCountry = SecurityUtils.getPkCountry();
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_5, pkCountry), param.getPay5());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_6, pkCountry), param.getPay6());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_32, pkCountry), param.getPay32());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_33, pkCountry), param.getPay33());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_81, pkCountry), param.getPay81());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_82, pkCountry), param.getPay82());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_83, pkCountry), param.getPay83());
redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_84, pkCountry), param.getPay84());
// redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_83, pkCountry), param.getPay83());
// redisTemplate.opsForValue().set(String.format(PayConfigConstants.PAY_CONFIG_84, pkCountry), param.getPay84());
return AjaxResult.success();
}

View File

@ -102,27 +102,27 @@ public class OnlineRefundController extends BaseController {
* @param pkId 退款ID
* @return
*/
@GetMapping("/query-status")
public AjaxResult queryStatus(@RequestParam Long pkId) {
TOnlineRefund tOnlineRefund = itOnlineRefundService.getById(pkId);
if (ERefundStatus.REFUNDED.getValue() == tOnlineRefund.getRefundStatus()) {
// 已经退款成功
return AjaxResult.error("当前数据已退款");
}
String str;
if (EPayChannel.HUIFU.getValue() == tOnlineRefund.getRefundChannel()) {
// 新汇付退款
str = iRefundService.queryHuifuRefundHandle(tOnlineRefund);
} else {
return AjaxResult.error("暂不支持该数据查询");
}
if (null == str) {
return AjaxResult.success();
}
return AjaxResult.error(str);
}
// @GetMapping("/query-status")
// public AjaxResult queryStatus(@RequestParam Long pkId) {
// TOnlineRefund tOnlineRefund = itOnlineRefundService.getById(pkId);
// if (ERefundStatus.REFUNDED.getValue() == tOnlineRefund.getRefundStatus()) {
// // 已经退款成功
// return AjaxResult.error("当前数据已退款");
// }
//
// String str;
//
// if (EPayChannel.HUIFU.getValue() == tOnlineRefund.getRefundChannel()) {
// // 新汇付退款
// str = iRefundService.queryHuifuRefundHandle(tOnlineRefund);
// } else {
// return AjaxResult.error("暂不支持该数据查询");
// }
//
// if (null == str) {
// return AjaxResult.success();
// }
// return AjaxResult.error(str);
// }
}

View File

@ -0,0 +1,243 @@
package com.hzs.third.pay.controller.notify;
import cn.hutool.json.JSONUtil;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.core.enums.EPayType;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.third.pay.config.JdPayConfig;
import com.hzs.third.pay.constants.JdPayConstants;
import com.hzs.third.pay.controller.base.JdBaseController;
import com.hzs.third.pay.dto.jd.JdPayNotifyBody;
import com.hzs.third.pay.jdpay.dto.JdPayTradeSuccessNotify;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.service.IPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* 京东支付回调控制器
*/
@Slf4j
@RestController
@RequestMapping("/jd")
public class JdPayNotifyController extends JdBaseController {
@Autowired
private JdPayConfig jdPayConfig;
@Autowired
private IPayService iPayService;
/**
* 京东支付回调
*
* @param request
*/
@PostMapping("/notify")
public String notify(HttpServletRequest request) {
try {
String reqBody = this.getRequestBody(request);
log.info("京东支付回调! body {}", reqBody);
if (this.checkToken(request, reqBody, jdPayConfig.getSecretKey())) {
// 签名校验通过
JdPayNotifyBody notifyBody = JSONUtil.toBean(reqBody, JdPayNotifyBody.class);
// 支付流水号
String payNumber = notifyBody.getOrderNum();
// 支付时间
Date payTime = DateUtils.parseDate(notifyBody.getCompleteTime(), DateUtils.YYYY_MM_DD_HH_MM_SS);
// 支付金额
BigDecimal payMoney = new BigDecimal(notifyBody.getOrderAmount());
// 支付扩展类型
String type = notifyBody.getExtraInfo();
// 回调订单编号
String thirdOrderCode = notifyBody.getRequestNum();
// 支付信息编号
String orderCode = thirdOrderCode;
// 处理订单号以及扩展类型
if (orderCode.indexOf("-") > 0) {
// 带有分隔需要处理
orderCode = orderCode.split("-")[0];
}
// 支付后续业务处理
if (iPayService.notifyHandle(type, orderCode, thirdOrderCode, payNumber, payTime, payMoney, EPayChannel.JD, "")) {
return JdPayConstants.RETURN_SUCCESS;
}
} else {
log.error("京东支付回调签名校验失败!");
}
} catch (Exception e) {
log.error("京东支付回调处理异常", e);
}
return JdPayConstants.RETURN_FAIL;
}
@Resource(name = "jdPayBank")
private JdPay jdPay;
/**
* 京东收银台处理成功返回
*/
private static final String SUCCESS = "SUCCESS";
/**
* 京东收银台处理失败返回
*/
private static final String ERROR = "ERROR";
/**
* 京东收银台异步支付回调
*
* @param reqText
* @return 成功返回"SUCCESS", 失败返回"ERROR",京东支付会再次发起通知通知频次见接口文档
*/
@PostMapping("/trade-notify")
public String tradeNotify(@RequestBody String reqText) {
log.info("京东收银台支付异步回调! reqText {}", reqText);
try {
// 验证签名与解密
String interData = jdPay.verifyResponse(reqText);
JdPayTradeSuccessNotify successNotify = GsonUtil.fromJson(interData, JdPayTradeSuccessNotify.class);
if (null != successNotify) {
if ("FINI".equals(successNotify.getTradeStatus())) {
// 支付成功处理
// 商户订单号
String payNumber = successNotify.getTradeNo();
// 渠道流水号
String channelNumber = "";
// 支付完成时间
Date payTime = DateUtils.parseDateOne(successNotify.getFinishDate(), DateUtils.YAMMERERS);
// 支付扩展类型
String type = successNotify.getReturnParams();
// 回调订单编号
String thirdOrderCode = successNotify.getOutTradeNo();
// 订单编号
String orderCode = thirdOrderCode;
String acqOrderId = successNotify.getAcqOrderId();
// 订单金额
int tradeAmount = Integer.parseInt(successNotify.getTradeAmount());
BigDecimal payMoney = new BigDecimal(tradeAmount).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
Integer payType = convertPayType(successNotify.getPayTool());
// 支付后续业务处理
if (iPayService.notifyHandle(type, orderCode, thirdOrderCode, payNumber, payTime, payMoney, EPayChannel.JD, channelNumber, payType, acqOrderId)) {
return SUCCESS;
}
} else {
log.error("京东收银台支付异步回调失败resultDesc: {}", successNotify.getResultDesc());
}
} else {
log.error("京东收银台支付异步回调解密为空");
}
} catch (Exception e) {
log.error("京东收银台支付异步回调异常", e);
}
return ERROR;
}
private Integer convertPayType(String payName){
Integer result = EPayType.WECHAT.getValue();
switch (payName){
case "XJK":
result = EPayType.MINI_TREASURY.getValue();
break;
case "JIOU":
result = EPayType.CREDIT_LINE.getValue();
break;
case "SJIOU":
result = EPayType.SUPER_CREDIT_LINE.getValue();
break;
case "ACCT":
result = EPayType.WALLET_BALANCE.getValue();
break;
case "EXPR":
result = EPayType.BANK_CARD.getValue();
break;
case "WX":
result = EPayType.WECHAT.getValue();
break;
case "ALIPAY":
result = EPayType.ALIPAY.getValue();
break;
case "YSF":
result = EPayType.CLOUD_PAY.getValue();
break;
}
return result;
}
/**
* 京东收银台页面回调
*
* @param request
* @return
*/
@PostMapping("/sync-notify")
public String syncNotify(HttpServletRequest request) {
// 签名验证逻辑需要支持添加通知字段不影响结果
Map<String, String> respMap = getAllRequestParam(request);
log.info("京东收银台支付页面回调! request: {}", respMap);
try {
if (jdPay.verifyPageCallBack(respMap)) {
if ("FINI".equals(respMap.get("status"))) {
// todo 支付成功
return SUCCESS;
} else if ("WPAR".equals(respMap.get("status"))) {
// todo 支付处理中
log.error("京东支付页面回调,支付处理中");
} else {
// todo 支付失败
log.error("京东支付页面回调,支付失败");
}
} else {
// 签名异常报错
log.error("京东收银台支付页面回调!签名异常!");
}
} catch (Exception e) {
log.error("京东支付页面回调参数", e);
}
return ERROR;
}
/**
* 获取客户端请求参数中所有的信息
*/
private Map<String, String> getAllRequestParam(final HttpServletRequest request) {
Map<String, String> respMap = new HashMap<>();
Enumeration<?> temp = request.getParameterNames();
if (null != temp) {
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
respMap.put(en, value);
//如果字段的值为空判断若值为空则删除这个字段>
if (null == respMap.get(en) || "".equals(respMap.get(en))) {
respMap.remove(en);
}
}
}
return respMap;
}
}

View File

@ -0,0 +1,148 @@
package com.hzs.third.pay.controller.notify;
import cn.hutool.json.JSONUtil;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.third.pay.config.JdPayConfig;
import com.hzs.third.pay.constants.JdPayConstants;
import com.hzs.third.pay.controller.base.JdBaseController;
import com.hzs.third.pay.jdpay.dto.JdPayRefundSuccessNotify;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.service.IRefundService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 京东退填回调控制器
* @Author: jiang chao
* @Time: 2023/8/17 15:50
* @Classname: JdRefundNotifyController
* @PackageName: com.hzs.third.pay.controller.notify
*/
@Slf4j
@RestController
@RequestMapping("/jd-refund")
public class JdRefundNotifyController extends JdBaseController {
@Autowired
private JdPayConfig jdPayConfig;
@Autowired
private IRefundService iRefundService;
/**
* 京东退款回调
*
* @param request
* @return
* @throws Exception
*/
@PostMapping("/notify")
public String refundNotify(HttpServletRequest request) {
try {
String reqBody = this.getRequestBody(request);
log.info("京东退款回调! body {}", reqBody);
if (this.checkToken(request, reqBody, jdPayConfig.getSecretKey())) {
// 签名校验通过
Map<String, String> dataMap = JSONUtil.toBean(reqBody, HashMap.class);
// 退款编号
String refundCode = dataMap.get("refundRequestNum");
// 京东交易流水号
String refundNumber = dataMap.get("orderNum");
// 退款完成时间
Date finishTime = DateUtils.parseDate(dataMap.get("refundSuccessTime"), DateUtils.YYYY_MM_DD_HH_MM_SS);
// 退款金额
BigDecimal finishMoney = new BigDecimal(dataMap.get("refundAmount"));
if (JdPayConstants.ORDER_SUCCESS.equals(dataMap.get("refundStatus"))) {
// 退款成功
// 退款后续业务处理
if (iRefundService.notifyHandle(EPayChannel.JD, refundCode, refundNumber, finishTime, finishMoney)) {
return JdPayConstants.RETURN_SUCCESS;
}
} else {
// 退款失败
log.error("京东退款失败: {}", dataMap.get("failReason"));
if (iRefundService.notifyErrorHandle(EPayChannel.JD, refundCode, refundNumber, dataMap.get("failReason"))) {
return JdPayConstants.RETURN_SUCCESS;
}
}
} else {
log.error("京东支付回调签名校验失败!");
}
} catch (Exception e) {
log.error("京东支付回调处理异常", e);
}
return JdPayConstants.RETURN_FAIL;
}
@Resource(name = "jdPayBank")
private JdPay jdPay;
/**
* 京东收银台处理成功返回
*/
private static final String SUCCESS = "SUCCESS";
/**
* 京东收银台处理失败返回
*/
private static final String ERROR = "ERROR";
/**
* 京东收银台退款异步支付回调
*
* @param reqText
* @return 成功返回"SUCCESS", 失败返回"ERROR",退款通知会再次发起通知通知频次见接口文档
*/
@PostMapping("/trade-notify")
public String tradeNotify(@RequestBody String reqText) {
log.info("京东收银台退款异步回调! reqText {}", reqText);
try {
// 验证签名与解密
String interData = jdPay.verifyResponse(reqText);
JdPayRefundSuccessNotify successNotify = GsonUtil.fromJson(interData, JdPayRefundSuccessNotify.class);
if (null != successNotify) {
if ("FINI".equals(successNotify.getTradeStatus())) {
// 退款编号
String refundCode = successNotify.getOutTradeNo();
// 京东交易流水号
String refundNumber = successNotify.getTradeNo();
// 退款完成时间
Date finishTime = DateUtils.parseDate(successNotify.getFinishDate(), DateUtils.YAMMERERS);
// 退款金额
int tradeAmount = Integer.parseInt(successNotify.getTradeAmount());
BigDecimal finishMoney = new BigDecimal(tradeAmount).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP);
// 退款后续业务处理
if (iRefundService.notifyHandle(EPayChannel.JD, refundCode, refundNumber, finishTime, finishMoney)) {
return SUCCESS;
}
} else {
log.error("京东收银台退款异步回调失败resultDesc: {}", successNotify.getResultDesc());
}
} else {
log.error("京东收银台退款异步回调解密为空");
}
} catch (Exception e) {
log.error("京东收银台退款异步回调异常", e);
}
return ERROR;
}
}

View File

@ -0,0 +1,68 @@
package com.hzs.third.pay.dto.jd;
import lombok.Data;
/**
* 京东支付回调body体
*/
@Data
public class JdPayNotifyBody {
/**
* 处理状态
*/
private String status;
/**
* 商户订单编号
*/
private String requestNum;
/**
* 订单金额
*/
private String orderAmount;
/**
* 订单完成时间
*/
private String completeTime;
/**
* 支付类型支付宝微信等
*/
private String payWay;
/**
* 扩展内容
*/
private String extraInfo;
/**
* 订单编号京东系统订单号
*/
private String orderNum;
/**
* 银行订单号银行流水号
*/
private String bankOutTradeNum;
/**
* 银行流水号渠道订单流水号
*/
private String bankRequestNum;
/**
* 订单类型
*/
private String orderType;
/**
* 订单子类型
*/
private String subOrderType;
/**
* 订单失败原因
*/
private String failReason;
/**
* 订单失败编码
*/
private String faiCode;
}

View File

@ -0,0 +1,28 @@
package com.hzs.third.pay.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 京东应用类型
*/
@AllArgsConstructor
@Getter
public enum EJdAppType {
IOS("IOS", "IOS"),
AND("AND", "AND"),
H5("H5", "H5"),
WX("WX", "WX"),
OTHER("OTHER", "OTHER"),
;
private final String value;
private final String label;
}

View File

@ -0,0 +1,52 @@
package com.hzs.third.pay.enums;
import lombok.Getter;
/**
* 京东快捷支付银行
*/
@Getter
public enum EJdFastBankCode {
ICBC("ICBC", "工商银行"),
BOC("BOC", "中国银行"),
CCB("CCB", "建设银行"),
CMBCHINA("CMBCHINA", "招商银行"),
POST("POST", "邮政储蓄银行"),
ECITIC("ECITIC", "中信银行"),
CEB("CEB", "光大银行"),
BOCO("BOCO", "交通银行"),
CIB("CIB", "兴业银行"),
CMBC("CMBC", "民生银行"),
SZCB("SZCB", "平安银行"),
CGB("CGB", "广发银行"),
BCCB("BCCB", "北京银行"),
HXB("HXB", "华夏银行"),
SPDB("SPDB", "浦发银行"),
SHB("SHB", "上海银行"),
;
private final String value;
private final String label;
EJdFastBankCode(String value, String label) {
this.value = value;
this.label = label;
}
/**
* 根据value获取label
*
* @param value
* @return
*/
public static EJdFastBankCode getEnumByValue(String value) {
for (EJdFastBankCode eJdFastBankCode : EJdFastBankCode.values()) {
if (eJdFastBankCode.getValue().equals(value)) {
return eJdFastBankCode;
}
}
return null;
}
}

View File

@ -0,0 +1,27 @@
package com.hzs.third.pay.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 京东终端类型
*/
@AllArgsConstructor
@Getter
public enum EJdTerminalType {
IMEI("IMEI", "IMEI"),
MAC("MAC", "MAC"),
// 针对IOS系统
UUID("UUID", "UUID"),
OTHER("OTHER", "OTHER"),
;
private final String value;
private final String label;
}

View File

@ -0,0 +1,58 @@
package com.hzs.third.pay.jdpay.config;
import com.hzs.third.pay.config.JdPayBankProperties;
import com.hzs.third.pay.config.JdPayWechatAlipayProperties;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.sdk.JdPayDefaultNewConfig;
import com.hzs.third.pay.jdpay.sdk.JdPayNewConfig;
import com.hzs.third.pay.jdpay.util.FileUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*************************************************
*
* 京东支付sdk初始化
*
*************************************************/
@Configuration
public class JdPayAutoConfiguration {
@Autowired
private JdPayBankProperties jdPayBankProperties;
@Autowired
private JdPayWechatAlipayProperties jdPayWechatAlipayProperties;
@Bean(name = "jdPayBank")
public JdPay initJdBankPay() {
// 加载商户私钥证书
// byte[] privateCert = FileUtil.readFile(priCert);
byte[] privateCert = FileUtil.readFile(jdPayBankProperties.getPriCert());
// 加载商户公钥证书
// byte[] publicCert = FileUtil.readFile(pubCert);
byte[] publicCert = FileUtil.readFile(jdPayBankProperties.getPubCert());
// 检查商户证书
checkCert(privateCert, publicCert);
// 初始化京东支付配置对象
// JdPayNewConfig myConfig = new JdPayDefaultNewConfig(merchantNo, signKey, privateCert, priCertPwd, publicCert, apiDomain);
JdPayNewConfig myConfig = new JdPayDefaultNewConfig(jdPayBankProperties.getMerchantNo()
, jdPayBankProperties.getSignKey()
, privateCert
, jdPayBankProperties.getPriCertPwd()
, publicCert
, jdPayBankProperties.getApiDomain());
return new JdPay(myConfig);
}
private void checkCert(byte[] privateCert, byte[] publicCert) {
if (privateCert == null) {
throw new RuntimeException("读取京东支付商户私钥证书为空");
}
if (publicCert == null) {
throw new RuntimeException("读取京东支付商户公钥证书为空");
}
}
}

View File

@ -0,0 +1,133 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 京东聚合下单请求
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayAggregateCreateOrderRequest implements Serializable {
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 订单总金额
*/
private String tradeAmount;
/**
* 订单创建时间
*/
private String createDate;
/**
* 订单时效时间 分钟
*/
private String tradeExpiryTime;
/**
* 交易名称
*/
private String tradeSubject;
/**
* 交易类型
*/
private String tradeType;
/**
* 交易描述
*/
private String tradeRemark;
/**
* 币种
*/
private String currency;
/**
* 用户ip
*/
private String userIp;
/**
* 回传字段
*/
private String returnParams;
/**
* 商品信息list-- json串
*/
private String goodsInfo;
/**
* 商户用户标识
*/
private String userId;
/**
* 交易异步通知url
*/
private String notifyUrl;
/**
* 同步通知页面url
*/
private String pageBackUrl;
/**
* 点击完成按钮后跳转页面
*/
private String callbackUrl;
/**
* 风控信息map-- json串
*/
private String riskInfo;
/**
* 行业分类码
*/
private String categoryCode;
/**
* 业务类型
*/
private String bizTp;
/**
* 订单类型
*/
private String orderType;
/**
* 报文格式
*/
private String messageFormat;
/**
* 收货信息
*/
private String receiverInfo;
/**
* 交易场景
*/
private String sceneType;
/**
* 指定支付信息
*/
private String identity;
/**
* 接入方式
*/
private String accessType;
/**
* openId
*/
private String subOpenId;
/**
* appId
*/
private String subAppId;
/**
* 分帐信息
*/
private String divisionAccount;
/**
* 门店号
*/
private String storeNum;
}

View File

@ -0,0 +1,52 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 京东聚合下单响应
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayAggregateCreateOrderResponse implements Serializable {
/**
* 业务结果
*/
private String resultCode;
/**
* 响应描述
*/
private String resultDesc;
/**
* 商户号
*/
private String merchantNo;
/**
* 京东交易订单号
*/
private String tradeNo;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 跳转收银台链接
*/
private String webUrl;
/**
* PC扫码支付
*/
private String qrCode;
/**
* 唤起通道参数
*/
private String payInfo;
}

View File

@ -0,0 +1,45 @@
package com.hzs.third.pay.jdpay.dto;
import java.io.Serializable;
import java.util.List;
/**
* 分账业务字段
*/
public class JdPayDivisionAccount implements Serializable {
/**
* 版本号
*/
private String version = "v2";
// private String version;
/**
* 分账交易信息
*/
private List<JdPayDivisionAccountTradeInfo> divisionAccountTradeInfoList;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public List<JdPayDivisionAccountTradeInfo> getDivisionAccountTradeInfoList() {
return divisionAccountTradeInfoList;
}
public void setDivisionAccountTradeInfoList(List<JdPayDivisionAccountTradeInfo> divisionAccountTradeInfoList) {
this.divisionAccountTradeInfoList = divisionAccountTradeInfoList;
}
@Override
public String toString() {
return "{\"JdPayDivisionAccount\":{"
+ "\"version\":\"" + version + "\""
+ ", \"divisionAccountTradeInfoList\":" + divisionAccountTradeInfoList
+ "}}";
}
}

View File

@ -0,0 +1,46 @@
package com.hzs.third.pay.jdpay.dto;
import java.io.Serializable;
import java.util.List;
/**
* 分账业务字段
* @author liulian115
*/
public class JdPayDivisionAccountRefund implements Serializable {
/**
* 版本号
*/
private String version="V2";
/**
* 分账交易信息
* @see JdPayDivisionAccountRefundInfo
*/
private List<JdPayDivisionAccountRefundInfo> divisionAccountRefundInfoList;
@Override
public String toString() {
return "{\"JdPayDivisionAccountRefund\":{"
+ "\"version\":\"" + version + "\""
+ ", \"divisionAccountRefundInfoList\":" + divisionAccountRefundInfoList
+ "}}";
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public List<JdPayDivisionAccountRefundInfo> getDivisionAccountRefundInfoList() {
return divisionAccountRefundInfoList;
}
public void setDivisionAccountRefundInfoList(List<JdPayDivisionAccountRefundInfo> divisionAccountRefundInfoList) {
this.divisionAccountRefundInfoList = divisionAccountRefundInfoList;
}
}

View File

@ -0,0 +1,80 @@
package com.hzs.third.pay.jdpay.dto;
import java.io.Serializable;
/**
* 分账交易信息
*/
public class JdPayDivisionAccountRefundInfo implements Serializable {
/**
* 分账子商户号
*/
private String merchantNo;
/**
* 分账子商户订单号
*/
private String outTradeNo;
/**
* 分账子商户业务单号
*/
private String bizTradeNo;
/**
* 分账子单金额
*/
private String tradeAmount;
/**
* 原商户订单号
*/
private String originalOutTradeNo;
public String getOriginalOutTradeNo() {
return originalOutTradeNo;
}
public void setOriginalOutTradeNo(String originalOutTradeNo) {
this.originalOutTradeNo = originalOutTradeNo;
}
public String getMerchantNo() {
return merchantNo;
}
public void setMerchantNo(String merchantNo) {
this.merchantNo = merchantNo;
}
public String getOutTradeNo() {
return outTradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getBizTradeNo() {
return bizTradeNo;
}
public void setBizTradeNo(String bizTradeNo) {
this.bizTradeNo = bizTradeNo;
}
public String getTradeAmount() {
return tradeAmount;
}
public void setTradeAmount(String tradeAmount) {
this.tradeAmount = tradeAmount;
}
@Override
public String toString() {
return "{\"JdPayDivisionAccountRefundInfo\":{"
+ "\"merchantNo\":\"" + merchantNo + "\""
+ ", \"outTradeNo\":\"" + outTradeNo + "\""
+ ", \"bizTradeNo\":\"" + bizTradeNo + "\""
+ ", \"tradeAmount\":\"" + tradeAmount + "\""
+ ", \"originalOutTradeNo\":\"" + originalOutTradeNo + "\""
+ "}}";
}
}

View File

@ -0,0 +1,67 @@
package com.hzs.third.pay.jdpay.dto;
import java.io.Serializable;
/**
* 分账交易信息
*/
public class JdPayDivisionAccountTradeInfo implements Serializable {
/**
* 分账子商户号
*/
private String merchantNo;
/**
* 分账子商户订单号
*/
private String outTradeNo;
/**
* 分账子商户业务单号
*/
private String bizTradeNo;
/**
* 分账子单金额
*/
private String tradeAmount;
public String getMerchantNo() {
return merchantNo;
}
public void setMerchantNo(String merchantNo) {
this.merchantNo = merchantNo;
}
public String getOutTradeNo() {
return outTradeNo;
}
public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}
public String getBizTradeNo() {
return bizTradeNo;
}
public void setBizTradeNo(String bizTradeNo) {
this.bizTradeNo = bizTradeNo;
}
public String getTradeAmount() {
return tradeAmount;
}
public void setTradeAmount(String tradeAmount) {
this.tradeAmount = tradeAmount;
}
@Override
public String toString() {
return "{\"JdPayDivisionAccountTradeInfo\":{"
+ "\"merchantNo\":\"" + merchantNo + "\""
+ ", \"outTradeNo\":\"" + outTradeNo + "\""
+ ", \"bizTradeNo\":\"" + bizTradeNo + "\""
+ ", \"tradeAmount\":\"" + tradeAmount + "\""
+ "}}";
}
}

View File

@ -0,0 +1,16 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayQueryEnterpriseWalletBalanceRequest implements Serializable {
private String accountNo;
}

View File

@ -0,0 +1,25 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayQueryEnterpriseWalletBalanceResponse implements Serializable {
/**
* 业务结果
*/
private String resultCode;
/**
* 响应描述
*/
private String resultDesc;
}

View File

@ -0,0 +1,54 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 京东退款响应
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayRefundRequest implements Serializable {
/**
* 商户原交易号
*/
private String originalOutTradeNo;
/**
* 商户退款单号
*/
private String outTradeNo;
/**
* 退款金额
*/
private String tradeAmount;
/**
* 异步通知URL
*/
private String notifyUrl;
/**
* 回传字段
*/
private String returnParams;
/**
* 币种
*/
private String currency;
/**
* 交易时间
*/
private String tradeDate;
/**
* 退款分账信息
*
* @see
*/
private String divisionAccountRefund;
}

View File

@ -0,0 +1,58 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 京东退款请求
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayRefundResponse implements Serializable {
/**
* 业务结果
*/
private String resultCode;
/**
* 响应描述
*/
private String resultDesc;
/**
* 京东退款订单号
*/
private String tradeNo;
/**
* 商户退款订单号
*/
private String outTradeNo;
/**
* 商户原正单订单号
*/
private String originalOutTradeNo;
/**
* 订单总金额
*/
private String tradeAmount;
/**
* 交易状态
*/
private String tradeStatus;
/**
* 币种
*/
private String currency;
/**
* 退款完成时间
*/
private String finishDate;
}

View File

@ -0,0 +1,66 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @Description: 退款成功通知
* @Author: jiang chao
* @Time: 2025/3/21 10:24
* @Classname: JdPayRefundSuccessNotify
* @PackageName: com.hzs.third.pay.jdpay.dto
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayRefundSuccessNotify implements Serializable {
/**
* 业务结果
*/
private String resultCode;
/**
* 响应描述
*/
private String resultDesc;
/**
* 京东退款订单号
*/
private String tradeNo;
/**
* 商户退款订单号
*/
private String outTradeNo;
/**
* 商户原正单订单号
*/
private String originalOutTradeNo;
/**
* 订单总金额
*/
private String tradeAmount;
/**
* 退款完成时间
*/
private String finishDate;
/**
* 交易状态
*/
private String tradeStatus;
/**
* 回传字段
*/
private String returnParams;
/**
* 币种
*/
private String currency;
}

View File

@ -0,0 +1,99 @@
package com.hzs.third.pay.jdpay.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @Description: 交易成功通知
* @Author: jiang chao
* @Time: 2025/3/21 10:24
* @Classname: JdPayRefundSuccessNotify
* @PackageName: com.hzs.third.pay.jdpay.dto
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class JdPayTradeSuccessNotify implements Serializable {
/**
* 业务结果
*/
private String resultCode;
/**
* 响应描述
*/
private String resultDesc;
/**
* 京东交易订单号
*/
private String tradeNo;
/**
* 商户订单号
*/
private String outTradeNo;
/**
* 订单总金额
*/
private String tradeAmount;
/**
* 币种
*/
private String currency;
/**
* 支付完成时间
*/
private String finishDate;
/**
* 交易类型
*/
private String tradeType;
/**
* 交易状态
*/
private String tradeStatus;
/**
* 回传字段
*/
private String returnParams;
/**
* 请求的端
*/
private String clientType;
/**
* 商户用户标识
*/
private String userId;
/**
* 优惠金额
*/
private String discountAmount;
/**
* 支付工具
*/
private String payTool;
/**
* 掩码卡号
*/
private String maskCardNo;
/**
* 卡类型
*/
private String cardType;
/**
* 银行编码
*/
private String bankCode;
/**
* 白条分期数
*/
private String installmentNum;
private String acqOrderId;
}

View File

@ -0,0 +1,177 @@
package com.hzs.third.pay.jdpay.sdk;
import com.hzs.third.pay.jdpay.dto.*;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.jdpay.util.JdPayApiUtil;
import com.hzs.third.pay.jdpay.util.SignUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/*************************************************
*
* 京东支付api实现类
*
*************************************************/
@Slf4j
public class JdPay {
private JdPayNewConfig jdPayNewConfig;
private JdPayHttpClientProxy jdPayHttpClientProxy;
public JdPay(JdPayNewConfig jdPayNewConfig) {
this.jdPayNewConfig = jdPayNewConfig;
this.jdPayHttpClientProxy = new JdPayHttpClientProxy(jdPayNewConfig, new JdPayHttpClient());
}
// /**
// * 作用统一下单
// * 场景京东支付
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayCreateOrderResponse 返回数据
// * @throws Exception
// */
// public JdPayCreateOrderResponse createOrder(JdPayCreateOrderRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.CREATE_ORDER_URL, request, JdPayCreateOrderResponse.class);
// }
/**
* 作用三方聚合统一下单
* 场景三方聚合
*
* @param request 向jdPay post的请求数据
* @return JdPayAggregateCreateOrderResponse 返回数据
* @throws Exception
*/
public JdPayAggregateCreateOrderResponse aggregateCreateOrder(JdPayAggregateCreateOrderRequest request) throws Exception {
return this.baseExecute(JdPayConstant.AGGREGATE_CREATE_ORDER_URL, request, JdPayAggregateCreateOrderResponse.class);
}
// /**
// * 作用订单查询
// * 场景查询订单信息 - 包括首次支付订单与代扣订单
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayQueryOrderResponse 返回数据
// * @throws Exception
// */
// public JdPayQueryOrderResponse queryOrder(JdPayQueryOrderRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.TRADE_QUERY_URL, request, JdPayQueryOrderResponse.class);
// }
//
// /**
// * 作用代扣
// * 场景代扣交易场景
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayAgreementPayResponse 返回数据
// * @throws Exception
// */
// public JdPayAgreementPayResponse agreementPay(JdPayAgreementPayRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.AGREEMENT_PAY_URL, request, JdPayAgreementPayResponse.class);
// }
/**
* 作用申请退款
* 场景退款
*
* @param request 向jdPay post的请求数据
* @return JdPayRefundResponse 返回数据
* @throws Exception
*/
public JdPayRefundResponse refund(JdPayRefundRequest request) throws Exception {
return this.baseExecute(JdPayConstant.REFUND_URL, request, JdPayRefundResponse.class);
}
// /**
// * 作用退款查询
// * 场景查询退款信息
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayQueryOrderResponse 返回数据
// * @throws Exception
// */
// public JdPayRefundQueryResponse refundQuery(JdPayRefundQueryRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.REFUND_QUERY_URL, request, JdPayRefundQueryResponse.class);
// }
//
// /**
// * 作用解约
// * 场景接触签约关系
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayAgreementCancelRequest 返回数据
// * @throws Exception
// */
// public JdPayAgreementCancelResponse agreementCancel(JdPayAgreementCancelRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.AGREEMENT_CANCEL_URL, request, JdPayAgreementCancelResponse.class);
// }
//
//
// /**
// * 作用解约查询
// * 场景查询签约关系
// *
// * @param request 向jdPay post的请求数据
// * @return JdPayAgreementCancelRequest 返回数据
// * @throws Exception
// */
// public JdPayAgreementQueryResponse agreementQuery(JdPayAgreementQueryRequest request) throws Exception {
// return this.baseExecute(JdPayConstant.AGREEMENT_QUERY_URL, request, JdPayAgreementQueryResponse.class);
// }
//
// public JdPayAgreementSignResponse agreementNewSign(JdPayAgreementSignRequest request) throws Exception{
// return this.baseExecute(JdPayConstant.AGREEMENT_NEW_SIGN_URL, request, JdPayAgreementSignResponse.class);
// }
/**
* 验证接口参数签名
* 场景:api接口返回参数异步回调请求参数
*/
public String verifyResponse(String respText) throws Exception {
String interData = JdPayApiUtil.decryptAndVerifySign(jdPayNewConfig, respText);
log.info("京东支付异步通知-解析数据:{}", interData);
return interData;
}
/**
* 验证页面回调参数
*
* @param respMap 页面回调参数
* @return 验证结果
* @throws Exception 异常
*/
public boolean verifyPageCallBack(Map<String, String> respMap) throws Exception {
return SignUtil.verifyPageCallBackSign(respMap, jdPayNewConfig.getSignKey());
}
/**
* 执行接口调用
*
* @param request 请求对象
* @param clazz 返回对象类型
* @return 返回对象
* @throws Exception 异常
*/
public <REQ, RES> RES baseExecute(String urlSuffix, REQ request, Class<RES> clazz) throws Exception {
String reqJson = GsonUtil.toJson(request);
String respJson = jdPayHttpClientProxy.execute(urlSuffix, reqJson);
return GsonUtil.fromJson(respJson, clazz);
}
// /**
// * 账户签约
// * @param request
// * @return
// * @throws Exception
// */
// public JdPayAgreementSignApplyResponse agreementSignApply(JdPayAgreementSignApplyRequest request) throws Exception{
// return this.baseExecute(JdPayConstant.AGREEMENT_SIGN_APPLY_URL, request, JdPayAgreementSignApplyResponse.class);
// }
public JdPayQueryEnterpriseWalletBalanceResponse queryEnterpriseWalletBalance(JdPayQueryEnterpriseWalletBalanceRequest request) throws Exception{
return this.baseExecute(JdPayConstant.QUERY_ENTERPRISE_WALLET_BALANCE_URL, request, JdPayQueryEnterpriseWalletBalanceResponse.class);
}
}

View File

@ -0,0 +1,98 @@
package com.hzs.third.pay.jdpay.sdk;
import org.apache.http.client.HttpClient;
public class JdPayConstant {
/**
* 京东支付统一收单 url
**/
public static final String CREATE_ORDER_URL = "/api/createOrder";
/**
* 三方聚合统一收单 url
**/
// public static final String AGGREGATE_CREATE_ORDER_URL = "/api/createAggregateOrder";
public static final String AGGREGATE_CREATE_ORDER_URL = "/api/createIndustryOrder";
/**
* 交易查询 url
**/
public static final String TRADE_QUERY_URL = "/api/queryOrder";
/**
* 退款申请 url
**/
public static final String REFUND_URL = "/api/refund";
/**
* 退款查询 url
**/
public static final String REFUND_QUERY_URL = "/api/refundQuery";
/**
* 代扣 url
**/
public static final String AGREEMENT_PAY_URL = "/api/agreementPay";
/**
* 签约关系查询 url
**/
public static final String AGREEMENT_QUERY_URL = "/api/agreementQuery";
/**
* 解约申请 url
**/
public static final String AGREEMENT_CANCEL_URL = "/api/agreementCancel";
/**
* 无卡号签约
*/
public static final String AGREEMENT_NEW_SIGN_URL = "/api/agreementNewSign";
/**
* 账户签约
*/
public static final String AGREEMENT_SIGN_APPLY_URL = "/api/agreementSignApply";
public static final String QUERY_ENTERPRISE_WALLET_BALANCE_URL = "/api/queryEnterpriseWalletBalance";
/**
* 随机字符常量
*/
public static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 公共字段常量 start
**/
public static final String CCM = "CCM";
public static final String MERCHANT_NO = "merchantNo";
public static final String REQ_NO = "reqNo";
public static final String CHARSET = "charset";
public static final String FORMAT_TYPE = "formatType";
public static final String SIGN_TYPE = "signType";
public static final String ENC_TYPE = "encType";
public static final String UTF8 = "UTF-8";
public static final String JSON = "JSON";
public static final String SHA256 = "SHA-256";
public static final String AP7 = "AP7";
public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static final String SIGN_DATA = "signData";
public static final String ENC_DATA = "encData";
public static final String RESP_DATA = "respData";
public static final String CODE = "code";
public static final String DESC = "desc";
public static final String SUCCESS_CODE = "00000";
/* 公共字段常量 end */
/**
* http请求常量 start
**/
public static final String HTTP = "http";
public static final String HTTPS = "https";
public static final String CONTENT_TYPE = "Content-Type";
public static final String UA = "User-Agent";
public static final String PKCS12 = "PKCS12";
public static final String TLS = "TLS";
public static final String APPLICATION_JSON = "application/json";
public static final String USER_AGENT = "jdPay" +
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion();
/* http请求常量 end */
public static final String URL_PATH = "/api/";
}

View File

@ -0,0 +1,73 @@
package com.hzs.third.pay.jdpay.sdk;
import com.hzs.third.pay.jdpay.util.FileUtil;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/*************************************************
*
* 商户默认配置类
* 商户也可以自行实现JdPayConfig调整配置属性
*
*************************************************/
public class JdPayDefaultNewConfig extends JdPayNewConfig {
private String merchantNo;
private String signKey;
private byte[] priCert;
private String priCertPwd;
private byte[] pubCert;
private String apiDomain;
public JdPayDefaultNewConfig(String merchantNo, String signKey, byte[] priCert, String priCertPwd, byte[] pubCert, String apiDomain) {
this.merchantNo = merchantNo;
this.signKey = signKey;
this.priCert = priCert;
this.priCertPwd = priCertPwd;
this.pubCert = pubCert;
this.apiDomain = apiDomain;
}
public JdPayDefaultNewConfig(String merchantNo, String signKey, String priCertPwd, String apiDomain, String priCertPath, String pubCertPath) {
this.merchantNo = merchantNo;
this.signKey = signKey;
this.priCertPwd = priCertPwd;
this.apiDomain = apiDomain;
//加载商户私钥证书
this.priCert = FileUtil.readFile(priCertPath);
//加载商户公钥证书
this.pubCert = FileUtil.readFile(pubCertPath);
}
@Override
public String getMerchantNo() {
return this.merchantNo;
}
@Override
public String getSignKey() {
return this.signKey;
}
@Override
public InputStream getPriCert() {
return new ByteArrayInputStream(this.priCert);
}
@Override
public String getPriCertPwd() {
return this.priCertPwd;
}
@Override
public InputStream getPubCert() {
return new ByteArrayInputStream(this.pubCert);
}
@Override
public String getApiDomain() {
return this.apiDomain;
}
}

View File

@ -0,0 +1,173 @@
package com.hzs.third.pay.jdpay.sdk;
import com.hzs.common.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
@Slf4j
public class JdPayHttpClient {
private CloseableHttpClient httpClient = null;
public JdPayHttpClient() {
}
/**
* 远程调用
*
* @param jdPayNewConfig 京东支付配置
* @param urlSuffix 接口url后缀
* @param request 请求参数
* @return 返回参数看
* @throws Exception 远程调用异常信息
*/
public String execute(JdPayNewConfig jdPayNewConfig, String urlSuffix, String request) {
HttpClient httpClient = buildHttpClient(jdPayNewConfig);
String url = jdPayNewConfig.getApiDomain() + urlSuffix;
int connectTimeoutMs = jdPayNewConfig.getHttpConnectTimeoutMs();
int readTimeoutMs = jdPayNewConfig.getHttpReadTimeoutMs(urlSuffix);
try {
return this.sendRequest(httpClient, url, request, connectTimeoutMs, readTimeoutMs);
} catch (IOException e) {
throw new ServiceException("HTTP read timeout");
}
}
/**
* 获取httpClient
*
* @param jdPayNewConfig 京东支付配置
* @return httpClient
*/
private HttpClient buildHttpClient(JdPayNewConfig jdPayNewConfig) {
if (jdPayNewConfig.useHttpConnectPool()) {
return getHttpClientByPoolingConnectionManager(jdPayNewConfig);
} else {
return getHttpClientByBasicConnectionManager();
}
}
/**
* 发送请求
*
* @param httpClient httpClient
* @param url 请求地址
* @param data 请求数据
* @param connectTimeoutMs 连接超时时间单位是毫秒
* @param readTimeoutMs 读超时时间单位是毫秒
* @return api接口返回参数
*/
private String sendRequest(HttpClient httpClient, String url, String data, int connectTimeoutMs, int readTimeoutMs) throws IOException {
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(data, JdPayConstant.UTF8);
httpPost.addHeader(JdPayConstant.CONTENT_TYPE, JdPayConstant.APPLICATION_JSON);
httpPost.addHeader(JdPayConstant.UA, JdPayConstant.USER_AGENT);
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (HttpStatus.SC_OK != statusCode) {
throw new ServiceException(String.format("httpStatusCode: %s", statusCode));
}
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, JdPayConstant.UTF8);
}
private HttpClient getHttpClientByBasicConnectionManager() {
HttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(JdPayConstant.HTTP, PlainConnectionSocketFactory.getSocketFactory())
.register(JdPayConstant.HTTPS, SSLConnectionSocketFactory.getSocketFactory())
.build(),
null,
null,
null
);
return HttpClientBuilder.create()
// 连接池
.setConnectionManager(connManager)
// 重试策略
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build();
}
private HttpClient getHttpClientByPoolingConnectionManager(JdPayNewConfig jdPayNewConfig) {
if (httpClient != null) {
return httpClient;
}
synchronized (this) {
if (httpClient == null) {
long start = System.currentTimeMillis();
PoolingHttpClientConnectionManager httpClientConnectionManager = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(JdPayConstant.HTTP, PlainConnectionSocketFactory.getSocketFactory())
.register(JdPayConstant.HTTPS, SSLConnectionSocketFactory.getSocketFactory())
.build()
);
// 设置最大连接数
httpClientConnectionManager.setMaxTotal(jdPayNewConfig.getHttpConnectMaxTotal());
// 将每个路由默认最大连接数
httpClientConnectionManager.setDefaultMaxPerRoute(jdPayNewConfig.getHttpConnectDefaultTotal());
httpClient = HttpClients.custom()
// 设置连接池
.setConnectionManager(httpClientConnectionManager)
// 连接存活策略
.setKeepAliveStrategy(getConnectionKeepAliveStrategy())
// 重试策略
.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build();
// 连接回收策略
JdPayHttpConnectionMonitor idleConnectionMonitor = new JdPayHttpConnectionMonitor(httpClientConnectionManager, jdPayNewConfig.getHttpConnectIdleAliveMs());
idleConnectionMonitor.start();
log.info("初始化http连接池耗时:{}", System.currentTimeMillis() - start);
}
}
return httpClient;
}
private ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
return new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && "timeout".equalsIgnoreCase(param)) {
try {
log.info("Keep-Alive指定时长:{}", value);
return Long.parseLong(value) * 1000;
} catch (NumberFormatException ignore) {
}
}
}
// Keep alive for 300 seconds only
return 300 * 1000;
}
};
}
}

View File

@ -0,0 +1,58 @@
package com.hzs.third.pay.jdpay.sdk;
import com.hzs.third.pay.jdpay.util.JdPayApiUtil;
import lombok.extern.slf4j.Slf4j;
import java.security.SecureRandom;
import java.util.Random;
@Slf4j
public class JdPayHttpClientProxy {
private static final Random RANDOM = new SecureRandom();
private final JdPayNewConfig jdPayNewConfig;
private final JdPayHttpClient jdPayHttpClient;
public JdPayHttpClientProxy(JdPayNewConfig jdPayNewConfig, JdPayHttpClient jdPayHttpClient) {
this.jdPayNewConfig = jdPayNewConfig;
this.jdPayHttpClient = jdPayHttpClient;
}
/**
* 获取随机字符串含数字和大小写英文字母
*/
private static String genNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = JdPayConstant.SYMBOLS.charAt(RANDOM.nextInt(JdPayConstant.SYMBOLS.length()));
}
return new String(nonceChars);
}
public String execute(String urlSuffix, String request) throws Exception {
long startTimestampMs = System.currentTimeMillis();
String response;
// 接口名称
String apiName = urlSuffix.replaceFirst(JdPayConstant.URL_PATH, "");
// 唯一请求号
String reqNo = genNonceStr();
try {
log.info("1.{}接口请求参数:{}", apiName, request);
// 请求参数加密和签名
String httpRequest = JdPayApiUtil.encryptAndSignature(jdPayNewConfig, reqNo, request);
log.info("2.{}远程调用请求参数:{}", apiName, httpRequest);
String httpResponse = jdPayHttpClient.execute(jdPayNewConfig, urlSuffix, httpRequest);
log.info("3.{}远程调用返回参数:{}", apiName, httpResponse);
// 验证和解析返回参数
response = JdPayApiUtil.decryptAndVerifySign(jdPayNewConfig, httpResponse);
log.info("4.{}耗时:{},接口返回参数:{}", apiName, (System.currentTimeMillis() - startTimestampMs), response);
} catch (Exception e) {
log.error("{}远程调用异常,接口参数:{}", apiName, request, e);
throw e;
}
return response;
}
}

View File

@ -0,0 +1,55 @@
package com.hzs.third.pay.jdpay.sdk;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.HttpClientConnectionManager;
import java.util.concurrent.TimeUnit;
/**
* 用于监控空闲的连接池连接
*/
@Slf4j
public class JdPayHttpConnectionMonitor extends Thread {
// 轮询检查时间间隔单位毫秒
private static final int MONITOR_INTERVAL_MS = 5000;
// 连接最大空闲时间单位毫秒
private static int IDLE_ALIVE_MS = 20000;
private final HttpClientConnectionManager httpClientConnectionManager;
private volatile boolean shutdown;
JdPayHttpConnectionMonitor(HttpClientConnectionManager httpClientConnectionManager, int idleAliveMs) {
super();
this.httpClientConnectionManager = httpClientConnectionManager;
IDLE_ALIVE_MS = idleAliveMs;
this.shutdown = false;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(MONITOR_INTERVAL_MS);
// 关闭无效的连接
httpClientConnectionManager.closeExpiredConnections();
// 关闭空闲时间超过IDLE_ALIVE_MS的连接
httpClientConnectionManager.closeIdleConnections(IDLE_ALIVE_MS, TimeUnit.MILLISECONDS);
}
}
} catch (InterruptedException e) {
log.error("连接池管理任务异常:", e);
}
}
// 关闭后台连接
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}

View File

@ -0,0 +1,154 @@
package com.hzs.third.pay.jdpay.sdk;
import java.io.InputStream;
import java.util.HashMap;
/**
* 京东支付配置类
*/
public abstract class JdPayNewConfig {
/**
* 接口超时时间全局配置
*/
private static final HashMap<String, Integer> HTTP_READ_TIMEOUT_CONFIG = new HashMap<String, Integer>() {{
put(JdPayConstant.CREATE_ORDER_URL, 15000);
put(JdPayConstant.TRADE_QUERY_URL, 5000);
put(JdPayConstant.REFUND_URL, 5000);
put(JdPayConstant.REFUND_QUERY_URL, 5000);
put(JdPayConstant.AGREEMENT_PAY_URL, 10000);
put(JdPayConstant.AGREEMENT_QUERY_URL, 5000);
put(JdPayConstant.AGREEMENT_CANCEL_URL, 5000);
}};
/**
* 获取merchantNo
*
* @return merchantNo
*/
public abstract String getMerchantNo();
/**
* 获取signKey
*
* @return signKey
*/
public abstract String getSignKey();
/**
* 获取 私钥证书
*
* @return 私钥证书
*/
public abstract InputStream getPriCert();
/**
* 获取 私钥证书密钥
*
* @return 私钥证书密钥
*/
public abstract String getPriCertPwd();
/**
* 获取 公钥证书
*
* @return 公钥证书
*/
public abstract InputStream getPubCert();
/**
* 获取域名-新api接口
*
* @return 域名-新api接口
*/
public abstract String getApiDomain();
/**
* HTTP(S) 连接超时时间单位毫秒
*
* @return 连接时间
*/
public int getHttpConnectTimeoutMs() {
return 6 * 1000;
}
/**
* 设置HTTP(S) 读数据超时时间单位毫秒
*
* @param urlSuffix api地址除去域名后的路径
* @param httpReadTimeoutMs 读超时时间单位毫秒
*/
public void setHttpReadTimeoutMs(String urlSuffix, int httpReadTimeoutMs) {
HTTP_READ_TIMEOUT_CONFIG.put(urlSuffix, httpReadTimeoutMs);
}
/**
* 查询HTTP(S) 读数据超时时间单位毫秒
*
* @return 读超时时间单位毫秒
*/
public int getHttpReadTimeoutMs(String requestPath) {
if (!HTTP_READ_TIMEOUT_CONFIG.containsKey(requestPath)) {
return 15000;
}
return HTTP_READ_TIMEOUT_CONFIG.get(requestPath);
}
/**
* 是否自动上报异常请求默认为 true
* 若要关闭子类中实现该函数返回 false 即可
*/
public boolean shouldAutoReport() {
return true;
}
/**
* 进行异常上报的线程的数量
*/
public int getReportWorkerNum() {
return 1;
}
/**
* 批量上报一次报多条异常数据
*/
public int getReportBatchSize() {
return 5;
}
/**
* 异常上报缓存消息队列最大数量
* 队列满后不会上报新增的异常信息
*/
public int getReportQueueMaxSize() {
return 50;
}
/**
* 是否使用http连接池
*/
public boolean useHttpConnectPool() {
return false;
}
/**
* http连接池最大连接数量
*/
public int getHttpConnectMaxTotal() {
return 800;
}
/**
* http连接池默认连接数量
*/
public int getHttpConnectDefaultTotal() {
return 100;
}
/**
* http连接最大闲置时长单位毫秒
*/
public int getHttpConnectIdleAliveMs() {
return 20000;
}
}

View File

@ -0,0 +1,19 @@
package com.hzs.third.pay.jdpay.sdk;
import org.bouncycastle.util.encoders.Base64;
import java.io.InputStream;
public class JdPaySecurity {
public String signEnvelop(InputStream signCert, String password, InputStream envelopCert, byte[] orgData) {
byte[] signData = JdPaySign.getInstance().attachSign(signCert, password, orgData);
byte[] envelop = JdPaySign.getInstance().encryptEnvelop(envelopCert, signData);
return new String(Base64.encode(envelop));
}
public byte[] verifyEnvelop(InputStream envelopCert, String password, byte[] envelopData) {
byte[] signData = JdPaySign.getInstance().decryptEnvelop(envelopCert, password, envelopData);
return JdPaySign.getInstance().verifyAttachSign(signData);
}
}

View File

@ -0,0 +1,200 @@
package com.hzs.third.pay.jdpay.sdk;
import com.hzs.common.core.exception.ServiceException;
import com.hzs.third.pay.jdpay.util.CertUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import javax.security.auth.x500.X500PrivateCredential;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@Slf4j
public class JdPaySign {
private static final String SIGN_ALGORITHMS = "SHA1WITHRSA";
private static final String BC = "BC";
private static JdPaySign INSTANCE = null;
static {
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
private JdPaySign() {
}
/**
* 单例双重校验
*/
public static JdPaySign getInstance() {
if (INSTANCE == null) {
synchronized (JdPaySign.class) {
if (INSTANCE == null) {
INSTANCE = new JdPaySign();
}
}
}
return INSTANCE;
}
public byte[] attachSign(InputStream priCert, String password, byte[] data) {
return this.sign(priCert, password, data, true);
}
public byte[] detachSign(InputStream priCert, String password, byte[] data) {
return this.sign(priCert, password, data, false);
}
private byte[] sign(InputStream priCert, String password, byte[] data, boolean isDetach) {
try {
X500PrivateCredential privateCert = CertUtil.getPrivateCert(priCert, password.toCharArray());
X509Certificate x509Certificate = privateCert.getCertificate();
PrivateKey privateKey = privateCert.getPrivateKey();
List<X509Certificate> certList = new ArrayList<>();
certList.add(x509Certificate);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder(SIGN_ALGORITHMS).setProvider(BC).build(privateKey);
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build()).build(sha1Signer, x509Certificate));
generator.addCertificates(certs);
CMSTypedData msg = new CMSProcessableByteArray(data);
return generator.generate(msg, isDetach).getEncoded();
} catch (CertificateException e) {
log.error("=====", e);
log.error(e.getMessage());
throw new ServiceException(e.getMessage());
} catch (Exception e) {
log.error("-----", e);
log.error(e.getMessage());
throw new ServiceException("签名异常");
}
}
@SuppressWarnings("rawtypes")
public int verifyDetachSign(byte[] data, byte[] signData) {
try {
CMSProcessable content = new CMSProcessableByteArray(data);
CMSSignedData s = new CMSSignedData(content, signData);
Store certStore = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
int verified = 0, size = 0;
while (it.hasNext()) {
size++;
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder) certIt.next();
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
verified++;
}
}
if (size == verified) {
return 1;
}
} catch (Exception e) {
return 0;
}
return 0;
}
@SuppressWarnings({"rawtypes"})
public byte[] verifyAttachSign(byte[] signData) {
try {
byte[] data = null;
CMSSignedData s = new CMSSignedData(signData);
Store certStore = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
int verified = 0, size = 0;
while (it.hasNext()) {
size++;
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = certStore.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder) certIt.next();
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))) {
verified++;
CMSTypedData cmsData = s.getSignedContent();
data = (byte[]) cmsData.getContent();
}
}
if (size == verified) {
return data;
}
} catch (Exception e) {
return null;
}
return null;
}
public byte[] encryptEnvelop(InputStream pubCert, byte[] bOrgData) {
try {
X509Certificate publicCert = CertUtil.getPublicCert(pubCert);
CMSEnvelopedDataGenerator generator = new CMSEnvelopedDataGenerator();
generator.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(publicCert).setProvider(BC));
CMSEnvelopedData enveloped = generator.generate(new CMSProcessableByteArray(bOrgData), new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider(BC).build());
ByteArrayOutputStream out = new ByteArrayOutputStream();
new DEROutputStream(out).writeObject(enveloped.toASN1Structure());
byte[] result = out.toByteArray();
out.close();
return result;
} catch (CertificateException | CMSException | IOException e) {
log.error("加密异常:{}", e.getMessage());
throw new ServiceException("加密异常");
}
}
public byte[] decryptEnvelop(InputStream priCert, String privateKeyPassword, byte[] bEnvelop) {
try {
CMSEnvelopedData enveloped = new CMSEnvelopedData(bEnvelop);
RecipientInformationStore ris = enveloped.getRecipientInfos();
if (ris == null) {
log.error("数字信封格式不对:{}", new String(bEnvelop));
throw new ServiceException("验签异常");
}
X500PrivateCredential privateCert = CertUtil.getPrivateCert(priCert, privateKeyPassword.toCharArray());
PrivateKey privateKey = privateCert.getPrivateKey();
byte[] sign = null;
Collection recipients = ris.getRecipients();
for (Object object : recipients) {
RecipientInformation recipient = (RecipientInformation) object;
sign = recipient.getContent(new JceKeyTransEnvelopedRecipient(privateKey).setProvider(BC));
}
return sign;
} catch (CMSException | CertificateException e) {
log.error("验签异常:{}", e.getMessage());
throw new ServiceException("验签异常");
}
}
}

View File

@ -0,0 +1,110 @@
package com.hzs.third.pay.jdpay.util;
import lombok.extern.slf4j.Slf4j;
import javax.security.auth.x500.X500PrivateCredential;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
@Slf4j
public class CertUtil {
private static final String PKCS12 = "PKCS12";
public static X500PrivateCredential getPrivateCert(InputStream pfxCert, char[] privateKeyPassword) throws CertificateException {
KeyStore keyStore;
String keyStoreAlias = null;
/* Load KeyStore contents from file */
try {
keyStore = KeyStore.getInstance(CertUtil.PKCS12);
keyStore.load(pfxCert, privateKeyPassword);
/* Get aliases */
Enumeration aliases = keyStore.aliases();
if (aliases != null) {
while (aliases.hasMoreElements()) {
keyStoreAlias = (String) aliases.nextElement();
Certificate[] certs = keyStore.getCertificateChain(keyStoreAlias);
if (certs == null || certs.length == 0) {
continue;
}
X509Certificate cert = (X509Certificate) certs[0];
if (matchUsage(cert.getKeyUsage(), 1)) {
try {
cert.checkValidity();
} catch (CertificateException e) {
continue;
}
break;
}
}
}
} catch (GeneralSecurityException | IOException e) {
log.error("===============", e);
throw new CertificateException("Error initializing keystore");
}
if (keyStoreAlias == null) {
throw new CertificateException("None certificate for sign in this keystore");
}
/* Get certificate chain and create a certificate path */
Certificate[] fromKeyStore;
try {
fromKeyStore = keyStore.getCertificateChain(keyStoreAlias);
if (fromKeyStore == null
|| fromKeyStore.length == 0
|| !(fromKeyStore[0] instanceof X509Certificate)) {
throw new CertificateException("Unable to find X.509 certificate chain in keystore");
}
} catch (KeyStoreException e) {
throw new CertificateException("Error using keystore");
}
/* Get PrivateKey */
Key privateKey;
try {
privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword);
if (!(privateKey instanceof PrivateKey)) {
throw new CertificateException("Unable to recover key from keystore");
}
} catch (KeyStoreException | NoSuchAlgorithmException e) {
throw new CertificateException("Error using keystore");
} catch (UnrecoverableKeyException e) {
throw new CertificateException("Unable to recover key from keystore");
}
X509Certificate certificate = (X509Certificate) fromKeyStore[0];
return new X500PrivateCredential(certificate, (PrivateKey) privateKey, keyStoreAlias);
}
public static X509Certificate getPublicCert(InputStream publicCert) throws CertificateException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
Certificate certificate = cf.generateCertificate(publicCert);
return (X509Certificate) certificate;
} catch (CertificateException e) {
throw new CertificateException("Error loading public key certificate");
}
}
private static boolean matchUsage(boolean[] keyUsage, int usage) {
if (usage == 0 || keyUsage == null) {
return true;
}
for (int i = 0; i < Math.min(keyUsage.length, 32); i++) {
if ((usage & (1 << i)) != 0 && !keyUsage[i]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,45 @@
package com.hzs.third.pay.jdpay.util;
import org.springframework.core.io.ClassPathResource;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class FileUtil {
public FileUtil() {
}
public static byte[] readFile(String filename) {
ClassPathResource classPathResource = new ClassPathResource(filename);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BufferedInputStream in = null;
try {
in = new BufferedInputStream(classPathResource.getInputStream());
byte[] buffer = new byte[1024];
int len;
while (-1 != (len = in.read(buffer, 0, 1024))) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
} catch (IOException var21) {
var21.printStackTrace();
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException var20) {
var20.printStackTrace();
}
try {
bos.close();
} catch (IOException var19) {
var19.printStackTrace();
}
}
return null;
}
}

View File

@ -0,0 +1,171 @@
package com.hzs.third.pay.jdpay.util;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.Primitives;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
/*************************************************
*
* Gson工具类对Google的gson工具进行封装
* (1)日期格式yyyyMMddHHmmss
* (2)toJson时支持掩码
* (3)toJson时支持跳过指定属性
*************************************************/
public class GsonUtil {
private static final String EMPTY_JSON = "{}";
private static final String EMPTY_JSON_ARRAY = "[]";
private static final String DEFAULT_DATE_PATTERN = "yyyyMMddHHmmss";
private static final Gson DEFAULT_GSON;
static {
GsonBuilder builder = new GsonBuilder();
builder.setDateFormat(DEFAULT_DATE_PATTERN);
DEFAULT_GSON = builder.create();
DEFAULT_GSON.toJson(null);
}
public static String toJson(Object target) {
return toJson(target, null, null, null);
}
public static String toJson(Object target, Type targetType) {
return toJson(target, targetType, null, null);
}
public static String toMaskJson(Object target) {
return toJson(target, null, null, null);
}
public static String toMaskJson(Object target, List<String> excludeFields) {
return toJson(target, null, null, excludeFields);
}
/**
* 打印目标对象的json串
*
* @param target 目标对象
* @param targetType 对象类型可为null
* @param datePattern 日期格式可为null若为null则按 DEFAULT_DATE_PATTERN格式输出
* @param excludeFields 不打印的字段可为null
* @return 目标对象的标准json串
*/
public static String toJson(Object target, Type targetType, String datePattern, final List<String> excludeFields) {
if (target == null) {
return EMPTY_JSON;
}
Gson gson = DEFAULT_GSON;
if (null != datePattern && !"".equals(datePattern)) {
gson = getGson(datePattern);
}
if (isNotEmpty(excludeFields)) {
gson = getStrategyGson(excludeFields);
}
String result = emptyResult(target);
try {
if (targetType == null) {
targetType = target.getClass();
}
result = gson.toJson(target, targetType);
} catch (Exception ignore) {
}
return result;
}
public static <T> T fromJson(String json, TypeToken<T> token) {
return fromJson(json, token, null);
}
public static <T> T fromJson(String json, TypeToken<T> token, String datePattern) {
return fromJson(json, token.getType(), datePattern);
}
public static <T> T fromJson(String json, Class<T> classOfT) {
Object object = fromJson(json, (Type) classOfT, null);
return Primitives.wrap(classOfT).cast(object);
}
public static <T> T fromJson(String json, Class<T> classOfT, String datePattern) {
Object object = fromJson(json, (Type) classOfT, datePattern);
return Primitives.wrap(classOfT).cast(object);
}
/**
* 将json串转化为目标对象
*
* @param json json串
* @param type 目标对象类型
* @param datePattern json串中日期格式若为null则按两个标准日期尝试解析DEFAULT_DATE_PATTERN和DEFAULT_DATE_PATTERN_1
* @param <T>
* @return 目标对象
*/
public static <T> T fromJson(String json, Type type, String datePattern) {
if (null == json || "".equals(json)) {
return null;
}
if (null == datePattern || "".equals(datePattern)) {
try {
return DEFAULT_GSON.fromJson(json, type);
} catch (Exception ignore) {
}
return null;
}
try {
Gson gson = getGson(datePattern);
return gson.fromJson(json, type);
} catch (Exception ignore) {
}
return null;
}
private static Gson getGson(String datePattern) {
GsonBuilder builder = new GsonBuilder();
builder.setDateFormat(datePattern);
return builder.create();
}
private static Gson getStrategyGson(final List<String> excludeFields) {
ExclusionStrategy myExclusionStrategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fa) {
return excludeFields != null && excludeFields.contains(fa.getName());
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
};
return new GsonBuilder().setExclusionStrategies(myExclusionStrategy).create();
}
private static boolean isNotEmpty(final List<String> fieldNames) {
return fieldNames != null && !fieldNames.isEmpty();
}
private static String emptyResult(Object target) {
if (target == null) {
return EMPTY_JSON;
}
if (target instanceof Collection
|| target instanceof Iterator
|| target instanceof Enumeration
|| target.getClass().isArray()) {
return EMPTY_JSON_ARRAY;
}
return EMPTY_JSON;
}
}

View File

@ -0,0 +1,81 @@
package com.hzs.third.pay.jdpay.util;
import com.google.gson.reflect.TypeToken;
import com.hzs.third.pay.jdpay.sdk.JdPayConstant;
import com.hzs.third.pay.jdpay.sdk.JdPayNewConfig;
import com.hzs.third.pay.jdpay.sdk.JdPaySecurity;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/*************************************************
*
* 京东支付接口工具类
*
*************************************************/
public class JdPayApiUtil {
/**
* 加密和签名
*/
public static String encryptAndSignature(JdPayNewConfig jdPayNewConfig, String reqNo, String jsonParam) throws IOException {
// 组装公共请求参数
Map<String, String> commonParam = fillCommonParam(jdPayNewConfig.getMerchantNo(), reqNo);
// 加密
byte[] dataBytes = jsonParam.getBytes(StandardCharsets.UTF_8);
JdPaySecurity se = new JdPaySecurity();
String encData = se.signEnvelop(jdPayNewConfig.getPriCert(), jdPayNewConfig.getPriCertPwd(), jdPayNewConfig.getPubCert(), dataBytes);
commonParam.put(JdPayConstant.ENC_DATA, encData);
// 签名
String sign = SignUtil.sign(commonParam, JdPayConstant.SHA256, jdPayNewConfig.getSignKey(), JdPayConstant.UTF8);
commonParam.put(JdPayConstant.SIGN_DATA, sign);
return GsonUtil.toJson(commonParam);
}
/**
* 解密和验签
*/
public static String decryptAndVerifySign(JdPayNewConfig jdPayNewConfig, String respText) throws Exception {
Map<String, String> respMap = GsonUtil.fromJson(respText, new TypeToken<Map<String, String>>() {
});
String code = respMap.get(JdPayConstant.CODE);
if (!JdPayConstant.SUCCESS_CODE.equals(code)) {
return respText;
}
if (!respMap.containsKey(JdPayConstant.SIGN_DATA)) {
throw new Exception(String.format("No sign field in response: %s", respText));
}
String sign = respMap.remove(JdPayConstant.SIGN_DATA);
String signType = respMap.get(JdPayConstant.SIGN_TYPE);
String charset = respMap.get(JdPayConstant.CHARSET);
boolean isRespSignValid = SignUtil.verify(sign, respMap, signType, jdPayNewConfig.getSignKey(), charset);
if (!isRespSignValid) {
throw new Exception(String.format("Invalid sign value in response: %s", respText));
}
return SignUtil.decodeBase64(respMap.get(JdPayConstant.RESP_DATA), respMap.get(JdPayConstant.CHARSET), false, false);
}
/**
* 组装api公共参数赋值
*/
private static Map<String, String> fillCommonParam(String merchantNo, String reqNo) {
Map<String, String> reqMap = new HashMap<>();
// 二级商户号
reqMap.put(JdPayConstant.MERCHANT_NO, merchantNo);
//商户生成的唯一标识可以与outTradeNo一致
reqMap.put(JdPayConstant.REQ_NO, reqNo);
//字符集
reqMap.put(JdPayConstant.CHARSET, JdPayConstant.UTF8);
//固定值
reqMap.put(JdPayConstant.FORMAT_TYPE, JdPayConstant.JSON);
//签名类型
reqMap.put(JdPayConstant.SIGN_TYPE, JdPayConstant.SHA256);
//固定值证书加密
reqMap.put(JdPayConstant.ENC_TYPE, JdPayConstant.AP7);
return reqMap;
}
}

View File

@ -0,0 +1,147 @@
package com.hzs.third.pay.jdpay.util;
import com.hzs.third.pay.jdpay.sdk.JdPayConstant;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.BaseNCodec;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
public class SignUtil {
private static final String[] HEX_STRINGS;
static {
HEX_STRINGS = new String[256];
for (int i = 0; i < 256; i++) {
StringBuilder d = new StringBuilder(2);
char ch = Character.forDigit(((byte) i >> 4) & 0x0F, 16);
d.append(Character.toUpperCase(ch));
ch = Character.forDigit((byte) i & 0x0F, 16);
d.append(Character.toUpperCase(ch));
HEX_STRINGS[i] = d.toString();
}
}
/**
* 计算签名
*
* @param map 有key和value的map使用=&拼接所有参数
* "sign_type", "sign_data", "encrypt_type", "encrypt_data"不参加计算
* @param algorithm 签名算法 MD5, SHA-1, SHA-256
* @param salt 签名密钥
* @param charset 字符串编码
* @return 签名
*/
public static String sign(Map<String, String> map, String algorithm, String salt, String charset) throws UnsupportedEncodingException {
String linkString = map2LinkString(map);
String data = linkString + salt;
return digestHex(algorithm, data, charset);
}
/**
* 验证签名正确性
*
* @param sign 签名数据
* @param map 数据
* @param algorithm 签名算法 MD5, SHA-1, SHA-256
* @param salt 签名密钥
* @param charset 字符串
* @return 验证结果
*/
public static boolean verify(String sign,
Map<String, String> map,
String algorithm,
String salt,
String charset) throws UnsupportedEncodingException {
if (sign == null || "".equals(sign.trim()) || map.size() == 0) {
return false;
}
String newSign = sign(map, algorithm, salt, charset);
return newSign.equals(sign);
}
/**
* 验证页面回调
*
* @param respMap 页面回调参数
* @param signKey signKey
* @return 验证结果
* @throws NoSuchAlgorithmException
*/
public static boolean verifyPageCallBackSign(Map<String, String> respMap, String signKey) throws UnsupportedEncodingException {
String sign = respMap.remove("sign");
String newSign = sign(respMap, JdPayConstant.SHA256, signKey, JdPayConstant.UTF8);
return newSign.equals(sign);
}
/**
* 将MAP数据用=&拼接成String
*
* @param map 数据
* @return 字符串
*/
public static String map2LinkString(Map<String, String> map) {
ArrayList<String> mapKeys = new ArrayList<String>(map.keySet());
Collections.sort(mapKeys);
StringBuilder link = new StringBuilder(2048);
for (String key : mapKeys) {
String value = map.get(key);
// 属性为空不参与签名
if (value == null || "".equals(value.trim())) {
continue;
}
link.append(key).append("=").append(value).append("&");
}
// 删除末尾的&
link.deleteCharAt(link.length() - 1);
return link.toString();
}
/**
* 对数据进行指定算法的数据摘要
*
* @param algorithm 算法名如MD2, MD5, SHA-1, SHA-256, SHA-512
* @param data 待计算的数据
* @param charset 字符串的编码
* @return 摘要结果
*/
public static String digestHex(String algorithm, String data, String charset) throws UnsupportedEncodingException {
byte[] digest = DigestUtils.getDigest(algorithm).digest(data.getBytes(charset));
return hexString(digest);
}
/**
* 将字节数组转换成HEX String
*
* @param b
* @return HEX String
*/
public static String hexString(byte[] b) {
StringBuilder d = new StringBuilder(b.length * 2);
for (byte aB : b) {
d.append(HEX_STRINGS[(int) aB & 0xFF]);
}
return d.toString();
}
/**
* 对数据进行BASE64解码
*
* @param base64Data Base64数据
* @param charset 解码的编码格式
* @param urlSafe 是否是URL安全的如果为true则将会被URL编码的'+', '/'转成'-', '_'
* @param oneLine 是否是一行
* @return 解码后数据
*/
public static String decodeBase64(String base64Data, String charset, boolean urlSafe, boolean oneLine) throws UnsupportedEncodingException {
Base64 base64 = oneLine ? new Base64(BaseNCodec.MIME_CHUNK_SIZE, null, urlSafe) : new Base64(urlSafe);
byte[] binaryData = base64.decode(base64Data);
return new String(binaryData, charset);
}
}

View File

@ -0,0 +1,16 @@
package com.hzs.third.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
/**
* <p>
* 支付请求子表(分账) Mapper 接口
* </p>
*
* @author bd
* @since 2025-07-01
*/
public interface TOnlinePaymentSepAccDMapper extends BaseMapper<TOnlinePaymentSepAccD> {
}

View File

@ -0,0 +1,17 @@
package com.hzs.third.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* 支付请求主表 Mapper 接口
* </p>
*
* @author bd
* @since 2025-07-01
*/
public interface TOnlinePaymentSepAccMapper extends BaseMapper<TOnlinePaymentSepAcc> {
TOnlinePaymentSepAcc getSepAccByOrderCode(@Param("orderCode") String orderCode);
}

View File

@ -12,23 +12,34 @@ import lombok.Data;
@Data
public class OnlinePayConfigParam {
/**
* 宝付微信
*/
private Boolean pay11 = Boolean.FALSE;
/**
* 宝付微信扫码
*/
private Boolean pay12 = Boolean.FALSE;
// /**
// * 京东银行卡
// */
// private Boolean pay4 = Boolean.FALSE;
/**
* 汇付微信
* 京东收银台H5
*/
private Boolean pay13 = Boolean.FALSE;
private Boolean pay5 = Boolean.FALSE;
/**
* 汇付银行卡
* 京东收银台PC
*/
private Boolean pay15 = Boolean.FALSE;
private Boolean pay6 = Boolean.FALSE;
// /**
// * 微信APP
// */
// private Boolean pay20 = Boolean.FALSE;
/**
* 通联微信
*/
private Boolean pay32 = Boolean.FALSE;
/**
* 通联银行卡
*/
private Boolean pay33 = Boolean.FALSE;
/**
* 新汇付PC微信扫码支付
@ -38,13 +49,5 @@ public class OnlinePayConfigParam {
* 新汇付PC支付宝扫码支付
*/
private Boolean pay82 = Boolean.FALSE;
/**
* 新汇付H5微信支付
*/
private Boolean pay83 = Boolean.FALSE;
/**
* 新汇付H5支付宝支付
*/
private Boolean pay84 = Boolean.FALSE;
}

View File

@ -75,4 +75,10 @@ public class OnlinePaymentParam {
private Integer systemType;
/**
* 订单类型 EOrderType
* -1为充值
*/
private Integer orderType;
}

View File

@ -0,0 +1,43 @@
package com.hzs.third.pay.param;
import lombok.Data;
/**
* 支付入参
*/
@Data
public class PayParam {
/**
* 业务类型EPayBusinessType
*/
private Integer businessType;
/**
* 业务单号
*/
private String businessCode;
/**
* 支付渠道EPayChannel
*/
private Integer payChannel;
/**
* 支付类型EPayType
*/
private Integer payType;
/**
* 绑卡编号银行卡支付使用
*/
private String bindCode;
/**
* 小程序支付标记为0则小程序支付
*/
private Integer appletFlag;
/**
* 支付-前端回调页面的扩展参数
*/
private String extParam;
}

View File

@ -47,4 +47,9 @@ public class UnifiedOrderParam implements Serializable {
*/
private Integer appletFlag;
/**
* 支付-前端回调页面的扩展参数
*/
private String extParam;
}

View File

@ -0,0 +1,41 @@
package com.hzs.third.pay.provider;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.third.pay.ITOnlinePaymentServiceApi;
import com.hzs.third.pay.service.IRefundService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Objects;
@DubboService
public class ITOnlinePaymentServiceProvider implements ITOnlinePaymentServiceApi {
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@Autowired
private IRefundService iRefundService;
@Override
public R<TOnlinePayment> getOnlinePayment(String orderNo) {
LambdaQueryWrapper<TOnlinePayment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TOnlinePayment::getBusinessCode, orderNo);
return R.ok(itOnlinePaymentService.getOne(queryWrapper, false));
}
@Override
public R<String> refund(String orderNo, Long userId) {
LambdaQueryWrapper<TOnlinePayment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TOnlinePayment::getBusinessCode, orderNo);
TOnlinePayment tOnlinePayment = itOnlinePaymentService.getOne(queryWrapper, false);
if(ObjectUtil.isNotEmpty(tOnlinePayment)){
if (Objects.requireNonNull(EPayChannel.getEnumByValue(tOnlinePayment.getPayChannel())) == EPayChannel.JD) {
return R.ok(iRefundService.jdRefundDivision(orderNo, userId));
}
}
return R.fail();
}
}

View File

@ -1,23 +1,21 @@
package com.hzs.third.pay.service;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.EDataSource;
import com.hzs.common.domain.third.pay.TOnlinePayment;
/**
* @Description: 京东支付服务
* @Author: jiang chao
* @Time: 2025/2/6 9:57
* @Classname: IJdPayService
* @PackageName: com.hzs.third.pay.service
* 京东支付服务
*/
public interface IJdPayService {
/**
* 京东收银台支付
* 收银台支付
*
* @param tOnlinePayment 在线支付信息
* @param onlinePayment
* @param dataSource
* @return
*/
R<String> cashDeskPay(TOnlinePayment tOnlinePayment);
R<String> cashRegister(TOnlinePayment onlinePayment, EDataSource dataSource);
}

View File

@ -27,6 +27,10 @@ public interface IPayService {
boolean notifyHandle(String businessType, String businessCode, String originalOrder,
String payNumber, Date payTime, BigDecimal payMoney,
EPayChannel ePayChannel, String channelNumber);
boolean notifyHandle(String businessType, String businessCode, String originalOrder,
String payNumber, Date payTime, BigDecimal payMoney,
EPayChannel ePayChannel, String channelNumber, Integer payType, String acqOrderId);
/**
* 业务处理重试

View File

@ -2,36 +2,24 @@ package com.hzs.third.pay.service;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.TOnlineRefund;
import com.hzs.third.pay.dto.RefundDTO;
import java.math.BigDecimal;
import java.util.Date;
/**
* @Description: 基础退款服务
* @Author: jiang chao
* @Time: 2023/8/16 17:00
* @Classname: IRefundService
* @PackageName: com.hzs.third.pay.service
* 基础退款服务
*/
public interface IRefundService {
/**
* 新汇付退款处理
* 通联退款处理
*
* @param tOnlinePayment 在线支付信息
* @param refundDTO 业务类型
* @param tOnlinePayment 退款对应支付信息
* @return
*/
String huifuRefundHandle(TOnlinePayment tOnlinePayment);
/**
* 新汇付退款查询
*
* @param tOnlineRefund 退款信息
* @return
*/
String queryHuifuRefundHandle(TOnlineRefund tOnlineRefund);
String allInRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment);
/**
* 退款回调成功处理
@ -56,4 +44,25 @@ public interface IRefundService {
*/
boolean notifyErrorHandle(EPayChannel ePayChannel, String refundCode, String refundNumber, String errorMsg);
/**
* 京东退款处理
*
* @param refundDTO 业务类型
* @param tOnlinePayment 退款对应支付信息
* @return
*/
String jdRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment);
/**
* 京东收银台退款处理
*
* @param refundDTO 业务类型
* @param tOnlinePayment 退款对应支付信息
* @return
*/
String jdCashRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment);
String jdRefundDivision(String orderCode, Long userId);
String jdQueryEnterpriseWalletBalance();
}

View File

@ -0,0 +1,16 @@
package com.hzs.third.pay.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
/**
* <p>
* 支付请求子表(分账) 服务类
* </p>
*
* @author bd
* @since 2025-07-01
*/
public interface ITOnlinePaymentSepAccDService extends IService<TOnlinePaymentSepAccD> {
}

View File

@ -0,0 +1,18 @@
package com.hzs.third.pay.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
/**
* <p>
* 支付请求主表 服务类
* </p>
*
* @author bd
* @since 2025-07-01
*/
public interface ITOnlinePaymentSepAccService extends IService<TOnlinePaymentSepAcc> {
void saveOrUpdateReq(TOnlinePaymentSepAcc tOnlinePaymentSepAcc);
TOnlinePaymentSepAcc getSepAccByOrderCode(String code);
}

View File

@ -9,9 +9,6 @@ import java.util.List;
/**
* 在线支付信息 服务类
*
* @author hzs
* @since 2022-07-28
*/
public interface ITOnlinePaymentService extends IService<TOnlinePayment> {

View File

@ -9,9 +9,6 @@ import java.util.List;
/**
* 在线退款信息 服务类
*
* @author hzs
* @since 2023-08-16
*/
public interface ITOnlineRefundService extends IService<TOnlineRefund> {

View File

@ -1,23 +1,199 @@
package com.hzs.third.pay.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.hzs.common.core.config.BdConfig;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.EDataSource;
import com.hzs.common.core.enums.EEnv;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.third.pay.config.JdPayBankProperties;
import com.hzs.third.pay.config.JdPayConfig;
import com.hzs.third.pay.config.JdPaySeparateAccountConfig;
import com.hzs.third.pay.config.JdPayWechatAlipayProperties;
import com.hzs.third.pay.jdpay.dto.JdPayAggregateCreateOrderRequest;
import com.hzs.third.pay.jdpay.dto.JdPayAggregateCreateOrderResponse;
import com.hzs.third.pay.jdpay.dto.JdPayDivisionAccount;
import com.hzs.third.pay.jdpay.dto.JdPayDivisionAccountTradeInfo;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.service.IJdPayService;
import com.hzs.third.pay.service.ITOnlineCardService;
import com.hzs.third.pay.service.ITOnlinePaymentSepAccService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* @Description: 京东支付服务
* @Author: jiang chao
* @Time: 2025/2/6 9:57
* @Classname: JdPayServiceImpl
* @PackageName: com.hzs.third.pay.service.impl
* 京东支付服务
*/
@Slf4j
@Service
public class JdPayServiceImpl implements IJdPayService {
@Autowired
private JdPayConfig jdPayConfig;
@Autowired
private ITOnlineCardService itOnlineCardService;
@Resource(name = "jdPayBank")
private JdPay jdPay;
@Autowired
private JdPayBankProperties jdPayBankProperties;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ITOnlinePaymentSepAccService itOnlinePaymentSepAccService;
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@Resource
private JdPayWechatAlipayProperties jdPayWechatAlipayProperties;
@Override
public R<String> cashDeskPay(TOnlinePayment tOnlinePayment) {
return null;
public R<String> cashRegister(TOnlinePayment onlinePayment, EDataSource dataSource) {
try {
// 交易场景ONLINE_APP线上移动端 ONLINE_PC:线上PC
String sceneType = "ONLINE_APP";
// 交易类型AGGRE聚合收银台 AGGRE_QRPC扫码
String tradeType = "AGGRE";
if (null != dataSource && EDataSource.PC.getValue().equals(dataSource.getValue())) {
sceneType = "ONLINE_PC";
tradeType = "AGGRE_QR";
}
// String userId = onlinePayment.getPkCreator().toString();
String userId = SecurityUtils.getMemberCode();
if (EEnv.TEST.getValue().equals(BdConfig.getEnv())) {
userId = "T_" + userId;
}
JdPayAggregateCreateOrderRequest request = JdPayAggregateCreateOrderRequest.builder()
// 商户订单号最大32位
.outTradeNo(onlinePayment.getBusinessCode())
// 订单总金额单位
.tradeAmount(onlinePayment.getPayMoney().multiply(new BigDecimal("100")).intValue() + "")
// 订单创建时间最大14位yyyyMMddHHmmss
.createDate(DateUtils.parseDateToFormat(DateUtils.YAMMERERS, onlinePayment.getCreationTime()))
// 订单有效时长分钟
.tradeExpiryTime("1440")
// 交易名称
.tradeSubject("支付:" + onlinePayment.getBusinessCode())
// 交易描述
.tradeRemark("支付:" + onlinePayment.getBusinessCode())
// 币种
.currency("CNY")
// 用户IP
.userIp(InetAddress.getLocalHost().getHostAddress())
// 通道业务类型
.bizTp("100001")
// 回传字段
.returnParams(onlinePayment.getBusinessType().toString())
// 用户标识收银台必传
.userId(userId)
// 同步通知URL收银台必传页面回调地址
.pageBackUrl(jdPayBankProperties.getPageBackUrl() + "?extParam=" + onlinePayment.getExtParam())
.callbackUrl(jdPayBankProperties.getPageBackUrl() + "?extParam=" + onlinePayment.getExtParam())
// 支付回调地址
.notifyUrl(jdPayBankProperties.getNotifyUrl())
// 交易类型
.tradeType(tradeType)
// 交易场景ONLINE_APP线上移动端 ONLINE_PC:线上PC
.sceneType(sceneType)
.accessType("MINIAPP")
.build();
JdPayDivisionAccount divisionAccount = new JdPayDivisionAccount();
List<JdPayDivisionAccountTradeInfo> divisionAccountTradeInfoList = new ArrayList<JdPayDivisionAccountTradeInfo>();
List<TOnlinePaymentSepAccD> sepAccDList = new ArrayList<>();
if(ObjectUtil.isNotEmpty(jdPayBankProperties)){
List<JdPaySeparateAccountConfig> separateAccounts = jdPayBankProperties.getSeparateAccounts();
separateAccounts.sort(Comparator.comparing(JdPaySeparateAccountConfig::getProportion).reversed());
if(ObjectUtil.isNotEmpty(separateAccounts)){
BigDecimal tradeAmount = onlinePayment.getPayMoney();
BigDecimal remaining = onlinePayment.getPayMoney();
for (int i = 0; i < separateAccounts.size(); i++) {
JdPaySeparateAccountConfig separateAccount = separateAccounts.get(i);
BigDecimal subTradeAmount;
if(i == separateAccounts.size() - 1){
subTradeAmount = remaining;
log.info("分账 最后一个账户( 总 - 已分账 ) : {}, {}", subTradeAmount, remaining);
onlinePayment.setSubAccountMoney(subTradeAmount);
}else{
subTradeAmount = tradeAmount.subtract(tradeAmount.multiply(separateAccount.getProportion().setScale(2, BigDecimal.ROUND_DOWN)).setScale(2, BigDecimal.ROUND_DOWN));
remaining = remaining.subtract(subTradeAmount).setScale(2, BigDecimal.ROUND_DOWN);
onlinePayment.setMainAccountMoney(subTradeAmount);
log.info("分账 过程 : {}, {}, {}", subTradeAmount, remaining, separateAccount.getProportion().setScale(2, BigDecimal.ROUND_DOWN));
}
JdPayDivisionAccountTradeInfo divisionAccountTradeInfo = new JdPayDivisionAccountTradeInfo();
divisionAccountTradeInfo.setMerchantNo(separateAccount.getAccount());
divisionAccountTradeInfo.setOutTradeNo(onlinePayment.getBusinessCode() + "_" + (i + 1));
divisionAccountTradeInfo.setTradeAmount(subTradeAmount.multiply(new BigDecimal(100)).intValue() + "");
divisionAccountTradeInfoList.add(divisionAccountTradeInfo);
sepAccDList.add(
TOnlinePaymentSepAccD.builder()
.account(separateAccount.getAccount())
.proportion(separateAccount.getProportion())
.tradeAmount(subTradeAmount)
.outTradeNo(divisionAccountTradeInfo.getOutTradeNo())
.build()
);
}
}
}
divisionAccount.setVersion( "V2" );
divisionAccount.setDivisionAccountTradeInfoList(divisionAccountTradeInfoList);
request.setDivisionAccount(GsonUtil.toJson(divisionAccount));
// 请求京东支付接口
JdPayAggregateCreateOrderResponse response = jdPay.aggregateCreateOrder(request);
TOnlinePaymentSepAcc sepAcc = TOnlinePaymentSepAcc.builder()
.sepAccDList(sepAccDList)
.reqContent(request.getDivisionAccount())
.reqContentSepAcc(JSONUtil.toJsonStr(divisionAccountTradeInfoList))
.version(divisionAccount.getVersion())
.respContent(JSONUtil.toJsonStr(response))
.tradeAmount(onlinePayment.getPayMoney())
.outTradeNo(request.getOutTradeNo())
.build();
if ("00000".equals(response.getResultCode())) {
// 请求响应成功
// 京东唯一订单号
onlinePayment.setPayNumber(response.getTradeNo());
// 将controller内入库逻辑移至此为止
onlinePayment.setPayMoney(null);
itOnlinePaymentService.saveOrUpdate(onlinePayment);
// 2025年7月1日 保存分账主表&子表
sepAcc.setPkPayment(onlinePayment.getPkId());
itOnlinePaymentSepAccService.saveOrUpdateReq(sepAcc);
if ("AGGRE_QR".equals(tradeType)) {
return R.ok(response.getQrCode());
}
return R.ok(response.getWebUrl());
} else {
log.error("京东收银台返回失败resultDesc: {}", response.getResultDesc());
return R.fail("调用京东收银台返回失败");
}
} catch (Exception e) {
log.error("京东收银台处理异常!", e);
return R.fail("京东收银台处理异常");
}
}
}

View File

@ -1,5 +1,6 @@
package com.hzs.third.pay.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hzs.common.core.constant.MagicNumberConstants;
import com.hzs.common.core.constant.RabbitMqConstants;
@ -10,7 +11,8 @@ import com.hzs.member.account.IMemberTradeServiceApi;
import com.hzs.retail.sale.IRetailOrderServiceApi;
import com.hzs.sale.ticket.ITicketServiceApi;
import com.hzs.third.mq.dto.RefundOrderDTO;
import com.hzs.third.pay.service.*;
import com.hzs.third.pay.service.IPayService;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import com.hzs.third.pay.util.PayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
@ -58,7 +60,9 @@ public class PayServiceImpl implements IPayService {
String payNumber, Date payTime, BigDecimal payMoney,
EPayChannel ePayChannel, String channelNumber) {
// 校验支付信息状态业务与支付金额是否一致等
TOnlinePayment onlinePayment = itOnlinePaymentService.queryByBusiness(Integer.valueOf(businessType), businessCode, null);
TOnlinePayment onlinePayment = itOnlinePaymentService.queryByBusiness(
ObjectUtil.isNotEmpty(businessType) ? Integer.valueOf(businessType) : null
, businessCode, null);
if (null == onlinePayment || EPayStatus.PAID.getValue() == onlinePayment.getPayStatus()) {
log.error("支付信息不存在或已支付");
return false;
@ -103,6 +107,60 @@ public class PayServiceImpl implements IPayService {
return false;
}
@Override
public boolean notifyHandle(String businessType, String businessCode, String originalOrder, String payNumber,
Date payTime, BigDecimal payMoney, EPayChannel ePayChannel, String channelNumber,
Integer payType, String acqOrderId) {
// 校验支付信息状态业务与支付金额是否一致等
TOnlinePayment onlinePayment = itOnlinePaymentService.queryByBusiness(
ObjectUtil.isNotEmpty(businessType) ? Integer.valueOf(businessType) : null
, businessCode, null);
if (null == onlinePayment || EPayStatus.PAID.getValue() == onlinePayment.getPayStatus()) {
log.error("支付信息不存在或已支付");
return false;
}
try {
if (!PayUtil.checkAmount(onlinePayment.getBusinessMoney(), payMoney)) {
// 业务金额 大于 实际支付金额
log.error("业务金额大于实际支付金额,支付编号:{}, 支付金额:{}", onlinePayment.getPaymentCode(), payMoney);
}
onlinePayment.setPayStatus(EPayStatus.PAID.getValue());
onlinePayment.setPayNumber(payNumber);
onlinePayment.setPayTime(payTime);
onlinePayment.setPayMoney(payMoney);
onlinePayment.setOriginalOrder(originalOrder);
onlinePayment.setChannelNumber(channelNumber);
onlinePayment.setPkModified(MagicNumberConstants.PK_ADMIN);
onlinePayment.setModifiedTime(new Date());
onlinePayment.setPayType(payType);
onlinePayment.setChannelNumber(acqOrderId);
if (null != ePayChannel) {
onlinePayment.setPayChannel(ePayChannel.getValue());
}
// 更新支付相关信息
if (itOnlinePaymentService.updateById(onlinePayment)) {
// 进行支付业务处理
this.handleBusiness(onlinePayment, true);
// 更新业务处理信息
itOnlinePaymentService.updateById(onlinePayment);
return true;
}
} catch (Exception e) {
log.error("在线支付信息回调处理异常", e);
// 更新业务处理信息
itOnlinePaymentService.update(Wrappers.<TOnlinePayment>lambdaUpdate()
.eq(TOnlinePayment::getPkId, onlinePayment.getPkId())
.set(TOnlinePayment::getCallbackStatus, ECallbackStatus.FAIL.getValue())
.set(TOnlinePayment::getCallbackInfo, e.getMessage())
);
}
return false;
}
@Override
public boolean retryHandle(TOnlinePayment tOnlinePayment, boolean mqPush) {
// 支付业务处理

View File

@ -1,165 +1,66 @@
package com.hzs.third.pay.service.impl;
import cn.hutool.json.JSONUtil;
import com.huifu.bspay.sdk.opps.client.BasePayClient;
import com.huifu.bspay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
import com.huifu.bspay.sdk.opps.core.request.V2TradePaymentScanpayRefundqueryRequest;
import com.huifu.bspay.sdk.opps.core.utils.DateTools;
import com.hzs.common.core.constant.MagicNumberConstants;
import com.hzs.common.core.domain.R;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.utils.*;
import com.hzs.common.core.utils.CommonUtil;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.core.utils.StringUtils;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
import com.hzs.common.domain.third.pay.TOnlineRefund;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.sale.refund.ISaRefundServiceApi;
import com.hzs.third.pay.config.HuiFuConfig;
import com.hzs.third.pay.constants.HuiFuPayConstants;
import com.hzs.third.pay.service.IRefundService;
import com.hzs.third.pay.service.ITOnlineRefundService;
import com.hzs.third.pay.config.JdPayBankProperties;
import com.hzs.third.pay.config.JdPayConfig;
import com.hzs.third.pay.dto.RefundDTO;
import com.hzs.third.pay.jdpay.dto.*;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @Description: 基础退款服务
* @Author: jiang chao
* @Time: 2023/8/16 17:00
* @Classname: RefundServiceImpl
* @PackageName: com.hzs.third.pay.service.impl
* 基础退款服务
*/
@Slf4j
@Service
public class RefundServiceImpl implements IRefundService {
@Autowired
private HuiFuConfig huiFuConfig;
@Autowired
private ITOnlineRefundService itOnlineRefundService;
@Autowired
private JdPayConfig jdPayConfig;
@DubboReference
ISaRefundServiceApi iSaRefundServiceApi;
@Override
public String huifuRefundHandle(TOnlinePayment tOnlinePayment) {
// 返回信息
String returnStr = null;
try {
// 退款单号
String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);
// 保存退款信息
TOnlineRefund tOnlineRefund = TOnlineRefund.builder()
.refundCode(refundCode)
.businessCode(tOnlinePayment.getBusinessCode())
.refundStatus(ERefundStatus.REFUND_REQUEST.getValue())
.refundMoney(tOnlinePayment.getPayMoney())
.refundChannel(tOnlinePayment.getPayChannel())
.pkOnlinePayment(tOnlinePayment.getPkId())
.build();
tOnlineRefund.setPkCreator(MagicNumberConstants.PK_ADMIN);
tOnlineRefund.setPkCountry(tOnlinePayment.getPkCountry());
itOnlineRefundService.save(tOnlineRefund);
@Resource(name = "jdPayBank")
private JdPay jdPay;
@Resource
private JdPayBankProperties jdPayBankProperties;
// 组装交易参数
V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
// 请求日期
request.setReqDate(DateTools.getCurrentDateYYYYMMDD());
// 请求流水号保证唯一
request.setReqSeqId(refundCode);
// 商户号
request.setHuifuId(huiFuConfig.getSysId());
// 申请退款金额
request.setOrdAmt(new DecimalFormat("#0.00").format(tOnlinePayment.getPayMoney()));
// 原交易请求日期
request.setOrgReqDate(DateTools.dateToDateString(tOnlinePayment.getCreationTime(), DateTools.DATE_YYYYMMDD));
// 设置非必填字段
Map<String, Object> extendInfoMap = new HashMap<>();
// 原交易流水号
extendInfoMap.put("org_hf_seq_id", tOnlinePayment.getPayNumber());
// 追加参数
request.setExtendInfo(extendInfoMap);
@Autowired
private ITOnlinePaymentService itOnlinePaymentService;
@Autowired
private ITOnlinePaymentSepAccService sepAccService;
@Autowired
private ITOnlinePaymentSepAccDService sepAccDService;
log.info("新汇付退款调用参数:{}", JSONUtil.toJsonStr(request));
Map<String, Object> response = BasePayClient.request(request);
log.info("新汇付退款调用返回:{}", JSONUtil.toJsonStr(response));
tOnlineRefund.setRefundNumber(response.get("hf_seq_id").toString());
if (HuiFuPayConstants.TRANS_SUCCESS.equals(response.get("trans_stat").toString())) {
// 处理成功
tOnlineRefund.setRefundStatus(ERefundStatus.REFUNDED.getValue());
tOnlineRefund.setFinishTime(DateUtils.parseDateOne(response.get("trans_finish_time"), DateUtils.YAMMERERS));
if (null != response.get("actual_ref_amt")) {
tOnlineRefund.setFinishMoney(new BigDecimal(response.get("actual_ref_amt").toString()));
}
} else if (HuiFuPayConstants.TRANS_PROCESSING.equals(response.get("trans_stat").toString())) {
// 处理中
tOnlineRefund.setRefundStatus(ERefundStatus.REFUNDING.getValue());
} else {
// 退款失败
tOnlineRefund.setRefundStatus(ERefundStatus.REFUND_FAILED.getValue());
tOnlineRefund.setErrorMsg(response.get("resp_desc").toString());
log.error("新汇付退款交易失败! resp_desc: {}, bank_message: {}, hf_seq_id: {}", response.get("resp_desc"), response.get("bank_message"), response.get("hf_seq_id"));
returnStr = "新汇付退款交易失败";
}
// 更新接口返回状态
itOnlineRefundService.updateById(tOnlineRefund);
} catch (Exception e) {
log.error("新汇付退款处理异常", e);
returnStr = "新汇付退款处理异常:" + e.getMessage();
}
return returnStr;
}
@Override
public String queryHuifuRefundHandle(TOnlineRefund tOnlineRefund) {
try {
V2TradePaymentScanpayRefundqueryRequest request = new V2TradePaymentScanpayRefundqueryRequest();
// 商户号
request.setHuifuId(huiFuConfig.getSysId());
// 退款请求日期
request.setOrgReqDate(DateTools.dateToDateString(tOnlineRefund.getCreationTime(), DateTools.DATE_YYYYMMDD));
// 退款全局流水号退款请求流水号,退款全局流水号,终端订单号三选一不能都为空
request.setOrgHfSeqId(tOnlineRefund.getRefundNumber());
log.info("新汇付退款查询调用参数:{}", JSONUtil.toJsonStr(request));
Map<String, Object> response = BasePayClient.request(request);
log.info("新汇付退款查询调用返回:{}", JSONUtil.toJsonStr(response));
if (HuiFuPayConstants.TRANS_SUCCESS.equals(response.get("trans_stat").toString())) {
// 处理成功
tOnlineRefund.setRefundStatus(ERefundStatus.REFUNDED.getValue());
tOnlineRefund.setFinishTime(DateUtils.parseDateOne(response.get("trans_finish_time"), DateUtils.YAMMERERS));
if (null != response.get("actual_ref_amt")) {
tOnlineRefund.setFinishMoney(new BigDecimal(response.get("actual_ref_amt").toString()));
}
} else if (HuiFuPayConstants.TRANS_PROCESSING.equals(response.get("trans_stat").toString())) {
// 处理中
tOnlineRefund.setRefundStatus(ERefundStatus.REFUNDING.getValue());
} else if (HuiFuPayConstants.TRANS_FAIL.equals(response.get("trans_stat").toString())) {
// 退款失败
tOnlineRefund.setRefundStatus(ERefundStatus.REFUND_FAILED.getValue());
tOnlineRefund.setErrorMsg(response.get("resp_desc").toString());
} else {
return "新汇付退款查询状态:初始化,需要联系汇付技术人员处理";
}
return null;
} catch (Exception e) {
log.error("新汇付退款查询处理异常", e);
return "新汇付退款查询处理异常:" + e.getMessage();
}
public String allInRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
return null;
}
@Override
@ -189,7 +90,6 @@ public class RefundServiceImpl implements IRefundService {
// 退款业务处理
if (StringUtils.isNotEmpty(onlineRefund.getPayBusinessCode())) {
// 如果退款业务单号存在则进行业务回调处理如果不存在则表示直接根据支付信息进行退款不需要进行业务回调
if (this.handleBusiness(onlineRefund)) {
// 更新业务处理信息
if (itOnlineRefundService.updateById(onlineRefund)) {
@ -270,19 +170,19 @@ public class RefundServiceImpl implements IRefundService {
// 服务调用返回信息
String resultMsg = "";
// 根据业务类型调用具体业务处理 dubbo 服务
switch (businessType) {
case MEMBER_ORDER:
// 会员订单
R<String> saRefundR = iSaRefundServiceApi.onlinePayRefundCallback(onlineRefund.getPayBusinessCode(), onlineRefund.getBusinessCode(), onlineRefund.getRefundStatus(), onlineRefund.getErrorMsg());
if (saRefundR.isSuccess()) {
resultBool = true;
} else {
resultBool = false;
resultMsg = saRefundR.getMsg();
}
break;
default:
}
// switch (businessType) {
// case DIRECT_SELLING:
// // 直销订单
// R<String> saRefundR = iSaRefundServiceApi.onlinePayRefundCallback(onlineRefund.getPayBusinessCode(), onlineRefund.getRefundCode(), onlineRefund.getRefundStatus(), onlineRefund.getErrorMsg());
// if (saRefundR.isSuccess()) {
// resultBool = true;
// } else {
// resultBool = false;
// resultMsg = saRefundR.getMsg();
// }
// break;
// default:
// }
if (null != resultBool) {
if (resultBool) {
@ -304,4 +204,152 @@ public class RefundServiceImpl implements IRefundService {
return false;
}
@Override
public String jdRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
return null;
}
@Override
public String jdCashRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
try {
// 退款金额默认按全款退
BigDecimal amount = tOnlinePayment.getPayMoney();
if (null != refundDTO.getRefundAmount()) {
// 如果传入实际退款金额则按实际退款金额处理
amount = refundDTO.getRefundAmount();
}
// 退款编号
String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);
// 当前时间
Date nowDate = new Date();
JdPayRefundRequest request = JdPayRefundRequest.builder()
// 退款订单号
.outTradeNo(refundCode)
// 原订单号需要退款的订单号
.originalOutTradeNo(tOnlinePayment.getBusinessCode())
// 退款时间
.tradeDate(DateUtils.parseDateToFormat(DateUtils.YAMMERERS, nowDate))
// 退款金额
.tradeAmount(amount.multiply(new BigDecimal("100")).intValue() + "")
// 货币种类
.currency("CNY")
// 退款回调地址
.notifyUrl(jdPayBankProperties.getRefundNotifyUrl())
// 回传信息
// .returnParams("")
.build();
// 请求京东退款接口
JdPayRefundResponse response = jdPay.refund(request);
if ("0000".equals(response.getResultCode())) {
// 请求响应成功
// 申请退款成功保存退款
TOnlineRefund tOnlineRefund = new TOnlineRefund();
tOnlineRefund.setRefundCode(refundCode);
tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
tOnlineRefund.setRefundMoney(amount);
tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
tOnlineRefund.setRefundNumber(response.getTradeNo());
tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
tOnlineRefund.setPkCreator(refundDTO.getUserId());
if (itOnlineRefundService.save(tOnlineRefund)) {
return null;
} else {
log.error("调用京东收银台退款入库处理失败desc: {}", response.getResultDesc());
return "退款失败,请联系系统管理员处理";
}
}
log.error("调用京东收银台退款处理失败desc: {}", response.getResultDesc());
return "退款失败,请重试";
} catch (Exception e) {
log.error("调用京东收银台退款处理返回异常!", e);
return "退款异常,请重试";
}
}
@Override
public String jdRefundDivision(String orderCode, Long userId) {
TOnlinePaymentSepAcc sepAcc = sepAccService.getSepAccByOrderCode(orderCode);
LambdaQueryWrapper<TOnlinePaymentSepAccD> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TOnlinePaymentSepAccD::getPkSepAcc, sepAcc.getPkId());
queryWrapper.eq(TOnlinePaymentSepAccD::getDelFlag, EDelFlag.UN_DELETE.getValue());
queryWrapper.orderByAsc(TOnlinePaymentSepAccD::getPkId);
LambdaQueryWrapper<TOnlinePayment> paymentQueryWrapper = new LambdaQueryWrapper<>();
paymentQueryWrapper.eq(TOnlinePayment::getBusinessCode, orderCode);
TOnlinePayment tOnlinePayment = itOnlinePaymentService.getOne(paymentQueryWrapper, false);
List<TOnlinePaymentSepAccD> sepAccDList = sepAccDService.list(queryWrapper);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String now = sdf.format(new Date());
String refundCode = "R_" + orderCode;
JdPayRefundRequest request = JdPayRefundRequest.builder()
.outTradeNo("R_" + orderCode)
.originalOutTradeNo(orderCode)
.tradeAmount(sepAcc.getTradeAmount().multiply(new BigDecimal("100")).intValue() + "")
.currency("CNY")
.tradeDate(now)
.returnParams("")
.notifyUrl(jdPayBankProperties.getRefundNotifyUrl())
.build();
JdPayDivisionAccountRefund divisionAccountRefund = new JdPayDivisionAccountRefund();
List<JdPayDivisionAccountRefundInfo> divisionAccountTradeInfoList = new ArrayList<>();
for (int i = 0; i < sepAccDList.size(); i++) {
TOnlinePaymentSepAccD sepAccD = sepAccDList.get(i);
JdPayDivisionAccountRefundInfo divisionAccountTradeInfo = new JdPayDivisionAccountRefundInfo();
divisionAccountTradeInfo.setMerchantNo(sepAccD.getAccount());
divisionAccountTradeInfo.setOutTradeNo("R_" + orderCode + "_" + ( i + 1 ));
divisionAccountTradeInfo.setOriginalOutTradeNo(sepAccD.getOutTradeNo());
divisionAccountTradeInfo.setTradeAmount(sepAccD.getTradeAmount().multiply(new BigDecimal("100")).intValue() + "");
divisionAccountTradeInfoList.add(divisionAccountTradeInfo);
}
divisionAccountRefund.setDivisionAccountRefundInfoList(divisionAccountTradeInfoList);
request.setDivisionAccountRefund(GsonUtil.toJson(divisionAccountRefund));
try {
JdPayRefundResponse response = jdPay.refund(request);
log.info("JdPayRefundResponse -> {}", JSONUtil.toJsonStr(response));
if ("0000".equals(response.getResultCode())) {
log.info("提交退款成功");
TOnlineRefund tOnlineRefund = new TOnlineRefund();
tOnlineRefund.setRefundCode(refundCode);
tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
tOnlineRefund.setBusinessCode(tOnlinePayment.getBusinessCode());
tOnlineRefund.setRefundMoney(tOnlinePayment.getPayMoney());
tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
tOnlineRefund.setRefundNumber(response.getTradeNo());
tOnlineRefund.setPkCountry(1);
tOnlineRefund.setPkCreator(userId);
itOnlineRefundService.save(tOnlineRefund);
return "提交退款成功";
} else {
log.info("提交退款失败");
return "提交退款失败!["+response.getResultDesc()+"]";
}
} catch (Exception e) {
return "提交退款异常! ["+e.getMessage()+"]";
}
}
@Override
public String jdQueryEnterpriseWalletBalance() {
JdPayQueryEnterpriseWalletBalanceRequest request1 = JdPayQueryEnterpriseWalletBalanceRequest.builder()
.accountNo("153428607005")
.build();
JdPayQueryEnterpriseWalletBalanceRequest request2 = JdPayQueryEnterpriseWalletBalanceRequest.builder()
.accountNo("153428607007")
.build();
try {
JdPayQueryEnterpriseWalletBalanceResponse response1 = jdPay.queryEnterpriseWalletBalance(request1);
log.info("response1 -> {}", JSONUtil.toJsonStr(response1));
JdPayQueryEnterpriseWalletBalanceResponse response2 = jdPay.queryEnterpriseWalletBalance(request2);
log.info("response2 -> {}", JSONUtil.toJsonStr(response2));
} catch (Exception e) {
throw new RuntimeException(e);
}
return "";
}
}

View File

@ -0,0 +1,20 @@
package com.hzs.third.pay.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
import com.hzs.third.pay.mapper.TOnlinePaymentSepAccDMapper;
import com.hzs.third.pay.service.ITOnlinePaymentSepAccDService;
import org.springframework.stereotype.Service;
/**
* <p>
* 支付请求子表(分账) 服务实现类
* </p>
*
* @author bd
* @since 2025-07-01
*/
@Service
public class TOnlinePaymentSepAccDServiceImpl extends ServiceImpl<TOnlinePaymentSepAccDMapper, TOnlinePaymentSepAccD> implements ITOnlinePaymentSepAccDService {
}

View File

@ -0,0 +1,67 @@
package com.hzs.third.pay.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.core.enums.EDelFlag;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
import com.hzs.common.security.utils.SecurityUtils;
import com.hzs.third.pay.mapper.TOnlinePaymentSepAccMapper;
import com.hzs.third.pay.service.ITOnlinePaymentSepAccDService;
import com.hzs.third.pay.service.ITOnlinePaymentSepAccService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* 支付请求主表 服务实现类
* </p>
*
* @author bd
* @since 2025-07-01
*/
@Service
public class TOnlinePaymentSepAccServiceImpl extends ServiceImpl<TOnlinePaymentSepAccMapper, TOnlinePaymentSepAcc> implements ITOnlinePaymentSepAccService {
@Autowired
private ITOnlinePaymentSepAccDService itOnlinePaymentSepAccDService;
@Override
public void saveOrUpdateReq(TOnlinePaymentSepAcc tOnlinePaymentSepAcc) {
LambdaQueryWrapper<TOnlinePaymentSepAcc> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TOnlinePaymentSepAcc::getPkPayment, tOnlinePaymentSepAcc.getPkPayment());
TOnlinePaymentSepAcc source = this.getOne(wrapper, false);
if(ObjectUtil.isNotEmpty(source)){
tOnlinePaymentSepAcc.setPkId(source.getPkId());
}else{
tOnlinePaymentSepAcc.setCreationTime(DateUtils.currentDateTime());
tOnlinePaymentSepAcc.setPkCreator(SecurityUtils.getUserId());
}
tOnlinePaymentSepAcc.setDelFlag(EDelFlag.UN_DELETE.getValue());
tOnlinePaymentSepAcc.setModifiedTime(DateUtils.currentDateTime());
tOnlinePaymentSepAcc.setPkModified(SecurityUtils.getUserId());
this.saveOrUpdate(tOnlinePaymentSepAcc);
LambdaQueryWrapper<TOnlinePaymentSepAccD> wrapperD = new LambdaQueryWrapper<>();
wrapperD.eq(TOnlinePaymentSepAccD::getPkSepAcc, tOnlinePaymentSepAcc.getPkId());
itOnlinePaymentSepAccDService.remove(wrapperD);
if(CollUtil.isNotEmpty(tOnlinePaymentSepAcc.getSepAccDList())){
for (TOnlinePaymentSepAccD tOnlinePaymentSepAccD : tOnlinePaymentSepAcc.getSepAccDList()) {
tOnlinePaymentSepAccD.setPkSepAcc(tOnlinePaymentSepAcc.getPkId());
tOnlinePaymentSepAccD.setCreationTime(DateUtils.currentDateTime());
tOnlinePaymentSepAccD.setPkCreator(SecurityUtils.getUserId());
tOnlinePaymentSepAccD.setModifiedTime(DateUtils.currentDateTime());
tOnlinePaymentSepAccD.setPkModified(SecurityUtils.getUserId());
tOnlinePaymentSepAccD.setDelFlag(EDelFlag.UN_DELETE.getValue());
}
itOnlinePaymentSepAccDService.saveBatch(tOnlinePaymentSepAcc.getSepAccDList());
}
}
@Override
public TOnlinePaymentSepAcc getSepAccByOrderCode(String code) {
return baseMapper.getSepAccByOrderCode(code);
}
}

View File

@ -1,7 +1,9 @@
package com.hzs.third.pay.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.core.enums.EOrderType;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.ext.TOnlinePaymentExt;
import com.hzs.third.pay.mapper.TOnlinePaymentMapper;
@ -36,7 +38,9 @@ public class TOnlinePaymentServiceImpl extends ServiceImpl<TOnlinePaymentMapper,
if (null != pkCountry) {
queryWrapper.eq(TOnlinePayment::getPkCountry, pkCountry);
}
queryWrapper.eq(TOnlinePayment::getBusinessType, businessType);
if(ObjectUtil.isNotEmpty(businessType)){
queryWrapper.eq(TOnlinePayment::getBusinessType, businessType);
}
queryWrapper.eq(TOnlinePayment::getBusinessCode, businessCode);
return baseMapper.selectOne(queryWrapper);
}
@ -51,7 +55,15 @@ public class TOnlinePaymentServiceImpl extends ServiceImpl<TOnlinePaymentMapper,
@Override
public List<TOnlinePaymentExt> queryList(OnlinePaymentParam param, Integer pkCountry) {
return baseMapper.queryList(param, pkCountry);
List<TOnlinePaymentExt> result = baseMapper.queryList(param, pkCountry);
for (TOnlinePaymentExt tOnlinePaymentExt : result) {
if(ObjectUtil.isNotEmpty(tOnlinePaymentExt.getOrderType())){
tOnlinePaymentExt.setOrderTypeVal(EOrderType.getLabelByValue(tOnlinePaymentExt.getOrderType()));
}else{
tOnlinePaymentExt.setOrderTypeVal("充值订单");
}
}
return result;
}
}

View File

@ -12,6 +12,52 @@ import lombok.Data;
@Data
public class OnlinePayConfigVO {
/**
* 京东银行卡
*/
private Boolean pay4 = Boolean.FALSE;
/**
* 京东收银台H5
*/
private Boolean pay5 = Boolean.FALSE;
/**
* 京东收银台PC
*/
private Boolean pay6 = Boolean.FALSE;
/**
* 宝付微信
*/
private Boolean pay11 = Boolean.FALSE;
/**
* 宝付微信扫码
*/
private Boolean pay12 = Boolean.FALSE;
/**
* 汇付微信
*/
private Boolean pay13 = Boolean.FALSE;
/**
* 汇付银行卡
*/
private Boolean pay15 = Boolean.FALSE;
/**
* 微信APP
*/
private Boolean pay20 = Boolean.FALSE;
/**
* 通联微信
*/
private Boolean pay32 = Boolean.FALSE;
/**
* 通联银行卡
*/
private Boolean pay33 = Boolean.FALSE;
/**
* 新汇付PC微信扫码支付
*/
@ -20,13 +66,5 @@ public class OnlinePayConfigVO {
* 新汇付PC支付宝扫码支付
*/
private Boolean pay82 = Boolean.FALSE;
/**
* 新汇付H5微信支付
*/
private Boolean pay83 = Boolean.FALSE;
/**
* 新汇付H5支付宝支付
*/
private Boolean pay84 = Boolean.FALSE;
}

View File

@ -1,7 +1,9 @@
package com.hzs.third.pay.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.hzs.common.core.annotation.BigDecimalFormat;
import com.hzs.common.core.annotation.Excel;
import com.hzs.common.core.annotation.Transaction;
import com.hzs.common.core.constant.EnumsPrefixConstants;
@ -144,4 +146,22 @@ public class OnlinePaymentVO {
*/
private String callbackInfo;
/**
* 主账户金额
*/
@Excel(name = "主账户金额")
@BigDecimalFormat
private BigDecimal mainAccountMoney;
/**
* 子账户金额
*/
@Excel(name = "子账户金额")
@BigDecimalFormat
private BigDecimal subAccountMoney;
private Integer orderType;
@Excel(name = "订单类型")
private String orderTypeVal;
}

Binary file not shown.

Binary file not shown.

View File

@ -30,13 +30,14 @@
<!-- 查询列表 -->
<select id="queryList" resultType="com.hzs.common.domain.third.pay.ext.TOnlinePaymentExt">
select top.*, cm.member_code, cm.member_name, cm.phone
select top.*, cm.member_code, cm.member_name,
so.order_type orderType
from T_ONLINE_PAYMENT top
left join cu_member cm
on cm.pk_id = top.pk_creator
left join sa_order so on so.ORDER_CODE = top.business_code
where top.del_flag = 0
and top.pk_country = #{pkCountry}
and cm.system_type = #{param.systemType}
<if test="param.memberCode != null and param.memberCode != ''">
and cm.member_code like #{param.memberCode} || '%'
</if>
@ -72,6 +73,12 @@
<if test="param.payNumber != null">
and top.pay_number like #{param.payNumber} || '%'
</if>
<if test="param.orderType != null and param.orderType > 0">
and so.order_type = #{param.orderType}
</if>
<if test="param.orderType != null and param.orderType == -1">
and so.order_type is null
</if>
order by top.creation_time desc
</select>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hzs.third.pay.mapper.TOnlinePaymentSepAccDMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD">
<id column="PK_ID" property="pkId" />
<result column="DEL_FLAG" property="delFlag" />
<result column="CREATION_TIME" property="creationTime" />
<result column="MODIFIED_TIME" property="modifiedTime" />
<result column="PK_CREATOR" property="pkCreator" />
<result column="PK_MODIFIED" property="pkModified" />
<result column="PK_SEP_ACC" property="pkSepAcc" />
<result column="ACCOUNT" property="account" />
<result column="PROPORTION" property="proportion" />
<result column="TRADE_AMOUNT" property="tradeAmount" />
<result column="OUT_TRADE_NO" property="outTradeNo" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
DEL_FLAG,
CREATION_TIME,
MODIFIED_TIME,
PK_CREATOR,
PK_MODIFIED,
PK_ID, PK_SEP_ACC, ACCOUNT, PROPORTION
</sql>
</mapper>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hzs.third.pay.mapper.TOnlinePaymentSepAccMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc">
<id column="PK_ID" property="pkId" />
<result column="DEL_FLAG" property="delFlag" />
<result column="CREATION_TIME" property="creationTime" />
<result column="MODIFIED_TIME" property="modifiedTime" />
<result column="PK_CREATOR" property="pkCreator" />
<result column="PK_MODIFIED" property="pkModified" />
<result column="PK_PAYMENT" property="pkPayment" />
<result column="VERSION" property="version" />
<result column="REQ_CONTENT" property="reqContent" />
<result column="REQ_CONTENT_SEP_ACC" property="reqContentSepAcc" />
<result column="RESP_CONTENT" property="respContent" />
<result column="TRADE_AMOUNT" property="tradeAmount" />
<result column="OUT_TRADE_NO" property="outTradeNo" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
DEL_FLAG,
CREATION_TIME,
MODIFIED_TIME,
PK_CREATOR,
PK_MODIFIED,
PK_ID, PK_PAYMENT, VERSION, REQ_CONTENT, REQ_CONTENT_SEP_ACC, RESP_CONTENT
</sql>
<select id="getSepAccByOrderCode" resultType="com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc">
select * from T_ONLINE_PAYMENT_SEP_ACC where pk_payment in (select pk_id from T_ONLINE_PAYMENT where BUSINESS_CODE = #{orderCode})
</select>
</mapper>