之前手把手教大家将三方支付接口打包(TypeScript版)反响还不错,最近在工作中发现,其实还有很多朋友使用的是Java。那么本次,还是以拉卡拉开放平台的接口为例,演示开发JAVA SDK包。
一、开发环境准备
1. 环境配置
确保已安装以下工具:
- JDK 1.8+(推荐 11)
- Maven 3.6+
- IDE(IntelliJ IDEA 或 Eclipse)
检查环境:
java -version
mvn -version
2. 创建 Maven 项目
在 IDE 中创建新的 Maven 项目,GroupId 设为com.example
,ArtifactId 设为third-party-payment-sdk
,版本号1.0.0
。
项目结构如下:
third-party-payment-sdk/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── config/ // 配置类
│ │ │ ├── api/ // 接口调用类
│ │ │ ├── model/ // 数据模型
│ │ │ └── util/ // 工具类
│ │ └── resources/
│ └── test/
│ └── java/
│ └── com/
│ └── example/
├── pom.xml
└── README.md
3. 配置 pom.xml
添加核心依赖:
<dependencies>
<!-- HTTP客户端 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.8</version>
<scope>test</scope>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
二、核心功能开发
1. 配置类实现
创建PaymentConfig
类管理支付接口的环境配置:
package com.example.config;
public class PaymentConfig {
private boolean debug;
private String appId;
private String serialNo;
private String hostPro;
private String hostTest;
private String sm4Key;
private String privateKeyPath;
private String platformCertPath;
// 构造器
public PaymentConfig() {
// 默认配置
this.debug = false;
this.hostPro = "https://s2.lakala.com";
this.hostTest = "https://test.wsmsd.cn/sit";
}
// 获取当前环境主机地址
public String getHost() {
return debug ? hostTest : hostPro;
}
// Getter和Setter方法
public boolean isDebug() { return debug; }
public void setDebug(boolean debug) { this.debug = debug; }
public String getAppId() { return appId; }
public void setAppId(String appId) { this.appId = appId; }
// 其他属性的Getter/Setter省略...
}
2. 数据模型定义
创建请求和响应的实体类,以交易查询为例:
package com.example.model.request;
import com.fasterxml.jackson.annotation.JsonProperty;
public class TradeQueryRequest {
@JsonProperty("req_time")
private String reqTime;
@JsonProperty("version")
private String version = "3.0";
@JsonProperty("req_data")
private TradeQueryData reqData;
// Getter和Setter
public String getReqTime() { return reqTime; }
public void setReqTime(String reqTime) { this.reqTime = reqTime; }
public TradeQueryData getReqData() { return reqData; }
public void setReqData(TradeQueryData reqData) { this.reqData = reqData; }
}
// 内部数据类
class TradeQueryData {
@JsonProperty("merchant_no")
private String merchantNo;
@JsonProperty("term_no")
private String termNo;
@JsonProperty("out_trade_no")
private String outTradeNo;
@JsonProperty("trade_no")
private String tradeNo;
// Getter和Setter省略...
}
响应模型:
package com.example.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
public class PaymentResponse<T> {
@JsonProperty("code")
private int code;
@JsonProperty("message")
private String message;
@JsonProperty("data")
private T data;
// Getter和Setter
public boolean isSuccess() {
return code == 0;
}
// 其他Getter/Setter省略...
}
3. 接口调用核心类
实现PaymentClient
类封装 HTTP 请求逻辑:
package com.example.api;
import com.example.config.PaymentConfig;
import com.example.model.response.PaymentResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PaymentClient {
private static final Logger logger = LoggerFactory.getLogger(PaymentClient.class);
private final PaymentConfig config;
private final ObjectMapper objectMapper = new ObjectMapper();
public PaymentClient(PaymentConfig config) {
this.config = config;
}
/**
* 发送POST请求
*/
public <T, R> PaymentResponse<R> post(String path, T request, Class<R> responseDataClass) {
try {
// 1. 构建URL
String url = config.getHost() + path;
// 2. 转换请求对象为JSON
String requestJson = objectMapper.writeValueAsString(request);
logger.info("请求URL: {}, 参数: {}", url, requestJson);
// 3. 发送请求
String responseJson = Request.post(url)
.bodyString(requestJson, ContentType.APPLICATION_JSON)
.execute()
.returnContent()
.asString();
logger.info("响应: {}", responseJson);
// 4. 解析响应
return objectMapper.readValue(responseJson,
objectMapper.getTypeFactory().constructParametricType(PaymentResponse.class, responseDataClass));
} catch (Exception e) {
logger.error("请求异常", e);
throw new RuntimeException("接口调用失败", e);
}
}
}
4. 工具类实现
创建PaymentUtils
处理时间、签名等通用逻辑:
package com.example.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
import java.security.MessageDigest;
public class PaymentUtils {
/**
* 生成格式为YYYYMMDDHHmmss的时间戳
*/
public static String getCurrentTime() {
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
}
/**
* 生成签名
*/
public static String generateSign(Map<String, String> params, String secret) {
// 1. 参数排序
Map<String, String> sortedParams = new TreeMap<>(params);
// 2. 拼接参数
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
if (entry.getValue() != null && !entry.getValue().isEmpty()) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
}
sb.append("secret=").append(secret);
// 3. 计算MD5
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(sb.toString().getBytes("UTF-8"));
// 转换为十六进制字符串
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
result.append("0");
}
result.append(hex);
}
return result.toString().toUpperCase();
} catch (Exception e) {
throw new RuntimeException("签名生成失败", e);
}
}
}
三、接口封装示例
以交易查询接口为例,创建专用的 API 类:
package com.example.api;
import com.example.model.request.TradeQueryRequest;
import com.example.model.request.TradeQueryData;
import com.example.model.response.PaymentResponse;
import com.example.model.response.TradeQueryResponseData;
import com.example.util.PaymentUtils;
public class TradeApi {
private final PaymentClient client;
public TradeApi(PaymentClient client) {
this.client = client;
}
/**
* 查询交易
*/
public PaymentResponse<TradeQueryResponseData> queryTrade(String merchantNo, String outTradeNo) {
// 1. 构建请求参数
TradeQueryRequest request = new TradeQueryRequest();
request.setReqTime(PaymentUtils.getCurrentTime());
TradeQueryData data = new TradeQueryData();
data.setMerchantNo(merchantNo);
data.setOutTradeNo(outTradeNo);
request.setReqData(data);
// 2. 调用接口
return client.post("/api/v3/labs/query/tradequery",
request, TradeQueryResponseData.class);
}
}
四、单元测试
编写测试类验证功能:
package com.example.api;
import com.example.config.PaymentConfig;
import com.example.model.response.PaymentResponse;
import com.example.model.response.TradeQueryResponseData;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TradeApiTest {
private TradeApi tradeApi;
@BeforeEach
void setUp() {
// 初始化配置
PaymentConfig config = new PaymentConfig();
config.setDebug(true); // 测试环境
config.setAppId("你的appId");
config.setSerialNo("你的serialNo");
// 创建客户端
PaymentClient client = new PaymentClient(config);
tradeApi = new TradeApi(client);
}
@Test
void testQueryTrade() {
PaymentResponse<TradeQueryResponseData> response =
tradeApi.queryTrade("822290070111135", "TEST" + System.currentTimeMillis());
assertTrue(response.isSuccess(), "查询失败: " + response.getMessage());
System.out.println("交易状态: " + response.getData().getTradeStatus());
}
}
五、打包与发布
1. 打包为 JAR
执行 Maven 命令打包:
mvn clean package
生成的 JAR 包位于target/
目录下。
2. 发布到 Maven 仓库(可选)
配置pom.xml
中的分发仓库信息,然后执行:
mvn deploy
六、SDK 使用示例
其他项目引入 SDK 后,使用方式如下:
import com.example.api.TradeApi;
import com.example.api.PaymentClient;
import com.example.config.PaymentConfig;
import com.example.model.response.PaymentResponse;
import com.example.model.response.TradeQueryResponseData;
public class PaymentDemo {
public static void main(String[] args) {
// 1. 初始化配置
PaymentConfig config = new PaymentConfig();
config.setDebug(false); // 生产环境
config.setAppId("你的appId");
config.setSerialNo("你的serialNo");
// 2. 创建客户端和API实例
PaymentClient client = new PaymentClient(config);
TradeApi tradeApi = new TradeApi(client);
// 3. 调用接口
try {
PaymentResponse<TradeQueryResponseData> response =
tradeApi.queryTrade("商户号", "订单号");
if (response.isSuccess()) {
System.out.println("查询成功,交易号: " + response.getData().getTradeNo());
} else {
System.err.println("查询失败: " + response.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
总结
本文通过 Java 实现了三方支付接口的 SDK 封装,涵盖了配置管理、数据模型、HTTP 通信、签名工具等核心模块。使用 SDK 可以屏蔽支付接口的底层细节,让开发者专注于业务逻辑。实际开发中,可根据具体支付平台的接口文档(如文档中心)扩展更多功能,如退款、订单创建等接口封装。
通过标准化的 SDK 开发,不仅能提高团队协作效率,还能降低对接多个支付渠道的维护成本,是企业级支付系统开发的最佳实践。
PS: 其实官方已经有JAVA版的SDK供大家使用了,大家可以对比着来看~~~