no message

This commit is contained in:
2025-09-08 03:04:04 +08:00
commit d814831822
105 changed files with 10519 additions and 0 deletions

View File

@ -0,0 +1,21 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2024 All Rights Reserved.
*/
package com.sczx.pay.utils;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayResponse;
/**
* @author jishupei.jsp
* @version : AlipayApiCallback, v0.1 2024年03月27日 5:46 下午 jishupei.jsp Exp $
*/
public interface AlipayApiCallback<T, R extends AlipayResponse> {
R process() throws AlipayApiException;
T getData(R response);
String getApiName();
}

View File

@ -0,0 +1,50 @@
/**
* Alipay.com Inc.
* Copyright (c) 2004-2024 All Rights Reserved.
*/
package com.sczx.pay.utils;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayResponse;
import com.sczx.pay.alipay.vo.OpenResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @author jishupei.jsp
* @version : AlipayApiTemplate, v0.1 2024年03月27日 5:48 下午 jishupei.jsp Exp $
*/
public class AlipayApiTemplate {
private static final Logger logger = LogManager.getLogger(AlipayApiTemplate.class);
public static <T, R extends AlipayResponse> OpenResponse<T> execute(AlipayApiCallback<T, R> callback) {
try {
//执行
R response = callback.process();
OpenResponse<T> tOpenResponse = new OpenResponse<>(response);
if (response.isSuccess()) {
//获取data
T data = callback.getData(response);
tOpenResponse.setData(data);
logger.info(callback.getApiName() + "调用成功:" + response.getBody());
} else {
logger.error(callback.getApiName() + "调用失败:" + JSON.toJSONString(response));
}
return tOpenResponse;
} catch (AlipayApiException e) {
//异常处理
logger.error(callback.getApiName() + "调用失败", e);
return new OpenResponse<>(e);
} catch (Exception e) {
//异常处理
logger.error(callback.getApiName() + "调用失败", e);
OpenResponse<T> response = new OpenResponse<>();
response.setCode("SYSTEM_ERROR");
response.setMsg("系统错误");
response.setSubMsg(e.getMessage());
return response;
}
}
}

View File

