skyfffire 1 săptămână în urmă
părinte
comite
26e3fa2e2a
2 a modificat fișierele cu 528 adăugiri și 4 ștergeri
  1. 4 4
      pom.xml
  2. 524 0
      src/main/java/common/utils/tl/AllinpaySDK.java

+ 4 - 4
pom.xml

@@ -81,11 +81,11 @@
             <version>1.2.11</version> <!-- 匹配 SLF4J 1.7.x 的最新版本 -->
         </dependency>
 
-        <!-- 微信支付SDK -->
+        <!-- Hutool工具库,包含SM2加密等功能 -->
         <dependency>
-            <groupId>com.github.wechatpay-apiv3</groupId>
-            <artifactId>wechatpay-java</artifactId>
-            <version>0.2.17</version>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.22</version>
         </dependency>
     </dependencies>
 

+ 524 - 0
src/main/java/common/utils/tl/AllinpaySDK.java

@@ -0,0 +1,524 @@
+package common.utils.tl;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.crypto.asymmetric.SM2;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jfinal.kit.HttpKit;
+import com.jfinal.kit.StrKit;
+import java.util.*;
+
+/**
+ * 通联支付SDK
+ * 支持H5收银台支付功能
+ * 
+ * @author skyff
+ * @date 2024
+ */
+public class AllinpaySDK {
+    
+    // 生产环境配置
+    private static final String BASE_URL = "https://syb.allinpay.com";
+    private static final String H5_PAY_URL = BASE_URL + "/apiweb/h5unionpay/unionorder";
+    private static final String QUERY_URL = BASE_URL + "/apiweb/unitorder/query";
+    
+    // 商户配置信息(测试用静态配置)
+    private static final String APP_ID = "00000051";
+    private static final String CUS_ID = "990581007426001";
+    private static final String VERSION = "11";
+    private static final String SIGN_TYPE = "SM2";
+    
+    // SM2密钥对(测试用)
+    private static final String SM2_PRIVATE_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgNqz1EieIP8QVzV7vEmx5e8f7XN7/MIzoeXgEinxcG0agCgYIKoEcz1UBgi2hRANCAAQNfkEgaCQ4cdZ4aD2LWMcnkk5LALQfL05oY8x8XQDIyUM44N15YcTwtFNvHYgyeNRa93vlEUutp935n6rp4yuf";
+    
+    // 通联公钥(用于验签,实际使用时需要从通联获取)
+    private static final String ALLINPAY_PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEDX5BIGgkOHHWeGg9i1jHJ5JOSwC0Hy9OaGPMfF0AyMlDOODdeWHE8LRTbx2IMnjUWvd75RFLrae9+Z+q6eMrnw==";
+    
+    private static final SM2 sm2 = SmUtil.sm2(SM2_PRIVATE_KEY, ALLINPAY_PUBLIC_KEY);
+    
+    /**
+     * H5收银台支付
+     * 
+     * @param trxamt 交易金额(分)
+     * @param reqsn 商户交易单号
+     * @param body 商品描述
+     * @param remark 备注
+     * @param notify_url 异步通知地址
+     * @param return_url 同步跳转地址
+     * @return 支付结果
+     */
+    public static PaymentResult h5Pay(Long trxamt, String reqsn, String body, String remark, 
+                                     String notify_url, String return_url) {
+        try {
+            // 1. 构建请求参数
+            Map<String, String> params = new TreeMap<>();
+            params.put("appid", APP_ID);
+            params.put("cusid", CUS_ID);
+            params.put("version", "12");
+            params.put("signtype", SIGN_TYPE);
+            params.put("randomstr", RandomUtil.randomString(8));
+            params.put("trxamt", String.valueOf(trxamt));
+            params.put("reqsn", reqsn);
+            params.put("body", body);
+            params.put("remark", remark);
+            params.put("notify_url", notify_url);
+            params.put("return_url", return_url);
+            params.put("charset", "UTF-8");
+            
+            // 2. 生成签名
+            String sign = generateSign(params);
+            params.put("sign", sign);
+            
+            // 3. 发送请求
+            String response = sendPostRequest(H5_PAY_URL, params);
+            
+            // 4. 解析响应
+            return parsePaymentResponse(response);
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return PaymentResult.error("支付请求失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 查询支付结果
+     * 
+     * @param reqsn 商户交易单号
+     * @return 查询结果
+     */
+    public static QueryResult queryPayment(String reqsn) {
+        try {
+            // 1. 构建请求参数
+            Map<String, String> params = new TreeMap<>();
+            params.put("appid", APP_ID);
+            params.put("cusid", CUS_ID);
+            params.put("version", VERSION);
+            params.put("signtype", SIGN_TYPE);
+            params.put("randomstr", RandomUtil.randomString(8));
+            params.put("reqsn", reqsn);
+            
+            // 2. 生成签名
+            String sign = generateSign(params);
+            params.put("sign", sign);
+            
+            // 3. 发送请求
+            String response = sendPostRequest(QUERY_URL, params);
+            
+            // 4. 解析响应
+            return parseQueryResponse(response);
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return QueryResult.error("查询失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 处理异步通知
+     * 
+     * @param notifyParams 通知参数
+     * @return 处理结果
+     */
+    public static NotifyResult handleNotify(Map<String, String> notifyParams) {
+        try {
+            System.out.println("收到异步通知: " + notifyParams);
+            
+            // 1. 验证签名
+            String sign = notifyParams.get("sign");
+            if (sign == null || sign.trim().isEmpty()) {
+                return NotifyResult.error("签名为空");
+            }
+            
+            // 构建验签参数
+            Map<String, String> verifyParams = new TreeMap<>();
+            for (Map.Entry<String, String> entry : notifyParams.entrySet()) {
+                if (!"sign".equals(entry.getKey()) && entry.getValue() != null) {
+                    verifyParams.put(entry.getKey(), entry.getValue());
+                }
+            }
+            
+            String signString = buildSignString(verifyParams);
+            System.out.println("通知验签字符串: " + signString);
+            
+            // 使用通联公钥验签
+            boolean verifyResult = sm2.verifyHex(signString, sign);
+            
+            if (!verifyResult) {
+                return NotifyResult.error("签名验证失败");
+            }
+            
+            // 2. 解析通知内容
+            String reqsn = notifyParams.get("reqsn");
+            String trxstatus = notifyParams.get("trxstatus");
+            String trxid = notifyParams.get("trxid");
+            String trxamt = notifyParams.get("trxamt");
+            
+            return NotifyResult.success(reqsn, trxstatus, trxid, trxamt, "通知处理成功");
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return NotifyResult.error("通知处理失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 生成SM2签名
+     */
+    private static String generateSign(Map<String, String> params) throws Exception {
+        // 1. 排序并拼接参数(排除sign字段)
+        String signString = buildSignString(params);
+        System.out.println("签名字符串: " + signString);
+        
+        // 2. 使用SM2私钥签名
+        String sign = sm2.signHex(signString);
+        
+        System.out.println("生成的签名: " + sign);
+        return sign;
+    }
+    
+    /**
+     * 构建签名字符串
+     */
+    private static String buildSignString(Map<String, String> params) {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            
+            // 排除sign字段和空值
+            if (!"sign".equals(key) && value != null && !value.trim().isEmpty()) {
+                if (!first) {
+                    sb.append("&");
+                }
+                sb.append(key).append("=").append(value);
+                first = false;
+            }
+        }
+        
+        return sb.toString();
+    }
+    
+    /**
+     * 发送POST请求
+     */
+    private static String sendPostRequest(String url, Map<String, String> params) throws Exception {
+        // 构建表单参数
+        StringBuilder formData = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (!first) {
+                formData.append("&");
+            }
+            formData.append(entry.getKey()).append("=").append(entry.getValue());
+            first = false;
+        }
+        
+        // 设置请求头
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+        
+        // 使用JFinal的HttpKit发送请求
+        String response = HttpKit.post(url, null, formData.toString(), headers);
+        
+        if (StrKit.isBlank(response)) {
+            throw new RuntimeException("API响应为空");
+        }
+        
+        return response;
+    }
+    
+    /**
+     * 解析支付响应
+     */
+    private static PaymentResult parsePaymentResponse(String response) {
+        try {
+            System.out.println("支付响应: " + response);
+            
+            JSONObject jsonResponse = JSONUtil.parseObj(response);
+            
+            // 验证响应签名
+            if (!verifyResponseSign(jsonResponse)) {
+                return PaymentResult.error("响应签名验证失败");
+            }
+            
+            String retcode = jsonResponse.getStr("retcode");
+            String retmsg = jsonResponse.getStr("retmsg");
+            
+            if ("SUCCESS".equals(retcode)) {
+                String payUrl = jsonResponse.getStr("payinfo");
+                return PaymentResult.success(payUrl, "支付链接生成成功");
+            } else {
+                return PaymentResult.error("支付失败: " + retmsg);
+            }
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return PaymentResult.error("响应解析失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 解析查询响应
+     */
+    private static QueryResult parseQueryResponse(String response) {
+        try {
+            System.out.println("查询响应: " + response);
+            
+            JSONObject jsonResponse = JSONUtil.parseObj(response);
+            
+            // 验证响应签名
+            if (!verifyResponseSign(jsonResponse)) {
+                return QueryResult.error("响应签名验证失败");
+            }
+            
+            String retcode = jsonResponse.getStr("retcode");
+            String retmsg = jsonResponse.getStr("retmsg");
+            
+            if ("SUCCESS".equals(retcode)) {
+                String trxstatus = jsonResponse.getStr("trxstatus");
+                String trxid = jsonResponse.getStr("trxid");
+                String trxamt = jsonResponse.getStr("trxamt");
+                
+                return QueryResult.success(trxstatus, trxid, trxamt, "查询成功");
+            } else {
+                return QueryResult.error("查询失败: " + retmsg);
+            }
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return QueryResult.error("响应解析失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 验证响应签名
+     */
+    private static boolean verifyResponseSign(JSONObject response) {
+        try {
+            String sign = response.getStr("sign");
+            if (sign == null || sign.trim().isEmpty()) {
+                return false;
+            }
+            
+            // 构建验签字符串
+            Map<String, String> params = new TreeMap<>();
+            for (String key : response.keySet()) {
+                if (!"sign".equals(key)) {
+                    Object value = response.get(key);
+                    if (value != null) {
+                        params.put(key, value.toString());
+                    }
+                }
+            }
+            
+            String signString = buildSignString(params);
+            System.out.println("验签字符串: " + signString);
+            
+            // 使用通联公钥验签
+            boolean result = sm2.verifyHex(signString, sign);
+            
+            System.out.println("验签结果: " + result);
+            return result;
+            
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+    
+    /**
+     * 支付结果类
+     */
+    public static class PaymentResult {
+        private boolean success;
+        private String payUrl;
+        private String message;
+        private String errorCode;
+        
+        private PaymentResult(boolean success, String payUrl, String message, String errorCode) {
+            this.success = success;
+            this.payUrl = payUrl;
+            this.message = message;
+            this.errorCode = errorCode;
+        }
+        
+        public static PaymentResult success(String payUrl, String message) {
+            return new PaymentResult(true, payUrl, message, null);
+        }
+        
+        public static PaymentResult error(String message) {
+            return new PaymentResult(false, null, message, "ERROR");
+        }
+        
+        // Getters
+        public boolean isSuccess() { return success; }
+        public String getPayUrl() { return payUrl; }
+        public String getMessage() { return message; }
+        public String getErrorCode() { return errorCode; }
+        
+        @Override
+        public String toString() {
+            return "PaymentResult{" +
+                    "success=" + success +
+                    ", payUrl='" + payUrl + '\'' +
+                    ", message='" + message + '\'' +
+                    ", errorCode='" + errorCode + '\'' +
+                    '}';
+        }
+    }
+    
+    /**
+     * 查询结果类
+     */
+    public static class QueryResult {
+        private boolean success;
+        private String trxstatus;
+        private String trxid;
+        private String trxamt;
+        private String message;
+        private String errorCode;
+        
+        private QueryResult(boolean success, String trxstatus, String trxid, String trxamt, String message, String errorCode) {
+            this.success = success;
+            this.trxstatus = trxstatus;
+            this.trxid = trxid;
+            this.trxamt = trxamt;
+            this.message = message;
+            this.errorCode = errorCode;
+        }
+        
+        public static QueryResult success(String trxstatus, String trxid, String trxamt, String message) {
+            return new QueryResult(true, trxstatus, trxid, trxamt, message, null);
+        }
+        
+        public static QueryResult error(String message) {
+            return new QueryResult(false, null, null, null, message, "ERROR");
+        }
+        
+        // Getters
+        public boolean isSuccess() { return success; }
+        public String getTrxstatus() { return trxstatus; }
+        public String getTrxid() { return trxid; }
+        public String getTrxamt() { return trxamt; }
+        public String getMessage() { return message; }
+        public String getErrorCode() { return errorCode; }
+        
+        @Override
+        public String toString() {
+            return "QueryResult{" +
+                    "success=" + success +
+                    ", trxstatus='" + trxstatus + '\'' +
+                    ", trxid='" + trxid + '\'' +
+                    ", trxamt='" + trxamt + '\'' +
+                    ", message='" + message + '\'' +
+                    ", errorCode='" + errorCode + '\'' +
+                    '}';
+        }
+    }
+    
+    /**
+     * 通知结果类
+     */
+    public static class NotifyResult {
+        private boolean success;
+        private String reqsn;
+        private String trxstatus;
+        private String trxid;
+        private String trxamt;
+        private String message;
+        private String errorCode;
+        
+        private NotifyResult(boolean success, String reqsn, String trxstatus, String trxid, String trxamt, String message, String errorCode) {
+            this.success = success;
+            this.reqsn = reqsn;
+            this.trxstatus = trxstatus;
+            this.trxid = trxid;
+            this.trxamt = trxamt;
+            this.message = message;
+            this.errorCode = errorCode;
+        }
+        
+        public static NotifyResult success(String reqsn, String trxstatus, String trxid, String trxamt, String message) {
+            return new NotifyResult(true, reqsn, trxstatus, trxid, trxamt, message, null);
+        }
+        
+        public static NotifyResult error(String message) {
+            return new NotifyResult(false, null, null, null, null, message, "ERROR");
+        }
+        
+        // Getters
+        public boolean isSuccess() { return success; }
+        public String getReqsn() { return reqsn; }
+        public String getTrxstatus() { return trxstatus; }
+        public String getTrxid() { return trxid; }
+        public String getTrxamt() { return trxamt; }
+        public String getMessage() { return message; }
+        public String getErrorCode() { return errorCode; }
+        
+        @Override
+        public String toString() {
+            return "NotifyResult{" +
+                    "success=" + success +
+                    ", reqsn='" + reqsn + '\'' +
+                    ", trxstatus='" + trxstatus + '\'' +
+                    ", trxid='" + trxid + '\'' +
+                    ", trxamt='" + trxamt + '\'' +
+                    ", message='" + message + '\'' +
+                    ", errorCode='" + errorCode + '\'' +
+                    '}';
+        }
+    }
+    
+    /**
+     * 测试主方法
+     */
+    public static void main(String[] args) {
+        System.out.println("=== 通联支付SDK测试 ===");
+        
+        // 测试参数
+        Long trxamt = 100L; // 1元(分为单位)
+        String reqsn = "TEST" + System.currentTimeMillis(); // 商户订单号
+        String body = "测试商品";
+        String remark = "SDK测试订单";
+        String notify_url = "https://your-domain.com/api/payment/callback";
+        String return_url = "https://your-domain.com/payment-result";
+        
+        System.out.println("测试订单号: " + reqsn);
+        System.out.println("交易金额: " + trxamt + "分");
+        
+        // 测试1: H5支付
+        System.out.println("\n=== 测试H5支付 ===");
+        PaymentResult payResult = h5Pay(trxamt, reqsn, body, remark, notify_url, return_url);
+        System.out.println("支付结果: " + payResult);
+        
+        if (payResult.isSuccess()) {
+            System.out.println("支付链接: " + payResult.getPayUrl());
+            System.out.println("请在浏览器中打开上述链接完成支付");
+            
+            // 测试2: 查询支付结果
+            System.out.println("\n=== 测试查询支付结果 ===");
+            QueryResult queryResult = queryPayment(reqsn);
+            System.out.println("查询结果: " + queryResult);
+        } else {
+            System.out.println("支付失败: " + payResult.getMessage());
+        }
+        
+        // 测试3: 模拟异步通知处理
+        System.out.println("\n=== 测试异步通知处理 ===");
+        Map<String, String> notifyParams = new HashMap<>();
+        notifyParams.put("reqsn", reqsn);
+        notifyParams.put("trxstatus", "0000");
+        notifyParams.put("trxid", "TL" + System.currentTimeMillis());
+        notifyParams.put("trxamt", String.valueOf(trxamt));
+        notifyParams.put("sign", "test_sign"); // 实际使用时这里应该是真实的签名
+        
+        NotifyResult notifyResult = handleNotify(notifyParams);
+        System.out.println("通知处理结果: " + notifyResult);
+        
+        System.out.println("\n=== 测试完成 ===");
+    }
+}