diff --git a/electripper-admin/src/main/resources/application.yml b/electripper-admin/src/main/resources/application.yml index c5287d3..0a90f7a 100644 --- a/electripper-admin/src/main/resources/application.yml +++ b/electripper-admin/src/main/resources/application.yml @@ -243,3 +243,29 @@ et: appcode: 32b6c6445b1a42ed862dd4202392c47d repairAdmin: wx operateAdmin: root + + +# 收银宝 +syb: + # 集团ID + orgId: "" + # 商户ID + cusId: 56340307399QTL2 + # 应用ID + appId: "00322693" + # MD5密钥 + md5AppKey: allinpay888 + # Api地址 + apiUrl: https://vsp.allinpay.com/apiweb/unitorder + # 签名方式 + signType: RSA + # 商户RSA私钥,用于向通联发起请求前进行签名 + rsaCusPriKey: MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkngbbMX1kgcp8ClMDqO3r2lHP8MIdG+lrwDXoMtxLXv/sohyvmZIGv1QKPDZhU3b+tMfjfyvpCQ53Ji885YVocThz7TP8nNnshhNzhFVIQ+LcfakuitHtmsLL0wohU6NiEOi3k9iZkSuTEHgXkJSoVv3yHicjdDfbcq/rsMhx2RLa9yffVgwJY6it9EO73phc6xHP/VrxYjwjF1EXM22HyF7mNvzSdMpl7k8IBm8r8uvuB108J+sL4yPoJqHSXkJYESA9r0vLPH6hmJ534UG/umleBxnJ3l6NplcgM7NOOVNIF8a0Cu2+l+hfW+E/oAEdNImA2H7NVQy3b0vsi0S9AgMBAAECggEAGTIJtRtcSJmX8wfGgHEm5zotWvEL2NY4khIjfhRoxM13yNFgKB1ObSN0GrPwE9HpTN9BF+gTQbHt6Z0pzj2H/34SyKWF1zRvo2S7DetzfSHM03gVQKclLeOJzJYSvxqUz3HsmIpztL90Jqh25Q5vPlvANwJX9bR4RymE1QNkDo6OjfSaXvPFK5xuQ+mHmWft2Gbh/OmVONcJ6bIguQvIgyXD3wlmH16Bh1D8trqEHfw6QzpWPF3+N9WHbuFjn/u9oO+HwmVu3s6qmYlrEfvBWzkrcyzDGmUxz9oIEx1Ruh+PS+fn5mfFvmARSe5vVd6ipFw+f4aumJd2mCQbf3J34QKBgQDixyqlMCs2xg+Rv+PmIgO7BTSNzcbmpO0o74OVnSIAJMF99M1TaQYOWoXqXKN2sS8lAUd9Kf48xuXX1lEzDsNZ2kKnLqDd0I28bapZ1hawdrcKMnot2j3gt3LIe2Mq7qjAu6qEjBgA5BC2WjpKuNT++MDE9XEnQdsu8DsT2vDTYQKBgQC51FXo93qcNnGj6JAZvax1x9X/z/jHMczBDfHOhjJwGaogael+uGLwmRCI3cKd0Hau1wp5wZ+ySyWGZsLYs2cB3jqqmRQGV8wHHLZKUCO+agft5zeyu17Rw1hRAxF57Fcrk5LzyMZXeO6ny6apxLPcLEB/phAMgs24CrqRMEMK3QKBgGxXlqognySl7x5EvM5fhcS9sePlYZcjSCJ8ezLpRSsoZECWydmFnV0SJlRXOckk0U8uk0ba3xmONJSvUU/BR9cPjTRt9HDEiFJzWiVdQK3BqaT3hKOPVmybd/0L5c7HAlxBzC9S9szGKK3V4W99alT95qqyUALfVaXRI4hS2ydBAoGAO528RfYHNtBcmsoEWM254ztOEziERltapeC0X5471izVoHp59zZuP2PLoy0Jv5leSe8hJDiS5B8plbmn7t93P2ktBwf/HZ3TDcEPVj3pia/4hUE1ZDiqTb9OuQaz95pzyL4aWe1ifDOG1PwHptU9Inhr5MfAxFK+brm6atPB3l0CgYBMnyT/eaMC2PZS9jYAOBDHGTBKMxX5ddRMcI/dZzspzDNfQ6cjWH5RiXFAU4PIU2o9LbnIJNuwjdD3NspQblwbB2/c28qW4iY08jGna7MRJ46mTQle3varn7hWcQ3LMgP7XavgPDKS/qRRv0xFA/cjbnYGFn4iQp5BxcbGCy5Gdg== + # 通联平台RSA公钥,用于请求返回或者通联通知的验签 + rsaTlPubKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB + # 商户sm2私钥,用于向通联发起请求前进行签名 + sm2CusPriKey: MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgaPcLc/a1wqUa9wpDetd0PtjbPv+ldPq8vPc7UeDmlWegCgYIKoEcz1UBgi2hRANCAASo8eWV80+/3elREuKbSukOwkP+tekaq1bsk9zrreR14RAzQrDJWrq8PBso8Ctpvew51w6aVhFubw+oDFFNLE/w + # 通联平台sm2公钥,用于请求返回或者通联通知的验签 + sm2TlPubKey: MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEBQicgWm0KAMqhO3bdqMUEDrKQvYg8cCXHhdGwq7CGE6oJDzJ1P/94HpuVdBf1KidmPxr7HOH+0DAnpeCcx9TcQ== + # 支付通知地址 + notifyUrl: http://124.221.246.124:2290/app/pay/notify/tl diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/config/SybConfig.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/config/SybConfig.java new file mode 100644 index 0000000..a231b54 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/config/SybConfig.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.pay.syb.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author wjh + * 2024/9/9 + */ +@Component +@ConfigurationProperties(prefix = "syb") +@Data +public class SybConfig { + + // 集团ID + private String orgId; + + // 商户ID + private String cusId; + + // 应用ID + private String appId; + + // MD5密钥 + private String md5AppKey; + + // Api地址 + private String apiUrl; + + // 签名方式 + private String signType; + + // 商户RSA私钥,用于向通联发起请求前进行签名 + private String rsaCusPriKey; + + // 通联平台RSA公钥,用于请求返回或者通联通知的验签 + private String rsaTlPubKey; + + // 商户sm2私钥,用于向通联发起请求前进行签名 + private String sm2CusPriKey; + + // 通联平台sm2公钥,用于请求返回或者通联通知的验签 + private String sm2TlPubKey; + + // 通知地址 + private String notifyUrl; + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxCode.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxCode.java new file mode 100644 index 0000000..342b04c --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxCode.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.pay.syb.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 收银宝交易类型 + * @author wjh + * 2024/9/10 + */ +@Getter +@AllArgsConstructor +public enum SybTrxCode { + + WX_PAY("VSP501", "微信支付"), + WX_REFUND("VSP503", "微信支付退款"); + + private final String code; + + private final String msg; + + public boolean equalsCode(String code) { + return this.getCode().equals(code); + } + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxStatus.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxStatus.java new file mode 100644 index 0000000..596fd38 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/enums/SybTrxStatus.java @@ -0,0 +1,41 @@ +package com.ruoyi.common.pay.syb.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 交易结果 + * @author wjh + * 2024/9/10 + */ +@Getter +@AllArgsConstructor +public enum SybTrxStatus { + SUCCESS("0000", "交易成功"), + PROCESSING("2000", "交易处理中"), + NOT_EXIST("1001", "交易不存在"), + REPEAT("3888", "流水号重复"), + CHANNEL_MERCHANT_ERROR("3099", "渠道商户错误"), + TRANSACTION_AMOUNT_LESS_THAN_SERVICE_CHARGE("3014", "交易金额小于应收手续费"), + CHECK_REAL_NAME_INFO_FAILED("3031", "校验实名信息失败"), + TRANSACTION_NOT_PAID("3088", "交易未支付"), + TRANSACTION_REVERSED("3089", "撤销异常"), + TRANSACTION_REVOKED("3050", "交易已被撤销"); + + public static boolean isSuccess(String code) { + return SUCCESS.getCode().equals(code); + } + + public static SybTrxStatus getByCode(String code) { + for (SybTrxStatus value : SybTrxStatus.values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } + + private final String code; + private final String msg; + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/ApiTestV2.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/ApiTestV2.java new file mode 100644 index 0000000..ef852e1 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/ApiTestV2.java @@ -0,0 +1,53 @@ +package com.ruoyi.common.pay.syb.service; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + + +@RestController +@RequestMapping("/syb") +public class ApiTestV2 extends BaseController { + + @Autowired + private SybPayClient service; + + @GetMapping("/scanPay") + public AjaxResult testScanPay() throws Exception { + // TODO Auto-generated method stub + String reqsn = String.valueOf(System.currentTimeMillis()); + Map map = service.scanPay(1, reqsn, "标题", "备注", "134775931316089668", "", "", "", ""); + return success(map); + } + + @GetMapping("/query") + public AjaxResult testQuery(@RequestParam String trxid) throws Exception { + Map map = service.query("", trxid); + return success(map); + } + + @PostMapping("/refund") + public AjaxResult testRefund() throws Exception { + String reqsn = String.valueOf(System.currentTimeMillis()); + Map map = service.refund(1, reqsn, "", "20160712167578.2547"); + return success(map); + } + + @PostMapping("/cancel") + public AjaxResult testCancel() throws Exception { + String reqsn = String.valueOf(System.currentTimeMillis()); + Map map = service.cancel(1, reqsn, "112094120001088316", ""); + return success(map); + } + + @PostMapping("/pay") + public AjaxResult testPay() throws Exception { + + String reqsn = String.valueOf(System.currentTimeMillis()); + Map map = service.pay(1L, reqsn, "W06", "标题", "备注", "ol5kD7eeXNGeYE5z7uIhk12K-rBA", "123", "https://test.allinpaygd.com/JWeb/NotifyServlet", "", "", "", "", "", "", "", "", "", "", "", ""); + return success(map); + } +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayClient.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayClient.java new file mode 100644 index 0000000..e41a355 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayClient.java @@ -0,0 +1,272 @@ +package com.ruoyi.common.pay.syb.service; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.pay.syb.config.SybConfig; +import com.ruoyi.common.pay.syb.util.HttpConnectionUtil; +import com.ruoyi.common.pay.syb.util.SybUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.TreeMap; + +@Component +public class SybPayClient { + + @Autowired + private SybConfig sybConfig; + + + + /** + * + * @param trxamt + * @param reqsn + * @param paytype + * @param body + * @param remark + * @param acct + * @param validtime + * @param notify_url + * @param limit_pay + * @param idno + * @param truename + * @param asinfo + * @param sub_appid + * @param goods_tag 单品优惠信息 + * @param chnlstoreid + * @param subbranch + * @param cusip 限云闪付JS支付业务 + * @param fqnum 限支付宝分期业务 + * @return + * @throws Exception + */ + public Map pay(long trxamt, String reqsn, String paytype, String body, String remark, String acct, String validtime, String notify_url, String limit_pay, + String idno, String truename, String asinfo, String sub_appid, String goods_tag, String benefitdetail, String chnlstoreid, String subbranch, String extendparams, String cusip, String fqnum) throws Exception{ + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/pay"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) { + params.put("orgid", sybConfig.getOrgId()); + } + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("trxamt", String.valueOf(trxamt)); + params.put("reqsn", reqsn); + params.put("paytype", paytype); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("body", body); + params.put("remark", remark); + params.put("validtime", validtime); + params.put("acct", acct); + params.put("notify_url", notify_url); + params.put("limit_pay", limit_pay); + params.put("sub_appid", sub_appid); + params.put("goods_tag", goods_tag); + params.put("benefitdetail", benefitdetail); + params.put("chnlstoreid", chnlstoreid); + params.put("subbranch", subbranch); + params.put("extendparams", extendparams); + params.put("cusip", cusip); + params.put("fqnum", fqnum); + params.put("idno", idno); + params.put("truename", truename); + params.put("asinfo", asinfo); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) { + appkey = sybConfig.getRsaCusPriKey(); + } else if(sybConfig.getSignType().equals("SM2")) { + appkey = sybConfig.getSm2CusPriKey(); + } else { + appkey = sybConfig.getMd5AppKey(); + } + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + + } + + public Map cancel(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{ + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/cancel"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) + params.put("orgid", sybConfig.getOrgId()); + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("trxamt", String.valueOf(trxamt)); + params.put("reqsn", reqsn); + params.put("oldtrxid", oldtrxid); + params.put("oldreqsn", oldreqsn); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaCusPriKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2CusPriKey(); + else + appkey = sybConfig.getMd5AppKey(); + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + } + + /** + * 关闭支付订单 + */ + public Map close(String oldtrxid,String oldreqsn) throws Exception{ + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/close"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) + params.put("orgid", sybConfig.getOrgId()); + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("oldtrxid", oldtrxid); + params.put("oldreqsn", oldreqsn); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaCusPriKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2CusPriKey(); + else + appkey = sybConfig.getMd5AppKey(); + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + } + + public Map refund(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{ + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/refund"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) + params.put("orgid", sybConfig.getOrgId()); + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("trxamt", String.valueOf(trxamt)); + params.put("reqsn", reqsn); + params.put("oldreqsn", oldreqsn); + params.put("oldtrxid", oldtrxid); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaCusPriKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2CusPriKey(); + else + appkey = sybConfig.getMd5AppKey(); + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + } + + public Map query(String reqsn,String trxid) throws Exception{ + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/query"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) + params.put("orgid", sybConfig.getOrgId()); + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("reqsn", reqsn); + params.put("trxid", trxid); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaCusPriKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2CusPriKey(); + else + appkey = sybConfig.getMd5AppKey(); + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Map handleResult(String result) throws Exception{ + System.out.println("ret:"+result); + Map map = JSON.parseObject(result, Map.class); + if(map == null){ + throw new Exception("返回数据错误"); + } + if("SUCCESS".equals(map.get("retcode"))){ + TreeMap tmap = new TreeMap(); + tmap.putAll(map); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaTlPubKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2TlPubKey(); + else + appkey = sybConfig.getMd5AppKey(); + if(SybUtil.validSign(tmap, appkey, sybConfig.getSignType())){ + System.out.println("签名成功"); + return map; + }else{ + throw new Exception("验证签名失败"); + } + + }else{ + throw new Exception(map.get("retmsg").toString()); + } + } + + public Map scanPay(long trxamt,String reqsn,String body,String remark,String authcode,String limit_pay,String idno,String truename,String asinfo) throws Exception{ + // TODO Auto-generated method stub + HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/scanqrpay"); + http.init(); + TreeMap params = new TreeMap(); + if(!SybUtil.isEmpty(sybConfig.getOrgId())) + params.put("orgid", sybConfig.getOrgId()); + params.put("cusid", sybConfig.getCusId()); + params.put("appid", sybConfig.getAppId()); + params.put("version", "11"); + params.put("trxamt", String.valueOf(trxamt)); + params.put("reqsn", reqsn); + params.put("randomstr", SybUtil.getValidatecode(8)); + params.put("body", body); + params.put("remark", remark); + params.put("authcode", authcode); + params.put("limit_pay", limit_pay); + params.put("asinfo", asinfo); + params.put("signtype", sybConfig.getSignType()); + String appkey = ""; + if(sybConfig.getSignType().equals("RSA")) + appkey = sybConfig.getRsaCusPriKey(); + else if(sybConfig.getSignType().equals("SM2")) + appkey = sybConfig.getSm2CusPriKey(); + else + appkey = sybConfig.getMd5AppKey(); + params.put("sign", SybUtil.unionSign(params,appkey,sybConfig.getSignType())); + byte[] bys = http.postParams(params, true); + String result = new String(bys,"UTF-8"); + Map map = handleResult(result); + return map; + } + + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java new file mode 100644 index 0000000..7275e93 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/service/SybPayService.java @@ -0,0 +1,125 @@ +package com.ruoyi.common.pay.syb.service; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.syb.config.SybConfig; +import com.ruoyi.common.pay.syb.util.SybUtil; +import com.ruoyi.common.pay.wx.Payable; +import com.ruoyi.common.pay.wx.RefundAble; +import com.ruoyi.common.utils.StringUtils; +import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.TreeMap; + +/** + * @author wjh + * 2024/9/9 + */ +@Service +public class SybPayService { + + @Autowired + private SybPayClient sybPayClient; + + @Autowired + private SybConfig sybConfig; + + /** + * 微信小程序预下单 + */ + public PrepayWithRequestPaymentResponse prepayWxApp(Payable payable) { + try { + Map result = sybPayClient.pay( + payable.getAmount(), + payable.getOutTradeNo(), + "W06", + payable.getDescription(), + payable.getAttach(), + payable.getOpenid(), + "", + sybConfig.getNotifyUrl(), + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ); + + if (result != null) { + String payInfo = result.get("payinfo"); + if (StringUtils.hasText(payInfo)) { + return JSON.parseObject(payInfo, PrepayWithRequestPaymentResponse.class); + } + } + throw new ServiceException("支付数据为空"); + } catch(Exception e) { + throw new ServiceException("调起支付失败:" + e.getMessage()); + } + } + + public boolean validSign(TreeMap params) { + try { + String appkey = ""; + if("RSA".equals(params.get("signtype"))) { + appkey = sybConfig.getRsaTlPubKey(); + } else if("SM2".equals(params.get("signtype"))) { + appkey = sybConfig.getSm2TlPubKey(); + } else { + appkey = sybConfig.getMd5AppKey(); + } + boolean isSign = SybUtil.validSign(params, appkey, params.get("signtype"));// 接受到推送通知,首先验签 + System.out.println("验签结果:"+isSign); + return isSign; + } catch (Exception e) { + throw new ServiceException("验签异常:" + e.getMessage()); + } + } + + /** + * 使用商户自定义订单编号查询支付订单 + */ + public Map queryOrderByOutTradeNo(String outTradeNo) { + try { + return sybPayClient.query(outTradeNo, ""); + } catch (Exception e) { + throw new ServiceException("查询支付订单异常:" + e.getMessage()); + } + } + + /** + * 微信退款 + */ + public Map refundWx(RefundAble refundAble) { + try { + return sybPayClient.refund( + refundAble.refundAmount().getRefund().longValue(), + refundAble.refundOutRefundNo(), + "", + refundAble.refundOutTradeNo() + ); + } catch (Exception e) { + throw new ServiceException("发起退款异常:" + e.getMessage()); + } + } + + /** + * 关闭微信支付订单 + */ + public void closeOrderWx(String outTradeNo) { + try { + sybPayClient.close("", outTradeNo); + } catch (Exception e) { + throw new ServiceException("关闭支付订单异常:" + e.getMessage()); + } + } +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/HttpConnectionUtil.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/HttpConnectionUtil.java new file mode 100644 index 0000000..6e8b373 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/HttpConnectionUtil.java @@ -0,0 +1,122 @@ +package com.ruoyi.common.pay.syb.util; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSession; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Map; + + +public class HttpConnectionUtil { + private HttpURLConnection conn; + private String connectUrl; + + public HttpConnectionUtil(String connectUrl){ + this.connectUrl = connectUrl; + } + + public void init() throws Exception{ + URL url = new URL(connectUrl); + System.setProperty("java.protocol.handler.pkgs", "javax.net.ssl"); + HostnameVerifier hv = new HostnameVerifier() { + public boolean verify(String urlHostName, SSLSession session) { + return urlHostName.equals(session.getPeerHost()); + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(hv); + URLConnection conn = url.openConnection(); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setReadTimeout(60000); + conn.setConnectTimeout(30000); + if (conn instanceof HttpsURLConnection){ + HttpsURLConnection httpsConn = (HttpsURLConnection)conn; + httpsConn.setSSLSocketFactory(SSLUtil.getInstance().getSSLSocketFactory()); + } else if (conn instanceof HttpURLConnection){ + HttpURLConnection httpConn = (HttpURLConnection)conn; + } else { + throw new Exception("不是http/https协议的url"); + } + this.conn = (HttpURLConnection)conn; + initDefaultPost(); + } + + public void destory(){ + try{ + if(this.conn!=null){ + this.conn.disconnect(); + } + }catch(Exception e){ + + } + } + + private void initDefaultPost() throws Exception{ + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setRequestMethod("POST"); + conn.setUseCaches(false); + conn.setInstanceFollowRedirects(true); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + } + + public byte[] postParams(Map params,boolean readreturn) throws IOException { + StringBuilder outBuf = new StringBuilder(); + boolean isNotFirst = false; + for (Map.Entry entry: params.entrySet()){ + if (isNotFirst) + outBuf.append('&'); + isNotFirst = true; + outBuf + .append(entry.getKey()) + .append('=') + .append(URLEncoder.encode(entry.getValue(), "UTF-8")); + } + System.out.println("参数:"+outBuf.toString()); + return postParams(outBuf.toString(),readreturn); + } + + public byte[] postParams(String message,boolean readreturn) throws IOException { + DataOutputStream out = new DataOutputStream(conn.getOutputStream()); + out.write(message.getBytes("UTF-8")); + out.close(); + if(readreturn){ + return readBytesFromStream(conn.getInputStream()); + }else{ + return null; + } + } + + public byte[] postParams(byte[] message,boolean readreturn) throws IOException { + DataOutputStream out = new DataOutputStream(conn.getOutputStream()); + out.write(message); + out.close(); + if(readreturn){ + return readBytesFromStream(conn.getInputStream()); + }else{ + return null; + } + } + + private byte[] readBytesFromStream(InputStream is) throws IOException{ + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int readLen; + byte[] tmpBuf = new byte[4096]; + while ((readLen = is.read(tmpBuf)) > 0) + baos.write(tmpBuf, 0, readLen); + is.close(); + return baos.toByteArray(); + } + + public HttpURLConnection getConn() { + return conn; + } + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SSLUtil.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SSLUtil.java new file mode 100644 index 0000000..3f07d08 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SSLUtil.java @@ -0,0 +1,53 @@ +package com.ruoyi.common.pay.syb.util; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * SSL管理助手类 + * @author Administrator + * + */ +public class SSLUtil implements X509TrustManager { + private SSLSocketFactory sslFactory = null; + + private SSLUtil(){ + } + + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + /** 获取SSL Socket工厂 */ + public SSLSocketFactory getSSLSocketFactory(){ + return sslFactory; + } + + private static SSLUtil _instance = null; + + /** 获取SSL管理助手类实例 */ + synchronized public static SSLUtil getInstance() throws NoSuchAlgorithmException, KeyManagementException { + if (_instance == null){ + _instance = new SSLUtil(); + SSLContext sc = SSLContext.getInstance("TLSv1.2"); + sc.init(null, new TrustManager[]{new SSLUtil()}, null); + _instance.sslFactory = sc.getSocketFactory(); + } + return _instance; + } + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SmUtil.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SmUtil.java new file mode 100644 index 0000000..81646b5 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SmUtil.java @@ -0,0 +1,133 @@ +package com.ruoyi.common.pay.syb.util; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.jcajce.spec.SM2ParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + +public class SmUtil { + static{ + Security.addProvider(new BouncyCastleProvider()); + } + /**算法常量:SM3withSM2*/ + public static final String ALGORITHM_SM3SM2_BCPROV = "SM3withSM2"; + private final static int SM3withSM2_RS_LEN=32; + + public static void main(String[] args) throws Exception { + /**商户平台分配的appid,也是签名的certid**/ + String appid = "00000156"; + /**商户sm2私钥,用于向通联发起请求前进行签名**/ + String cusPrivateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgjj4Rk+b0YjwO+UwXofnHf4bK+kaaY5Btkd8nMP2VimmgCgYIKoEcz1UBgi2hRANCAAQqlALW4qGC3bP1x3wo5QsKxaCMEZJ2ODTTwOQ+d8UGU7GoK/y/WMBQWf5upMnFU06p5FxGooXYYoBtldgm03hq"; + /**商户sm2公钥,需要配置到通联商户平台**/ + String cusPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEKpQC1uKhgt2z9cd8KOULCsWgjBGSdjg008DkPnfFBlOxqCv8v1jAUFn+bqTJxVNOqeRcRqKF2GKAbZXYJtN4ag=="; + + /**通联平台sm2公钥,用于请求返回或者通联通知的验签**/ + String tlPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/BnA8BawehBtH0ksPyayo4pmzL/u1FQ2sZcqwOp6bjVqQX4tjo930QAvHZPJ2eez8sCz/RYghcqv4LvMq+kloQ=="; + + String blankStr = "请求待签名数据"; + PrivateKey privkey = privKeySM2FromBase64Str(cusPrivateKey); + String sign = signSM3SM2RetBase64(privkey, appid, blankStr.getBytes("UTF-8"));//签名 + System.out.println(sign); + + String rspBlankStr = "返回待验签数据";//通联返回的明文 + String rspSign = "AovBKQGUe0xuJ0ox7FgIIX+yB3DzbudgUsnNvJmDV0IdHZtU2Y8vdeUY1pd2vmPUf08hNgdkoz+4WP/D/ktOcA==";//通联返回的签名 + PublicKey publicKey = pubKeySM2FromBase64Str(tlPubKey); + boolean isOk = verifySM3SM2(publicKey, "Allinpay", Base64.decodeBase64(rspSign), rspBlankStr.getBytes("UTF-8")); + System.out.println("验签结果:"+isOk); + + + } + + /**签名并BASE64编码-SM3WithSM2 */ + public static String signSM3SM2RetBase64(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{ + return Base64.encodeBase64String(signSM3SM2(privateKey, certid, data)); + } + + /**签名-SM3WithSM2 */ + public static byte[] signSM3SM2(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{ + SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes()); + Signature signer = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC"); + signer.setParameter(parameterSpec); + signer.initSign(privateKey, new SecureRandom()); + signer.update(data); + return byteAsn12BytePlain(signer.sign()); + } + + /** 验证签名-SM3WithSM2*/ + public static boolean verifySM3SM2(final PublicKey publicKey,String certid,final byte[] signData, final byte[] srcData) throws Exception { + SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes()); + Signature verifier = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC"); + verifier.setParameter(parameterSpec); + verifier.initVerify(publicKey); + verifier.update(srcData); + return verifier.verify(bytePlain2ByteAsn1(signData)); + } + + /**从字符串读取私钥-目前支持PKCS8(keystr为BASE64格式)*/ + public static PrivateKey privKeySM2FromBase64Str(String keystr) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(keystr))); + } + + /**从字符串读取RSA公钥(keystr为BASE64格式)*/ + public static PublicKey pubKeySM2FromBase64Str(String keystr) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(keystr))); + } + + /** + * 将普通字节数组转换为ASN1字节数组 适用于SM3withSM2验签时验签明文转换 + */ + private static byte[] bytePlain2ByteAsn1(byte[] data) { + if (data.length != SM3withSM2_RS_LEN * 2) throw new RuntimeException("err data. "); + BigInteger r = new BigInteger(1, Arrays.copyOfRange(data, 0, SM3withSM2_RS_LEN)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(data, SM3withSM2_RS_LEN, SM3withSM2_RS_LEN * 2)); + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + try { + return new DERSequence(v).getEncoded("DER"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** + * 将ASN1字节数组转换为普通字节数组 适用于SM3withSM2签名时签名结果转换 + */ + private static byte[] byteAsn12BytePlain(byte[] dataAsn1) { + ASN1Sequence seq = ASN1Sequence.getInstance(dataAsn1); + byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue()); + byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue()); + byte[] result = new byte[SM3withSM2_RS_LEN * 2]; + System.arraycopy(r, 0, result, 0, r.length); + System.arraycopy(s, 0, result, SM3withSM2_RS_LEN, s.length); + return result; + } + + private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) { + byte[] rs = rOrS.toByteArray(); + if (rs.length == SM3withSM2_RS_LEN) return rs; + else if (rs.length == SM3withSM2_RS_LEN + 1 && rs[0] == 0) + return Arrays.copyOfRange(rs, 1, SM3withSM2_RS_LEN + 1); + else if (rs.length < SM3withSM2_RS_LEN) { + byte[] result = new byte[SM3withSM2_RS_LEN]; + Arrays.fill(result, (byte) 0); + System.arraycopy(rs, 0, result, SM3withSM2_RS_LEN - rs.length, rs.length); + return result; + } else { + throw new RuntimeException("err rs: " + Hex.toHexString(rs)); + } + } + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SybUtil.java b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SybUtil.java new file mode 100644 index 0000000..8fabd13 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/syb/util/SybUtil.java @@ -0,0 +1,230 @@ +package com.ruoyi.common.pay.syb.util; + +import org.apache.commons.codec.binary.Base64; + +import javax.servlet.http.HttpServletRequest; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; + +public class SybUtil { + + /** + * md5 + * + * @param b + * @return + */ + public static String md5(byte[] b) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.reset(); + md.update(b); + byte[] hash = md.digest(); + StringBuffer outStrBuf = new StringBuffer(32); + for (int i = 0; i < hash.length; i++) { + int v = hash[i] & 0xFF; + if (v < 16) { + outStrBuf.append('0'); + } + outStrBuf.append(Integer.toString(v, 16).toLowerCase()); + } + return outStrBuf.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return new String(b); + } + } + + /** + * 判断字符串是否为空 + * + * @param s + * @return + */ + public static boolean isEmpty(String s) { + if (s == null || "".equals(s.trim())) + return true; + return false; + } + + /** + * 生成随机码 + * + * @param n + * @return + */ + public static String getValidatecode(int n) { + Random random = new Random(); + String sRand = ""; + n = n == 0 ? 4 : n;// default 4 + for (int i = 0; i < n; i++) { + String rand = String.valueOf(random.nextInt(10)); + sRand += rand; + } + return sRand; + } + + + + public static boolean validSign(TreeMap param, + String appkey, String signType) throws Exception { + if (param != null && !param.isEmpty()) { + if (!param.containsKey("sign")) + return false; + String sign = param.remove("sign"); + if ("MD5".equals(signType)) {// 如果是md5则需要把md5的key加入到排序 + param.put("key", appkey); + } + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : param.entrySet()) { + if (entry.getValue() != null && entry.getValue().length() > 0) { + sb.append(entry.getKey()).append("=") + .append(entry.getValue()).append("&"); + } + } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + if ("MD5".equals(signType)) { + return sign.toLowerCase().equals( + md5(sb.toString().getBytes("UTF-8")).toLowerCase()); + } else if("SM2".equals(signType)){ + PublicKey publicKey = SmUtil.pubKeySM2FromBase64Str(appkey); + return SmUtil.verifySM3SM2(publicKey, "Allinpay", Base64.decodeBase64(sign), sb.toString().getBytes("UTF-8")); + }else { + return rsaVerifyPublickey(sb.toString(), sign, appkey, "UTF-8"); + } + } + return false; + } + + public static boolean rsaVerifyPublickey(String content, String sign, + String publicKey, String charset) throws Exception { + try { + PublicKey pubKey = getPublicKeyFromX509("RSA", + Base64.decodeBase64(publicKey.getBytes())); + return rsaVerifyPublickey(content, sign, pubKey, charset); + } catch (Exception e) { + e.printStackTrace(); + throw new Exception("RSAcontent = " + content + ",sign=" + sign + + ",charset = " + charset, e); + } + } + + public static boolean rsaVerifyPublickey(String content, String sign, + PublicKey pubKey, String charset) throws Exception { + try { + Signature signature = Signature + .getInstance("SHA1WithRSA"); + + signature.initVerify(pubKey); + + if (charset == null || "".equals(charset)) { + signature.update(content.getBytes()); + } else { + signature.update(content.getBytes(charset)); + } + + return signature.verify(Base64.decodeBase64(sign.getBytes())); + } catch (Exception e) { + throw e; + } + } + public static String unionSign(TreeMap params,String appkey, + String signType) throws Exception { + // TODO Auto-generated method stub + + params.remove("sign"); + if ("MD5".equals(signType)) {// 如果是md5则需要把md5的key加入到排序 + params.put("key", appkey); + } + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + if (entry.getValue() != null && entry.getValue().length() > 0) { + sb.append(entry.getKey()).append("=").append(entry.getValue()) + .append("&"); + } + } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + String sign = ""; + if ("MD5".equals(signType)) { + System.out.println(sb.toString()); + sign = md5(sb.toString().getBytes("UTF-8"));// 记得是md5编码的加签 + params.remove("key"); + }else if("SM2".equals(signType)){ + System.out.println(sb.toString()); + PrivateKey privkey = SmUtil.privKeySM2FromBase64Str(appkey); + sign = SmUtil.signSM3SM2RetBase64(privkey, params.get("appid"), sb.toString().getBytes("UTF-8"));//签名 + } else { + System.out.println(sb.toString()); + sign = rsaSign(sb.toString(), appkey, "UTF-8"); + } + return sign; + } + + public static String rsaSign(String content, String privateKey, + String charset) throws Exception { + PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", + Base64.decodeBase64(privateKey.getBytes())); + return rsaSign(content, priKey, charset); + } + + public static String rsaSign(String content, byte[] privateKey, + String charset) throws Exception { + PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", privateKey); + return rsaSign(content, priKey, charset); + } + + public static String rsaSign(String content, PrivateKey priKey, + String charset) throws Exception { + Signature signature = Signature + .getInstance("SHA1WithRSA"); + signature.initSign(priKey); + if (charset == null || "".equals(charset)) { + signature.update(content.getBytes()); + } else { + signature.update(content.getBytes(charset)); + } + byte[] signed = signature.sign(); + + return new String(Base64.encodeBase64(signed)); + } + + public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, + byte[] encodedKey) throws Exception { + + KeyFactory keyFactory = KeyFactory.getInstance(algorithm); + + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); + } + + public static PublicKey getPublicKeyFromX509(String algorithm, + byte[] encodedKey) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance(algorithm); + + return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + } + + + /** + * 动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常 + * @param request + * @return + */ + public static TreeMap getParams(HttpServletRequest request){ + TreeMap map = new TreeMap(); + Map reqMap = request.getParameterMap(); + for(Object key:reqMap.keySet()){ + String value = ((String[])reqMap.get(key))[0]; +// System.out.println(key+";"+value); + map.put(key.toString(),value); + } + return map; + } +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/wx/Payable.java b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/Payable.java new file mode 100644 index 0000000..779de31 --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/Payable.java @@ -0,0 +1,37 @@ +package com.ruoyi.common.pay.wx; + +import lombok.Data; + +/** + * @author qzz + * 2024/9/12 + */ +@Data +public class Payable { + + /** + * 金额(分) + */ + private long amount; + + /** + * 描述 + */ + private String description; + + /** + * 附加信息 + */ + private String attach; + + /** + * 外部订单号 + */ + private String outTradeNo; + + /** + * 微信openId + */ + private String openid; + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/wx/RefundAble.java b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/RefundAble.java new file mode 100644 index 0000000..2a9a54c --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/RefundAble.java @@ -0,0 +1,23 @@ +package com.ruoyi.common.pay.wx; + +import com.wechat.pay.java.service.refund.model.AmountReq; + +/** + * @author wjh + * 2024/7/10 + */ +public interface RefundAble { + + // 原商户订单号 + String refundOutTradeNo(); + + // 退款单号 + String refundOutRefundNo(); + + // 退款原因 + String refundReason(); + + // 退款金额(分) + AmountReq refundAmount(); + +} diff --git a/electripper-common/src/main/java/com/ruoyi/common/pay/wx/service/RefundVO.java b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/service/RefundVO.java new file mode 100644 index 0000000..3b0aedf --- /dev/null +++ b/electripper-common/src/main/java/com/ruoyi/common/pay/wx/service/RefundVO.java @@ -0,0 +1,48 @@ +//package com.ruoyi.common.pay.wx.service; +// +//import com.ruoyi.common.pay.wx.domain.RefundAble; +//import com.wechat.pay.java.service.refund.model.AmountReq; +//import io.swagger.annotations.ApiModelProperty; +//import lombok.Data; +// +//import java.math.BigDecimal; +// +///** +// * @author wjh +// * 2024/7/9 +// */ +//@Data +//public class RefundVO extends Refund implements RefundAble { +// +// @ApiModelProperty("原订单编号") +// private String payNo; +// +// @ApiModelProperty("原订单交易总额") +// private BigDecimal payAmount; +// +// +// @Override +// public String refundOutTradeNo() { +// return getPayNo(); +// } +// +// @Override +// public String refundOutRefundNo() { +// return this.getRefundNo(); +// } +// +// @Override +// public String refundReason() { +// return this.getReason(); +// } +// +// @Override +// public AmountReq refundAmount() { +// BigDecimal decimal100 = new BigDecimal(100); +// AmountReq amount = new AmountReq(); +// amount.setRefund(this.getAmount().multiply(decimal100).longValue()); +// amount.setTotal(getPayAmount().multiply(decimal100).longValue()); +// amount.setCurrency("CNY"); +// return amount; +// } +//} diff --git a/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java b/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java index c5f3f5e..15b94a7 100644 --- a/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java +++ b/electripper-system/src/main/java/com/ruoyi/system/service/impl/WxPayService.java @@ -4,14 +4,13 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.ServiceConstants; import com.ruoyi.common.core.domain.entity.AsUser; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.redis.RedisLock; import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.pay.wx.Payable; import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.system.domain.EtAreaDept; import com.ruoyi.system.domain.EtOrder; @@ -98,9 +97,9 @@ public class WxPayService implements IWxPayService { // String isProfitSharing = sysDept.getIsProfitSharing(); // 获取JSAPI所需参数 - PrepayRequest request = new PrepayRequest(); - request.setAmount(getAmount(order.getPayFee())); - JsapiServiceExtension jsapiServiceExtension = getJsapiServiceExtension(sysDept); +// PrepayRequest request = new PrepayRequest(); + Payable payable = new Payable(); + payable.setAmount(order.getPayFee().longValue()); if(StrUtil.isNotBlank(order.getOutTradeNo())){ String tradeNo = order.getOutTradeNo(); // 关闭订单 @@ -111,27 +110,19 @@ public class WxPayService implements IWxPayService { } String outTradeNo = IdUtils.getOrderNo("wx"); order.setOutTradeNo(outTradeNo); -// order.setLocking("1"); int updateEtOrder = etOrderService.updateEtOrder(order); if(updateEtOrder == 0){ throw new ServiceException("更新订单outTradeNo失败"); } - request.setOutTradeNo(outTradeNo); - request.setAppid(sysDept.getAppid()); - request.setMchid(sysDept.getMerchantId()); + payable.setOutTradeNo(outTradeNo); +// request.setOutTradeNo(outTradeNo); +// request.setMchid(sysDept.getMerchantId()); String type = order.getType(); String description = type.equals(ServiceConstants.ORDER_TYPE_RIDING) ? "骑行订单-"+billNo : "押金充值-"+billNo; -// LoginUser loginUser = SecurityUtils.getLoginUser(); -// log.info("【预下单】获取登录用户信息:"+JSON.toJSONString(loginUser)); request.setAttach(JSON.toJSONString(new AttachVo(payType,user.getUserId(), ""))); request.setDescription(description); request.setNotifyUrl(sysDept.getNotifyUrl()); request.setPayer(getPayer(user.getWxopenid())); - SettleInfo settleInfo = new SettleInfo(); - settleInfo.setProfitSharing(false);//暂时关闭分账 -// settleInfo.setProfitSharing( "true".equalsIgnoreCase(isProfitSharing)); - request.setSettleInfo(settleInfo); -// JsapiServiceExtension jsapiServiceExtension = getJsapiServiceExtension(sysDept); PrepayWithRequestPaymentResponse res = jsapiServiceExtension.prepayWithRequestPayment(request); return res; }else{