|
@@ -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=== 测试完成 ===");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|