1. 支付
This commit is contained in:
parent
fb772973e0
commit
9dcaa811f6
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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<String, String> map = service.scanPay(1, reqsn, "标题", "备注", "134775931316089668", "", "", "", "");
|
||||
return success(map);
|
||||
}
|
||||
|
||||
@GetMapping("/query")
|
||||
public AjaxResult testQuery(@RequestParam String trxid) throws Exception {
|
||||
Map<String, String> map = service.query("", trxid);
|
||||
return success(map);
|
||||
}
|
||||
|
||||
@PostMapping("/refund")
|
||||
public AjaxResult testRefund() throws Exception {
|
||||
String reqsn = String.valueOf(System.currentTimeMillis());
|
||||
Map<String, String> map = service.refund(1, reqsn, "", "20160712167578.2547");
|
||||
return success(map);
|
||||
}
|
||||
|
||||
@PostMapping("/cancel")
|
||||
public AjaxResult testCancel() throws Exception {
|
||||
String reqsn = String.valueOf(System.currentTimeMillis());
|
||||
Map<String, String> map = service.cancel(1, reqsn, "112094120001088316", "");
|
||||
return success(map);
|
||||
}
|
||||
|
||||
@PostMapping("/pay")
|
||||
public AjaxResult testPay() throws Exception {
|
||||
|
||||
String reqsn = String.valueOf(System.currentTimeMillis());
|
||||
Map<String, String> map = service.pay(1L, reqsn, "W06", "标题", "备注", "ol5kD7eeXNGeYE5z7uIhk12K-rBA", "123", "https://test.allinpaygd.com/JWeb/NotifyServlet", "", "", "", "", "", "", "", "", "", "", "", "");
|
||||
return success(map);
|
||||
}
|
||||
}
|
|
@ -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<String,String> 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<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
|
||||
}
|
||||
|
||||
public Map<String,String> cancel(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{
|
||||
HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/cancel");
|
||||
http.init();
|
||||
TreeMap<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭支付订单
|
||||
*/
|
||||
public Map<String,String> close(String oldtrxid,String oldreqsn) throws Exception{
|
||||
HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/close");
|
||||
http.init();
|
||||
TreeMap<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String,String> refund(long trxamt,String reqsn,String oldtrxid,String oldreqsn) throws Exception{
|
||||
HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/refund");
|
||||
http.init();
|
||||
TreeMap<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String,String> query(String reqsn,String trxid) throws Exception{
|
||||
HttpConnectionUtil http = new HttpConnectionUtil(sybConfig.getApiUrl()+"/query");
|
||||
http.init();
|
||||
TreeMap<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Map<String,String> 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<String, String> 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<String,String> params = new TreeMap<String,String>();
|
||||
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<String,String> map = handleResult(result);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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<String, String> 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<String, String> 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<String, String> queryOrderByOutTradeNo(String outTradeNo) {
|
||||
try {
|
||||
return sybPayClient.query(outTradeNo, "");
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("查询支付订单异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信退款
|
||||
*/
|
||||
public Map<String, String> 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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String, String> params,boolean readreturn) throws IOException {
|
||||
StringBuilder outBuf = new StringBuilder();
|
||||
boolean isNotFirst = false;
|
||||
for (Map.Entry<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, String> 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<String, String> 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<String, String> 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<String, String> 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<String, String> getParams(HttpServletRequest request){
|
||||
TreeMap<String, String> map = new TreeMap<String, String>();
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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;
|
||||
// }
|
||||
//}
|
|
@ -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{
|
||||
|
|
Loading…
Reference in New Issue
Block a user