diff --git a/doc/123.txt b/doc/123.txt index 5b104170..9fd5742f 100644 --- a/doc/123.txt +++ b/doc/123.txt @@ -1,12 +1,9 @@ im权限 cai:user:im - - ALTER TABLE cai_consume_log ADD INDEX idx_consume_filter(type, status, one_user_id); - 菜单 - 单账户管理 101 proxyUser - 统计 0 proxyUserTotal cai/proxyUserTotal/index cai:proxyUser:proxyTotal diff --git a/doc/20251112.sql b/doc/20251112.sql new file mode 100644 index 00000000..ca0577b5 --- /dev/null +++ b/doc/20251112.sql @@ -0,0 +1,2 @@ +ALTER TABLE cai_pay_trd_config + ADD COLUMN `extend_data` JSON; diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/PayController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/PayController.java index 9f743af5..50c9e18b 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/PayController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/cai/app/PayController.java @@ -14,7 +14,6 @@ import com.ijpay.core.kit.HttpKit; import com.ijpay.core.kit.WxPayKit; import com.ijpay.wxpay.WxPayApi; import com.ijpay.wxpay.model.UnifiedOrderModel; -import com.ruoyi.cai.domain.OrderLogs; import com.ruoyi.cai.domain.PayConfig; import com.ruoyi.cai.domain.PayTrdConfig; import com.ruoyi.cai.dto.app.vo.pay.OrderPayStatusResp; @@ -24,24 +23,36 @@ import com.ruoyi.cai.pay.*; import com.ruoyi.cai.service.OrderLogsService; import com.ruoyi.cai.service.PayTrdConfigService; import com.ruoyi.cai.trdpay.TrdPayManager; -import com.ruoyi.cai.trdpay.TrdPayProperties; import com.ruoyi.cai.trdpay.TrdPayTypeEnum; +import com.ruoyi.cai.trdpay.V14Manager; import com.ruoyi.cai.trdpay.dto.NotifyResp; +import com.ruoyi.cai.trdpay.dto.extend.V14ExtendMapDTO; +import com.ruoyi.cai.trdpay.dto.v14.V14Token; +import com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI.WechatJSAPIResponse; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; -import com.sun.org.apache.bcel.internal.generic.RETURN; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; @@ -126,6 +137,82 @@ public class PayController { } } + @Autowired + private V14Manager v14Manager; + + @PostMapping(value = "/efps/wx") + @Operation(summary = "微信支付efps") + @Log(title = "微信支付efps", businessType = BusinessType.OTHER, isSaveDb = true) + @SaIgnore + public R efpsWx(HttpServletRequest request, @RequestBody V14PayDTO payDTO){ + String efpsToken = request.getHeader("Efps-Token"); + if(StringUtils.isBlank(efpsToken)){ + return R.fail(600,"支付失败,鉴权失败或者未找到订单"); + } + V14Token v14Token = v14Manager.checkToken(efpsToken); + if(v14Token == null){ + return R.fail(600,"支付失败,鉴权失败或者未找到订单"); + } + PayTrdConfig payTrdConfig = payTrdConfigService.getById(v14Token.getPayTrdConfigId()); + if(payTrdConfig == null){ + return R.fail(600,"支付失败,未找到支付通道信息"); + } + PayOrderInfoDTO payOrderInfo = payManager.getOrderInfo(v14Token.getOrderNo()); + if(payOrderInfo == null){ + return R.fail(600,"支付失败,未找到订单"); + } + try { + V14ExtendMapDTO extendMap = JSON.parseObject(payTrdConfig.getExtendData(), V14ExtendMapDTO.class); + String openId = getOpenIdByCode(payDTO.getWx_code(), extendMap.getMinAppId(), extendMap.getMinSecret()); + payOrderInfo.setOpenId(openId); + } catch (IOException e) { + log.error("获取openid失败",e); + return R.fail(600,"微信登录失败"); + } + WechatJSAPIResponse pay = v14Manager.pay(payOrderInfo, payTrdConfig); + return R.ok(pay); + } + + + public String getOpenIdByCode(String code,String appId,String secret) throws IOException { + String url = "https://api.weixin.qq.com/sns/jscode2session"; + url += "?appid="+appId;//自己的appid + url += "&secret="+secret;//自己的appSecret + url += "&js_code=" + code; + url += "&grant_type=authorization_code"; + url += "&connect_redirect=1"; + String res = null; + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + // DefaultHttpClient(); + HttpGet httpget = new HttpGet(url); //GET方式 + CloseableHttpResponse response = null; + // 配置信息 + RequestConfig requestConfig = RequestConfig.custom() // 设置连接超时时间(单位毫秒) + .setConnectTimeout(5000) // 设置请求超时时间(单位毫秒) + .setConnectionRequestTimeout(5000) // socket读写超时时间(单位毫秒) + .setSocketTimeout(5000) // 设置是否允许重定向(默认为true) + .setRedirectsEnabled(false).build(); // 将上面的配置信息 运用到这个Get请求里 + httpget.setConfig(requestConfig); // 由客户端执行(发送)Get请求 + response = httpClient.execute(httpget); // 从响应模型中获取响应实体 + HttpEntity responseEntity = response.getEntity(); + log.info("获取openId 响应状态为: {}", response.getStatusLine()); + if (responseEntity != null) { + res = EntityUtils.toString(responseEntity); + log.info("获取openId url:{}, res:{}", url,res); + } + // 释放资源 + if (httpClient != null) { + httpClient.close(); + } + if (response != null) { + response.close(); + } + JSONObject jo = JSON.parseObject(res); + return jo.getString("openid"); + } + + + @PostMapping(value = "/merge/ali") @Operation(summary = "支付宝聚合支付") @Log(title = "支付宝聚合支付", businessType = BusinessType.OTHER, isSaveDb = true) @@ -348,7 +435,7 @@ public class PayController { @Operation(hidden = true) @Log(title = "第三方微信支付回调", businessType = BusinessType.OTHER, isSaveDb = false) @SaIgnore - public String trdWxNotifyUrl(HttpServletRequest request,@PathVariable("type") String type) { + public String trdWxNotifyUrl(HttpServletRequest request, HttpServletResponse response, @PathVariable("type") String type) { try { if(type == null){ log.error("收到第三方微信支付回调,支付类型为空"); @@ -359,6 +446,22 @@ public class PayController { log.error("收到第三方微信支付回调,未找到支付类型 type={}",type); return "failure"; } + if(trdPayTypeEnum == TrdPayTypeEnum.V14){ + try { + JSONObject jsonObject = v14Manager.notifyDeal(request); + PrintWriter writer = response.getWriter(); + writer.print(jsonObject.toJSONString()); + writer.close(); + }catch (Exception e){ + log.error("v14微信支付回调异常!",e); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("returnCode", "0001"); + PrintWriter writer = response.getWriter(); + writer.print(jsonObject.toJSONString()); + writer.close(); + } + return null; + } Map params = AliPayApi.toMap(request); log.info("收到第三方微信支付回调 {}:{}",trdPayTypeEnum.name(), JSON.toJSONString(params)); NotifyResp notifyResp = trdPayManager.getNotifyResp(params, trdPayTypeEnum); diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index a567a265..202cd0d6 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -20,7 +20,7 @@ spring: driverClassName: com.mysql.cj.jdbc.Driver # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) - url: jdbc:mysql://124.222.254.188:4306/cai_wanyan?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true + url: jdbc:mysql://124.222.254.188:4306/cai_v6?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true username: root password: tyYrk487R4y7FENM # 从库数据源 diff --git a/ruoyi-admin/src/main/resources/v14/EFPS-PublicKey.cer b/ruoyi-admin/src/main/resources/v14/EFPS-PublicKey.cer new file mode 100644 index 00000000..03d623eb --- /dev/null +++ b/ruoyi-admin/src/main/resources/v14/EFPS-PublicKey.cer @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEIjCCAwqgAwIBAgIUJciVbai01yHdyTuMJ3UAOf6uWPQwDQYJKoZIhvcNAQEL +BQAwgZYxCzAJBgNVBAYTAkNOMTkwNwYDVQQKDDDljJfkuqzlpKnlqIHor5rkv6Hn +lLXlrZDllYbliqHmnI3liqHmnInpmZDlhazlj7gxFTATBgNVBAsMDOS8geS4muiv +geS5pjE1MDMGA1UEAwws5aSp5aiB6K+a5L+h5pWw5a2X6K6k6K+B5Lit5b+D5LyB +5Lia6K+B5LmmQ0EwHhcNMTgwMTExMDgwNjE4WhcNMTkwMTExMDgwNjE4WjCBjDE5 +MDcGA1UECgww5YyX5Lqs5aSp5aiB6K+a5L+h55S15a2Q5ZWG5Yqh5pyN5Yqh5pyJ +6ZmQ5YWs5Y+4MRgwFgYDVQQLDA/ov5Dnu7TmlK/mjIHpg6gxNTAzBgNVBAMMLOaY +k+elqOiBlOaUr+S7mOaciemZkOWFrOWPuC1FRlBT5ZWG5oi36Zeo5oi3MIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAptTghT1nY9twX4VKrvJigGB/klcs +P8tH+9aV5TofKskh8PQg8neTSprPMmEsidFGMJ26a4x/E88nJ6wMHAZqeWP0x64/ +GLtIff8r+WZjqBYP9WNiW71NcAsoJEBrlLlLrc5W/9mC+3z0oWYiz+LB9E+uMeCV +3ocHP42Z6oGeCV6jSe1Sx2UBsCEROYh1nTnZQ13pHIghqO1Fc5MZTcTW5k3zviuF +L7IJYUW79AIzh9SttcMbUZqGWkf808Ux3BhZzgUTL0g76fpCRjPilmlCJ/NdyGJ4 +8E0IfMXHeTq3uiEjnO2nzsEV4zvhNoii0F2iH+fgMXBDwGd9O3mLv23gIQIDAQAB +o3AwbjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDBUBgNVHR8ETTBLMEmgR6BFhkNo +dHRwOi8vdG9wY2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0kIAoK +JENBX1NFUklBTE5VTUJFUiQkMA0GCSqGSIb3DQEBCwUAA4IBAQCUEFIx+vOeOQi2 +ZWR8TnADKYYTNsqv52e9WBz8VKfjbzIDA/hXqJUvtylyyEL4pBn82xG0WMk9UaAE +EGrkpdA0figlbUInyXZE8WZYcE/7Nlr4aupH+JETp1OqvAsS0l5M2mH4OdVMDA/K +pdwTMBa0it7f3QA8k2lXalHlifi35jrKj2q3DFKvy2n9pcwblPX2jpC7pZ1Y66tq +7SapaGyCo12Q6o0vSCgGodFkZsczYmGdp4ZbphUbACVQ3Lhw/moqB3PadT6V0jB6 +4BDDHnkDkaoUI5XUr1BMmTAyy+chBLHzk83Dx9ioNHdF0rHpEyUD73GfV11R7BIy +zl5g8FpF +-----END CERTIFICATE----- diff --git a/ruoyi-admin/src/main/resources/v14/user-rsa.pfx b/ruoyi-admin/src/main/resources/v14/user-rsa.pfx new file mode 100644 index 00000000..baad59aa Binary files /dev/null and b/ruoyi-admin/src/main/resources/v14/user-rsa.pfx differ diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/PayTrdConfigController.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/PayTrdConfigController.java index 37e701db..6e864b61 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/PayTrdConfigController.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/PayTrdConfigController.java @@ -55,7 +55,9 @@ public class PayTrdConfigController extends BaseController { @GetMapping("/{id}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { - return R.ok(payTrdConfigService.getById(id)); + PayTrdConfig payTrdConfig = payTrdConfigService.getById(id); + payTrdConfig.refreshSelect(); + return R.ok(payTrdConfig); } /** @@ -66,6 +68,7 @@ public class PayTrdConfigController extends BaseController { @RepeatSubmit() @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody PayTrdConfig bo) { + bo.refreshUpdateOrSave(); boolean save = payTrdConfigService.save(bo); payTrdConfigService.resetPayTrdConfig(); return toAjax(save); @@ -89,6 +92,7 @@ public class PayTrdConfigController extends BaseController { @RepeatSubmit() @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody PayTrdConfig bo) { + bo.refreshUpdateOrSave(); boolean save = payTrdConfigService.updateById(bo); payTrdConfigService.resetPayTrdConfig(); return toAjax(save); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/PayTrdConfig.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/PayTrdConfig.java index 9136779b..97b61c1a 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/PayTrdConfig.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/domain/PayTrdConfig.java @@ -1,12 +1,19 @@ package com.ruoyi.cai.domain; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.ruoyi.common.utils.StringUtils; import lombok.Data; +import org.apache.commons.collections.map.HashedMap; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; /** * 四方支付配置对象 cai_pay_trd_config @@ -67,10 +74,28 @@ public class PayTrdConfig implements Serializable { */ private Integer deleteFlag; + private String extendData; + + @TableField(exist = false) + private Map extendMap; + private LocalDateTime createTime; public String getProductId(boolean wx){ return wx?wxProductId:aliProductId; } + public void refreshUpdateOrSave(){ + if(extendMap != null){ + this.extendData = JSON.toJSONString(extendMap); + } + } + + public void refreshSelect(){ + if(StringUtils.isNotBlank(extendData)){ + this.extendMap = JSON.parseObject(this.extendData,Map.class); + }else{ + this.extendMap = new HashMap<>(); + } + } } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/filter/EncryptionFilter.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/filter/EncryptionFilter.java index 2d085b90..8196212f 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/filter/EncryptionFilter.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/filter/EncryptionFilter.java @@ -27,6 +27,7 @@ public class EncryptionFilter implements Filter { IGNORE_URL.add("/api/pay/trd/notify/wx/*"); IGNORE_URL.add("/api/pay/trd/notify/ali/*"); IGNORE_URL.add("/api/pay/trd/notify/*"); + IGNORE_URL.add("/api/pay/efps/wx"); } private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher(); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayManager.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayManager.java index 0c15867b..c88f8639 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayManager.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayManager.java @@ -184,6 +184,7 @@ public class PayManager { dto.setOrderNo(vipOrder.getOrderNo()); dto.setAppid(vipOrder.getAppid()); dto.setPlatformType(vipOrder.getPlatformType()); + dto.setSubjectId(vipOrder.getVipId()+""); break; case RECHARGE_ORDER_SUB: RechargeOrder rechargeOrder = rechargeOrderService.getByOrderNo(orderNo); @@ -200,6 +201,7 @@ public class PayManager { dto.setOrderNo(rechargeOrder.getOrderNo()); dto.setAppid(rechargeOrder.getAppid()); dto.setPlatformType(rechargeOrder.getPlatformType()); + dto.setSubjectId(rechargeOrder.getRechargeId()+""); break; default: break; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java index 3f6e8394..62f06bba 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java @@ -9,15 +9,21 @@ import java.math.BigDecimal; public class PayOrderInfoDTO { private String body; private String subject; + private String subjectId; private BigDecimal price; private String orderNo; private String appid; private String platformType; + private String openId; public String getPriceFenStr(){ return NumberUtil.mul(price,100).longValue()+""; } + public Long getPriceFen(){ + return NumberUtil.mul(price,100).longValue(); + } + public String getPriceYuanStr(){ return price.toString(); } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayReturnResp.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayReturnResp.java index c9de0bc5..7e98876d 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayReturnResp.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayReturnResp.java @@ -1,15 +1,23 @@ package com.ruoyi.cai.pay; +import com.ruoyi.cai.trdpay.handle.v12new.utils.JacksonUtil; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data public class PayReturnResp { private String data; - @Schema(description = "H5,APP") + @Schema(description = "H5,APP,DESAN,EFPS") private String appType; private String payType; + public static PayReturnResp createEfps(Object data){ + PayReturnResp resp = new PayReturnResp(); + resp.setData(JacksonUtil.objToJson(data)); + resp.setAppType("EFPS"); + resp.setPayType("EFPS"); + return resp; + } public static PayReturnResp createDesan(String data){ PayReturnResp resp = new PayReturnResp(); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/V14PayDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/V14PayDTO.java new file mode 100644 index 00000000..472b7f78 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/V14PayDTO.java @@ -0,0 +1,9 @@ +package com.ruoyi.cai.pay; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +@Data +public class V14PayDTO { + private String wx_code; +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java index 2d405cac..09645ad5 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java @@ -135,7 +135,8 @@ public enum TrdPayTypeEnum { * 1016 * Wsz6yTi6AG5X6Cxt5Zt6rXKGKXitsX5I */ - V13("https://pp123.bghyvwk.cn","/mapi.php","/api.php","/api/pay/trd/notify/v12","success"), + V13("https://pp123.bghyvwk.cn","/mapi.php","/api.php","/api/pay/trd/notify/v13","success"), + V14("https://efps.epaylinks.cn","/mapi.php","/api.php","/api/pay/trd/notify/v14","success"), ; private final String gatewayUrl; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/V14Manager.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/V14Manager.java new file mode 100644 index 00000000..925c7f57 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/V14Manager.java @@ -0,0 +1,222 @@ +package com.ruoyi.cai.trdpay; + +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.RandomUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.cai.domain.PayTrdConfig; +import com.ruoyi.cai.enums.SystemConfigEnum; +import com.ruoyi.cai.manager.SystemConfigManager; +import com.ruoyi.cai.pay.PayManager; +import com.ruoyi.cai.pay.PayOrderInfoDTO; +import com.ruoyi.cai.pay.PayTypeEnum; +import com.ruoyi.cai.service.OrderLogsService; +import com.ruoyi.cai.service.PayTrdConfigService; +import com.ruoyi.cai.trdpay.dto.NotifyResp; +import com.ruoyi.cai.trdpay.dto.extend.V14ExtendMapDTO; +import com.ruoyi.cai.trdpay.dto.v14.NotifyContent; +import com.ruoyi.cai.trdpay.dto.v14.V14Token; +import com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI.WechatJSAPIRequest; +import com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI.WechatJSAPIResponse; +import com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI.WxJsOrderGoods; +import com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI.WxJsOrderInfo; +import com.ruoyi.cai.trdpay.handle.v12new.enums.AsymmetricTypeEnum; +import com.ruoyi.cai.trdpay.handle.v12new.utils.AESUtil; +import com.ruoyi.cai.trdpay.handle.v12new.utils.JacksonUtil; +import com.ruoyi.cai.trdpay.handle.v12new.utils.RSAUtil; +import com.ruoyi.cai.util.OkHttp3Util; +import com.ruoyi.cai.util.RestTemplateUtil; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.redisson.api.RBucket; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.math.BigDecimal; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class V14Manager { + + @Autowired + private SystemConfigManager systemConfigManager; + @Autowired + private PayTrdConfigService payTrdConfigService; + @Autowired + private OrderLogsService orderLogsService; + @Autowired + private RedissonClient redissonClient; + @Autowired + private PayManager payManager; + private static final String PUBLIC_KEY_PATH = "/home/server/api/sign/v14/EFPS-PublicKey.cer"; + + private boolean init = false; + private static PrivateKey merchantPrivateKey = null; + + + public synchronized void init(String privateKeyPath,String privateKeyPassword){ + if(!init){ + log.info("初始化ETSP SDK privateKeyPath:{} privateKeyPassword:{}", privateKeyPath, privateKeyPassword); + merchantPrivateKey = RSAUtil.getPrivateKeyByPath(privateKeyPath, privateKeyPassword); + init = true; + } + } + + public WechatJSAPIResponse pay(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig){ + String extendData = payTrdConfig.getExtendData(); + if(StringUtils.isBlank(extendData)){ + throw new ServiceException("参数异常"); + } + V14ExtendMapDTO extendMap = JSON.parseObject(extendData, V14ExtendMapDTO.class); + String privateKeyPath = extendMap.getPrivateKeyPath(); + String privateKeyPassword = extendMap.getPrivateKeyPassword(); + this.init(privateKeyPath, privateKeyPassword); + TrdPayTypeEnum type = TrdPayTypeEnum.V14; + String version = "3.0"; // 版本号 + String outTradeNo = payOrderInfoDTO.getOrderNo(); // 交易编号,商户侧唯一 + String customerCode = payTrdConfig.getMchId(); + long payAmount = payOrderInfoDTO.getPriceFen(); // 支付金额,分为单位 + String payCurrency = "CNY"; // 币种,写死 + String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), true); + String transactionStartTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); // 交易开始时间 +// String transactionEndTime = ""; // 交易结束时间 + WxJsOrderInfo orderInfo = new WxJsOrderInfo(); + orderInfo.setId(payOrderInfoDTO.getSubjectId()); + orderInfo.setBusinessType("100007"); + orderInfo.addGood(new WxJsOrderGoods(payOrderInfoDTO.getSubject(), payOrderInfoDTO.getBody(), payOrderInfoDTO.getPriceFen()));// 填写真实的订单信息如new + WechatJSAPIRequest order = new WechatJSAPIRequest(outTradeNo, customerCode, orderInfo, payAmount, payCurrency, + notifyUrl, transactionStartTime, null); + order.setAppId(extendMap.getMinAppId()); + order.setOpenId(payOrderInfoDTO.getOpenId()); + order.setNonceStr(RandomUtil.randomString(16)); + order.setPayMethod("35"); + order.setVersion(version); + order.setChannelMchtNo(extendMap.getLastMchId()); + order.setAreaInfo("420100"); + Map header = new HashMap<>(); + header.put("x-efps-sign-no",extendMap.getSignNo()); + header.put("x-efps-sign-type","SHA256withRSA"); + String requestJson = JacksonUtil.objToJson(order); + String sign = RSAUtil.sign(requestJson, merchantPrivateKey, AsymmetricTypeEnum.RSA); + header.put("x-efps-sign",sign); + header.put("x-efps-timestamp",LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); + String gatewayUrl = getGatewayUrl(payTrdConfig); + String createOrderUrl = gatewayUrl + "/api/txs/pay/WxJSAPIPayment"; + String body = RestTemplateUtil.postJsonData(createOrderUrl, JacksonUtil.objToJson(order), header); + log.info("v14调用支付日志 url:{} body:{} header:{} response:{}", createOrderUrl,JacksonUtil.objToJson(order),JacksonUtil.objToJson(header),body); + WechatJSAPIResponse response = JacksonUtil.jsonToObj(body, WechatJSAPIResponse.class); + if(!response.isSuccess()){ + orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl, + JacksonUtil.objToJson(order), body, false, type, "四方微信支付"); + throw new ServiceException("调用支付失败"); + } + orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl, + JacksonUtil.objToJson(order), body, true, type, "四方微信支付"); + return response; + } + + public static void main(String[] args) { + V14Manager v14Manager = new V14Manager(); + PayOrderInfoDTO payOrderInfoDTO = new PayOrderInfoDTO(); + payOrderInfoDTO.setBody("15钻石"); + payOrderInfoDTO.setSubject("15钻石"); + payOrderInfoDTO.setSubjectId("1"); + payOrderInfoDTO.setPrice(BigDecimal.valueOf(200)); + payOrderInfoDTO.setOrderNo("OJSKDJKASDLWBJ22EQKWL123"); + payOrderInfoDTO.setOpenId("wx0ca162500b16e82321"); + PayTrdConfig config = new PayTrdConfig(); + config.setNotifyUrl("https://wewqw.wwiwi.com/ds/asd"); + v14Manager.pay(payOrderInfoDTO,config); + } + + + public String getGatewayUrl(PayTrdConfig payTrdConfig){ + TrdPayTypeEnum type = TrdPayTypeEnum.V14; + String gatewayUrl = type.getGatewayUrl(); + if (StringUtils.isNotBlank(payTrdConfig.getGatewayUrl())) { + gatewayUrl = payTrdConfig.getGatewayUrl(); + } + return gatewayUrl; + } + + public String createToken(String orderNo,Long payTrdConfigId,String appId,String secret){ + V14Token token = new V14Token(); + token.setPayTrdConfigId(payTrdConfigId); + token.setOrderNo(orderNo); + token.setAppId(appId); + token.setSecret(secret); + String uuid = UUID.randomUUID().toString(); + RBucket bucket = redissonClient.getBucket(uuid); + bucket.set(token, 40, TimeUnit.MINUTES); + return uuid; + } + + public V14Token checkToken(String token){ + RBucket bucket = redissonClient.getBucket(token); + return bucket.get(); + } + + + public JSONObject notifyDeal(HttpServletRequest request) throws IOException { + String sign = request.getHeader("x-efps-sign"); + BufferedReader br = request.getReader(); + String str = ""; + StringBuilder wholeStr = new StringBuilder(); + while ((str = br.readLine()) != null) { + wholeStr.append(str); + } + String body = wholeStr.toString(); + log.info("收到第三方微信支付回调 sign={} {}:{}","V14", sign, wholeStr); + JSONObject jsonObject = new JSONObject(); + if (StringUtils.isNotBlank(body) && StringUtils.isNotBlank(sign)) { + boolean verifySign = RSAUtil.verifySign(body, sign, RSAUtil.getPublicKeyByPath(PUBLIC_KEY_PATH), AsymmetricTypeEnum.RSA); + if(!verifySign) { + log.info("v14回调处理失败 验签失败! body={} sign={}", body, sign); + jsonObject.put("returnCode", "0001"); + return jsonObject; + } + NotifyContent notify = JSONObject.parseObject(body, NotifyContent.class); + String payState = notify.getPayState(); + NotifyResp notifyResp = new NotifyResp(); + notifyResp.setOrderNo(notify.getOutTradeNo()); // 商户订单号 + notifyResp.setTrdOrderNo(notify.getTransactionNo()); // 平台订单号 + notifyResp.setPayTypeEnum(TrdPayTypeEnum.V14); + notifyResp.setSuccess("00".equals(payState)); + if(notifyResp.isSuccess()){ + JSONObject parse = JSONObject.parse(body); + Map javaObject = JsonToMap(parse); + payManager.callBack(notifyResp.getOrderNo(),notifyResp.getTrdOrderNo(),javaObject,notifyResp.getPayTypeEnum().name(), PayTypeEnum.WX); + } + jsonObject.put("returnCode", "0000"); + jsonObject.put("returnMsg", ""); + return jsonObject; + } + jsonObject.put("returnCode", "0001"); + return jsonObject; + } + + public static Map JsonToMap(JSONObject j){ + Map map = new HashMap<>(); + Set strings = j.keySet(); + for (String key : strings) { + Object o = j.get(key); + if(o instanceof String){ + map.put(key,(String)o); + } + } + return map; + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/extend/V14ExtendMapDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/extend/V14ExtendMapDTO.java new file mode 100644 index 00000000..fe3af6dc --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/extend/V14ExtendMapDTO.java @@ -0,0 +1,15 @@ +package com.ruoyi.cai.trdpay.dto.extend; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class V14ExtendMapDTO implements Serializable { + private String minAppId; + private String minSecret; + private String signNo; + private String lastMchId; + private String privateKeyPath; + private String privateKeyPassword; +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/NotifyContent.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/NotifyContent.java new file mode 100644 index 00000000..d556deb4 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/NotifyContent.java @@ -0,0 +1,125 @@ +package com.ruoyi.cai.trdpay.dto.v14; + +import java.util.UUID; + +public class NotifyContent { + + private String customerCode; + private String outTradeNo; + private String transactionNo; + private String amount; + private String payState; + private String payTime; + private String settCycle; + private String settCycleInterval; + private String procedureFee; + private String attachData; + private String nonceStr = UUID.randomUUID().toString().replaceAll("-", ""); + + public NotifyContent() { + } + + public NotifyContent(String customerCode, String outTradeNo, String transactionNo, + String amount, String payState, String payTime, String settCycle, + String settCycleInterval, String procedureFee, String attachData, String nonceStr) { + this.customerCode = customerCode; + this.outTradeNo = outTradeNo; + this.transactionNo = transactionNo; + this.amount = amount; + this.payState = payState; + this.payTime = payTime; + this.settCycle = settCycle; + this.settCycleInterval = settCycleInterval; + this.procedureFee = procedureFee; + this.attachData = attachData; + this.nonceStr = nonceStr; + } + + public String getCustomerCode() { + return customerCode; + } + + public void setCustomerCode(String customerCode) { + this.customerCode = customerCode; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getTransactionNo() { + return transactionNo; + } + + public void setTransactionNo(String transactionNo) { + this.transactionNo = transactionNo; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getPayState() { + return payState; + } + + public void setPayState(String payState) { + this.payState = payState; + } + + public String getPayTime() { + return payTime; + } + + public void setPayTime(String payTime) { + this.payTime = payTime; + } + + public String getSettCycle() { + return settCycle; + } + + public void setSettCycle(String settCycle) { + this.settCycle = settCycle; + } + + public String getSettCycleInterval() { + return settCycleInterval; + } + + public void setSettCycleInterval(String settCycleInterval) { + this.settCycleInterval = settCycleInterval; + } + + public String getProcedureFee() { + return procedureFee; + } + + public void setProcedureFee(String procedureFee) { + this.procedureFee = procedureFee; + } + + public String getAttachData() { + return attachData; + } + + public void setAttachData(String attachData) { + this.attachData = attachData; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/Response.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/Response.java new file mode 100644 index 00000000..e24cde08 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/Response.java @@ -0,0 +1,53 @@ +package com.ruoyi.cai.trdpay.dto.v14; + +/** + * @Author : Author + * @Date : 2018/1/29 14:48 + * @Description : 下单返回参数封装 + */ +public class Response { + + /** + * 返回状态码; 0000:处理成功; 0000:处理成功 + */ + private String returnCode; + /** + * 信息 + */ + private String returnMsg; + /** + * 打印控制台信息。注意:此属性仅用于页面显示调试信息,实际交易中并不存在 + */ + private String consoleMsg; + + public boolean isSuccess(){ + return "0000".equals(returnCode); + } + + public String getReturnCode() { + return returnCode; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public String getReturnMsg() { + return returnMsg; + } + + public void setReturnMsg(String returnMsg) { + this.returnMsg = returnMsg; + } + + public String getConsoleMsg() { + return consoleMsg; + } + + public void setConsoleMsg(String consoleMsg) { + // this.consoleMsg = consoleMsg; + } + + + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14PayResp.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14PayResp.java new file mode 100644 index 00000000..15baa0fb --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14PayResp.java @@ -0,0 +1,15 @@ +package com.ruoyi.cai.trdpay.dto.v14; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +public class V14PayResp implements Serializable { + private String payToken; + private String orderNo; + private BigDecimal price; + private String subject; + private String sourceMinId = "gh_e126be743731"; +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14Token.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14Token.java new file mode 100644 index 00000000..3af59615 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/V14Token.java @@ -0,0 +1,11 @@ +package com.ruoyi.cai.trdpay.dto.v14; + +import lombok.Data; + +@Data +public class V14Token { + private String orderNo; + private Long payTrdConfigId; + private String appId; + private String secret; +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/cashierPay/TerminalInfo.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/cashierPay/TerminalInfo.java new file mode 100644 index 00000000..1b184e22 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/cashierPay/TerminalInfo.java @@ -0,0 +1,180 @@ +package com.ruoyi.cai.trdpay.dto.v14.cashierPay; + +public class TerminalInfo { + private String location; + private String network_license; + private String terminal_type; + private String serial_num; + private String terminal_id;//终端设备号 + private String encrypt_rand_num; + private String secret_text; + private String app_version; + private String terminal_ip; + private String mobile_country_cd; + private String mobile_net_num; + private String icc_id; + private String location_cd1; + private String lbs_num1; + private String lbs_signal1; + private String location_cd2; + private String lbs_num2; + private String lbs_signal2; + private String location_cd3; + private String lbs_num3; + private String lbs_signal3; + private String telecom_sys_id; + private String telecom_net_id; + private String telecom_lbs; + private String telecom_lbs_signal; + public String getLocation() { + return location; + } + public void setLocation(String location) { + this.location = location; + } + public String getNetwork_license() { + return network_license; + } + public void setNetwork_license(String network_license) { + this.network_license = network_license; + } + public String getTerminal_type() { + return terminal_type; + } + public void setTerminal_type(String terminal_type) { + this.terminal_type = terminal_type; + } + public String getSerial_num() { + return serial_num; + } + public void setSerial_num(String serial_num) { + this.serial_num = serial_num; + } + public String getTerminal_id() { + return terminal_id; + } + public void setTerminal_id(String terminal_id) { + this.terminal_id = terminal_id; + } + public String getEncrypt_rand_num() { + return encrypt_rand_num; + } + public void setEncrypt_rand_num(String encrypt_rand_num) { + this.encrypt_rand_num = encrypt_rand_num; + } + public String getSecret_text() { + return secret_text; + } + public void setSecret_text(String secret_text) { + this.secret_text = secret_text; + } + public String getApp_version() { + return app_version; + } + public void setApp_version(String app_version) { + this.app_version = app_version; + } + public String getTerminal_ip() { + return terminal_ip; + } + public void setTerminal_ip(String terminal_ip) { + this.terminal_ip = terminal_ip; + } + public String getMobile_country_cd() { + return mobile_country_cd; + } + public void setMobile_country_cd(String mobile_country_cd) { + this.mobile_country_cd = mobile_country_cd; + } + public String getMobile_net_num() { + return mobile_net_num; + } + public void setMobile_net_num(String mobile_net_num) { + this.mobile_net_num = mobile_net_num; + } + public String getIcc_id() { + return icc_id; + } + public void setIcc_id(String icc_id) { + this.icc_id = icc_id; + } + public String getLocation_cd1() { + return location_cd1; + } + public void setLocation_cd1(String location_cd1) { + this.location_cd1 = location_cd1; + } + public String getLbs_num1() { + return lbs_num1; + } + public void setLbs_num1(String lbs_num1) { + this.lbs_num1 = lbs_num1; + } + public String getLbs_signal1() { + return lbs_signal1; + } + public void setLbs_signal1(String lbs_signal1) { + this.lbs_signal1 = lbs_signal1; + } + public String getLocation_cd2() { + return location_cd2; + } + public void setLocation_cd2(String location_cd2) { + this.location_cd2 = location_cd2; + } + public String getLbs_num2() { + return lbs_num2; + } + public void setLbs_num2(String lbs_num2) { + this.lbs_num2 = lbs_num2; + } + public String getLbs_signal2() { + return lbs_signal2; + } + public void setLbs_signal2(String lbs_signal2) { + this.lbs_signal2 = lbs_signal2; + } + public String getLocation_cd3() { + return location_cd3; + } + public void setLocation_cd3(String location_cd3) { + this.location_cd3 = location_cd3; + } + public String getLbs_num3() { + return lbs_num3; + } + public void setLbs_num3(String lbs_num3) { + this.lbs_num3 = lbs_num3; + } + public String getLbs_signal3() { + return lbs_signal3; + } + public void setLbs_signal3(String lbs_signal3) { + this.lbs_signal3 = lbs_signal3; + } + public String getTelecom_sys_id() { + return telecom_sys_id; + } + public void setTelecom_sys_id(String telecom_sys_id) { + this.telecom_sys_id = telecom_sys_id; + } + public String getTelecom_net_id() { + return telecom_net_id; + } + public void setTelecom_net_id(String telecom_net_id) { + this.telecom_net_id = telecom_net_id; + } + public String getTelecom_lbs() { + return telecom_lbs; + } + public void setTelecom_lbs(String telecom_lbs) { + this.telecom_lbs = telecom_lbs; + } + public String getTelecom_lbs_signal() { + return telecom_lbs_signal; + } + public void setTelecom_lbs_signal(String telecom_lbs_signal) { + this.telecom_lbs_signal = telecom_lbs_signal; + } + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/split/SplitInfo.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/split/SplitInfo.java new file mode 100644 index 00000000..47a8bcc6 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/split/SplitInfo.java @@ -0,0 +1,48 @@ +package com.ruoyi.cai.trdpay.dto.v14.split; + +/** + * @Author : Author + * @Date : 2018/2/6 17:18 + */ +public class SplitInfo { + + private String customerCode;//分账目标商户在EFPS的客户编码 + private long amount;//分账金额,单位分 + private Integer isProcedureCustomer;//在分账中必填,且保证只有一个客户的该值为1。在包含分账信息的退款中不填。 + + public SplitInfo() { } + + public SplitInfo(String customerCode, long amount) { + this.customerCode = customerCode; + this.amount = amount; + } + + public SplitInfo(String customerCode, long amount, Integer isProcedureCustomer) { + this.customerCode = customerCode; + this.amount = amount; + this.isProcedureCustomer = isProcedureCustomer; + } + + public String getCustomerCode() { + return customerCode; + } + public void setCustomerCode(String customerCode) { + this.customerCode = customerCode; + } + public Long getAmount() { + return amount; + } + public void setAmount(Long amount) { + this.amount = amount; + } + public Integer getIsProcedureCustomer() { + return isProcedureCustomer; + } + public void setIsProcedureCustomer(Integer isProcedureCustomer) { + this.isProcedureCustomer = isProcedureCustomer; + } + +} + + + diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIRequest.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIRequest.java new file mode 100644 index 00000000..e78c3674 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIRequest.java @@ -0,0 +1,263 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; + + +import com.ruoyi.cai.trdpay.dto.v14.cashierPay.TerminalInfo; +import com.ruoyi.cai.trdpay.dto.v14.split.SplitInfo; + +import java.util.List; + +/** + * @Author : Author + * @Date : 2018/1/29 11:27 + * @Description : + */ +public class WechatJSAPIRequest { + + private String outTradeNo; + private String customerCode; + private String terminalCode; + private String clientIp; + private WxJsOrderInfo orderInfo; + private long payAmount; + private String payCurrency; + private String version; + private String notifyUrl; + private String attachData; + private String transactionStartTime; + private String transactionEndTime; + private String nonceStr ; + private String appId; + private String openId; + private Boolean needSplit; + private List splitInfoList; + private String splitNotifyUrl; + private String splitAttachData; + + private String rechargeMemCustCode; + + private Boolean noCreditCards; + private String payMethod; + private String channelMchtNo; + + public String getChannelMchtNo() { + return channelMchtNo; + } + + public void setChannelMchtNo(String channelMchtNo) { + this.channelMchtNo = channelMchtNo; + } + + public String getPayMethod() { + return payMethod; + } + + public void setPayMethod(String payMethod) { + this.payMethod = payMethod; + } + + private TerminalInfo terminalInfo; + private String areaInfo; + public String getAreaInfo() { + return areaInfo; + } + + public void setAreaInfo(String areaInfo) { + this.areaInfo = areaInfo; + } + + public TerminalInfo getTerminalInfo() { + return terminalInfo; + } + + public void setTerminalInfo(TerminalInfo terminalInfo) { + this.terminalInfo = terminalInfo; + } + public WechatJSAPIRequest() { + } + + public WechatJSAPIRequest(String outTradeNo, String customerCode, + WxJsOrderInfo orderInfo, long payAmount, String payCurrency, String notifyUrl, + String transactionStartTime, String transactionEndTime) { + + this.outTradeNo = outTradeNo; + this.customerCode = customerCode; + + this.orderInfo = orderInfo; + this.payAmount = payAmount; + this.payCurrency = payCurrency; + this.notifyUrl = notifyUrl; + + this.transactionStartTime = transactionStartTime; + this.transactionEndTime = transactionEndTime; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getCustomerCode() { + return customerCode; + } + + public void setCustomerCode(String customerCode) { + this.customerCode = customerCode; + } + + + public String getTerminalCode() { + return terminalCode; + } + + public void setTerminalCode(String terminalCode) { + this.terminalCode = terminalCode; + } + + public String getClientIp() { + return clientIp; + } + + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } + + public WxJsOrderInfo getOrderInfo() { + return orderInfo; + } + + public void setOrderInfo(WxJsOrderInfo orderInfo) { + this.orderInfo = orderInfo; + } + + public long getPayAmount() { + return payAmount; + } + + public void setPayAmount(long payAmount) { + this.payAmount = payAmount; + } + + public String getPayCurrency() { + return payCurrency; + } + + public void setPayCurrency(String payCurrency) { + this.payCurrency = payCurrency; + } + + public String getVersion() { + return version; + } + + public void setVersion(String Version) { + this.version = Version; + } + + public String getNotifyUrl() { + return notifyUrl; + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } + + public String getAttachData() { + return attachData; + } + + public void setAttachData(String attachData) { + this.attachData = attachData; + } + + public String getTransactionStartTime() { + return transactionStartTime; + } + + public void setTransactionStartTime(String transactionStartTime) { + this.transactionStartTime = transactionStartTime; + } + + public String getTransactionEndTime() { + return transactionEndTime; + } + + public void setTransactionEndTime(String transactionEndTime) { + this.transactionEndTime = transactionEndTime; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public Boolean getNeedSplit() { + return needSplit; + } + + public void setNeedSplit(Boolean needSplit) { + this.needSplit = needSplit; + } + + public List getSplitInfoList() { + return splitInfoList; + } + + public void setSplitInfoList(List splitInfoList) { + this.splitInfoList = splitInfoList; + } + + public String getSplitNotifyUrl() { + return splitNotifyUrl; + } + + public void setSplitNotifyUrl(String splitNotifyUrl) { + this.splitNotifyUrl = splitNotifyUrl; + } + + public String getSplitAttachData() { + return splitAttachData; + } + + public void setSplitAttachData(String splitAttachData) { + this.splitAttachData = splitAttachData; + } + + public String getRechargeMemCustCode() { + return rechargeMemCustCode; + } + + public void setRechargeMemCustCode(String rechargeMemCustCode) { + this.rechargeMemCustCode = rechargeMemCustCode; + } + + public Boolean getNoCreditCards() { + return noCreditCards; + } + + public void setNoCreditCards(Boolean noCreditCards) { + this.noCreditCards = noCreditCards; + } + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIResponse.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIResponse.java new file mode 100644 index 00000000..e33e1979 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WechatJSAPIResponse.java @@ -0,0 +1,60 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.cai.trdpay.dto.v14.Response; + +import java.util.List; + +public class WechatJSAPIResponse extends Response { + + private WxJsapiParam wxJsapiParam; + + private String outTradeNo; + + private Long amount; + + private String nonceStr; + + private List couponDetails; + + public WxJsapiParam getWxJsapiParam() { + return this.wxJsapiParam; + } + + public void setWxJsapiParam(WxJsapiParam wxJsapiParam) { + this.wxJsapiParam = wxJsapiParam; + } + + public String getOutTradeNo() { + return this.outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public Long getAmount() { + return this.amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public String getNonceStr() { + return this.nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + public List getCouponDetails() { + return this.couponDetails; + } + + public void setCouponDetails(List couponDetails) { + this.couponDetails = couponDetails; + } + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderGoods.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderGoods.java new file mode 100644 index 00000000..e1bec527 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderGoods.java @@ -0,0 +1,22 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; + +public class WxJsOrderGoods { + private String name; + private String number; + private long amount; + + public WxJsOrderGoods(){ } + + public WxJsOrderGoods(String name, String number, long amount) { + this.setName(name); + this.setNumber(number); + this.setAmount(amount); + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getNumber() { return number; } + public void setNumber(String number) { this.number = number; } + public long getAmount() { return amount; } + public void setAmount(long amount) { this.amount = amount; } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderInfo.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderInfo.java new file mode 100644 index 00000000..1db4fc04 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderInfo.java @@ -0,0 +1,17 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; +import java.util.ArrayList; +import java.util.List; + +public class WxJsOrderInfo { + private String id; + private String businessType; + private List goodsList = new ArrayList(10); + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + public String getBusinessType() { return businessType; } + public void setBusinessType(String businessType) { this.businessType = businessType; } + public List getGoodsList() { return goodsList; } + public void setGoodsList(List list) { this.goodsList = list; } + public void addGood(WxJsOrderGoods good) { this.goodsList.add(good); } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderNotify.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderNotify.java new file mode 100644 index 00000000..ce1977a8 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsOrderNotify.java @@ -0,0 +1,111 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; + +import java.util.UUID; + +/** + * @Author : Author + * @Date : 2018/1/29 11:27 + * @Description : + */ +public class WxJsOrderNotify { + + private String customerCode ;//商户在EFPS的编号 + private String outTradeNo ;//商户订单号 + private String transactionNo ;//EFPS订单号 + private Long amount ;//支付金额 + private String payState ;//支付结果 + private String payTime ;//支付完成时间 + private String settCycle ;//该支付交易所属的清算周期 + private Long settCycleInterval ;//清算周期长度 + private Long procedureFee ;//手续费 + private String attachData ;//附加数据 + private String nonceStr = UUID.randomUUID().toString().replaceAll("-", ""); + + public String getCustomerCode() { + return customerCode; + } + + public void setCustomerCode(String customerCode) { + this.customerCode = customerCode; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getTransactionNo() { + return transactionNo; + } + + public void setTransactionNo(String transactionNo) { + this.transactionNo = transactionNo; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public String getPayState() { + return payState; + } + + public void setPayState(String payState) { + this.payState = payState; + } + + public String getPayTime() { + return payTime; + } + + public void setPayTime(String payTime) { + this.payTime = payTime; + } + + public String getSettCycle() { + return settCycle; + } + + public void setSettCycle(String settCycle) { + this.settCycle = settCycle; + } + + public Long getSettCycleInterval() { + return settCycleInterval; + } + + public void setSettCycleInterval(Long settCycleInterval) { + this.settCycleInterval = settCycleInterval; + } + + public Long getProcedureFee() { + return procedureFee; + } + + public void setProcedureFee(Long procedureFee) { + this.procedureFee = procedureFee; + } + + public String getAttachData() { + return attachData; + } + + public void setAttachData(String attachData) { + this.attachData = attachData; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsapiParam.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsapiParam.java new file mode 100644 index 00000000..02c7dad9 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/dto/v14/wechatJSAPI/WxJsapiParam.java @@ -0,0 +1,82 @@ +package com.ruoyi.cai.trdpay.dto.v14.wechatJSAPI; +/* */ +/* */ import com.alibaba.fastjson.annotation.JSONField; +/* */ +/* */ public class WxJsapiParam { +/* */ private String appId; +/* */ +/* */ private String timeStamp; +/* */ +/* */ private String nonceStr; +/* */ +/* */ private String signType; +/* */ +/* */ private String wxPackage; +/* */ +/* */ @JSONField(name = "package") +/* */ private String Package; +/* */ +/* */ private String paySign; +/* */ +/* */ public String getAppId() { +/* 20 */ return this.appId; +/* */ } +/* */ +/* */ public void setAppId(String appId) { +/* 23 */ this.appId = appId; +/* */ } +/* */ +/* */ public String getTimeStamp() { +/* 26 */ return this.timeStamp; +/* */ } +/* */ +/* */ public void setTimeStamp(String timeStamp) { +/* 29 */ this.timeStamp = timeStamp; +/* */ } +/* */ +/* */ public String getNonceStr() { +/* 32 */ return this.nonceStr; +/* */ } +/* */ +/* */ public void setNonceStr(String nonceStr) { +/* 35 */ this.nonceStr = nonceStr; +/* */ } +/* */ +/* */ public String getSignType() { +/* 38 */ return this.signType; +/* */ } +/* */ +/* */ public void setSignType(String signType) { +/* 41 */ this.signType = signType; +/* */ } +/* */ +/* */ public String getWxPackage() { +/* 44 */ return this.wxPackage; +/* */ } +/* */ +/* */ public void setWxPackage(String wxPackage) { +/* 47 */ this.wxPackage = wxPackage; +/* */ } +/* */ +/* */ public String getPaySign() { +/* 50 */ return this.paySign; +/* */ } +/* */ +/* */ public void setPaySign(String paySign) { +/* 53 */ this.paySign = paySign; +/* */ } +/* */ +/* */ public String getPackage() { +/* 57 */ return this.Package; +/* */ } +/* */ +/* */ public void setPackage(String aPackage) { +/* 61 */ this.Package = aPackage; +/* */ } +/* */ } + + +/* Location: D:\1H\更新联调\TXS-6.10.18-3\BOOT-INF\classes\!\com\epaylinks\efps\txs\transaction\domain\wxJSAPI\WxJsapiParam.class + * Java compiler version: 8 (52.0) + * JD-Core Version: 1.1.3 + */ diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV14Service.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV14Service.java new file mode 100644 index 00000000..ef48b59d --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV14Service.java @@ -0,0 +1,60 @@ +package com.ruoyi.cai.trdpay.handle; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.cai.domain.PayTrdConfig; +import com.ruoyi.cai.pay.PayOrderInfoDTO; +import com.ruoyi.cai.pay.PayReturnResp; +import com.ruoyi.cai.trdpay.PayTrdService; +import com.ruoyi.cai.trdpay.TrdPayTypeEnum; +import com.ruoyi.cai.trdpay.V14Manager; +import com.ruoyi.cai.trdpay.dto.NotifyResp; +import com.ruoyi.cai.trdpay.dto.extend.V14ExtendMapDTO; +import com.ruoyi.cai.trdpay.dto.v14.V14PayResp; +import com.ruoyi.cai.trdpay.handle.v12new.utils.JacksonUtil; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +@Slf4j +public class PayTrdV14Service implements PayTrdService { + + @Autowired + private V14Manager v14Manager; + + @Override + public TrdPayTypeEnum getType() { + return TrdPayTypeEnum.V14; + } + @Override + public PayReturnResp createOrderAli(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig, boolean wx) { + V14PayResp v14PayResp = new V14PayResp(); + String extendData = payTrdConfig.getExtendData(); + V14ExtendMapDTO extendMap = JSON.parseObject(extendData, V14ExtendMapDTO.class); + v14PayResp.setPayToken(v14Manager.createToken(payOrderInfoDTO.getOrderNo(), payTrdConfig.getId(),extendMap.getMinAppId(),extendMap.getMinSecret())); + v14PayResp.setPrice(payOrderInfoDTO.getPrice()); + v14PayResp.setSubject(payOrderInfoDTO.getSubject()); + v14PayResp.setOrderNo(payOrderInfoDTO.getOrderNo()); + return PayReturnResp.createEfps(v14PayResp); + } + + @Override + public NotifyResp getNotifyResp(Map sourceData) { + throw new ServiceException("V14回调不走这里"); + } + + @Override + public JSONObject queryOrder(String orderNo, PayTrdConfig payTrdConfig) { + throw new ServiceException("V14不支持订单查询"); + } + + @Override + public JSONObject resetOrder(String orderNo, PayTrdConfig payTrdConfig, boolean updateData) { + throw new ServiceException("V14不支持订单查询"); + } + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/OkHttp3Util.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/OkHttp3Util.java new file mode 100644 index 00000000..815036cf --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/OkHttp3Util.java @@ -0,0 +1,60 @@ +package com.ruoyi.cai.util; + +import com.ruoyi.cai.trdpay.handle.v12new.utils.JacksonUtil; +import com.squareup.okhttp.logging.HttpLoggingInterceptor; +import okhttp3.*; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class OkHttp3Util { + /** + * 发送 JSON 格式的 POST 请求 + * @param url 请求地址 + * @param object 请求体对象(将被序列化为 JSON) + * @param headers 自定义请求头(可为 null) + * @return 响应体字符串 + * @throws IOException 当请求失败或序列化出错时抛出 + */ + public static String postJsonData(String url, Object object, Map headers) throws IOException { + // 1. 将请求对象序列化为 JSON 字符串 + String jsonBody = JacksonUtil.objToJson(object); + + // 2. 构建 JSON 类型的请求体 + RequestBody requestBody = RequestBody.create(jsonBody, MediaType.parse("application/json; charset=utf-8")); + // 3. 构建请求 builder + Request.Builder requestBuilder = new Request.Builder() + .url(url) + .post(requestBody); // 设置 POST 方法和请求体 + // 4. 添加自定义请求头(若有) + if (headers != null && !headers.isEmpty()) { + for (Map.Entry entry : headers.entrySet()) { + requestBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + // 5. 发送同步请求并获取响应 + try (Response response = OK_HTTP_CLIENT.newCall(requestBuilder.build()).execute()) { + // 检查响应状态(200-299 为成功) + if (!response.isSuccessful()) { + throw new IOException("请求失败: 状态码=" + response.code() + ", 原因=" + response.message()); + } + // 获取响应体(注意:response.body() 只能调用一次,且需确保非空) + ResponseBody responseBody = response.body(); + return responseBody != null ? responseBody.string() : null; + } + } + + // 日志拦截器(控制日志打印级别) + private static final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor( + message -> System.out.println("OkHttp Log: " + message) // 日志输出到控制台 + ); + + + // 全局 OkHttpClient 实例(单例模式,推荐复用) + private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) // 连接超时 + .readTimeout(15, TimeUnit.SECONDS) // 读取超时 + .writeTimeout(15, TimeUnit.SECONDS) // 写入超时 + .build(); +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java index 4badac59..accd7c88 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java @@ -1,5 +1,12 @@ package com.ruoyi.cai.util; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.cai.trdpay.handle.v12new.utils.JacksonUtil; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -9,15 +16,20 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; public class RestTemplateUtil { public static RestTemplate restTemplate; + public static RestTemplate restTemplateNoNull; public static RestTemplate NO_SSL_REST_TEMPLATE; static { @@ -34,6 +46,26 @@ public class RestTemplateUtil { } } + restTemplateNoNull = new RestTemplate(new BufferingClientHttpRequestFactory(requestFactory)); + List> messageConvertersNoneNull = restTemplate.getMessageConverters(); + //添加转换器 + for (HttpMessageConverter messageConverter : messageConvertersNoneNull) { + if (messageConverter instanceof StringHttpMessageConverter) { + StringHttpMessageConverter converter = (StringHttpMessageConverter) messageConverter; + converter.setDefaultCharset(StandardCharsets.UTF_8); + } + } + // 替换默认的 Jackson 消息转换器 + ObjectMapper objectMapper = new ObjectMapper(); + // 只序列化非 null 字段(若需忽略空字符串,需额外处理,见下方扩展) + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); + jackson2HttpMessageConverter.setObjectMapper(objectMapper); // 使用自定义的 ObjectMapper + restTemplate.getMessageConverters().removeIf(converter1 -> + converter1 instanceof MappingJackson2HttpMessageConverter + ); + restTemplate.getMessageConverters().add(jackson2HttpMessageConverter); + CloseableHttpClient httpClient = null; try { httpClient = CustomHttpClient.createClient(); @@ -85,4 +117,16 @@ public class RestTemplateUtil { HttpEntity> request = new HttpEntity<>(map, headers); return NO_SSL_REST_TEMPLATE.postForObject(url, request, String.class); } + + public static String postJsonData(String url, Object object, Map header) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + for (Map.Entry entry : header.entrySet()) { + headers.add(entry.getKey(),entry.getValue()); + } + HttpEntity request = new HttpEntity<>(object, headers); + return restTemplate.postForObject(url, request, String.class); + } + + }