@ -0,0 +1,242 @@
package com.sczx.pay.utils;
import com.alipay.api.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
@Component
@Configuration
public class AlipaySdkUtil {
private AlipayClient alipayClient;
@Value("${alipay.appid}")
private String appId;
// 应用私钥
@Value("${alipay.privatekey}")
private String privateKey;
// 商户IDalipay.pid
@Value("${alipay.pid}")
private String pid;
// 是否使用OpenIdalipay.use_open_id
@Value("${alipay.use_open_id}")
private boolean useOpenId;
// 使用OpenId时必填alipay.openid
@Value("${alipay.openid}")
private String openid;
// 用户ID和OpenId二选一alipay.userid
@Value("${alipay.userid}")
private String userid;
// 是否是服务商alipay.is_isv
@Value("${alipay.is_isv}")
private boolean isIsv;
// 服务商必填alipay.app_auth_token
@Value("${alipay.app_auth_token}")
private String appAuthToken;
@Value("${alipay.alipay-public-cert-path}")
private String alipayPublicCertPath;
@Value("${alipay.ali-public-cert-path}")
private String aliPublicCertPath;
@Value("${alipay.alipay-root-cert-path}")
private String alipayRootCertPath;
@PostConstruct
public void init() throws AlipayApiException {
// 初始化v2 SDK
this.alipayClient = new DefaultAlipayClient(getAlipayConfig());
}
public <T extends AlipayResponse> T execute(AlipayRequest<T> request) throws AlipayApiException {
if (isIsv) {
return alipayClient.certificateExecute(request, null, appAuthToken);
}
return alipayClient.certificateExecute(request);
}
private AlipayConfig getAlipayConfig() {
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
alipayConfig.setAppId(appId);
alipayConfig.setPrivateKey(privateKey);
//设置应用公钥证书路径
alipayConfig.setAppCertPath(alipayPublicCertPath);
//设置支付宝公钥证书路径
alipayConfig.setAlipayPublicCertPath(aliPublicCertPath);
//设置支付宝根证书路径
alipayConfig.setRootCertPath(alipayRootCertPath);
alipayConfig.setFormat("json");
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");
Map<String, String> headers = new HashMap<>();
headers.put("alipay-sdk-demo", "app-item-0.0.1");
alipayConfig.setCustomHeaders(headers);
return alipayConfig;
}
/**
* Getter method for property <tt>pid</tt>.
*
* @return property value of pid
*/
public String getPid() {
return pid;
}
/**
* Setter method for property <tt>pid</tt>.
*
* @param pid value to be assigned to property pid
*/
public void setPid(String pid) {
this.pid = pid;
}
/**
* Getter method for property <tt>appId</tt>.
*
* @return property value of appId
*/
public String getAppId() {
return appId;
}
/**
* Setter method for property <tt>appId</tt>.
*
* @param appId value to be assigned to property appId
*/
public void setAppId(String appId) {
this.appId = appId;
}
/**
* Getter method for property <tt>openid</tt>.
*
* @return property value of openid
*/
public String getOpenid() {
return openid;
}
/**
* Setter method for property <tt>openid</tt>.
*
* @param openid value to be assigned to property openid
*/
public void setOpenid(String openid) {
this.openid = openid;
}
/**
* Getter method for property <tt>userid</tt>.
*
* @return property value of userid
*/
public String getUserid() {
return userid;
}
/**
* Setter method for property <tt>userid</tt>.
*
* @param userid value to be assigned to property userid
*/
public void setUserid(String userid) {
this.userid = userid;
}
/**
* Getter method for property <tt>useOpenId</tt>.
*
* @return property value of useOpenId
*/
public boolean isUseOpenId() {
return useOpenId;
}
/**
* Setter method for property <tt>useOpenId</tt>.
*
* @param useOpenId value to be assigned to property useOpenId
*/
public void setUseOpenId(boolean useOpenId) {
this.useOpenId = useOpenId;
}
/**
* Getter method for property <tt>isIsv</tt>.
*
* @return property value of isIsv
*/
public boolean isv() {
return isIsv;
}
/**
* Setter method for property <tt>isIsv</tt>.
*
* @param isv value to be assigned to property isIsv
*/
public void setIsv(boolean isv) {
isIsv = isv;
}
/**
* Getter method for property <tt>appAuthToken</tt>.
*
* @return property value of appAuthToken
*/
public String getAppAuthToken() {
return appAuthToken;
}
/**
* Setter method for property <tt>appAuthToken</tt>.
*
* @param appAuthToken value to be assigned to property appAuthToken
*/
public void setAppAuthToken(String appAuthToken) {
this.appAuthToken = appAuthToken;
}
/**
* Getter method for property <tt>privateKey</tt>.
*
* @return property value of privateKey
*/
public String getPrivateKey() {
return privateKey;
}
/**
* Setter method for property <tt>privateKey</tt>.
*
* @param privateKey value to be assigned to property privateKey
*/
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}

View File

