From 513961ecebcfd0619ede7a7edb7ac5e27de28d26 Mon Sep 17 00:00:00 2001 From: Pu Zhibing <393733352@qq.com> Date: 星期三, 01 一月 2025 17:11:23 +0800 Subject: [PATCH] 增加第三方支付 --- ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/TaskUtil.java | 8 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundResult.java | 59 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/MD5AndKL.java | 112 + ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryOrderResult.java | 87 + ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/ShopService.java | 8 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/SaveWithdrawalAccount.java | 24 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopController.java | 10 ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/WithdrawalRequests.java | 16 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/FrpCodeEnum.java | 57 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/WithdrawalRequestsServiceImpl.java | 45 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayResult.java | 77 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayQueryResult.java | 63 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/PaymentUtil.java | 277 +++ ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/CloseOrderResult.java | 45 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/AccountBalanceQueryResult.java | 43 ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/Shop.java | 20 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayCallbackResult.java | 68 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayCallbackResult.java | 93 + ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ShopServiceImpl.java | 24 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java | 277 +++ ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopWithdrawController.java | 140 + ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/FrpCodeEnum.java | 57 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ShoppingCartServiceImpl.java | 217 ++ ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePay.java | 61 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/MD5AndKL.java | 112 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryOrderResult.java | 87 + ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/TransferUtil.java | 229 ++ ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayResult.java | 31 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryRefundResult.java | 62 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundCallbackResult.java | 63 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayQueryResult.java | 63 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/AccountBalanceQueryResult.java | 43 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayResult.java | 31 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryRefundResult.java | 62 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ShoppingCartService.java | 24 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/CloseOrderResult.java | 45 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayCallbackResult.java | 93 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/TransferUtil.java | 233 ++ ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayCallbackResult.java | 64 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayResult.java | 77 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundCallbackResult.java | 63 ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ShopBalanceStatement.java | 4 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/CloseOrderResult.java | 45 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryRefundResult.java | 62 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePay.java | 61 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/FrpCodeEnum.java | 57 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePay.java | 61 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryOrderResult.java | 87 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WithdrawalRequestsController.java | 121 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/AccountBalanceQueryResult.java | 43 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayResult.java | 77 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundCallbackResult.java | 63 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayResult.java | 31 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayQueryResult.java | 63 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundResult.java | 59 ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayCallbackResult.java | 64 /dev/null | 78 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/PaymentUtil.java | 276 +++ ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/TransferUtil.java | 226 ++ ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ShoppingCartController.java | 52 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundResult.java | 59 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/MD5AndKL.java | 112 + ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayCallbackResult.java | 93 + 63 files changed, 4,938 insertions(+), 126 deletions(-) diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/WithdrawalRequests.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/WithdrawalRequests.java index 9e0fa5d..078a363 100644 --- a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/WithdrawalRequests.java +++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/WithdrawalRequests.java @@ -48,6 +48,14 @@ @ApiModelProperty(value = "提现金额") @TableField("withdrawal_amount") private BigDecimal withdrawalAmount; + + @ApiModelProperty(value = "到账金额") + @TableField("arrival_amount") + private BigDecimal arrivalAmount; + + @ApiModelProperty(value = "手续费") + @TableField("service_charge") + private BigDecimal serviceCharge; @ApiModelProperty(value = "1微信2银行卡") @TableField("withdrawal_method") @@ -64,6 +72,14 @@ @ApiModelProperty(value = "审核状态 1'待审核',2'审核通过',3'审核拒绝' ") @TableField("audit_status") private Integer auditStatus; + + @ApiModelProperty(value = "状态(1=处理中,2=成功)") + @TableField("status") + private Integer status; + + @ApiModelProperty(value = "到账时间") + @TableField("arrival_time") + private LocalDateTime arrivalTime; @TableField(exist = false) private String userName; @TableField(exist = false) diff --git a/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/Shop.java b/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/Shop.java index 3a5830a..ba28085 100644 --- a/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/Shop.java +++ b/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/Shop.java @@ -169,6 +169,26 @@ @ApiModelProperty(value = "添加时间") @TableField("create_time") private LocalDateTime createTime; + + @ApiModelProperty("报备商户号") + @TableField("tradeMerchantNo") + private String tradeMerchantNo; + + @ApiModelProperty("收款银行卡号") + @TableField("receiverAccountNoEnc") + private String receiverAccountNoEnc; + + @ApiModelProperty("收款银行卡持卡人名称") + @TableField("receiverNameEnc") + private String receiverNameEnc; + + @ApiModelProperty("账户类型(对私账户201,对公账户204)") + @TableField("receiverAccountType") + private Integer receiverAccountType; + + @ApiModelProperty("收款账户联行号") + @TableField("receiverBankChannelNo") + private String receiverBankChannelNo; diff --git a/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ShopBalanceStatement.java b/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ShopBalanceStatement.java index 177e0f3..464f565 100644 --- a/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ShopBalanceStatement.java +++ b/ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ShopBalanceStatement.java @@ -39,9 +39,9 @@ @TableField("shop_id") private Integer shopId; - @ApiModelProperty(value = "变动类型(1=门店分佣,2=下级门店分佣,3=门店服务费)") + @ApiModelProperty(value = "变动类型(1=门店分佣,2=下级门店分佣,3=门店服务费,4=关联用户分佣,5=提现)") @TableField("type") - @Excel(name = "变更类型",readConverterExp = "1=门店分佣,2=下级门店分佣,3=门店服务费,4=关联用户分佣") + @Excel(name = "变更类型",readConverterExp = "1=门店分佣,2=下级门店分佣,3=门店服务费,4=关联用户分佣,5=提现") private Integer type; @ApiModelProperty(value = "历史余额") diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ObsUploadUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ObsUploadUtil.java deleted file mode 100644 index f5239c8..0000000 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/ObsUploadUtil.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.ruoyi.common.core.utils; - -import com.obs.services.ObsClient; -import com.obs.services.model.ObjectMetadata; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.io.InputStream; -import java.util.UUID; - -public class ObsUploadUtil { - - public static String endPoint = "obs.cn-southwest-2.myhuaweicloud.com"; - public static String accessKeyId = "LP9N1TLAYN8ERS1PVIYK"; - public static String accessKeySecret = "bV55lFHi1cG0SYBvnab8yIgDX6etKRSLh5j1gkPR"; - public static String bucketName = "haitunyingyu"; - public static String oss_domain = "https://haitunyingyu.obs.cn-southwest-2.myhuaweicloud.com/"; - // 创建ObsClient实例 - public static ObsClient obsClient = new ObsClient(accessKeyId, accessKeySecret, endPoint); - - public static String obsUpload(MultipartFile file) throws IOException{ - //CommonsMultipartFile file = (CommonsMultipartFile)multipartFile; - String fileName = ""; - if(file!=null && !"".equals(file.getOriginalFilename()) && file.getOriginalFilename()!=null){ - InputStream content = file.getInputStream();//获得指定文件的输入流 - ObjectMetadata meta = new ObjectMetadata();// 创建上传Object的Metadata - meta.setContentLength(file.getSize()); // 必须设置ContentLength - String originalFilename = file.getOriginalFilename(); - if (originalFilename.contains("apk")){ - fileName = "bf2fe5c5499341e5bc0d56c0c7d5fb2e.apk"; - System.err.println("apk"); - }else{ - fileName = UUID.randomUUID().toString().replaceAll("-","") + originalFilename.subSequence(originalFilename.lastIndexOf("."), originalFilename.length()); - } - obsClient.putObject(bucketName,"admin/"+fileName,content,meta);// 上传Object. - if(fileName != null && !"".equals(fileName)){ - System.out.println(fileName); - fileName = oss_domain+"admin/"+fileName; - } - } - return fileName; - } - - /** - * 删除某个Object - * - * @param bucketUrl - * @return - */ - public static boolean deleteObject(String bucketUrl) { - try { - bucketUrl=bucketUrl.replace(oss_domain+"web",""); - // 删除Object. - obsClient.deleteObject(bucketName, bucketUrl); - } catch (Exception e) { - e.printStackTrace(); - return false; - } finally { - //ossClient.shutdown(); - } - return true; - } - -// public static void createBucket(String bucketName) -// { -// //初始化 OSSClient -//// ossClient = new OssClient(endPoint, accessKeyId, accessKeySecret); -// -// // 新建一个Bucket -// Bucket bucket = ossClient.createBucket(bucketName); -// System.out.println(bucket.getName()); -// System.out.println(bucket.getCreationDate()); -// } -// -// public static void main(String[] args) { -// OssUploadUtil.createBucket("ssfdfsd"); -// } -} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WithdrawalRequestsController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WithdrawalRequestsController.java index 2763610..3e95d1a 100644 --- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WithdrawalRequestsController.java +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/WithdrawalRequestsController.java @@ -1,6 +1,7 @@ package com.ruoyi.account.controller; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.metadata.IPage; import com.ruoyi.account.api.model.AppUser; import com.ruoyi.account.api.model.BalanceChangeRecord; @@ -8,7 +9,13 @@ import com.ruoyi.account.dto.WithQuery; import com.ruoyi.account.dto.WithdrawalRequestsDTO; import com.ruoyi.account.service.AppUserService; +import com.ruoyi.account.service.BalanceChangeRecordService; import com.ruoyi.account.service.WithdrawalRequestsService; +import com.ruoyi.account.util.payment.TransferUtil; +import com.ruoyi.account.util.payment.model.AccountBalanceQueryResult; +import com.ruoyi.account.util.payment.model.SinglePay; +import com.ruoyi.account.util.payment.model.SinglePayCallbackResult; +import com.ruoyi.account.util.payment.model.SinglePayResult; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.web.domain.AjaxResult; import io.swagger.annotations.Api; @@ -17,6 +24,9 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; /** * <p> @@ -35,6 +45,9 @@ private WithdrawalRequestsService withdrawalRequestsService; @Resource private AppUserService appUserService; + + @Resource + private BalanceChangeRecordService balanceChangeRecordService; /** * 提现申请 @@ -46,6 +59,9 @@ return AjaxResult.success(); } + + + @PostMapping("/page") @ApiOperation(value = "提现申请列表", tags = {"后台"}) public R<IPage<WithdrawalRequests>> page(@RequestBody WithQuery withQuery){ @@ -55,24 +71,101 @@ } return R.ok(withdrawalRequestsIPage); } + + + + + @PostMapping("/auth") @ApiOperation(value = "提现申请审批", tags = {"后台"}) - public R<IPage<WithdrawalRequests>> auth(@RequestParam Long id,@ApiParam("2'审核通过',3'审核拒绝'") Integer auditStatus){ - WithdrawalRequests byId = withdrawalRequestsService.getById(id); - if (auditStatus==2){ - - //将用户待审核金额减少,已提现金额增加 - AppUser byId1 = appUserService.getById(byId.getAppUserId()); - byId1.setWithdrawnAmount(byId1.getWithdrawnAmount().add(byId.getWithdrawalAmount())); - appUserService.updateById(byId1); - //执行对应的第三方提现 todo + public R auth(@RequestParam Long id,@ApiParam("2'审核通过',3'审核拒绝'") Integer auditStatus){ + WithdrawalRequests withdrawal = withdrawalRequestsService.getById(id); + BigDecimal withdrawalAmount = withdrawal.getWithdrawalAmount(); + if(withdrawal.getAuditStatus() != 1){ + return R.fail("不能重复审核"); } - //通过 - byId.setAuditStatus(auditStatus); - withdrawalRequestsService.updateById(byId); + if (auditStatus==2){ + //先检查账户余额是否充足 + AccountBalanceQueryResult accountBalanceQueryResult = TransferUtil.accountBalanceQuery(); + if(null == accountBalanceQueryResult){ + return R.fail("查询账户余额出错"); + } + Double useAbleSettAmount = accountBalanceQueryResult.getUseAbleSettAmount(); + if(useAbleSettAmount < withdrawal.getArrivalAmount().doubleValue()){ + return R.fail("账户可用余额不足,请先补充账户余额"); + } + //执行转账操作 + if(withdrawal.getWithdrawalMethod() == 2){ + //银行卡转账 + SinglePay singlePay = new SinglePay(); + singlePay.setTradeMerchantNo(""); + singlePay.setMerchantOrderNo(withdrawal.getId().toString()); + singlePay.setReceiverAccountNoEnc(withdrawal.getBankCardNumber()); + singlePay.setReceiverNameEnc(withdrawal.getAccountHolder()); + singlePay.setReceiverAccountType(201); + singlePay.setPaidAmount(withdrawal.getArrivalAmount().doubleValue()); + singlePay.setPaidDesc("账户余额提现"); + singlePay.setPaidUse("205"); + singlePay.setCallbackUrl("/account/withdrawal-requests/withdrawalCallback"); + SinglePayResult singlePayResult = TransferUtil.singlePay(singlePay); + if(null == singlePayResult){ + return R.fail("转账失败"); + } + withdrawal.setStatus(1); + }else{ + //微信转账 + } + } + if(3 == auditStatus){ + //回退扣除的金额,添加明细记录 + //修改用户的可提现金额 + AppUser appUser = appUserService.getById(withdrawal.getAppUserId()); + BigDecimal withdrawableAmount = appUser.getWithdrawableAmount(); + BigDecimal withdrawnAmount = appUser.getWithdrawnAmount(); + BigDecimal balance = appUser.getBalance(); + appUser.setWithdrawableAmount(withdrawableAmount.add(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUser.setWithdrawnAmount(withdrawnAmount.subtract(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUser.setBalance(appUser.getBalance().add(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUserService.updateById(appUser); + //添加变动明细 + BalanceChangeRecord balanceChangeRecord = new BalanceChangeRecord(); + balanceChangeRecord.setAppUserId(appUser.getId()); + balanceChangeRecord.setOrderId(withdrawal.getId()); + balanceChangeRecord.setChangeType(2); + balanceChangeRecord.setBeforeAmount(balance); + balanceChangeRecord.setChangeAmount(withdrawalAmount); + balanceChangeRecord.setAfterAmount(appUser.getBalance()); + balanceChangeRecord.setDelFlag(0); + balanceChangeRecord.setCreateTime(LocalDateTime.now()); + balanceChangeRecordService.save(balanceChangeRecord); + } + withdrawal.setAuditStatus(auditStatus); + withdrawalRequestsService.updateById(withdrawal); return R.ok(); } - - + + + /** + * 提现审核通过后转账回调通知 + * @param singlePayCallbackResult + */ + @ResponseBody + @PostMapping("/withdrawalCallback") + public Object withdrawalCallback(@RequestBody SinglePayCallbackResult singlePayCallbackResult){ + Integer status = singlePayCallbackResult.getStatus(); + if(203 == status){ + String merchantOrderNo = singlePayCallbackResult.getMerchantOrderNo(); + WithdrawalRequests withdrawalRequests = withdrawalRequestsService.getById(merchantOrderNo); + if(1 == withdrawalRequests.getStatus()){ + withdrawalRequests.setStatus(2); + withdrawalRequests.setArrivalTime(LocalDateTime.now()); + withdrawalRequestsService.updateById(withdrawalRequests); + } + JSONObject jsonObject = new JSONObject(); + jsonObject.put("statusCode", 2001); + return jsonObject; + } + return new JSONObject(); + } } diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/WithdrawalRequestsServiceImpl.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/WithdrawalRequestsServiceImpl.java index 30754e1..e2133de 100644 --- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/WithdrawalRequestsServiceImpl.java +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/WithdrawalRequestsServiceImpl.java @@ -5,11 +5,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.api.feignClient.AppUserClient; import com.ruoyi.account.api.model.AppUser; +import com.ruoyi.account.api.model.BalanceChangeRecord; import com.ruoyi.account.api.model.UserClickLog; import com.ruoyi.account.api.model.WithdrawalRequests; import com.ruoyi.account.dto.WithQuery; import com.ruoyi.account.dto.WithdrawalRequestsDTO; import com.ruoyi.account.mapper.WithdrawalRequestsMapper; +import com.ruoyi.account.service.AppUserService; +import com.ruoyi.account.service.BalanceChangeRecordService; import com.ruoyi.account.service.VipSettingService; import com.ruoyi.account.service.WithdrawalRequestsService; import com.ruoyi.common.core.exception.ServiceException; @@ -23,6 +26,7 @@ import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.LocalDateTime; /** * <p> @@ -37,23 +41,26 @@ @Resource private TokenService tokenService; @Resource - private AppUserClient appUserClient; + private AppUserService appUserService; @Resource private VipSettingService vipSettingService; + + @Resource + private BalanceChangeRecordService balanceChangeRecordService; public static final BigDecimal MAX_WITHDRAWAL_AMOUNT = new BigDecimal("200"); public static final BigDecimal VIP_WITHDRAWAL_FEE_DENOMINATOR = new BigDecimal("100"); @Override public void withdrawalApply(WithdrawalRequestsDTO params) { - if (params.getWithdrawalAmount().compareTo(MAX_WITHDRAWAL_AMOUNT) > 0) { + BigDecimal withdrawalAmount = params.getWithdrawalAmount(); + if (withdrawalAmount.compareTo(MAX_WITHDRAWAL_AMOUNT) > 0) { throw new ServiceException("提现失败,单次提现金额不能超过200元!"); } LoginUser loginUserApplet = tokenService.getLoginUserApplet(); - AppUser appUser = appUserClient.getAppUserById(loginUserApplet.getUserid()); - - if (appUser.getWithdrawableAmount().compareTo(params.getWithdrawalAmount()) < 0) { + AppUser appUser = appUserService.getById(loginUserApplet.getUserid()); + if (appUser.getWithdrawableAmount().compareTo(withdrawalAmount) < 0) { throw new ServiceException("提现失败,可提现金额不足!"); } @@ -63,22 +70,46 @@ throw new ServiceException("提现失败,当前会员等级不允许提现!"); } BigDecimal vipWithdrawalMinAmount = vipSetting.getVipWithdrawalMinAmount(); - if (params.getWithdrawalAmount().compareTo(vipWithdrawalMinAmount) < 0) { + if (withdrawalAmount.compareTo(vipWithdrawalMinAmount) < 0) { throw new ServiceException("提现失败,提现金额不能小于" + vipWithdrawalMinAmount + "元!"); } + // 提现手续费 BigDecimal vipWithdrawalFee = vipSetting.getVipWithdrawalFee() .divide(VIP_WITHDRAWAL_FEE_DENOMINATOR, 2, RoundingMode.HALF_UP); // 减去手续费 + BigDecimal multiply = params.getWithdrawalAmount().multiply(vipWithdrawalFee); params.setWithdrawalAmount(params.getWithdrawalAmount() - .subtract(params.getWithdrawalAmount().multiply(vipWithdrawalFee))); + .subtract(multiply)); WithdrawalRequests withdrawalRequests = new WithdrawalRequests(); BeanUtils.copyBeanProp(withdrawalRequests, params); + withdrawalRequests.setWithdrawalAmount(withdrawalAmount); + withdrawalRequests.setArrivalAmount(withdrawalRequests.getWithdrawalAmount()); + withdrawalRequests.setServiceCharge(multiply); withdrawalRequests.setDelFlag(0); withdrawalRequests.setAppUserId(SecurityUtils.getUserId()); withdrawalRequests.setAuditStatus(1); save(withdrawalRequests); + //修改用户的可提现金额 + BigDecimal withdrawableAmount = appUser.getWithdrawableAmount(); + BigDecimal withdrawnAmount = appUser.getWithdrawnAmount(); + BigDecimal balance = appUser.getBalance(); + appUser.setWithdrawableAmount(withdrawableAmount.subtract(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUser.setWithdrawnAmount(withdrawnAmount.add(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUser.setBalance(appUser.getBalance().subtract(withdrawalAmount).setScale(2, RoundingMode.HALF_EVEN)); + appUserService.updateById(appUser); + //添加变动明细 + BalanceChangeRecord balanceChangeRecord = new BalanceChangeRecord(); + balanceChangeRecord.setAppUserId(appUser.getId()); + balanceChangeRecord.setOrderId(withdrawalRequests.getId()); + balanceChangeRecord.setChangeType(2); + balanceChangeRecord.setBeforeAmount(balance); + balanceChangeRecord.setChangeAmount(withdrawalAmount); + balanceChangeRecord.setAfterAmount(appUser.getBalance()); + balanceChangeRecord.setDelFlag(0); + balanceChangeRecord.setCreateTime(LocalDateTime.now()); + balanceChangeRecordService.save(balanceChangeRecord); } @Override diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/MD5AndKL.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/MD5AndKL.java new file mode 100644 index 0000000..cdacbfb --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/MD5AndKL.java @@ -0,0 +1,112 @@ +package com.ruoyi.account.util.payment; + +import java.security.MessageDigest; + +public class MD5AndKL { + + /** + * MD5加码。32位 + * + * @param inStr + * @return + */ + public static String MD5(String inStr) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + byte[] md5Bytes = md5.digest(inStr.getBytes()); + StringBuffer hexValue = new StringBuffer(); + for (int i = 0; i < md5Bytes.length; i++) { + int val = ((int) md5Bytes[i]) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } + + /** + * 可逆的加密算法 + * + * @param inStr + * @return + */ + public static String KL(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String s = new String(a); + return s; + } + + /** + * 加密后解密 + * + * @param inStr + * @return + */ + public static String JM(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String k = new String(a); + return k; + } + + + + private static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) + resultSb.append(byteToHexString(b[i])); + + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) + n += 256; + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static String MD5Encode(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (charsetname == null || "".equals(charsetname)){ + resultString = byteArrayToHexString(md.digest(resultString.getBytes())); + }else{ + resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return resultString; + } + + private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + + public static void main(String args[]) { + + System.out.println("MD5后再加密:" + KL(MD5("123456"))); + System.out.println(MD5("123456")); + // System.out.println("加密:" + KL(MD5("123456"))); + // s = KL(s); + // System.out.println("解密:" + KL("81dc9bdb52d04dc20036dbd8313ed055")); + // System.out.println("解密:" + JM(KL(s))); + // System.out.println("解密为MD5后的:" + KL(KL(MD5(s)))); + // System.out.println(JM("5d62957bb57d3e49dcf48a0df064be4c")); + // System.out.println(MD5AndKL.KL(MD5AndKL.MD5("admin"+"87654321"))); + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/PaymentUtil.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/PaymentUtil.java new file mode 100644 index 0000000..8069708 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/PaymentUtil.java @@ -0,0 +1,277 @@ +package com.ruoyi.account.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.account.util.payment.model.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +/** + * 支付工具类 + * @author zhibing.pu + * @Date 2024/12/27 17:00 + */ +@Slf4j +public class PaymentUtil { + + //微信公众号、微信小程序、微信 APP+/H5、云微小程序支付 + private static final String appId = "wxdeed472c98e42a54"; + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + /** + * 支付回调地址 + */ + private static final String callbackUrl = "http://221.182.45.100:9000"; + + + /** + * 支付 + * @param orderNo 商户订单号 + * @param amount 订单金额 + * @param productName 商品名称 + * @param productDesc 商品描述 + * @param mp 公用回传参数 + * @param notifyUrl 服务器异步通知地址 + * @param openId 微信 Openid + * @param tradeMerchantNo 报备商户号 + * @return + */ + public static UniPayResult uniPay(String orderNo, Double amount, String productName, String productDesc, String mp, String notifyUrl, String openId, String tradeMerchantNo){ + String url = "https://trade.joinpay.com/tradeRt/uniPay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //订单金额 + body.put("p3_Amount", amount); + //交易币种 + body.put("p4_Cur", "1"); + //商品名称 + body.put("p5_ProductName", productName); + //商品描述 + body.put("p6_ProductDesc", productDesc); + //公用回传参数 + body.put("p7_Mp", mp); + //服务器异步通知地址 + body.put("p9_NotifyUrl", callbackUrl + notifyUrl); + //交易类型 + body.put("q1_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + //微信 Openid + body.put("q5_OpenId", openId); + //APPID + body.put("q7_AppId", appId); + //报备商户号 + body.put("qa_TradeMerchantNo", tradeMerchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("支付接口异常:" + execute.body()); + return null; + } + UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class); + return uniPayResult; + } + + + /** + * 查询支付订单 + * @param orderNo 订单号 + * @return + */ + public static QueryOrderResult queryOrder(String orderNo){ + String url = "https://trade.joinpay.com/tradeRt/queryOrder"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("查询支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("查询支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("查询支付接口异常:" + execute.body()); + return null; + } + QueryOrderResult uniPayResult = JSON.parseObject(execute.body(), QueryOrderResult.class); + return uniPayResult; + } + + + /** + * 退款 + * @param orderNo 支付订单号 + * @param refundOrderNo 退款订单号 + * @param refundAmount 退款金额 + * @param notifyUrl 异步通知地址 + * @return + */ + public static RefundResult refund(String orderNo, String refundOrderNo, Double refundAmount, String notifyUrl){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //商户退款订单号 + body.put("p3_RefundOrderNo", refundOrderNo); + //退款金额 + body.put("p4_RefundAmount", refundAmount); + //服务器异步通知地址 + body.put("p6_NotifyUrl", notifyUrl); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + RefundResult uniPayResult = JSON.parseObject(execute.body(), RefundResult.class); + return uniPayResult; + } + + + /** + * 查询退款订单 + * @param refundOrderNo 退款订单号 + * @return + */ + public static QueryRefundResult queryRefund(String refundOrderNo){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户退款订单号 + body.put("p2_RefundOrderNo", refundOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + QueryRefundResult uniPayResult = JSON.parseObject(execute.body(), QueryRefundResult.class); + return uniPayResult; + } + + + /** + * 关闭订单(仅支持微信和支付宝) + * @param orderNo 订单号 + * @return + */ + public static CloseOrderResult closeOrder(String orderNo){ + String url = "https://www.joinpay.com/trade/closeOrder.action"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //交易类型 + body.put("p3_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("关闭订单接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("关闭订单接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("关闭订单接口异常:" + execute.body()); + return null; + } + CloseOrderResult uniPayResult = JSON.parseObject(execute.body(), CloseOrderResult.class); + return uniPayResult; + } + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + + + public static void main(String[] args) { + UniPayResult uniPayResult = PaymentUtil.uniPay("123456", 0.01D, "测试商品", "这是用于对接支付测试的商品描述", "", "", "", ""); + System.err.println(JSON.toJSONString(uniPayResult)); + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/TransferUtil.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/TransferUtil.java new file mode 100644 index 0000000..d503443 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/TransferUtil.java @@ -0,0 +1,233 @@ +package com.ruoyi.account.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.account.util.payment.model.AccountBalanceQueryResult; +import com.ruoyi.account.util.payment.model.SinglePay; +import com.ruoyi.account.util.payment.model.SinglePayQueryResult; +import com.ruoyi.account.util.payment.model.SinglePayResult; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * 转账代付工具类 + * @author zhibing.pu + * @Date 2024/12/30 19:54 + */ +@Slf4j +public class TransferUtil { + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + + private static final String format = "yyyy-MM-dd HH:mm:ss"; + /** + * 支付回调地址 + */ + private static final String callbackUrl = "http://221.182.45.100:9000"; + + + /** + * 单笔代付 + * @param singlePay + * @return + */ + public static SinglePayResult singlePay(SinglePay singlePay){ + String url = "https://www.joinpay.com/payment/pay/singlePay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //报备商户号 + body.put("tradeMerchantNo", singlePay.getTradeMerchantNo()); + //产品类型 + body.put("productCode", "BANK_PAY_DAILY_ORDER"); + //交易请求时间 + body.put("requestTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format))); + //商户订单号 + body.put("merchantOrderNo", singlePay.getMerchantOrderNo()); + //收款账户号,收款人银行卡卡号 + body.put("receiverAccountNoEnc", singlePay.getReceiverAccountNoEnc()); + //收款人,收款人银行卡持卡人名称 + body.put("receiverNameEnc", singlePay.getReceiverNameEnc()); + //账户类型 + body.put("receiverAccountType", singlePay.getReceiverAccountType()); + //收款账户联行号 对公账户必须填写此字段 + body.put("receiverBankChannelNo", singlePay.getReceiverBankChannelNo()); + //交易金额 + body.put("paidAmount", singlePay.getPaidAmount()); + //币种 + body.put("currency", "201"); + //是否复核 复核:201,不复核:202 + body.put("isChecked", "202"); + //代付说明 + body.put("paidDesc", singlePay.getPaidDesc()); + //代付用途 + /** + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + body.put("paidUse", singlePay.getPaidUse()); + //商户通知地址 + body.put("callbackUrl", callbackUrl + singlePay.getCallbackUrl()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayResult uniPayResult = jsonObject.getObject("data", SinglePayResult.class); + return uniPayResult; + } + + + /** + * 单笔代付查询接口 + * @param merchantOrderNo 订单号 + * @return + */ + public static SinglePayQueryResult singlePayQuery(String merchantOrderNo){ + String url = "https://www.joinpay.com/payment/pay/singlePayQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //商户订单号 + body.put("merchantOrderNo", merchantOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayQueryResult uniPayResult = jsonObject.getObject("data", SinglePayQueryResult.class); + return uniPayResult; + } + + + /** + * 可取余额查询 + * @return + */ + public static AccountBalanceQueryResult accountBalanceQuery(){ + String url = "https://www.joinpay.com/payment/pay/accountBalanceQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("可取余额查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("可取余额查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("可取余额查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("可取余额查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("可取余额查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + AccountBalanceQueryResult uniPayResult = jsonObject.getObject("data", AccountBalanceQueryResult.class); + return uniPayResult; + } + + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/AccountBalanceQueryResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/AccountBalanceQueryResult.java new file mode 100644 index 0000000..bcaf5fb --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/AccountBalanceQueryResult.java @@ -0,0 +1,43 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:34 + */ +@Data +public class AccountBalanceQueryResult { + /** + * 商户号 + */ + private String userNo; + /** + * 商户名称 + */ + private String userName; + /** + * 币种 + */ + private Integer currency; + /** + * 可取金额 + */ + private Double useAbleSettAmount; + /** + * 可结算冻结金额 + */ + private Double availableSettAmountFrozen; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/CloseOrderResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/CloseOrderResult.java new file mode 100644 index 0000000..4567d59 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/CloseOrderResult.java @@ -0,0 +1,45 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2025/1/1 10:15 + */ +@Data +public class CloseOrderResult { + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 业务结果 100:成功,101:失败 + */ + private String ra_Status; + /** + * 响应码 + * 0 系统连接超时 + * 4 服务不可用 + * 100 关单成功 + * 101 失败,详见响应码描述 + * 10080000 系统异常 + * 10080002 验证签名失败 + * 10080003 订单号不正确 + * 10080042 交易类型不合法 + * 10083001 订单正在处理中 + * 10083002 该订单请求多次交易 + * 10083003 订单已关闭,无需关单操作 + * 10083003 交易成功,无需关单操作 + * 10083004 通道系统异常,请用相同参数重新请求 + * 10083005 通道其他异常信息 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/FrpCodeEnum.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/FrpCodeEnum.java new file mode 100644 index 0000000..fc86596 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/FrpCodeEnum.java @@ -0,0 +1,57 @@ +package com.ruoyi.account.util.payment.model; + +/** + * 支付类型枚举 + * @author zhibing.pu + * @Date 2024/12/27 17:30 + */ +public enum FrpCodeEnum { + ALIPAY_NATIVE("支付宝扫码(主扫)", "ALIPAY_NATIVE"), + ALIPAY_CARD("支付宝刷卡(被扫)", "ALIPAY_CARD"), + ALIPAY_H5("支付宝 H5", "ALIPAY_H5"), + ALIPAY_FWC("支付宝服务窗", "ALIPAY_FWC"), + ALIPAY_SYT("支付宝收银台", "ALIPAY_SYT"), + WEIXIN_NATIVE("微信扫码(主扫)", "WEIXIN_NATIVE"), + WEIXIN_CARD("微信刷卡(被扫)", "WEIXIN_CARD"), + WEIXIN_APP3("微信 APP+支付", "WEIXIN_APP3"), + WEIXIN_H5_PLUS("微信 H5 支付", "WEIXIN_H5_PLUS"), + WEIXIN_GZH("微信公众号支付", "WEIXIN_GZH"), + WEIXIN_XCX("微信小程序支付", "WEIXIN_XCX"), + QQ_NATIVE("QQ 扫码(主扫)", "QQ_NATIVE"), + QQ_CARD("QQ 刷卡(被扫)", "QQ_CARD"), + QQ_APP("QQ APP 支付", "QQ_APP"), + QQ_H5("QQH5 支付", "QQ_H5"), + QQ_GZH("QQ 公众号支付", "QQ_GZH"), + UNIONPAY_NATIVE("银联扫码(主扫)", "UNIONPAY_NATIVE"), + UNIONPAY_CARD("银联刷卡(被扫)", "UNIONPAY_CARD"), + UNIONPAY_APP("银联 APP 支付", "UNIONPAY_APP"), + UNIONPAY_H5("银联 H5", "UNIONPAY_H5"), + UNIONPAY_SYT("银联统一收银台", "UNIONPAY_SYT"), + UNIONPAY_WXMP("银联云微小程序(无感支付)", "UNIONPAY_WXMP") + ; + + private String name; + + private String code; + + FrpCodeEnum(String name, String code) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryOrderResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryOrderResult.java new file mode 100644 index 0000000..904d8a9 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryOrderResult.java @@ -0,0 +1,87 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:30 + */ +@Data +public class QueryOrderResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 商品名称 + */ + private String r4_ProductName; + /** + * 交易流水号 + */ + private String r5_TrxNo; + /** + * 银行流水号 + */ + private String r6_BankTrxNo; + /** + * 订单手续费 + */ + private Double r7_Fee; + /** + * 交易类型 + */ + private String r8_FrpCode; + /** + * 订单状态 100:成功,101:失败,102:已创建,105:订单已关闭 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 支付时间 + */ + private String rf_PayTime; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 银行编码 + */ + private String rj_BankCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryRefundResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryRefundResult.java new file mode 100644 index 0000000..2cee1ea --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/QueryRefundResult.java @@ -0,0 +1,62 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:56 + */ +@Data +public class QueryRefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户退款订单号 + */ + private String r2_RefundOrderNo; + /** + * 退款金额 + */ + private Double r3_RefundAmount; + /** + * 退款流水号 + */ + private String r4_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r5_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r8_RefundWay; + /** + * 退款入账账户 + */ + private String r9_ReceiveAccountNo; + /** + * 退款状态 + * 100:退款成功 + * 101:退款失败 + * 102:退款处理中 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundCallbackResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundCallbackResult.java new file mode 100644 index 0000000..eec1b0b --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundCallbackResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:50 + */ +@Data +public class RefundCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r6_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r7_RefundWay; + /** + * 退款入账账户 + */ + private String r8_ReceiveAccountNo; + /** + * 退款状态 100:成功;101:失败 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundResult.java new file mode 100644 index 0000000..8972b66 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/RefundResult.java @@ -0,0 +1,59 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:43 + */ +@Data +public class RefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款申请状态 + * 100:成功, + * 101:失败 。 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 营销退款金额 + */ + private Double rd_MarketRefAmount; + /** + * 签名数据 + */ + private String hmac; + + +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePay.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePay.java new file mode 100644 index 0000000..4ecb550 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePay.java @@ -0,0 +1,61 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:28 + */ +@Data +public class SinglePay { + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 收款账户号,收款人银行卡卡号 + */ + private String receiverAccountNoEnc; + /** + * 收款人,收款人银行卡持卡人名称 + */ + private String receiverNameEnc; + /** + * 账户类型 对私账户201,对公账户204 + */ + private Integer receiverAccountType; + /** + * 收款账户联行号 对公账户必须填写此字段 + */ + private String receiverBankChannelNo; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 代付说明 + */ + private String paidDesc; + /** + * 代付用途 + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + private String paidUse; + /** + * 商户通知地址 + */ + private String callbackUrl; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayCallbackResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayCallbackResult.java new file mode 100644 index 0000000..973b2ef --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayCallbackResult.java @@ -0,0 +1,68 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * + * @author zhibing.pu + * @Date 2024/12/30 20:39 + */ +@Data +public class SinglePayCallbackResult { + /** + * 交易状态 + * 201 批次已创建 汇聚受理并创建该批次的初始状态 + * 202 处理中 批次正在处理中状态 + * 203 处理完成 批次中的每笔明细都明确了代付结果 + * 204 批次不存在 汇聚未受理该批次的请求,找不到该批次,明确失败 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorCodeDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private String fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayQueryResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayQueryResult.java new file mode 100644 index 0000000..b752f7b --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayQueryResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:10 + */ +@Data +public class SinglePayQueryResult { + /** + * 单笔代付查询请求的交易状态 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private Double fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayResult.java new file mode 100644 index 0000000..6231a45 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/SinglePayResult.java @@ -0,0 +1,31 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:36 + */ +@Data +public class SinglePayResult { + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayCallbackResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayCallbackResult.java new file mode 100644 index 0000000..3ea002b --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayCallbackResult.java @@ -0,0 +1,93 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:00 + */ +@Data +public class UniPayCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 交易币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 支付状态 + * 100:支付成功; + * 101:支付失败。 + */ + private String r6_Status; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行订单号 + */ + private String r8_BankOrderNo; + /** + * 银行流水号 + */ + private String r9_BankTrxNo; + /** + * 支付时间 + */ + private String ra_PayTime; + /** + * 交易结果通知时间 + */ + private String rb_DealTime; + /** + * 银行编码 + */ + private String rc_BankCode; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 订单手续费 + */ + private Double rj_Fee; + /** + * 交易类型 + */ + private String rk_FrpCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayResult.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayResult.java new file mode 100644 index 0000000..00a8135 --- /dev/null +++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/util/payment/model/UniPayResult.java @@ -0,0 +1,77 @@ +package com.ruoyi.account.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 10:47 + */ +@Data +public class UniPayResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 交易类型 + */ + private String r6_FrpCode; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行商户编码 + */ + private String r8_MerchantBankCode; + /** + * 响应码,返回 100 时表示成功 + */ + private String ra_Code; + /** + * 响应码描述 + */ + private String rb_CodeMsg; + /** + * 1.主扫支付返回二维码地址。 + * 2.支付宝 H5,mode1/2/3 参考请求参数q9_TransactionModel 说明。 + * 3.微信 H5_PLUS,获取支付信息的 openlink,通过手机端浏览器跳转并唤起微信 APP客户端,直接打开对应的小程序进行支付。 + * 3.公众号支付:需要商户参考微信的官方文档 JSAPI 支付接口进行处理,详情请见:https://pay.weixin.qq.com/wiki/doc/api/index.html + * 4.微信小程序支付返回支付信息。 + * 5.支付宝收银台返回支付宝收银台跳转链接,通过请求该链接跳转至支付宝。 + * 6.微信 app3 支付,返回预支付信息,集成微信 SDK 唤起小程序进行支付。 + * 7.支付宝服务窗支付返回银联交易号 trade_no,可用以唤起支付宝 APP,调起支付宝APP 收银台。 + * 8.银联 app 或银联统一收银台支付,返回预支付信息用此网址的接口调起支付。https://open.unionpay.com/tjweb/acproduct/list?apiservId=450#nav02 + * 9.银联云微小程序返回跳转地址,格式:{“cqpMpAppId”:”云闪付小程序 id”,”cqpMpPath”:”云闪付小程序 path”} + * 10.其他类型支付返回支付信息。 + */ + private String rc_Result; + /** + * 二维码图片码 + */ + private String rd_Pic; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ShoppingCartController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ShoppingCartController.java index b9be064..c7e9265 100644 --- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ShoppingCartController.java +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ShoppingCartController.java @@ -9,6 +9,7 @@ import com.ruoyi.common.core.web.page.TableDataInfo; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.order.service.ShoppingCartService; +import com.ruoyi.order.util.payment.model.UniPayCallbackResult; import com.ruoyi.order.vo.*; import com.ruoyi.other.api.domain.GoodsShop; import com.ruoyi.other.api.domain.Shop; @@ -24,6 +25,9 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -111,7 +115,53 @@ public R<String> shoppingCartPayment(@RequestBody ShoppingCartPayment shoppingCartPayment){ return shoppingCartService.shoppingCartPayment(shoppingCartPayment); } - + + /** + * 订单支付回调通知 + */ + @ResponseBody + @GetMapping("/shoppingCartPaymentCallback") + public void shoppingCartPaymentCallback(UniPayCallbackResult uniPayCallbackResult, HttpServletResponse response){ + R callback = shoppingCartService.shoppingCartPaymentCallback(uniPayCallbackResult); + if(callback.getCode() == 200){ + response.setStatus(200); + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (IOException e) { + throw new RuntimeException(e); + } + out.println("success"); + out.flush(); + out.close(); + } + } + + + /** + * 快递费支付回调 + * @param uniPayCallbackResult + * @param response + */ + @ResponseBody + @GetMapping("/shoppingCartMaterialFlowPaymentCallback") + public void shoppingCartMaterialFlowPaymentCallback(UniPayCallbackResult uniPayCallbackResult, HttpServletResponse response){ + R callback = shoppingCartService.shoppingCartMaterialFlowPaymentCallback(uniPayCallbackResult); + if(callback.getCode() == 200){ + response.setStatus(200); + PrintWriter out = null; + try { + out = response.getWriter(); + } catch (IOException e) { + throw new RuntimeException(e); + } + out.println("success"); + out.flush(); + out.close(); + } + } + + @ResponseBody @GetMapping("/getVerifiableShop") diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ShoppingCartService.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ShoppingCartService.java index 7e47d31..716921a 100644 --- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ShoppingCartService.java +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ShoppingCartService.java @@ -4,6 +4,7 @@ import com.ruoyi.account.api.model.AppUser; import com.ruoyi.common.core.domain.R; import com.ruoyi.order.model.ShoppingCart; +import com.ruoyi.order.util.payment.model.UniPayCallbackResult; import com.ruoyi.order.vo.*; import java.util.List; @@ -48,6 +49,29 @@ * @return */ R shoppingCartPayment(ShoppingCartPayment shoppingCartPayment); + + + /** + * 订单支付回调处理逻辑 + * @param uniPayCallbackResult + * @return + */ + R shoppingCartPaymentCallback(UniPayCallbackResult uniPayCallbackResult); + + /** + * 订单物流支付回调处理 + * @param uniPayCallbackResult + * @return + */ + R shoppingCartMaterialFlowPaymentCallback(UniPayCallbackResult uniPayCallbackResult); + Price getPrice(AppUser appUser, Integer goodsId, Integer shopId); + + + /** + * 定时任务关闭订单 + */ + void closeOrder(); + } diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ShoppingCartServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ShoppingCartServiceImpl.java index e533c48..0ad64c1 100644 --- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ShoppingCartServiceImpl.java +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ShoppingCartServiceImpl.java @@ -19,12 +19,18 @@ import com.ruoyi.order.model.OrderGood; import com.ruoyi.order.model.ShoppingCart; import com.ruoyi.order.service.*; +import com.ruoyi.order.util.payment.PaymentUtil; +import com.ruoyi.order.util.payment.model.CloseOrderResult; +import com.ruoyi.order.util.payment.model.UniPayCallbackResult; +import com.ruoyi.order.util.payment.model.UniPayResult; import com.ruoyi.order.vo.*; import com.ruoyi.other.api.domain.*; import com.ruoyi.other.api.feignClient.*; import com.ruoyi.other.api.vo.GetGoodsBargainPrice; import com.ruoyi.other.api.vo.GetGoodsShopByGoodsIds; import com.ruoyi.other.api.vo.GetSeckillActivityInfo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -32,9 +38,11 @@ import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.*; import java.util.stream.Collectors; +@Slf4j @Service public class ShoppingCartServiceImpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService { @@ -106,6 +114,11 @@ @Resource private OrderBalancePaymentService orderBalancePaymentService; + + @Resource + private RedisTemplate redisTemplate; + + @@ -726,9 +739,6 @@ for (MyShoppingCartVo myShoppingCartVo : goodsList) { earnPoint += (myShoppingCartVo.getEarnSpendingPoints() * myShoppingCartVo.getNumber()); } - if(null != shoppingCartPayment.getUserAddressId()){ - userAddressClient.getUserAddressById(shoppingCartPayment.getUserAddressId()).getData(); - } //获取快递策略,计算快递费 BigDecimal expressFee = BigDecimal.ZERO; @@ -867,10 +877,18 @@ //现金支付 paymentMoney = paymentMoney.add(expressFee).setScale(2, RoundingMode.HALF_EVEN); if(1 == shoppingCartPayment.getPaymentType()){ - //调起微信支付 TODO 待完善 - - - + //调起微信支付 + String goodsNames = goodsList.stream().map(MyShoppingCartVo::getName).collect(Collectors.joining("\n")); + UniPayResult uniPayResult = PaymentUtil.uniPay(order.getOrderNumber(), paymentMoney.doubleValue(), order.getOrderType() == 1 ? "购买服务商品" : "购买单品商品", + goodsNames, "", "/order/shopping-cart/shoppingCartPaymentCallback", appUser.getWxOpenid(), ""); + if(null == uniPayResult || !"100".equals(uniPayResult.getRa_Code())){ + return R.fail(null == uniPayResult ? "支付失败" : uniPayResult.getRb_CodeMsg()); + } + String rc_result = uniPayResult.getRc_Result(); + //将支付数据添加到redis队列中,便于定时任务去校验是否完成支付,没有完成支付支付,15分钟后关闭订单。 + long second = LocalDateTime.now().plusMinutes(15).toEpochSecond(ZoneOffset.UTC); + redisTemplate.opsForZSet().add("OrderPayment", order.getOrderNumber(), second); + return R.ok(rc_result); } //账户余额 BigDecimal redPacketAmount = BigDecimal.ZERO; @@ -956,6 +974,23 @@ } //积分支付 if(3 == shoppingCartPayment.getPaymentType()){ + //先完成快递费支付后再处理后续的逻辑 + if(expressFee.compareTo(BigDecimal.ZERO) > 0){ + if(shoppingCartPayment.getFreightPaymentType() == 1){ + //调起微信支付 + UniPayResult uniPayResult = PaymentUtil.uniPay(order.getOrderNumber() + appUser.getId(), expressFee.doubleValue(), order.getOrderType() == 1 ? "购买服务商品快递费" : "购买单品商品快递费", + "快递费", "", "/order/shopping-cart/shoppingCartMaterialFlowPaymentCallback", appUser.getWxOpenid(), ""); + if(null == uniPayResult || !"100".equals(uniPayResult.getRa_Code())){ + return R.fail(null == uniPayResult ? "支付失败" : uniPayResult.getRb_CodeMsg()); + } + String rc_result = uniPayResult.getRc_Result(); + //将支付数据添加到redis队列中,便于定时任务去校验是否完成支付,没有完成支付支付,15分钟后关闭订单。 + long second = LocalDateTime.now().plusMinutes(15).toEpochSecond(ZoneOffset.UTC); + redisTemplate.opsForZSet().add("MaterialFlowPayment", order.getOrderNumber() + appUser.getId(), second); + return R.ok(rc_result); + } + } + Integer lavePoint = appUser.getLavePoint(); PointSetting pointSetting = pointSettingClient.getPointSetting(appUser.getVipId()).getData(); int earnPoint1 = earnPoint; @@ -989,9 +1024,6 @@ userPointClient.saveUserPoint(userPoint); //如果有运费,需要先扣除账户积分,再进行支付。支付成功后修改订单状态,未支付成功则回退积分,删除的订单 if(expressFee.compareTo(BigDecimal.ZERO) > 0){ - if(shoppingCartPayment.getFreightPaymentType() == 1){ - //调起微信支付 - } if(shoppingCartPayment.getFreightPaymentType() == 2){ BigDecimal totalRedPacketAmount = appUser.getTotalRedPacketAmount(); BigDecimal totalDistributionAmount = appUser.getTotalDistributionAmount(); @@ -1067,7 +1099,7 @@ return R.ok(order.getId().toString()); } - + public String getNumber(Integer size){ String str = ""; for (Integer i = 0; i < size; i++) { @@ -1075,6 +1107,165 @@ } return str; } - - + + + /** + * 线上支付回调逻辑处理 + * @param uniPayCallbackResult + * @return + */ + @Override + public R shoppingCartPaymentCallback(UniPayCallbackResult uniPayCallbackResult) { + Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, uniPayCallbackResult.getR2_OrderNo())); + if(null == order || order.getPayStatus() == 2){ + return R.ok(); + } + Integer earnPoint = order.getGetPoint(); + AppUser appUser = appUserClient.getAppUserById(order.getAppUserId()); + BigDecimal paymentMoney = order.getPaymentAmount(); + //构建积分流水记录 + if(earnPoint > 0){ + PointSetting pointSetting = pointSettingClient.getPointSetting(appUser.getVipId()).getData(); + int earnPoint1 = earnPoint; + if(null != pointSetting && 1 == pointSetting.getBuyPointOpen()){ + earnPoint1 = new BigDecimal(earnPoint1).multiply(pointSetting.getBuyPoint().divide(new BigDecimal(100))).intValue(); + } + appUser.setShopPoint(appUser.getShopPoint() + earnPoint); + appUser.setLavePoint(appUser.getLavePoint() + earnPoint); + appUser.setTotalPoint(appUser.getTotalPoint() + earnPoint); + appUser.setAvailablePoint(appUser.getAvailablePoint() + earnPoint1); + + UserPoint userPoint = new UserPoint(); + userPoint.setType(1); + userPoint.setHistoricalPoint(appUser.getLavePoint() - earnPoint); + userPoint.setVariablePoint(earnPoint); + userPoint.setBalance(appUser.getLavePoint()); + userPoint.setCreateTime(LocalDateTime.now()); + userPoint.setAppUserId(appUser.getId()); + userPoint.setObjectId(order.getId()); + userPointClient.saveUserPoint(userPoint); + } + appUser.setShopAmount(appUser.getShopAmount().add(paymentMoney).setScale(2, RoundingMode.HALF_EVEN)); + appUser.setLastShopTime(LocalDateTime.now()); + appUserClient.editAppUserById(appUser); + //变更等级 + appUserClient.vipUpgrade(appUser.getId()); + //修改订支付状态 + order.setPayStatus(2); + //自提 + if(order.getOrderType() == 1 && StringUtils.isEmpty(order.getAddressJson())){ + order.setOrderStatus(2); + } + orderService.updateById(order); + //删除购物车数据 + Long userid = tokenService.getLoginUserApplet().getUserid(); + List<OrderGood> list = orderGoodService.list(new LambdaQueryWrapper<OrderGood>().eq(OrderGood::getOrderId, order.getId())); + List<Integer> goodsIds = list.stream().map(OrderGood::getGoodsId).collect(Collectors.toList()); + this.remove(new LambdaQueryWrapper<ShoppingCart>().eq(ShoppingCart::getAppUserId, userid).in(ShoppingCart::getGoodsId, goodsIds)); + return R.ok(); + } + + + /** + * 订单物流支付回调处理逻辑 + * @param uniPayCallbackResult + * @return + */ + @Override + public R shoppingCartMaterialFlowPaymentCallback(UniPayCallbackResult uniPayCallbackResult) { + String r2_orderNo = uniPayCallbackResult.getR2_OrderNo(); + r2_orderNo = r2_orderNo.substring(0, 23); + Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, r2_orderNo)); + if(null == order || order.getPayStatus() == 2){ + return R.ok(); + } + Integer earnPoint = order.getGetPoint(); + AppUser appUser = appUserClient.getAppUserById(order.getAppUserId()); + Integer lavePoint = appUser.getLavePoint(); + Integer orderPoint = order.getPoint(); + PointSetting pointSetting = pointSettingClient.getPointSetting(appUser.getVipId()).getData(); + int earnPoint1 = earnPoint; + //计算可用积分比例 + if(null != pointSetting && 1 == pointSetting.getBuyPointOpen()){ + earnPoint1 = new BigDecimal(earnPoint1).multiply(pointSetting.getBuyPoint().divide(new BigDecimal(100))).intValue(); + } + //扣减订单支付积分 + appUser.setLavePoint(appUser.getLavePoint() - orderPoint); + appUser.setAvailablePoint(appUser.getAvailablePoint() - orderPoint); + + appUser.setShopPoint(appUser.getShopPoint() + earnPoint); + appUser.setLavePoint(appUser.getLavePoint() + earnPoint); + appUser.setAvailablePoint(appUser.getAvailablePoint() + earnPoint1); + appUser.setTotalPoint(appUser.getTotalPoint() + earnPoint); + appUser.setLastShopTime(LocalDateTime.now()); + appUserClient.editAppUserById(appUser); + //变更等级 + appUserClient.vipUpgrade(appUser.getId()); + + //构建积分流水记录 + UserPoint userPoint = new UserPoint(); + userPoint.setType(1); + userPoint.setHistoricalPoint(lavePoint); + Integer point = appUser.getLavePoint() - lavePoint; + userPoint.setVariablePoint(point >= 0 ? point : point * -1); + userPoint.setBalance(appUser.getLavePoint()); + userPoint.setCreateTime(LocalDateTime.now()); + userPoint.setAppUserId(appUser.getId()); + userPoint.setObjectId(order.getId()); + userPointClient.saveUserPoint(userPoint); + //修改订支付状态 + order.setPayStatus(2); + //自提 + if(order.getOrderType() == 1 && StringUtils.isEmpty(order.getAddressJson())){ + order.setOrderStatus(2); + } + orderService.updateById(order); + //删除购物车数据 + Long userid = tokenService.getLoginUserApplet().getUserid(); + List<OrderGood> list = orderGoodService.list(new LambdaQueryWrapper<OrderGood>().eq(OrderGood::getOrderId, order.getId())); + List<Integer> goodsIds = list.stream().map(OrderGood::getGoodsId).collect(Collectors.toList()); + this.remove(new LambdaQueryWrapper<ShoppingCart>().eq(ShoppingCart::getAppUserId, userid).in(ShoppingCart::getGoodsId, goodsIds)); + return R.ok(); + } + + + /** + * 定时任务关闭订单 + */ + @Override + public void closeOrder() { + //订单支付数据 + long second = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC); + Set<String> orderPayment = redisTemplate.opsForZSet().range("OrderPayment", 0, second); + for (String code : orderPayment) { + Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, code)); + if(null == order || order.getPayStatus() != 1){ + continue; + } + //开始执行关闭订单操作 + CloseOrderResult closeOrderResult = PaymentUtil.closeOrder(code); + if((null == closeOrderResult || !closeOrderResult.getRa_Status().equals("100")) && + Arrays.asList("0", "4", "101", "10080000", "10080002", "10083004", "10083005").contains(closeOrderResult.getRb_Code())){ + redisTemplate.opsForZSet().add("OrderPayment", code, 0); + log.error("关闭订单失败:{}---->{}", code, JSON.toJSONString(closeOrderResult)); + } + } + + //快递支付 + Set<String> materialFlowPayment = redisTemplate.opsForZSet().range("MaterialFlowPayment", 0, second); + for (String code : materialFlowPayment) { + code = code.substring(0, 23); + Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, code)); + if(null == order || order.getPayStatus() != 1){ + continue; + } + //开始执行关闭订单操作 + CloseOrderResult closeOrderResult = PaymentUtil.closeOrder(code); + if((null == closeOrderResult || !closeOrderResult.getRa_Status().equals("100")) && + Arrays.asList("0", "4", "101", "10080000", "10080002", "10083004", "10083005").contains(closeOrderResult.getRb_Code())){ + redisTemplate.opsForZSet().add("MaterialFlowPayment", code, 0); + log.error("关闭订单失败:{}---->{}", code, JSON.toJSONString(closeOrderResult)); + } + } + } } diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/TaskUtil.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/TaskUtil.java index 7c77100..07d8334 100644 --- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/TaskUtil.java +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/TaskUtil.java @@ -1,6 +1,7 @@ package com.ruoyi.order.util; import com.ruoyi.order.service.CommissionService; +import com.ruoyi.order.service.ShoppingCartService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -16,10 +17,17 @@ @Resource private CommissionService commissionService; + + @Resource + private ShoppingCartService shoppingCartService; + + + @Scheduled(fixedRate = 60000) public void taskMonth() { commissionService.calculationCommission(); + shoppingCartService.closeOrder(); } // 每天晚上23:59:59执行的定时任务 diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/MD5AndKL.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/MD5AndKL.java new file mode 100644 index 0000000..8abf33a --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/MD5AndKL.java @@ -0,0 +1,112 @@ +package com.ruoyi.order.util.payment; + +import java.security.MessageDigest; + +public class MD5AndKL { + + /** + * MD5加码。32位 + * + * @param inStr + * @return + */ + public static String MD5(String inStr) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + byte[] md5Bytes = md5.digest(inStr.getBytes()); + StringBuffer hexValue = new StringBuffer(); + for (int i = 0; i < md5Bytes.length; i++) { + int val = ((int) md5Bytes[i]) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } + + /** + * 可逆的加密算法 + * + * @param inStr + * @return + */ + public static String KL(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String s = new String(a); + return s; + } + + /** + * 加密后解密 + * + * @param inStr + * @return + */ + public static String JM(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String k = new String(a); + return k; + } + + + + private static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) + resultSb.append(byteToHexString(b[i])); + + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) + n += 256; + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static String MD5Encode(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (charsetname == null || "".equals(charsetname)){ + resultString = byteArrayToHexString(md.digest(resultString.getBytes())); + }else{ + resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return resultString; + } + + private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + + public static void main(String args[]) { + + System.out.println("MD5后再加密:" + KL(MD5("123456"))); + System.out.println(MD5("123456")); + // System.out.println("加密:" + KL(MD5("123456"))); + // s = KL(s); + // System.out.println("解密:" + KL("81dc9bdb52d04dc20036dbd8313ed055")); + // System.out.println("解密:" + JM(KL(s))); + // System.out.println("解密为MD5后的:" + KL(KL(MD5(s)))); + // System.out.println(JM("5d62957bb57d3e49dcf48a0df064be4c")); + // System.out.println(MD5AndKL.KL(MD5AndKL.MD5("admin"+"87654321"))); + } +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/PaymentUtil.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/PaymentUtil.java new file mode 100644 index 0000000..6a6c88c --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/PaymentUtil.java @@ -0,0 +1,276 @@ +package com.ruoyi.order.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.order.util.payment.model.*; +import lombok.extern.slf4j.Slf4j; +import java.util.*; + +/** + * 支付工具类 + * @author zhibing.pu + * @Date 2024/12/27 17:00 + */ +@Slf4j +public class PaymentUtil { + + //微信公众号、微信小程序、微信 APP+/H5、云微小程序支付 + private static final String appId = "wxdeed472c98e42a54"; + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + /** + * 支付回调地址 + */ + private static final String callbackUrl = "http://221.182.45.100:9000"; + + + /** + * 支付 + * @param orderNo 商户订单号 + * @param amount 订单金额 + * @param productName 商品名称 + * @param productDesc 商品描述 + * @param mp 公用回传参数 + * @param notifyUrl 服务器异步通知地址 + * @param openId 微信 Openid + * @param tradeMerchantNo 报备商户号 + * @return + */ + public static UniPayResult uniPay(String orderNo, Double amount, String productName, String productDesc, String mp, String notifyUrl, String openId, String tradeMerchantNo){ + String url = "https://trade.joinpay.com/tradeRt/uniPay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //订单金额 + body.put("p3_Amount", amount); + //交易币种 + body.put("p4_Cur", "1"); + //商品名称 + body.put("p5_ProductName", productName); + //商品描述 + body.put("p6_ProductDesc", productDesc); + //公用回传参数 + body.put("p7_Mp", mp); + //服务器异步通知地址 + body.put("p9_NotifyUrl", callbackUrl + notifyUrl); + //交易类型 + body.put("q1_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + //微信 Openid + body.put("q5_OpenId", openId); + //APPID + body.put("q7_AppId", appId); + //报备商户号 + body.put("qa_TradeMerchantNo", tradeMerchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("支付接口异常:" + execute.body()); + return null; + } + UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class); + return uniPayResult; + } + + + /** + * 查询支付订单 + * @param orderNo 订单号 + * @return + */ + public static QueryOrderResult queryOrder(String orderNo){ + String url = "https://trade.joinpay.com/tradeRt/queryOrder"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("查询支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("查询支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("查询支付接口异常:" + execute.body()); + return null; + } + QueryOrderResult uniPayResult = JSON.parseObject(execute.body(), QueryOrderResult.class); + return uniPayResult; + } + + + /** + * 退款 + * @param orderNo 支付订单号 + * @param refundOrderNo 退款订单号 + * @param refundAmount 退款金额 + * @param notifyUrl 异步通知地址 + * @return + */ + public static RefundResult refund(String orderNo, String refundOrderNo, Double refundAmount, String notifyUrl){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //商户退款订单号 + body.put("p3_RefundOrderNo", refundOrderNo); + //退款金额 + body.put("p4_RefundAmount", refundAmount); + //服务器异步通知地址 + body.put("p6_NotifyUrl", notifyUrl); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + RefundResult uniPayResult = JSON.parseObject(execute.body(), RefundResult.class); + return uniPayResult; + } + + + /** + * 查询退款订单 + * @param refundOrderNo 退款订单号 + * @return + */ + public static QueryRefundResult queryRefund(String refundOrderNo){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户退款订单号 + body.put("p2_RefundOrderNo", refundOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + QueryRefundResult uniPayResult = JSON.parseObject(execute.body(), QueryRefundResult.class); + return uniPayResult; + } + + + /** + * 关闭订单(仅支持微信和支付宝) + * @param orderNo 订单号 + * @return + */ + public static CloseOrderResult closeOrder(String orderNo){ + String url = "https://www.joinpay.com/trade/closeOrder.action"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //交易类型 + body.put("p3_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("关闭订单接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("关闭订单接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("关闭订单接口异常:" + execute.body()); + return null; + } + CloseOrderResult uniPayResult = JSON.parseObject(execute.body(), CloseOrderResult.class); + return uniPayResult; + } + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + + + public static void main(String[] args) { + UniPayResult uniPayResult = PaymentUtil.uniPay("123456", 0.01D, "测试商品", "这是用于对接支付测试的商品描述", "", "", "", ""); + System.err.println(JSON.toJSONString(uniPayResult)); + } +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/TransferUtil.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/TransferUtil.java new file mode 100644 index 0000000..c60dbc8 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/TransferUtil.java @@ -0,0 +1,226 @@ +package com.ruoyi.order.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.order.util.payment.model.*; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * 转账代付工具类 + * @author zhibing.pu + * @Date 2024/12/30 19:54 + */ +@Slf4j +public class TransferUtil { + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + + private static final String format = "yyyy-MM-dd HH:mm:ss"; + + + /** + * 单笔代付 + * @param singlePay + * @return + */ + public static SinglePayResult singlePay(SinglePay singlePay){ + String url = "https://www.joinpay.com/payment/pay/singlePay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //报备商户号 + body.put("tradeMerchantNo", singlePay.getTradeMerchantNo()); + //产品类型 + body.put("productCode", "BANK_PAY_DAILY_ORDER"); + //交易请求时间 + body.put("requestTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format))); + //商户订单号 + body.put("merchantOrderNo", singlePay.getMerchantOrderNo()); + //收款账户号,收款人银行卡卡号 + body.put("receiverAccountNoEnc", singlePay.getReceiverAccountNoEnc()); + //收款人,收款人银行卡持卡人名称 + body.put("receiverNameEnc", singlePay.getReceiverNameEnc()); + //账户类型 + body.put("receiverAccountType", singlePay.getReceiverAccountType()); + //收款账户联行号 对公账户必须填写此字段 + body.put("receiverBankChannelNo", singlePay.getReceiverBankChannelNo()); + //交易金额 + body.put("paidAmount", singlePay.getPaidAmount()); + //币种 + body.put("currency", "201"); + //是否复核 复核:201,不复核:202 + body.put("isChecked", "202"); + //代付说明 + body.put("paidDesc", singlePay.getPaidDesc()); + //代付用途 + /** + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + body.put("paidUse", singlePay.getPaidUse()); + //商户通知地址 + body.put("callbackUrl", singlePay.getCallbackUrl()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayResult uniPayResult = jsonObject.getObject("data", SinglePayResult.class); + return uniPayResult; + } + + + /** + * 单笔代付查询接口 + * @param merchantOrderNo 订单号 + * @return + */ + public static SinglePayQueryResult singlePayQuery(String merchantOrderNo){ + String url = "https://www.joinpay.com/payment/pay/singlePayQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //商户订单号 + body.put("merchantOrderNo", merchantOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayQueryResult uniPayResult = jsonObject.getObject("data", SinglePayQueryResult.class); + return uniPayResult; + } + + + /** + * 可取余额查询 + * @return + */ + public static AccountBalanceQueryResult accountBalanceQuery(){ + String url = "https://www.joinpay.com/payment/pay/accountBalanceQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("可取余额查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("可取余额查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("可取余额查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("可取余额查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("可取余额查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + AccountBalanceQueryResult uniPayResult = jsonObject.getObject("data", AccountBalanceQueryResult.class); + return uniPayResult; + } + + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/AccountBalanceQueryResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/AccountBalanceQueryResult.java new file mode 100644 index 0000000..9e35f1f --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/AccountBalanceQueryResult.java @@ -0,0 +1,43 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:34 + */ +@Data +public class AccountBalanceQueryResult { + /** + * 商户号 + */ + private String userNo; + /** + * 商户名称 + */ + private String userName; + /** + * 币种 + */ + private Integer currency; + /** + * 可取金额 + */ + private Double useAbleSettAmount; + /** + * 可结算冻结金额 + */ + private Double availableSettAmountFrozen; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/CloseOrderResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/CloseOrderResult.java new file mode 100644 index 0000000..df16d5e --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/CloseOrderResult.java @@ -0,0 +1,45 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2025/1/1 10:15 + */ +@Data +public class CloseOrderResult { + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 业务结果 100:成功,101:失败 + */ + private String ra_Status; + /** + * 响应码 + * 0 系统连接超时 + * 4 服务不可用 + * 100 关单成功 + * 101 失败,详见响应码描述 + * 10080000 系统异常 + * 10080002 验证签名失败 + * 10080003 订单号不正确 + * 10080042 交易类型不合法 + * 10083001 订单正在处理中 + * 10083002 该订单请求多次交易 + * 10083003 订单已关闭,无需关单操作 + * 10083003 交易成功,无需关单操作 + * 10083004 通道系统异常,请用相同参数重新请求 + * 10083005 通道其他异常信息 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/FrpCodeEnum.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/FrpCodeEnum.java new file mode 100644 index 0000000..5b40201 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/FrpCodeEnum.java @@ -0,0 +1,57 @@ +package com.ruoyi.order.util.payment.model; + +/** + * 支付类型枚举 + * @author zhibing.pu + * @Date 2024/12/27 17:30 + */ +public enum FrpCodeEnum { + ALIPAY_NATIVE("支付宝扫码(主扫)", "ALIPAY_NATIVE"), + ALIPAY_CARD("支付宝刷卡(被扫)", "ALIPAY_CARD"), + ALIPAY_H5("支付宝 H5", "ALIPAY_H5"), + ALIPAY_FWC("支付宝服务窗", "ALIPAY_FWC"), + ALIPAY_SYT("支付宝收银台", "ALIPAY_SYT"), + WEIXIN_NATIVE("微信扫码(主扫)", "WEIXIN_NATIVE"), + WEIXIN_CARD("微信刷卡(被扫)", "WEIXIN_CARD"), + WEIXIN_APP3("微信 APP+支付", "WEIXIN_APP3"), + WEIXIN_H5_PLUS("微信 H5 支付", "WEIXIN_H5_PLUS"), + WEIXIN_GZH("微信公众号支付", "WEIXIN_GZH"), + WEIXIN_XCX("微信小程序支付", "WEIXIN_XCX"), + QQ_NATIVE("QQ 扫码(主扫)", "QQ_NATIVE"), + QQ_CARD("QQ 刷卡(被扫)", "QQ_CARD"), + QQ_APP("QQ APP 支付", "QQ_APP"), + QQ_H5("QQH5 支付", "QQ_H5"), + QQ_GZH("QQ 公众号支付", "QQ_GZH"), + UNIONPAY_NATIVE("银联扫码(主扫)", "UNIONPAY_NATIVE"), + UNIONPAY_CARD("银联刷卡(被扫)", "UNIONPAY_CARD"), + UNIONPAY_APP("银联 APP 支付", "UNIONPAY_APP"), + UNIONPAY_H5("银联 H5", "UNIONPAY_H5"), + UNIONPAY_SYT("银联统一收银台", "UNIONPAY_SYT"), + UNIONPAY_WXMP("银联云微小程序(无感支付)", "UNIONPAY_WXMP") + ; + + private String name; + + private String code; + + FrpCodeEnum(String name, String code) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryOrderResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryOrderResult.java new file mode 100644 index 0000000..ba5fff1 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryOrderResult.java @@ -0,0 +1,87 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:30 + */ +@Data +public class QueryOrderResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 商品名称 + */ + private String r4_ProductName; + /** + * 交易流水号 + */ + private String r5_TrxNo; + /** + * 银行流水号 + */ + private String r6_BankTrxNo; + /** + * 订单手续费 + */ + private Double r7_Fee; + /** + * 交易类型 + */ + private String r8_FrpCode; + /** + * 订单状态 100:成功,101:失败,102:已创建,105:订单已关闭 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 支付时间 + */ + private String rf_PayTime; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 银行编码 + */ + private String rj_BankCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryRefundResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryRefundResult.java new file mode 100644 index 0000000..d452e33 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/QueryRefundResult.java @@ -0,0 +1,62 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:56 + */ +@Data +public class QueryRefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户退款订单号 + */ + private String r2_RefundOrderNo; + /** + * 退款金额 + */ + private Double r3_RefundAmount; + /** + * 退款流水号 + */ + private String r4_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r5_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r8_RefundWay; + /** + * 退款入账账户 + */ + private String r9_ReceiveAccountNo; + /** + * 退款状态 + * 100:退款成功 + * 101:退款失败 + * 102:退款处理中 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundCallbackResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundCallbackResult.java new file mode 100644 index 0000000..35af562 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundCallbackResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:50 + */ +@Data +public class RefundCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r6_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r7_RefundWay; + /** + * 退款入账账户 + */ + private String r8_ReceiveAccountNo; + /** + * 退款状态 100:成功;101:失败 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundResult.java new file mode 100644 index 0000000..a559ca6 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/RefundResult.java @@ -0,0 +1,59 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:43 + */ +@Data +public class RefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款申请状态 + * 100:成功, + * 101:失败 。 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 营销退款金额 + */ + private Double rd_MarketRefAmount; + /** + * 签名数据 + */ + private String hmac; + + +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePay.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePay.java new file mode 100644 index 0000000..5ca9b5d --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePay.java @@ -0,0 +1,61 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:28 + */ +@Data +public class SinglePay { + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 收款账户号,收款人银行卡卡号 + */ + private String receiverAccountNoEnc; + /** + * 收款人,收款人银行卡持卡人名称 + */ + private String receiverNameEnc; + /** + * 账户类型 对私账户201,对公账户204 + */ + private Integer receiverAccountType; + /** + * 收款账户联行号 对公账户必须填写此字段 + */ + private String receiverBankChannelNo; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 代付说明 + */ + private String paidDesc; + /** + * 代付用途 + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + private String paidUse; + /** + * 商户通知地址 + */ + private String callbackUrl; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayCallbackResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayCallbackResult.java new file mode 100644 index 0000000..c4e45b1 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayCallbackResult.java @@ -0,0 +1,64 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * + * @author zhibing.pu + * @Date 2024/12/30 20:39 + */ +@Data +public class SinglePayCallbackResult { + /** + * 交易状态 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorCodeDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private String fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayQueryResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayQueryResult.java new file mode 100644 index 0000000..0460e12 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayQueryResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:10 + */ +@Data +public class SinglePayQueryResult { + /** + * 单笔代付查询请求的交易状态 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private Double fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayResult.java new file mode 100644 index 0000000..5001cc1 --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/SinglePayResult.java @@ -0,0 +1,31 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:36 + */ +@Data +public class SinglePayResult { + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayCallbackResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayCallbackResult.java new file mode 100644 index 0000000..8af2b3d --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayCallbackResult.java @@ -0,0 +1,93 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:00 + */ +@Data +public class UniPayCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 交易币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 支付状态 + * 100:支付成功; + * 101:支付失败。 + */ + private String r6_Status; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行订单号 + */ + private String r8_BankOrderNo; + /** + * 银行流水号 + */ + private String r9_BankTrxNo; + /** + * 支付时间 + */ + private String ra_PayTime; + /** + * 交易结果通知时间 + */ + private String rb_DealTime; + /** + * 银行编码 + */ + private String rc_BankCode; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 订单手续费 + */ + private Double rj_Fee; + /** + * 交易类型 + */ + private String rk_FrpCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayResult.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayResult.java new file mode 100644 index 0000000..8f8aecc --- /dev/null +++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/payment/model/UniPayResult.java @@ -0,0 +1,77 @@ +package com.ruoyi.order.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 10:47 + */ +@Data +public class UniPayResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 交易类型 + */ + private String r6_FrpCode; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行商户编码 + */ + private String r8_MerchantBankCode; + /** + * 响应码,返回 100 时表示成功 + */ + private String ra_Code; + /** + * 响应码描述 + */ + private String rb_CodeMsg; + /** + * 1.主扫支付返回二维码地址。 + * 2.支付宝 H5,mode1/2/3 参考请求参数q9_TransactionModel 说明。 + * 3.微信 H5_PLUS,获取支付信息的 openlink,通过手机端浏览器跳转并唤起微信 APP客户端,直接打开对应的小程序进行支付。 + * 3.公众号支付:需要商户参考微信的官方文档 JSAPI 支付接口进行处理,详情请见:https://pay.weixin.qq.com/wiki/doc/api/index.html + * 4.微信小程序支付返回支付信息。 + * 5.支付宝收银台返回支付宝收银台跳转链接,通过请求该链接跳转至支付宝。 + * 6.微信 app3 支付,返回预支付信息,集成微信 SDK 唤起小程序进行支付。 + * 7.支付宝服务窗支付返回银联交易号 trade_no,可用以唤起支付宝 APP,调起支付宝APP 收银台。 + * 8.银联 app 或银联统一收银台支付,返回预支付信息用此网址的接口调起支付。https://open.unionpay.com/tjweb/acproduct/list?apiservId=450#nav02 + * 9.银联云微小程序返回跳转地址,格式:{“cqpMpAppId”:”云闪付小程序 id”,”cqpMpPath”:”云闪付小程序 path”} + * 10.其他类型支付返回支付信息。 + */ + private String rc_Result; + /** + * 二维码图片码 + */ + private String rd_Pic; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopController.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopController.java index 617ff88..f024e49 100644 --- a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopController.java +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopController.java @@ -16,6 +16,7 @@ import com.ruoyi.other.service.ShopScoreService; import com.ruoyi.other.service.ShopService; import com.ruoyi.other.vo.NearbyShopVO; +import com.ruoyi.other.vo.SaveWithdrawalAccount; import com.ruoyi.other.vo.ShopDetailVO; import com.ruoyi.other.vo.ShopStatistics; import com.ruoyi.system.api.domain.SysUser; @@ -312,5 +313,14 @@ public void updateShop(@RequestBody Shop shop){ shopService.updateById(shop); } + + + + @PostMapping("/saveWithdrawalAccount") + @ApiOperation(value = "保存提现账户", tags = {"门店后台-财务统计-提现明细"}) + public R saveWithdrawalAccount(SaveWithdrawalAccount saveWithdrawalAccount) { + shopService.saveWithdrawalAccount(saveWithdrawalAccount); + return R.ok(); + } } diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopWithdrawController.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopWithdrawController.java index c804805..f761f1e 100644 --- a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopWithdrawController.java +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/ShopWithdrawController.java @@ -1,17 +1,30 @@ package com.ruoyi.other.controller; +import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.account.api.feignClient.AppUserClient; +import com.ruoyi.account.api.model.AppUser; +import com.ruoyi.account.api.model.BalanceChangeRecord; +import com.ruoyi.account.api.model.WithdrawalRequests; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.other.api.domain.Shop; +import com.ruoyi.other.api.domain.ShopBalanceStatement; import com.ruoyi.other.api.domain.ShopWithdraw; import com.ruoyi.other.dto.ShopBalanceDto; +import com.ruoyi.other.service.ShopBalanceStatementService; import com.ruoyi.other.service.ShopService; import com.ruoyi.other.service.ShopWithdrawService; +import com.ruoyi.other.util.payment.TransferUtil; +import com.ruoyi.other.util.payment.model.AccountBalanceQueryResult; +import com.ruoyi.other.util.payment.model.SinglePay; +import com.ruoyi.other.util.payment.model.SinglePayCallbackResult; +import com.ruoyi.other.util.payment.model.SinglePayResult; +import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.model.LoginUser; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -20,6 +33,7 @@ import javax.annotation.Resource; import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDateTime; /** @@ -41,6 +55,15 @@ private ShopService shopService; @Resource private TokenService tokenService; + + @Resource + private ShopBalanceStatementService shopBalanceStatementService; + + @Resource + private AppUserClient appUserClient; + + + /** * 提现申请列表 @@ -78,13 +101,18 @@ Shop byId = shopService.getById(objectId); return R.ok(byId); } + @GetMapping("/shop/with") @ApiOperation(value = "提现申请", notes = "提现申请列表", tags = {"门店后台"}) - public R<Shop> shopwith(@RequestParam BigDecimal money){ - Integer objectId = tokenService.getLoginUser().getSysUser().getObjectId(); - Shop byId = shopService.getById(objectId); - if (money.compareTo(byId.getCanWithdrawMoney())>0){ + public R shopwith(@RequestParam BigDecimal money){ + SysUser sysUser = tokenService.getLoginUser().getSysUser(); + Integer objectId = sysUser.getObjectId(); + Shop shop = shopService.getById(objectId); + if (money.compareTo(shop.getCanWithdrawMoney())>0){ return R.fail("提现金额不能大于可提现金额"); + } + if(StringUtils.isEmpty(shop.getReceiverAccountNoEnc())){ + return R.fail("请完善账户信息后再申请提现!"); } ShopWithdraw shopWithdraw = new ShopWithdraw(); shopWithdraw.setShopId(objectId); @@ -92,8 +120,26 @@ shopWithdraw.setAuditStatus(0); shopWithdraw.setStatus(1); shopWithdrawService.save(shopWithdraw); - - return R.ok(byId); + //扣除账户余额及添加变动明细 + BigDecimal balance = shop.getBalance(); + BigDecimal canWithdrawMoney = shop.getCanWithdrawMoney(); + BigDecimal withdrawMoney = shop.getWithdrawMoney(); + shop.setBalance(balance.subtract(money).setScale(2, RoundingMode.HALF_EVEN)); + shop.setCanWithdrawMoney(canWithdrawMoney.subtract(money).setScale(2, RoundingMode.HALF_EVEN)); + shop.setWithdrawMoney(withdrawMoney.add(money).setScale(2, RoundingMode.HALF_EVEN)); + shopService.updateById(shop); + //添加门店变动明细 + ShopBalanceStatement shopBalanceStatement = new ShopBalanceStatement(); + shopBalanceStatement.setShopId(shop.getId()); + shopBalanceStatement.setType(5); + shopBalanceStatement.setHistoricalBalance(balance); + shopBalanceStatement.setVariableAmount(money); + shopBalanceStatement.setBalance(shop.getBalance()); + shopBalanceStatement.setCreateUserId(sysUser.getUserId()); + shopBalanceStatement.setCreateTime(LocalDateTime.now()); + shopBalanceStatement.setObjectId(shopWithdraw.getId()); + shopBalanceStatementService.save(shopBalanceStatement); + return R.ok(); } @@ -105,6 +151,59 @@ public R<Void> audit(@RequestBody ShopWithdraw shopWithdraw) { LoginUser loginUser = tokenService.getLoginUser(); ShopWithdraw shopWithdraw1 = shopWithdrawService.getById(shopWithdraw.getId()); + if(0 != shopWithdraw1.getAuditStatus()){ + return R.fail("不能重复审核"); + } + Shop shop = shopService.getById(shopWithdraw1.getShopId()); + BigDecimal money = shopWithdraw1.getMoney(); + if(1 == shopWithdraw.getAuditStatus()){ + //先检查账户余额是否充足 + AccountBalanceQueryResult accountBalanceQueryResult = TransferUtil.accountBalanceQuery(); + if(null == accountBalanceQueryResult){ + return R.fail("查询账户余额出错"); + } + Double useAbleSettAmount = accountBalanceQueryResult.getUseAbleSettAmount(); + if(useAbleSettAmount < shopWithdraw1.getMoney().doubleValue()){ + return R.fail("账户可用余额不足,请先补充账户余额"); + } + //银行卡转账 + SinglePay singlePay = new SinglePay(); + singlePay.setTradeMerchantNo(""); + singlePay.setMerchantOrderNo(shopWithdraw1.getId().toString()); + singlePay.setReceiverAccountNoEnc(shop.getReceiverAccountNoEnc()); + singlePay.setReceiverNameEnc(shop.getReceiverNameEnc()); + singlePay.setReceiverAccountType(shop.getReceiverAccountType()); + singlePay.setPaidAmount(shopWithdraw1.getMoney().doubleValue()); + singlePay.setPaidDesc("账户余额提现"); + singlePay.setPaidUse("208"); + singlePay.setCallbackUrl("/other/shop-withdraw/withdrawalCallback"); + SinglePayResult singlePayResult = TransferUtil.singlePay(singlePay); + if(null == singlePayResult){ + return R.fail("转账失败"); + } + shopWithdraw1.setStatus(1); + } + if(2 == shopWithdraw.getAuditStatus()){ + //回退金额和添加变动明细 + BigDecimal balance = shop.getBalance(); + BigDecimal canWithdrawMoney = shop.getCanWithdrawMoney(); + BigDecimal withdrawMoney = shop.getWithdrawMoney(); + shop.setBalance(balance.add(money).setScale(2, RoundingMode.HALF_EVEN)); + shop.setCanWithdrawMoney(canWithdrawMoney.add(money).setScale(2, RoundingMode.HALF_EVEN)); + shop.setWithdrawMoney(withdrawMoney.subtract(money).setScale(2, RoundingMode.HALF_EVEN)); + shopService.updateById(shop); + //添加门店变动明细 + ShopBalanceStatement shopBalanceStatement = new ShopBalanceStatement(); + shopBalanceStatement.setShopId(shop.getId()); + shopBalanceStatement.setType(5); + shopBalanceStatement.setHistoricalBalance(balance); + shopBalanceStatement.setVariableAmount(money); + shopBalanceStatement.setBalance(shop.getBalance()); + shopBalanceStatement.setCreateUserId(loginUser.getUserid()); + shopBalanceStatement.setCreateTime(LocalDateTime.now()); + shopBalanceStatement.setObjectId(shopWithdraw.getId()); + shopBalanceStatementService.save(shopBalanceStatement); + } shopWithdraw1.setAuditStatus(shopWithdraw.getAuditStatus()); shopWithdraw1.setAuditUserId(loginUser.getUserid()); shopWithdraw1.setAuditTime(LocalDateTime.now()); @@ -112,9 +211,30 @@ shopWithdrawService.updateById(shopWithdraw1); return R.ok(); } - - - - + + + + /** + * 提现审核通过后转账回调通知 + * @param singlePayCallbackResult + */ + @ResponseBody + @PostMapping("/withdrawalCallback") + public Object withdrawalCallback(@RequestBody SinglePayCallbackResult singlePayCallbackResult){ + Integer status = singlePayCallbackResult.getStatus(); + if(203 == status){ + String merchantOrderNo = singlePayCallbackResult.getMerchantOrderNo(); + ShopWithdraw shopWithdraw = shopWithdrawService.getById(merchantOrderNo); + if(1 == shopWithdraw.getStatus()){ + shopWithdraw.setStatus(2); + shopWithdraw.setArrivalTime(LocalDateTime.now()); + shopWithdrawService.updateById(shopWithdraw); + } + JSONObject jsonObject = new JSONObject(); + jsonObject.put("statusCode", 2001); + return jsonObject; + } + return new JSONObject(); + } } diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/ShopService.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/ShopService.java index 8729cd7..0967eb1 100644 --- a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/ShopService.java +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/ShopService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.other.api.domain.Shop; import com.ruoyi.other.vo.NearbyShopVO; +import com.ruoyi.other.vo.SaveWithdrawalAccount; import com.ruoyi.other.vo.ShopDetailVO; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.RequestParam; @@ -28,5 +29,10 @@ ShopDetailVO getShopDetail(Integer shopId, BigDecimal longitude, BigDecimal latitude); Boolean cheUserByPhone(String phone); - + + /** + * 保存提现账户 + * @param saveWithdrawalAccount + */ + void saveWithdrawalAccount(SaveWithdrawalAccount saveWithdrawalAccount); } diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ShopServiceImpl.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ShopServiceImpl.java index 1ada38b..212f7ae 100644 --- a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ShopServiceImpl.java +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ShopServiceImpl.java @@ -16,7 +16,10 @@ import com.ruoyi.other.service.ShopScoreService; import com.ruoyi.other.service.ShopService; import com.ruoyi.other.vo.NearbyShopVO; +import com.ruoyi.other.vo.SaveWithdrawalAccount; import com.ruoyi.other.vo.ShopDetailVO; +import com.ruoyi.system.api.domain.SysUser; +import com.ruoyi.system.api.feignClient.SysUserClient; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -46,6 +49,8 @@ private AppUserClient appUserClient; @Resource private TokenService tokenService; + @Resource + private SysUserClient sysUserClient; @Override @@ -128,4 +133,23 @@ } return r.getData() != null; } + + + /** + * 保存提现账户 + * @param saveWithdrawalAccount + */ + @Override + public void saveWithdrawalAccount(SaveWithdrawalAccount saveWithdrawalAccount) { + Long userid = tokenService.getLoginUser().getUserid(); + SysUser sysUser = sysUserClient.getSysUser(userid).getData(); + Shop shop = this.getById(sysUser.getObjectId()); + if(null != shop){ + shop.setReceiverAccountNoEnc(saveWithdrawalAccount.getReceiverAccountNoEnc()); + shop.setReceiverNameEnc(saveWithdrawalAccount.getReceiverNameEnc()); + shop.setReceiverAccountType(saveWithdrawalAccount.getReceiverAccountType()); + shop.setReceiverBankChannelNo(saveWithdrawalAccount.getReceiverBankChannelNo()); + this.updateById(shop); + } + } } diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/MD5AndKL.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/MD5AndKL.java new file mode 100644 index 0000000..1a8ad14 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/MD5AndKL.java @@ -0,0 +1,112 @@ +package com.ruoyi.other.util.payment; + +import java.security.MessageDigest; + +public class MD5AndKL { + + /** + * MD5加码。32位 + * + * @param inStr + * @return + */ + public static String MD5(String inStr) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + byte[] md5Bytes = md5.digest(inStr.getBytes()); + StringBuffer hexValue = new StringBuffer(); + for (int i = 0; i < md5Bytes.length; i++) { + int val = ((int) md5Bytes[i]) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } + + /** + * 可逆的加密算法 + * + * @param inStr + * @return + */ + public static String KL(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String s = new String(a); + return s; + } + + /** + * 加密后解密 + * + * @param inStr + * @return + */ + public static String JM(String inStr) { + char[] a = inStr.toCharArray(); + for (int i = 0; i < a.length; i++) { + a[i] = (char) (a[i] ^ 't'); + } + String k = new String(a); + return k; + } + + + + private static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) + resultSb.append(byteToHexString(b[i])); + + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) + n += 256; + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static String MD5Encode(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (charsetname == null || "".equals(charsetname)){ + resultString = byteArrayToHexString(md.digest(resultString.getBytes())); + }else{ + resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return resultString; + } + + private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + + public static void main(String args[]) { + + System.out.println("MD5后再加密:" + KL(MD5("123456"))); + System.out.println(MD5("123456")); + // System.out.println("加密:" + KL(MD5("123456"))); + // s = KL(s); + // System.out.println("解密:" + KL("81dc9bdb52d04dc20036dbd8313ed055")); + // System.out.println("解密:" + JM(KL(s))); + // System.out.println("解密为MD5后的:" + KL(KL(MD5(s)))); + // System.out.println(JM("5d62957bb57d3e49dcf48a0df064be4c")); + // System.out.println(MD5AndKL.KL(MD5AndKL.MD5("admin"+"87654321"))); + } +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java new file mode 100644 index 0000000..d37bc30 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java @@ -0,0 +1,277 @@ +package com.ruoyi.other.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.other.util.payment.model.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +/** + * 支付工具类 + * @author zhibing.pu + * @Date 2024/12/27 17:00 + */ +@Slf4j +public class PaymentUtil { + + //微信公众号、微信小程序、微信 APP+/H5、云微小程序支付 + private static final String appId = "wxdeed472c98e42a54"; + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + /** + * 支付回调地址 + */ + private static final String callbackUrl = "http://221.182.45.100:9000"; + + + /** + * 支付 + * @param orderNo 商户订单号 + * @param amount 订单金额 + * @param productName 商品名称 + * @param productDesc 商品描述 + * @param mp 公用回传参数 + * @param notifyUrl 服务器异步通知地址 + * @param openId 微信 Openid + * @param tradeMerchantNo 报备商户号 + * @return + */ + public static UniPayResult uniPay(String orderNo, Double amount, String productName, String productDesc, String mp, String notifyUrl, String openId, String tradeMerchantNo){ + String url = "https://trade.joinpay.com/tradeRt/uniPay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //订单金额 + body.put("p3_Amount", amount); + //交易币种 + body.put("p4_Cur", "1"); + //商品名称 + body.put("p5_ProductName", productName); + //商品描述 + body.put("p6_ProductDesc", productDesc); + //公用回传参数 + body.put("p7_Mp", mp); + //服务器异步通知地址 + body.put("p9_NotifyUrl", callbackUrl + notifyUrl); + //交易类型 + body.put("q1_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + //微信 Openid + body.put("q5_OpenId", openId); + //APPID + body.put("q7_AppId", appId); + //报备商户号 + body.put("qa_TradeMerchantNo", tradeMerchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("支付接口异常:" + execute.body()); + return null; + } + UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class); + return uniPayResult; + } + + + /** + * 查询支付订单 + * @param orderNo 订单号 + * @return + */ + public static QueryOrderResult queryOrder(String orderNo){ + String url = "https://trade.joinpay.com/tradeRt/queryOrder"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.5"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("查询支付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("查询支付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("查询支付接口异常:" + execute.body()); + return null; + } + QueryOrderResult uniPayResult = JSON.parseObject(execute.body(), QueryOrderResult.class); + return uniPayResult; + } + + + /** + * 退款 + * @param orderNo 支付订单号 + * @param refundOrderNo 退款订单号 + * @param refundAmount 退款金额 + * @param notifyUrl 异步通知地址 + * @return + */ + public static RefundResult refund(String orderNo, String refundOrderNo, Double refundAmount, String notifyUrl){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //商户退款订单号 + body.put("p3_RefundOrderNo", refundOrderNo); + //退款金额 + body.put("p4_RefundAmount", refundAmount); + //服务器异步通知地址 + body.put("p6_NotifyUrl", notifyUrl); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + RefundResult uniPayResult = JSON.parseObject(execute.body(), RefundResult.class); + return uniPayResult; + } + + + /** + * 查询退款订单 + * @param refundOrderNo 退款订单号 + * @return + */ + public static QueryRefundResult queryRefund(String refundOrderNo){ + String url = "https://trade.joinpay.com/tradeRt/refund"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //版本号 + body.put("p0_Version", "2.3"); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户退款订单号 + body.put("p2_RefundOrderNo", refundOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("退款接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("退款接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("退款接口异常:" + execute.body()); + return null; + } + QueryRefundResult uniPayResult = JSON.parseObject(execute.body(), QueryRefundResult.class); + return uniPayResult; + } + + + /** + * 关闭订单(仅支持微信和支付宝) + * @param orderNo 订单号 + * @return + */ + public static CloseOrderResult closeOrder(String orderNo){ + String url = "https://www.joinpay.com/trade/closeOrder.action"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("p1_MerchantNo", merchantNo); + //商户订单号 + body.put("p2_OrderNo", orderNo); + //交易类型 + body.put("p3_FrpCode", FrpCodeEnum.WEIXIN_XCX.getCode()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("关闭订单接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("关闭订单接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("关闭订单接口异常:" + execute.body()); + return null; + } + CloseOrderResult uniPayResult = JSON.parseObject(execute.body(), CloseOrderResult.class); + return uniPayResult; + } + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + + + public static void main(String[] args) { + UniPayResult uniPayResult = PaymentUtil.uniPay("123456", 0.01D, "测试商品", "这是用于对接支付测试的商品描述", "", "", "", ""); + System.err.println(JSON.toJSONString(uniPayResult)); + } +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/TransferUtil.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/TransferUtil.java new file mode 100644 index 0000000..f8d6139 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/TransferUtil.java @@ -0,0 +1,229 @@ +package com.ruoyi.other.util.payment; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.other.util.payment.model.AccountBalanceQueryResult; +import com.ruoyi.other.util.payment.model.SinglePay; +import com.ruoyi.other.util.payment.model.SinglePayQueryResult; +import com.ruoyi.other.util.payment.model.SinglePayResult; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + * 转账代付工具类 + * @author zhibing.pu + * @Date 2024/12/30 19:54 + */ +@Slf4j +public class TransferUtil { + /** + * 商户密钥 + */ + private static final String key = "925899fcc374430f9e4b4ba3db05b448"; + /** + * 商户号 + */ + private static final String merchantNo = "888122600004175"; + + private static final String format = "yyyy-MM-dd HH:mm:ss"; + + + /** + * 单笔代付 + * @param singlePay + * @return + */ + public static SinglePayResult singlePay(SinglePay singlePay){ + String url = "https://www.joinpay.com/payment/pay/singlePay"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //报备商户号 + body.put("tradeMerchantNo", singlePay.getTradeMerchantNo()); + //产品类型 + body.put("productCode", "BANK_PAY_DAILY_ORDER"); + //交易请求时间 + body.put("requestTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern(format))); + //商户订单号 + body.put("merchantOrderNo", singlePay.getMerchantOrderNo()); + //收款账户号,收款人银行卡卡号 + body.put("receiverAccountNoEnc", singlePay.getReceiverAccountNoEnc()); + //收款人,收款人银行卡持卡人名称 + body.put("receiverNameEnc", singlePay.getReceiverNameEnc()); + //账户类型 + body.put("receiverAccountType", singlePay.getReceiverAccountType()); + //收款账户联行号 对公账户必须填写此字段 + body.put("receiverBankChannelNo", singlePay.getReceiverBankChannelNo()); + //交易金额 + body.put("paidAmount", singlePay.getPaidAmount()); + //币种 + body.put("currency", "201"); + //是否复核 复核:201,不复核:202 + body.put("isChecked", "202"); + //代付说明 + body.put("paidDesc", singlePay.getPaidDesc()); + //代付用途 + /** + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + body.put("paidUse", singlePay.getPaidUse()); + //商户通知地址 + body.put("callbackUrl", singlePay.getCallbackUrl()); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayResult uniPayResult = jsonObject.getObject("data", SinglePayResult.class); + return uniPayResult; + } + + + /** + * 单笔代付查询接口 + * @param merchantOrderNo 订单号 + * @return + */ + public static SinglePayQueryResult singlePayQuery(String merchantOrderNo){ + String url = "https://www.joinpay.com/payment/pay/singlePayQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + //商户订单号 + body.put("merchantOrderNo", merchantOrderNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("单笔代付查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("单笔代付查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("单笔代付查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("单笔代付查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("单笔代付查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + SinglePayQueryResult uniPayResult = jsonObject.getObject("data", SinglePayQueryResult.class); + return uniPayResult; + } + + + /** + * 可取余额查询 + * @return + */ + public static AccountBalanceQueryResult accountBalanceQuery(){ + String url = "https://www.joinpay.com/payment/pay/accountBalanceQuery"; + HttpRequest post = HttpUtil.createPost(url); + JSONObject body = new JSONObject(); + //商户编号 + body.put("userNo", merchantNo); + String sign = null; + try { + sign = sign(body); + } catch (Exception e) { + throw new RuntimeException(e); + } + body.put("hmac", sign); + post.body(body.toString()); + log.info("可取余额查询接口请求参数:" + body); + HttpResponse execute = post.execute(); + log.info("可取余额查询接口请求响应:" + execute.body()); + if(200 != execute.getStatus()){ + log.error("可取余额查询接口异常:" + execute.body()); + return null; + } + JSONObject jsonObject = JSON.parseObject(execute.body()); + String statusCode = jsonObject.getString("statusCode"); + if(!"2001".equals(statusCode) && !"2003".equals(statusCode)){ + log.error("可取余额查询接口异常:" + jsonObject.getString("message")); + return null; + } + if("2003".equals(statusCode)){ + //汇聚不能确定订单状态,建议 10 分钟后发起查询确认。 + log.error("可取余额查询接口异常:汇聚不能确定订单状态,建议 10 分钟后发起查询确认。"); + return null; + } + AccountBalanceQueryResult uniPayResult = jsonObject.getObject("data", AccountBalanceQueryResult.class); + return uniPayResult; + } + + + + + public static String sign(JSONObject body) throws Exception{ + Set<Map.Entry<String, Object>> entries = body.entrySet(); + List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries); + // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序) + Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() { + public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) { + return (o1.getKey()).toString().compareTo(o2.getKey()); + } + }); + // 构造签名键值对的格式 + StringBuilder sb = new StringBuilder(); + for (Map.Entry<String, Object> item : infoIds) { + if (item.getKey() != null || item.getKey() != "") { + Object val = item.getValue(); + if (!(val == "" || val == null)) { + sb.append(val); + } + } + } + sb.append(key); + return MD5AndKL.MD5(sb.toString()); + } + +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/AccountBalanceQueryResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/AccountBalanceQueryResult.java new file mode 100644 index 0000000..f302966 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/AccountBalanceQueryResult.java @@ -0,0 +1,43 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:34 + */ +@Data +public class AccountBalanceQueryResult { + /** + * 商户号 + */ + private String userNo; + /** + * 商户名称 + */ + private String userName; + /** + * 币种 + */ + private Integer currency; + /** + * 可取金额 + */ + private Double useAbleSettAmount; + /** + * 可结算冻结金额 + */ + private Double availableSettAmountFrozen; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/CloseOrderResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/CloseOrderResult.java new file mode 100644 index 0000000..72170da --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/CloseOrderResult.java @@ -0,0 +1,45 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2025/1/1 10:15 + */ +@Data +public class CloseOrderResult { + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 业务结果 100:成功,101:失败 + */ + private String ra_Status; + /** + * 响应码 + * 0 系统连接超时 + * 4 服务不可用 + * 100 关单成功 + * 101 失败,详见响应码描述 + * 10080000 系统异常 + * 10080002 验证签名失败 + * 10080003 订单号不正确 + * 10080042 交易类型不合法 + * 10083001 订单正在处理中 + * 10083002 该订单请求多次交易 + * 10083003 订单已关闭,无需关单操作 + * 10083003 交易成功,无需关单操作 + * 10083004 通道系统异常,请用相同参数重新请求 + * 10083005 通道其他异常信息 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/FrpCodeEnum.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/FrpCodeEnum.java new file mode 100644 index 0000000..086eab5 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/FrpCodeEnum.java @@ -0,0 +1,57 @@ +package com.ruoyi.other.util.payment.model; + +/** + * 支付类型枚举 + * @author zhibing.pu + * @Date 2024/12/27 17:30 + */ +public enum FrpCodeEnum { + ALIPAY_NATIVE("支付宝扫码(主扫)", "ALIPAY_NATIVE"), + ALIPAY_CARD("支付宝刷卡(被扫)", "ALIPAY_CARD"), + ALIPAY_H5("支付宝 H5", "ALIPAY_H5"), + ALIPAY_FWC("支付宝服务窗", "ALIPAY_FWC"), + ALIPAY_SYT("支付宝收银台", "ALIPAY_SYT"), + WEIXIN_NATIVE("微信扫码(主扫)", "WEIXIN_NATIVE"), + WEIXIN_CARD("微信刷卡(被扫)", "WEIXIN_CARD"), + WEIXIN_APP3("微信 APP+支付", "WEIXIN_APP3"), + WEIXIN_H5_PLUS("微信 H5 支付", "WEIXIN_H5_PLUS"), + WEIXIN_GZH("微信公众号支付", "WEIXIN_GZH"), + WEIXIN_XCX("微信小程序支付", "WEIXIN_XCX"), + QQ_NATIVE("QQ 扫码(主扫)", "QQ_NATIVE"), + QQ_CARD("QQ 刷卡(被扫)", "QQ_CARD"), + QQ_APP("QQ APP 支付", "QQ_APP"), + QQ_H5("QQH5 支付", "QQ_H5"), + QQ_GZH("QQ 公众号支付", "QQ_GZH"), + UNIONPAY_NATIVE("银联扫码(主扫)", "UNIONPAY_NATIVE"), + UNIONPAY_CARD("银联刷卡(被扫)", "UNIONPAY_CARD"), + UNIONPAY_APP("银联 APP 支付", "UNIONPAY_APP"), + UNIONPAY_H5("银联 H5", "UNIONPAY_H5"), + UNIONPAY_SYT("银联统一收银台", "UNIONPAY_SYT"), + UNIONPAY_WXMP("银联云微小程序(无感支付)", "UNIONPAY_WXMP") + ; + + private String name; + + private String code; + + FrpCodeEnum(String name, String code) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryOrderResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryOrderResult.java new file mode 100644 index 0000000..f4a8e1f --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryOrderResult.java @@ -0,0 +1,87 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:30 + */ +@Data +public class QueryOrderResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 商品名称 + */ + private String r4_ProductName; + /** + * 交易流水号 + */ + private String r5_TrxNo; + /** + * 银行流水号 + */ + private String r6_BankTrxNo; + /** + * 订单手续费 + */ + private Double r7_Fee; + /** + * 交易类型 + */ + private String r8_FrpCode; + /** + * 订单状态 100:成功,101:失败,102:已创建,105:订单已关闭 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 支付时间 + */ + private String rf_PayTime; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 银行编码 + */ + private String rj_BankCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryRefundResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryRefundResult.java new file mode 100644 index 0000000..6a2a632 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/QueryRefundResult.java @@ -0,0 +1,62 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:56 + */ +@Data +public class QueryRefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户退款订单号 + */ + private String r2_RefundOrderNo; + /** + * 退款金额 + */ + private Double r3_RefundAmount; + /** + * 退款流水号 + */ + private String r4_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r5_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r8_RefundWay; + /** + * 退款入账账户 + */ + private String r9_ReceiveAccountNo; + /** + * 退款状态 + * 100:退款成功 + * 101:退款失败 + * 102:退款处理中 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundCallbackResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundCallbackResult.java new file mode 100644 index 0000000..326d69a --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundCallbackResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:50 + */ +@Data +public class RefundCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款完成时间 + */ + private String r6_RefundCompleteTime; + /** + * 退款渠道 + */ + private String r7_RefundWay; + /** + * 退款入账账户 + */ + private String r8_ReceiveAccountNo; + /** + * 退款状态 100:成功;101:失败 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundResult.java new file mode 100644 index 0000000..98e1d5a --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/RefundResult.java @@ -0,0 +1,59 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:43 + */ +@Data +public class RefundResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 商户退款订单号 + */ + private String r3_RefundOrderNo; + /** + * 退款金额 + */ + private Double r4_RefundAmount; + /** + * 商户退款流水号 + */ + private String r5_RefundTrxNo; + /** + * 退款申请状态 + * 100:成功, + * 101:失败 。 + */ + private String ra_Status; + /** + * 响应码 + */ + private String rb_Code; + /** + * 响应码描述 + */ + private String rc_CodeMsg; + /** + * 营销退款金额 + */ + private Double rd_MarketRefAmount; + /** + * 签名数据 + */ + private String hmac; + + +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePay.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePay.java new file mode 100644 index 0000000..831cd7f --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePay.java @@ -0,0 +1,61 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:28 + */ +@Data +public class SinglePay { + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 收款账户号,收款人银行卡卡号 + */ + private String receiverAccountNoEnc; + /** + * 收款人,收款人银行卡持卡人名称 + */ + private String receiverNameEnc; + /** + * 账户类型 对私账户201,对公账户204 + */ + private Integer receiverAccountType; + /** + * 收款账户联行号 对公账户必须填写此字段 + */ + private String receiverBankChannelNo; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 代付说明 + */ + private String paidDesc; + /** + * 代付用途 + * 工资奖金 201 + * 活动经费 202 + * 养老金 203 + * 货款 204 + * 劳务费 205 + * 保险理财 206 + * 资金下发 207 + * 营业款 208 + * 退回款项 210 + * 消费款项 211 + */ + private String paidUse; + /** + * 商户通知地址 + */ + private String callbackUrl; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayCallbackResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayCallbackResult.java new file mode 100644 index 0000000..6953a5a --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayCallbackResult.java @@ -0,0 +1,64 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * + * @author zhibing.pu + * @Date 2024/12/30 20:39 + */ +@Data +public class SinglePayCallbackResult { + /** + * 交易状态 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorCodeDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private String fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayQueryResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayQueryResult.java new file mode 100644 index 0000000..5dac9d6 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayQueryResult.java @@ -0,0 +1,63 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/31 10:10 + */ +@Data +public class SinglePayQueryResult { + /** + * 单笔代付查询请求的交易状态 + */ + private Integer status; + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户号 + */ + private String userNo; + /** + * 报备商户号 + */ + private String tradeMerchantNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 平台流水号 + */ + private String platformSerialNo; + /** + * 收款账户号 + */ + private String receiverAccountNoEnc; + /** + * 收款人 + */ + private String receiverNameEnc; + /** + * 交易金额 + */ + private Double paidAmount; + /** + * 手续费 + */ + private Double fee; + /** + * 完成时间 + */ + private String completeTime; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayResult.java new file mode 100644 index 0000000..70b55ee --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/SinglePayResult.java @@ -0,0 +1,31 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 20:36 + */ +@Data +public class SinglePayResult { + /** + * 错误码 + */ + private String errorCode; + /** + * 错误描述 + */ + private String errorDesc; + /** + * 商户编号 + */ + private String userNo; + /** + * 商户订单号 + */ + private String merchantOrderNo; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayCallbackResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayCallbackResult.java new file mode 100644 index 0000000..cc84369 --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayCallbackResult.java @@ -0,0 +1,93 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 11:00 + */ +@Data +public class UniPayCallbackResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 交易币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 支付状态 + * 100:支付成功; + * 101:支付失败。 + */ + private String r6_Status; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行订单号 + */ + private String r8_BankOrderNo; + /** + * 银行流水号 + */ + private String r9_BankTrxNo; + /** + * 支付时间 + */ + private String ra_PayTime; + /** + * 交易结果通知时间 + */ + private String rb_DealTime; + /** + * 银行编码 + */ + private String rc_BankCode; + /** + * 用户标识 + */ + private String rd_OpenId; + /** + * 平台优惠金额 + */ + private Double re_DiscountAmount; + /** + * 卡类型 + */ + private String rh_cardType; + /** + * 订单手续费 + */ + private Double rj_Fee; + /** + * 交易类型 + */ + private String rk_FrpCode; + /** + * 签约 ID + */ + private String rl_ContractId; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayResult.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayResult.java new file mode 100644 index 0000000..a92ee3f --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/model/UniPayResult.java @@ -0,0 +1,77 @@ +package com.ruoyi.other.util.payment.model; + +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2024/12/30 10:47 + */ +@Data +public class UniPayResult { + /** + * 版本号 + */ + private Double r0_Version; + /** + * 商户编号 + */ + private String r1_MerchantNo; + /** + * 商户订单号 + */ + private String r2_OrderNo; + /** + * 支付金额 + */ + private Double r3_Amount; + /** + * 币种 + */ + private String r4_Cur; + /** + * 公用回传参数 + */ + private String r5_Mp; + /** + * 交易类型 + */ + private String r6_FrpCode; + /** + * 交易流水号 + */ + private String r7_TrxNo; + /** + * 银行商户编码 + */ + private String r8_MerchantBankCode; + /** + * 响应码,返回 100 时表示成功 + */ + private String ra_Code; + /** + * 响应码描述 + */ + private String rb_CodeMsg; + /** + * 1.主扫支付返回二维码地址。 + * 2.支付宝 H5,mode1/2/3 参考请求参数q9_TransactionModel 说明。 + * 3.微信 H5_PLUS,获取支付信息的 openlink,通过手机端浏览器跳转并唤起微信 APP客户端,直接打开对应的小程序进行支付。 + * 3.公众号支付:需要商户参考微信的官方文档 JSAPI 支付接口进行处理,详情请见:https://pay.weixin.qq.com/wiki/doc/api/index.html + * 4.微信小程序支付返回支付信息。 + * 5.支付宝收银台返回支付宝收银台跳转链接,通过请求该链接跳转至支付宝。 + * 6.微信 app3 支付,返回预支付信息,集成微信 SDK 唤起小程序进行支付。 + * 7.支付宝服务窗支付返回银联交易号 trade_no,可用以唤起支付宝 APP,调起支付宝APP 收银台。 + * 8.银联 app 或银联统一收银台支付,返回预支付信息用此网址的接口调起支付。https://open.unionpay.com/tjweb/acproduct/list?apiservId=450#nav02 + * 9.银联云微小程序返回跳转地址,格式:{“cqpMpAppId”:”云闪付小程序 id”,”cqpMpPath”:”云闪付小程序 path”} + * 10.其他类型支付返回支付信息。 + */ + private String rc_Result; + /** + * 二维码图片码 + */ + private String rd_Pic; + /** + * 签名数据 + */ + private String hmac; +} diff --git a/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/SaveWithdrawalAccount.java b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/SaveWithdrawalAccount.java new file mode 100644 index 0000000..a91eb6a --- /dev/null +++ b/ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/SaveWithdrawalAccount.java @@ -0,0 +1,24 @@ +package com.ruoyi.other.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author zhibing.pu + * @Date 2025/1/1 11:54 + */ +@Data +public class SaveWithdrawalAccount { + + @ApiModelProperty("收款银行卡号") + private String receiverAccountNoEnc; + + @ApiModelProperty("收款银行卡持卡人名称") + private String receiverNameEnc; + + @ApiModelProperty("账户类型(对私账户201,对公账户204)") + private Integer receiverAccountType; + + @ApiModelProperty("收款账户联行号") + private String receiverBankChannelNo; +} -- Gitblit v1.7.1