添加支付模块

This commit is contained in:
D 2024-01-16 23:47:18 +08:00
parent f377ff993f
commit 4e0c8d3f1b
7 changed files with 316 additions and 97 deletions

View File

@ -207,6 +207,13 @@
<version>${ruoyi.version}</version>
</dependency>
<!-- 集成支付 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-pay</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
@ -231,6 +238,7 @@
<module>ruoyi-generator</module>
<module>ruoyi-common</module>
<module>ruoyi-oauth</module>
<module>ruoyi-pay</module>
</modules>
<packaging>pom</packaging>

View File

@ -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));
}
}

View File

@ -26,6 +26,13 @@
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-pay-sqb</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -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">
<parent>
<artifactId>ruoyi</artifactId>
<artifactId>ruoyi-pay</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.7.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-oauth</artifactId>
<artifactId>ruoyi-pay-sqb</artifactId>
<description>
system系统模块
@ -23,6 +23,13 @@
<artifactId>ruoyi-common</artifactId>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>
</project>

View File

@ -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;
}

View File

@ -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); // 终端号

View File

@ -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;
}
}