@ -0,0 +1,159 @@
package com.sczx.pay.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* <取网卡物理地址--
* 1.在Windows,Linux系统下均可用
* 2.通过ipconifg,ifconfig获得计算机信息
* 3.再用模式匹配方式查找MAC地址与操作系统的语言无关>
*
* //* Description: <取计算机名--从环境变量中取>
* abstract 限制继承/创建实例
*/
public abstract class ComputerInfo {
private static String macAddressStr = null;
private static String computerName = System.getenv().get("COMPUTERNAME");
private static final String[] windowsCommand = { "ipconfig", "/all" };
private static final String[] linuxCommand = { "/sbin/ifconfig", "-a" };
private static final String[] macCommand = { "ifconfig", "-a" };
private static final Pattern macPattern = Pattern.compile(".*((:?[0-9a-f]{2}[-:]){5}[0-9a-f]{2}).*",
Pattern.CASE_INSENSITIVE);
/**
* 获取多个网卡地址
*
* @return
* @throws IOException
*/
private final static List<String> getMacAddressList() throws IOException {
final ArrayList<String> macAddressList = new ArrayList<String>();
final String os = System.getProperty("os.name");
final String command[];
if (os.startsWith("Windows")) {
command = windowsCommand;
} else if (os.startsWith("Linux")) {
command = linuxCommand;
} else if (os.startsWith("Mac")){
command = macCommand;
}
else {
throw new IOException("Unknow operating system:" + os);
}
// 执行命令
final Process process = Runtime.getRuntime().exec(command);
BufferedReader bufReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
for (String line = null; (line = bufReader.readLine()) != null;) {
Matcher matcher = macPattern.matcher(line);
if (matcher.matches()) {
macAddressList.add(matcher.group(1));
// macAddressList.add(matcher.group(1).replaceAll("[-:]",
// ""));//去掉MAC中的“-”
}
}
process.destroy();
bufReader.close();
return macAddressList;
}
/**
* 获取一个网卡地址(多个网卡时从中获取一个)
*
* @return
*/
public static String getMacAddress() {
if (macAddressStr == null || macAddressStr.equals("")) {
StringBuffer sb = new StringBuffer(); // 存放多个网卡地址用目前只取一个非0000000000E0隧道的值
try {
List<String> macList = getMacAddressList();
for (Iterator<String> iter = macList.iterator(); iter.hasNext();) {
String amac = iter.next();
if (!"0000000000E0".equals(amac)) {
sb.append(amac);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
macAddressStr = sb.toString();
}
return macAddressStr;
}
/**
* 获取电脑名
*
* @return
*/
public static String getComputerName() {
if (computerName == null || computerName.equals("")) {
computerName = System.getenv().get("COMPUTERNAME");
}
return computerName;
}
/**
* 获取客户端IP地址
*
* @return
*/
public static String getIpAddrAndName() throws IOException {
return InetAddress.getLocalHost().toString();
}
/**
* 获取客户端IP地址
*
* @return
*/
public static String getIpAddr() throws IOException {
return InetAddress.getLocalHost().getHostAddress().toString();
}
/**
* 获取电脑唯一标识
*
* @return
*/
public static String getComputerID() {
String id = getMacAddress();
if (id == null || id.equals("")) {
try {
id = getIpAddrAndName();
} catch (IOException e) {
e.printStackTrace();
}
}
return computerName;
}
/**
* 限制创建实例
*/
private ComputerInfo() {
}
public static void main(String[] args) throws IOException {
System.out.println(ComputerInfo.getMacAddress());
System.out.println(ComputerInfo.getComputerName());
System.out.println(ComputerInfo.getIpAddr());
System.out.println(ComputerInfo.getIpAddrAndName());
}
}

View File

@ -0,0 +1,135 @@
package com.sczx.pay.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
/**
* IP地址工具类
*/
public class IPUtils {
private static final Logger logger = LoggerFactory.getLogger(IPUtils.class);
// 多个IP查询服务以防某个服务不可用
private static final String[] IP_SERVICES = {
"https://api.ipify.org",
"https://icanhazip.com",
"https://ident.me"
};
private static String cachedPublicIP = null;
private static long lastFetchTime = 0;
private static final long CACHE_DURATION = 30 * 60 * 1000; // 30分钟缓存
/**
* 获取服务器公网IP地址
* @return 公网IP地址
*/
public static String getServerPublicIP() {
// 检查缓存
if (cachedPublicIP != null && (System.currentTimeMillis() - lastFetchTime) < CACHE_DURATION) {
return cachedPublicIP;
}
// 首先尝试从外部服务获取公网IP
String publicIP = fetchPublicIPFromExternalService();
if (publicIP != null && !publicIP.isEmpty()) {
cachedPublicIP = publicIP;
lastFetchTime = System.currentTimeMillis();
return publicIP;
}
// 如果外部服务不可用则获取本地IP
try {
String localIP = InetAddress.getLocalHost().getHostAddress();
logger.warn("无法获取公网IP使用本地IP: {}", localIP);
return localIP;
} catch (UnknownHostException e) {
logger.error("获取本地IP失败", e);
return "127.0.0.1";
}
}
/**
* 从外部服务获取公网IP
* @return 公网IP地址
*/
private static String fetchPublicIPFromExternalService() {
for (String serviceUrl : IP_SERVICES) {
try {
String ip = fetchIPFromService(serviceUrl);
if (isValidIP(ip)) {
logger.info("成功从 {} 获取公网IP: {}", serviceUrl, ip);
return ip;
}
} catch (Exception e) {
logger.warn("从服务 {} 获取IP失败: {}", serviceUrl, e.getMessage());
}
}
return null;
}
/**
* 从指定服务获取IP
* @param serviceUrl 服务URL
* @return IP地址
* @throws Exception 网络异常
*/
private static String fetchIPFromService(String serviceUrl) throws Exception {
URL url = new URL(serviceUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
return reader.readLine().trim();
} finally {
connection.disconnect();
}
}
/**
* 验证IP地址是否有效
* @param ip IP地址
* @return 是否有效
*/
private static boolean isValidIP(String ip) {
if (ip == null || ip.isEmpty()) {
return false;
}
// 简单的IP格式验证
String[] parts = ip.split("\\.");
if (parts.length != 4) {
return false;
}
for (String part : parts) {
try {
int num = Integer.parseInt(part);
if (num < 0 || num > 255) {
return false;
}
} catch (NumberFormatException e) {
return false;
}
}
// 排除私有IP和回环地址
if (ip.startsWith("127.") || ip.startsWith("10.") ||
ip.startsWith("192.168.") || ip.startsWith("172.")) {
// 私有地址也可以接受,因为可能在内网环境中使用
return true;
}
return true;
}
}

View File

@ -0,0 +1,243 @@
package com.sczx.pay.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;
public class WXPayUtil {
private static final Logger logger = LoggerFactory.getLogger(WXPayUtil.class);
private static final String CHARSET = "UTF-8";
private static final SecureRandom random = new SecureRandom();
/**
* 生成随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
}
/**
* 生成签名
*/
public static String generateSignature(Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, "MD5");
}
/**
* 生成签名
*/
public static String generateSignature(Map<String, String> data, String key, String signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[0]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals("sign")) {
continue;
}
String value = data.get(k);
if (value != null && value.trim().length() > 0) {
sb.append(k).append("=").append(value.trim()).append("&");
}
}
sb.append("key=").append(key);
if ("MD5".equals(signType)) {
return DigestUtils.md5Hex(sb.toString()).toUpperCase();
} else if ("HMAC-SHA256".equals(signType)) {
return HMACSHA256(sb.toString(), key);
} else {
throw new Exception("Invalid sign_type: " + signType);
}
}
/**
* HMAC-SHA256签名
*/
private static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(CHARSET), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes(CHARSET));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 验证签名
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
if (!data.containsKey("sign")) {
return false;
}
String sign = data.get("sign");
String calculatedSign = generateSignature(data, key);
return sign.equals(calculatedSign);
}
/**
* XML字符串转换为Map
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
Map<String, String> data = new HashMap<>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(new ByteArrayInputStream(strXML.getBytes(CHARSET)));
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
return data;
}
/**
* Map转换为XML字符串
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.newDocument();
Element root = document.createElement("xml");
document.appendChild(root);
for (String key : data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DOMSource source = new DOMSource(document);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
return writer.getBuffer().toString();
}
/**
* 验证微信支付通知签名
*/
public static boolean isSignatureValid(String xmlString, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlString);
return isSignatureValid(data, key);
}
/**
* 将XML字符串转换为指定类型的实体对象
*
* @param xmlString XML字符串
* @param clazz 实体类Class
* @param <T> 实体类型
* @return 实体对象
* @throws Exception 转换异常
*/
public static <T> T xmlToEntity(String xmlString, Class<T> clazz) throws Exception {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
return clazz.cast(unmarshaller.unmarshal(new StringReader(xmlString)));
}
public static String decryptReqInfo(String reqInfo, String apiKey) throws Exception {
try {
// 步骤1: 对加密串A做base64解码得到加密串B
byte[] encryptedData = Base64.decodeBase64(reqInfo);
// 步骤2: 对商户key做md5得到32位小写key*
String key = DigestUtils.md5Hex(apiKey).toLowerCase();
// 步骤3: 用key*对加密串B做AES-256-ECB解密PKCS7Padding)
byte[] decryptedData = decrypt(encryptedData, key);
return new String(decryptedData, "UTF-8");
} catch (Exception e) {
logger.error("解密微信退款通知req_info失败", e);
throw new Exception("解密失败: " + e.getMessage(), e);
}
}
/**
* AES解密方法
*
* @param encryptedData 加密数据
* @param key 解密密钥
* @return 解密后的字节数组
* @throws Exception 解密异常
*/
private static byte[] decrypt(byte[] encryptedData, String key) throws Exception {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return cipher.doFinal(encryptedData);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
IllegalBlockSizeException | BadPaddingException e) {
logger.error("AES解密失败", e);
throw new Exception("AES解密失败: " + e.getMessage(), e);
}
}
}