diff --git a/pom.xml b/pom.xml index 9b26a15..7d728d6 100644 --- a/pom.xml +++ b/pom.xml @@ -207,6 +207,13 @@ ${ruoyi.version} + + + com.ruoyi + ruoyi-pay + ${ruoyi.version} + + jakarta.servlet jakarta.servlet-api @@ -231,6 +238,7 @@ ruoyi-generator ruoyi-common ruoyi-oauth + ruoyi-pay pom diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java index c1c58db..43d770b 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -1,7 +1,10 @@ package com.ruoyi.common.utils.sign; +import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; + +import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,41 +13,32 @@ import org.slf4j.LoggerFactory; * * @author ruoyi */ -public class Md5Utils -{ +public class Md5Utils { private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); - private static byte[] md5(String s) - { + private static byte[] md5(String s) { MessageDigest algorithm; - try - { + try { algorithm = MessageDigest.getInstance("MD5"); algorithm.reset(); algorithm.update(s.getBytes("UTF-8")); byte[] messageDigest = algorithm.digest(); return messageDigest; - } - catch (Exception e) - { + } catch (Exception e) { log.error("MD5 Error...", e); } return null; } - private static final String toHex(byte hash[]) - { - if (hash == null) - { + private static final String toHex(byte hash[]) { + if (hash == null) { return null; } StringBuffer buf = new StringBuffer(hash.length * 2); int i; - for (i = 0; i < hash.length; i++) - { - if ((hash[i] & 0xff) < 0x10) - { + for (i = 0; i < hash.length; i++) { + if ((hash[i] & 0xff) < 0x10) { buf.append("0"); } buf.append(Long.toString(hash[i] & 0xff, 16)); @@ -52,16 +46,20 @@ public class Md5Utils return buf.toString(); } - public static String hash(String s) - { - try - { + public static String hash(String s) { + try { return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); - } - catch (Exception e) - { + } catch (Exception e) { log.error("not supported charset...{}", e); return s; } } + + public static String encryptMd5(String string) throws UnsupportedEncodingException { + return encryptMd5(string, "UTF-8"); + } + + public static String encryptMd5(String string, String charSet) throws UnsupportedEncodingException { + return DigestUtils.md5Hex(string.getBytes(charSet)); + } } diff --git a/ruoyi-pay/pom.xml b/ruoyi-pay/pom.xml index 2442c17..108a5f1 100644 --- a/ruoyi-pay/pom.xml +++ b/ruoyi-pay/pom.xml @@ -26,6 +26,13 @@ ruoyi-common ${ruoyi.version} + + + + com.ruoyi + ruoyi-pay-sqb + ${ruoyi.version} + diff --git a/ruoyi-pay/ruoyi-pay-sqb/pom.xml b/ruoyi-pay/ruoyi-pay-sqb/pom.xml index 0e57d0f..0d20665 100644 --- a/ruoyi-pay/ruoyi-pay-sqb/pom.xml +++ b/ruoyi-pay/ruoyi-pay-sqb/pom.xml @@ -3,13 +3,13 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - ruoyi + ruoyi-pay com.ruoyi 3.8.7.3.1 4.0.0 - ruoyi-oauth + ruoyi-pay-sqb system系统模块 @@ -23,6 +23,13 @@ ruoyi-common + + + org.apache.httpcomponents + httpclient + 4.5.2 + + \ No newline at end of file diff --git a/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/constant/SqbConstant.java b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/constant/SqbConstant.java new file mode 100644 index 0000000..e417d4a --- /dev/null +++ b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/constant/SqbConstant.java @@ -0,0 +1,71 @@ +package com.ruoyi.pay.sqb.constant; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +@Component +public class SqbConstant { + @Value("${pay.sqb.apiDomain}") + private String apiDomain; + + @Value("${pay.sqb.terminalSn}") + private String terminalSn; + + @Value("${pay.sqb.terminalKey}") + private String terminalKey; + + @Value("${pay.sqb.appId}") + private String appId; + + public String getApiDomain() { + return apiDomain; + } + + public void setApiDomain(String apiDomain) { + this.apiDomain = apiDomain; + } + + public String getTerminalSn() { + return terminalSn; + } + + public void setTerminalSn(String terminalSn) { + this.terminalSn = terminalSn; + } + + public String getTerminalKey() { + return terminalKey; + } + + public void setTerminalKey(String terminalKey) { + this.terminalKey = terminalKey; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getVendorSn() { + return vendorSn; + } + + public void setVendorSn(String vendorSn) { + this.vendorSn = vendorSn; + } + + public String getVendorKey() { + return vendorKey; + } + + public void setVendorKey(String vendorKey) { + this.vendorKey = vendorKey; + } + + @Value("${pay.sqb.vendorSn}") + private String vendorSn; + + @Value("${pay.sqb.vendorKey}") + private String vendorKey; +} diff --git a/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/third/service/Impl/SQBServiceImpl.java b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/service/Impl/SQBServiceImpl.java similarity index 55% rename from ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/third/service/Impl/SQBServiceImpl.java rename to ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/service/Impl/SQBServiceImpl.java index 9838f9f..e75217e 100644 --- a/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/third/service/Impl/SQBServiceImpl.java +++ b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/service/Impl/SQBServiceImpl.java @@ -1,28 +1,21 @@ -package org.jeecg.modules.sqb.service.impl; - - -import org.json.JSONObject; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.PropertySource; -import org.springframework.stereotype.Service; -import java.net.URLEncoder; -import java.net.URLDecoder; +package com.ruoyi.pay.sqb.service.Impl; import java.io.UnsupportedEncodingException; -import java.util.Random; +import java.net.URLEncoder; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.utils.sign.Md5Utils; +import com.ruoyi.pay.sqb.constant.SqbConstant; +import com.ruoyi.pay.sqb.utils.HttpUtil; @Service -@PropertySource("classpath:sqbpay.properties") -@ConfigurationProperties(prefix = "sqbpay") -@Data public class SQBServiceImpl { - private String apiDomain; - private String terminalSn; - private String terminalKey; - private String appId; - private String vendorSn; - private String vendorKey; - private final static String CHARSET_UTF8 = "utf8"; + @Autowired + SqbConstant sqbConstant; /** * 计算字符串的MD5值 @@ -30,9 +23,9 @@ public class SQBServiceImpl { * @param signStr:签名字符串 * @return */ - public String getSign(String signStr) { + private String getSign(String signStr) { try { - String md5 = MD5Util.encryptMd5(signStr); + String md5 = Md5Utils.encryptMd5(signStr); return md5; } catch (UnsupportedEncodingException e) { e.printStackTrace(); @@ -47,24 +40,23 @@ public class SQBServiceImpl { * @return {terminal_sn:"$终端号",terminal_key:"$终端密钥"} */ public JSONObject activate(String code, String deviceId, String clientSn, String name) { - String url = apiDomain + "/terminal/activate"; + String url = sqbConstant.getApiDomain() + "/terminal/activate"; JSONObject params = new JSONObject(); try { - params.put("app_id", appId); // app_id,必填 + params.put("app_id", sqbConstant.getAppId()); // app_id,必填 params.put("code", code); // 激活码,必填 params.put("device_id", deviceId); // 客户方收银终端序列号,需保证同一app_id下唯一,必填。为方便识别,建议格式为“品牌名+门店编号+‘POS’+POS编号“ params.put("client_sn", clientSn); // 客户方终端编号,一般客户方或系统给收银终端的编号,必填 params.put("name", name); // 客户方终端名称,必填 - String sign = getSign(params.toString() + vendorKey); - System.out.println(params.toString() + vendorKey); - String result = HttpUtil.httpPost(url, params.toString(), sign, vendorSn); - JSONObject retObj = new JSONObject(result); + String sign = getSign(params.toString() + sqbConstant.getVendorKey()); + String result = HttpUtil.httpPost(url, params.toString(), sign, sqbConstant.getVendorSn()); + JSONObject retObj = JSON.parseObject(result); String resCode = retObj.get("result_code").toString(); if (!resCode.equals("200")) return null; String responseStr = retObj.get("biz_response").toString(); - JSONObject terminal = new JSONObject(responseStr); + JSONObject terminal = JSON.parseObject(responseStr); if (terminal.get("terminal_sn") == null || terminal.get("terminal_key") == null) return null; return terminal; @@ -79,21 +71,21 @@ public class SQBServiceImpl { * @return {terminal_sn:"$终端号",terminal_key:"$终端密钥"} */ public JSONObject checkin() { - String url = apiDomain + "/terminal/checkin"; + String url = sqbConstant.getApiDomain() + "/terminal/checkin"; JSONObject params = new JSONObject(); try { - params.put("terminal_sn", terminalSn); + params.put("terminal_sn", sqbConstant.getTerminalSn()); params.put("device_id", "HUISUAN001POS01"); params.put("os_info", "Mac OS"); params.put("sdk_version", "Java SDK v1.0"); - String sign = getSign(params.toString() + terminalKey); - String result = HttpUtil.httpPost(url, params.toString(), sign, terminalSn); - JSONObject retObj = new JSONObject(result); + String sign = getSign(params.toString() + sqbConstant.getTerminalKey()); + String result = HttpUtil.httpPost(url, params.toString(), sign, sqbConstant.getTerminalSn()); + JSONObject retObj = JSON.parseObject(result); String resCode = retObj.get("result_code").toString(); if (!resCode.equals("200")) return null; String responseStr = retObj.get("biz_response").toString(); - JSONObject terminal = new JSONObject(responseStr); + JSONObject terminal = JSON.parseObject(responseStr); if (terminal.get("terminal_sn") == null || terminal.get("terminal_key") == null) return null; return terminal; @@ -107,18 +99,19 @@ public class SQBServiceImpl { * * @return */ - public String refund(SageOrder orderPay) { - String url = apiDomain + "/upay/v2/refund"; + public String refund(String clientSn, String total, String payway, String subject, String operator, + String terminalSn) { + String url = sqbConstant.getApiDomain() + "/upay/v2/refund"; JSONObject params = new JSONObject(); try { - params.put("terminal_sn", terminalSn); // 收钱吧终端ID - params.put("client_sn", orderPay.getSn()); // 商户系统订单号,必须在商户系统内唯一;且长度不超过64字节 - params.put("refund_amount", orderPay.getTotalAmount()); // 退款金额 + params.put("terminal_sn", sqbConstant.getTerminalSn()); // 收钱吧终端ID + params.put("client_sn", clientSn); // 商户系统订单号,必须在商户系统内唯一;且长度不超过64字节 + params.put("refund_amount", total); // 退款金额 params.put("refund_request_no", "2"); // 商户退款所需序列号,表明是第几次退款 params.put("operator", "kay"); // 门店操作员 - String sign = getSign(params.toString() + terminalKey); - String result = HttpUtil.httpPost(url, params.toString(), sign, terminalSn); + String sign = getSign(params.toString() + sqbConstant.getTerminalKey()); + String result = HttpUtil.httpPost(url, params.toString(), sign, sqbConstant.getTerminalSn()); return result; } catch (Exception e) { @@ -131,16 +124,18 @@ public class SQBServiceImpl { * * @return */ - public String query(SageOrder orderPay) { - String url = apiDomain + "/upay/v2/query"; + + public String query(String clientSn, String total, String payway, String subject, String operator, + String terminalSn) { + String url = sqbConstant.getApiDomain() + "/upay/v2/query"; JSONObject params = new JSONObject(); try { - params.put("terminal_sn", terminalSn); // 终端号 - params.put("client_sn", orderPay.getSn()); // 商户系统订单号,必须在商户系统内唯一;且长度不超过64字节 - System.out.println(params.toString() + terminalKey); - String sign = getSign(params.toString() + terminalKey); - String result = HttpUtil.httpPost(url, params.toString(), sign, terminalSn); - JSONObject retObj = new JSONObject(result); + params.put("terminal_sn", sqbConstant.getTerminalSn()); // 终端号 + params.put("client_sn", clientSn); // 商户系统订单号,必须在商户系统内唯一;且长度不超过64字节 + System.out.println(params.toString() + sqbConstant.getTerminalKey()); + String sign = getSign(params.toString() + sqbConstant.getTerminalKey()); + String result = HttpUtil.httpPost(url, params.toString(), sign, sqbConstant.getTerminalSn()); + JSONObject retObj = JSON.parseObject(result); String resCode = retObj.get("result_code").toString(); if (!resCode.equals("200")) return null; @@ -151,22 +146,23 @@ public class SQBServiceImpl { } } - public String payUrl(SageOrder orderPay) throws UnsupportedEncodingException { + public String payUrl(String clientSn, String total, String payway, String subject, String operator, + String terminalSn) throws UnsupportedEncodingException { String param = "" + - "client_sn=" + orderPay.getSn() + - "&operator=" + orderPay.getOperator() + + "client_sn=" + clientSn + + "&operator=" + operator + "&return_url=" + "https://www.shouqianba.com/" + - "&subject=" + orderPay.getSubject() + + "&subject=" + subject + "&terminal_sn=" + terminalSn + - "&total_amount=" + orderPay.getTotalAmount(); + "&total_amount=" + total; String urlParam = "" + - "client_sn=" + orderPay.getSn() + - "&operator=" + URLEncoder.encode(orderPay.getOperator(), "UTF-8") + + "client_sn=" + clientSn + + "&operator=" + URLEncoder.encode(operator, "UTF-8") + "&return_url=" + "https://www.shouqianba.com/" + - "&subject=" + URLEncoder.encode(orderPay.getSubject(), "UTF-8") + + "&subject=" + URLEncoder.encode(subject, "UTF-8") + "&terminal_sn=" + terminalSn + - "&total_amount=" + orderPay.getTotalAmount(); - String sign = getSign(param + "&key=" + terminalKey); + "&total_amount=" + total; + String sign = getSign(param + "&key=" + sqbConstant.getTerminalKey()); return "https://qr.shouqianba.com/gateway?" + urlParam + "&sign=" + sign.toUpperCase(); } @@ -175,19 +171,19 @@ public class SQBServiceImpl { * * @return */ - public String precreate(SageOrder orderPay) { - String url = apiDomain + "/upay/v2/precreate"; + public String precreate(String sn, String total, String payway, String subject, String operator) { + String url = sqbConstant.getApiDomain() + "/upay/v2/precreate"; JSONObject params = new JSONObject(); try { - params.put("terminal_sn", terminalSn); // 收钱吧终端ID - params.put("client_sn", orderPay.getSn()); // 商户系统订单号,必须在商户系统内唯一;且长度不超过32字节 - params.put("total_amount", orderPay.getTotalAmount()); // 交易总金额 - params.put("payway", orderPay.getPayway()); // 支付方式 - params.put("subject", orderPay.getSubject()); // 交易简介 - params.put("operator", orderPay.getOperator()); // 门店操作员 + params.put("terminal_sn", sqbConstant.getTerminalSn()); // 收钱吧终端ID + params.put("client_sn", sn); // 商户系统订单号,必须在商户系统内唯一;且长度不超过32字节 + params.put("total_amou nt", total); // 交易总金额 + params.put("payway", payway); // 支付方式 + params.put("subject", subject); // 交易简介 + params.put("operator", operator); // 门店操作员 - String sign = getSign(params.toString() + terminalKey); - String result = HttpUtil.httpPost(url, params.toString(), sign, terminalSn); + String sign = getSign(params.toString() + sqbConstant.getTerminalKey()); + String result = HttpUtil.httpPost(url, params.toString(), sign, sqbConstant.getTerminalSn()); return result; } catch (Exception e) { return null; @@ -202,7 +198,7 @@ public class SQBServiceImpl { * @return */ public String cancel(String terminal_sn, String terminal_key) { - String url = apiDomain + "/upay/v2/cancel"; + String url = sqbConstant.getApiDomain() + "/upay/v2/cancel"; JSONObject params = new JSONObject(); try { params.put("terminal_sn", terminal_sn); // 终端号 diff --git a/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/utils/HttpUtil.java b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/utils/HttpUtil.java new file mode 100644 index 0000000..6085178 --- /dev/null +++ b/ruoyi-pay/ruoyi-pay-sqb/src/main/java/com/ruoyi/pay/sqb/utils/HttpUtil.java @@ -0,0 +1,132 @@ +package com.ruoyi.pay.sqb.utils; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.util.EntityUtils; + +public class HttpUtil { + public static String httpPostWithoutException(String url, String string,String sign,String sn) { + String xmlRes = "{}"; + try { + xmlRes = httpPost(url, string,sign,sn); + } catch (UnrecoverableKeyException e) { + + } catch (NoSuchAlgorithmException e) { + + } catch (KeyStoreException e) { + + } catch (KeyManagementException e) { + + } + return xmlRes; + } + /** + * http POST 请求 + * @param url:请求地址 + * @param body: body实体字符串 + * @param sign:签名 + * @param sn: 序列号 + * @return + */ + public static String httpPost(String url, String body,String sign,String sn) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + String xmlRes = "{}"; + HttpClient client = createSSLClientDefault(); + HttpPost httpost = new HttpPost(url); + try { + System.out.println("Request string: " + body); + //所有请求的body都需采用UTF-8编码 + StringEntity entity = new StringEntity(body,"UTF-8");// + entity.setContentType("application/json"); + httpost.setEntity(entity); + + //支付平台所有的API仅支持JSON格式的请求调用,HTTP请求头Content-Type设为application/json + httpost.addHeader("Content-Type","application/json"); + + //支付平台所有的API调用都需要签名验证,签名首部: Authorization: sn + " " + sign + httpost.addHeader("Authorization",sn + " " + sign); + System.out.println("Authorization" + sn + " " + sign); + HttpResponse response = client.execute(httpost); + + //所有响应也采用UTF-8编码 + xmlRes = EntityUtils.toString(response.getEntity(), "UTF-8"); + System.out.println("Response string: " + xmlRes); + } catch (ClientProtocolException e) { + + } catch (IOException e) { + + } + return xmlRes; + } + + public static CloseableHttpClient createSSLClientDefault() { + try { + SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { + //信任所有 + public boolean isTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + return true; + } + }).build(); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); + return HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } catch (KeyManagementException e) { + + } catch (NoSuchAlgorithmException e) { + + } catch (KeyStoreException e) { + + } + return HttpClients.createDefault(); + } + + public static String doGet(String url,String parameter) + { + String uriAPI =url+"?"+parameter ; //"http://XXXXX?str=I+am+get+String"; + String result= ""; + HttpClient client = createSSLClientDefault(); + HttpGet httpRequst = new HttpGet(uriAPI); + try { + + HttpResponse httpResponse = client.execute(httpRequst);//其中HttpGet是HttpUriRequst的子类 + if(httpResponse.getStatusLine().getStatusCode() == 200) + { + HttpEntity httpEntity = httpResponse.getEntity(); + result = EntityUtils.toString(httpEntity);//取出应答字符串 + // 一般来说都要删除多余的字符 + result.replaceAll("\r", "");//去掉返回结果中的"\r"字符,否则会在结果字符串后面显示一个小方格 + } + else + httpRequst.abort(); + } catch (ClientProtocolException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + result = e.getMessage().toString(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + result = e.getMessage().toString(); + } + return result; + } + +}