13个文件已修改
1个文件已删除
49个文件已添加
| | |
| | | @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") |
| | |
| | | @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) |
| | |
| | | @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; |
| | | |
| | | |
| | | |
| | |
| | | @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 = "历史余额") |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | private WithdrawalRequestsService withdrawalRequestsService; |
| | | @Resource |
| | | private AppUserService appUserService; |
| | | |
| | | @Resource |
| | | private BalanceChangeRecordService balanceChangeRecordService; |
| | | |
| | | /** |
| | | * 提现申请 |
| | |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | @PostMapping("/page") |
| | | @ApiOperation(value = "提现申请列表", tags = {"后台"}) |
| | | public R<IPage<WithdrawalRequests>> page(@RequestBody WithQuery withQuery){ |
| | |
| | | } |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @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("提现失败,可提现金额不足!"); |
| | | } |
| | | |
| | |
| | | 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 |
New file |
| | |
| | | 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"))); |
| | | } |
| | | } |
New file |
| | |
| | | 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)); |
| | | } |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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") |
| | |
| | | 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; |
| | |
| | | * @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(); |
| | | |
| | | } |
| | |
| | | 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; |
| | |
| | | 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 { |
| | | |
| | |
| | | |
| | | @Resource |
| | | private OrderBalancePaymentService orderBalancePaymentService; |
| | | |
| | | @Resource |
| | | private RedisTemplate redisTemplate; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | |
| | | for (MyShoppingCartVo myShoppingCartVo : goodsList) { |
| | | earnPoint += (myShoppingCartVo.getEarnSpendingPoints() * myShoppingCartVo.getNumber()); |
| | | } |
| | | if(null != shoppingCartPayment.getUserAddressId()){ |
| | | userAddressClient.getUserAddressById(shoppingCartPayment.getUserAddressId()).getData(); |
| | | } |
| | | |
| | | //获取快递策略,计算快递费 |
| | | BigDecimal expressFee = BigDecimal.ZERO; |
| | |
| | | //现金支付 |
| | | 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; |
| | |
| | | } |
| | | //积分支付 |
| | | 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; |
| | |
| | | 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(); |
| | |
| | | return R.ok(order.getId().toString()); |
| | | } |
| | | |
| | | |
| | | |
| | | public String getNumber(Integer size){ |
| | | String str = ""; |
| | | for (Integer i = 0; i < size; i++) { |
| | |
| | | } |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | 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; |
| | | |
| | |
| | | |
| | | @Resource |
| | | private CommissionService commissionService; |
| | | |
| | | @Resource |
| | | private ShoppingCartService shoppingCartService; |
| | | |
| | | |
| | | |
| | | |
| | | @Scheduled(fixedRate = 60000) |
| | | public void taskMonth() { |
| | | commissionService.calculationCommission(); |
| | | shoppingCartService.closeOrder(); |
| | | } |
| | | |
| | | // 每天晚上23:59:59执行的定时任务 |
New file |
| | |
| | | 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"))); |
| | | } |
| | | } |
New file |
| | |
| | | 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)); |
| | | } |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | |
| | | 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; |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | |
| | | private ShopService shopService; |
| | | @Resource |
| | | private TokenService tokenService; |
| | | |
| | | @Resource |
| | | private ShopBalanceStatementService shopBalanceStatementService; |
| | | |
| | | @Resource |
| | | private AppUserClient appUserClient; |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 提现申请列表 |
| | |
| | | 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); |
| | |
| | | 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(); |
| | | } |
| | | |
| | | |
| | |
| | | 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()); |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | ShopDetailVO getShopDetail(Integer shopId, BigDecimal longitude, BigDecimal latitude); |
| | | |
| | | Boolean cheUserByPhone(String phone); |
| | | |
| | | |
| | | /** |
| | | * 保存提现账户 |
| | | * @param saveWithdrawalAccount |
| | | */ |
| | | void saveWithdrawalAccount(SaveWithdrawalAccount saveWithdrawalAccount); |
| | | } |
| | |
| | | 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; |
| | |
| | | private AppUserClient appUserClient; |
| | | @Resource |
| | | private TokenService tokenService; |
| | | @Resource |
| | | private SysUserClient sysUserClient; |
| | | |
| | | |
| | | @Override |
| | |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | 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"))); |
| | | } |
| | | } |
New file |
| | |
| | | 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)); |
| | | } |
| | | } |
New file |
| | |
| | | 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()); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |
New file |
| | |
| | | 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; |
| | | } |