如何在 JavaFX 项目中集成 Clover payment
结论
JavaFX 本身不影响 Clover payment 接入,关键是选对集成方式:
- Clover REST API 用于创建或修改服务端资源,一般不会触发 Android emulator 自动显示支付页面。
- 如果要在 Clover 设备上显示收款界面,需要使用适合设备端或半集成场景的 Clover SDK,并连接 JavaFX 应用与 Clover 设备。
- sandbox 和 production 的业务流程基本一致,但两者的 API 域名、OAuth 地址、应用凭证、merchant、设备和测试数据相互隔离,不能混用。
- 如果 generating environment 指 production environment,接入代码可以复用,环境配置则要分别管理。
因此,接口返回成功而模拟器没有出现支付页面,可能是接口类型不合适、请求没有到达设备端,也可能是模拟器不支持该支付流程。
先确定 Clover 集成方式
Clover 的不同接口用途不同。编码前,先确定支付最终在哪一端完成。
仅管理订单或查询数据
如果 JavaFX 程序只负责创建订单、读取商品或查询交易结果,可以通过自己的后端调用 Clover REST API。
调用流程如下:
JavaFX application
|
v
Application backend
|
v
Clover REST API
这类请求操作的是 Clover 云端资源。订单创建成功,并不会自动让某台 Clover 设备打开收款页面。
让 Clover 设备显示支付界面
如果收银操作由 JavaFX 桌面程序发起,并需要在指定的 Clover 设备上显示付款页面,应采用 Clover 支持的设备连接或半集成方案,例如对应平台的 Remote Pay SDK。
调用流程如下:
JavaFX application
|
v
Clover device connector
|
v
Clover device payment UI
SDK 的选择取决于 Clover 设备型号、Java 运行环境、连接方式和账户所在地区。可用平台和版本可能会调整,具体支持范围以当前 Clover 开发者文档为准。
应用直接运行在 Clover Android 设备上
如果应用安装在 Clover Android 设备中,应按照 Clover Android 应用的方式集成。普通 Android emulator 无法完全代替真实 Clover 设备,特别是涉及读卡器、支付服务和系统级支付 Activity 时。
sandbox 与 production 的差异
两套环境的数据结构和业务步骤通常相近,但下面这些配置必须分开:
| 配置项 | Sandbox | Production |
|---|---|---|
| API 地址 | Sandbox API 域名 | Production API 域名 |
| OAuth 地址 | Sandbox 授权地址 | Production 授权地址 |
| App ID、App Secret | Sandbox 应用凭证 | Production 应用凭证 |
| Access Token | 仅访问测试资源 | 仅访问正式资源 |
| Merchant ID | 测试商户 | 正式商户 |
| 订单和交易数据 | 测试数据 | 正式数据 |
| Clover 设备 | 测试设备或受支持的模拟环境 | 已激活的正式设备 |
切换环境时不能只替换 API 域名。如果请求仍带有另一环境的 token、merchant ID 或 device ID,通常会收到认证失败、资源不存在或权限不足等响应。
环境配置可以集中管理:
public enum CloverEnvironment {
SANDBOX("https://apisandbox.dev.clover.com"),
PRODUCTION("https://api.clover.com");
private final String apiBaseUrl;
CloverEnvironment(String apiBaseUrl) {
this.apiBaseUrl = apiBaseUrl;
}
public String apiBaseUrl() {
return apiBaseUrl;
}
}
以上域名是常见配置示例。上线前需对照当前 Clover 官方文档确认,以免继续使用已经变更的地址或接口版本。
不要把凭证写死在 JavaFX 代码中:
CloverEnvironment environment =
CloverEnvironment.valueOf(
System.getenv().getOrDefault("CLOVER_ENV", "SANDBOX")
);
String accessToken = System.getenv("CLOVER_ACCESS_TOKEN");
String merchantId = System.getenv("CLOVER_MERCHANT_ID");
if (accessToken == null || merchantId == null) {
throw new IllegalStateException("Missing Clover credentials");
}
如果 JavaFX 应用会分发到用户电脑,建议由 JavaFX 调用自己的后端,再由后端保存 Clover secret 和 access token。桌面客户端中的密钥很容易被提取。
如何确认 API 字段
Clover 各类 API 对字段的定义并不相同,不能仅凭字段名判断用途。排查时需要确认:
- 当前调用的是订单、支付、退款还是设备端接口。
- 字段位于请求体、路径参数还是查询参数中。
- 字段是否必填或只读。
- 金额采用什么单位。支付系统常用最小货币单位,仍需以对应接口的定义为准。
merchantId、orderId、employeeId、deviceId等标识是否属于同一环境和商户。- 当前 token 是否具有所需权限。
- 接口是否要求关联已有订单、员工或设备。
如果页面说明不完整,可以对照该接口的 schema、请求示例和响应示例。不要直接复制其他支付场景的 JSON,同名字段在不同 API 中可能有不同的约束。
调试时可以记录以下信息,但必须脱敏:
HTTP method
Request URL
HTTP status
Clover request/correlation identifier(如果响应中提供)
Response body
Merchant ID
Environment
Request timestamp
日志中不要写入 access token、App Secret、银行卡数据或完整支付凭证。
模拟器为什么没有弹出支付页面
最常见的情况是调用了云端 REST API,却希望它直接驱动模拟器界面。REST API 返回成功,通常只表示服务端接受了请求,无法证明设备已经收到打开支付 UI 的指令。
可以按下面几项继续检查。
1. 查看调用结果,不要只观察 JavaFX 界面
先检查 HTTP 状态码和响应体:
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("status=" + response.statusCode());
System.out.println("body=" + response.body());
401 或 403 通常与凭证、授权范围或商户权限有关。404 可能表示地址错误,也可能是资源属于另一环境。遇到 400 时,需要根据响应中的字段错误继续排查。
即使收到 2xx,也要确认响应表示“支付已启动”,还是只表示“订单已创建”。
2. 确认模拟器是否具备支付能力
普通 Android emulator 没有 Clover 设备的全部系统服务和硬件能力。即使安装了某个 Clover 应用,也未必能够模拟读卡器、支付服务或真实设备上的系统 Activity。
Clover 提供的测试工具如果只支持订单或应用界面测试,实际支付 UI 仍可能需要受支持的 Clover 测试设备。可在对应 SDK 的兼容性说明中确认当前流程是否支持模拟器。
3. 检查应用、商户和设备是否匹配
使用设备端方案时,需要确认:
- Clover 应用已经安装并登录。
- 登录商户与请求中的 merchant ID 一致。
- 应用已在 sandbox 中正确安装或授权。
- 设备注册信息仍然有效。
- JavaFX 连接的是目标设备,不是旧的模拟器实例或其他商户名下的设备。
4. 检查 SDK 连接状态
设备型 SDK 通常采用异步方式工作。发送支付请求前,需要确认连接已经建立,并注册交易状态回调。
下面只是结构示例,不对应某个特定版本的 Clover 类名:
paymentConnector.addListener(new PaymentListener() {
@Override
public void onConnected() {
Platform.runLater(() -> statusLabel.setText("Clover device connected"));
}
@Override
public void onPaymentSuccess(String paymentId) {
Platform.runLater(() ->
statusLabel.setText("Payment completed: " + paymentId)
);
}
@Override
public void onPaymentFailure(String message) {
Platform.runLater(() ->
statusLabel.setText("Payment failed: " + message)
);
}
});
paymentConnector.connect();
收到连接成功事件后,再发起设备支付。实际接口名称和回调参数应按项目使用的 Clover SDK 版本替换。
JavaFX 控件只能在 JavaFX Application Thread 中更新,所以 SDK 回调里通常需要调用 Platform.runLater(...)。
5. 检查金额和请求标识
支付金额不能为零或无效值。每次交易还应生成唯一的外部请求标识,防止重试时把旧交易当成新交易。
long amountInMinorUnit = 1999L;
String externalPaymentId = UUID.randomUUID().toString();
if (amountInMinorUnit <= 0) {
throw new IllegalArgumentException("Payment amount must be positive");
}
金额是否必须使用最小货币单位,以及外部标识应填写在哪个字段中,需要根据实际使用的 Clover API 或 SDK 确认。
6. 检查网络和日志
确认 JavaFX 主机、Android emulator 与 Clover 服务之间的通信没有被代理、防火墙或证书配置阻断。排查时可以查看:
- JavaFX 应用日志;
- SDK 的连接与交易回调;
- Android
logcat; - Clover 应用日志;
- Clover Dashboard 中的订单或交易记录。
如果 Dashboard 中已经出现订单,而设备端完全没有请求记录,问题多半出在设备连接层。如果服务器端也没有订单或交易记录,应先检查 REST 请求、认证和字段配置。
推荐的 JavaFX 调用结构
JavaFX 客户端不应直接保存 Clover secret。桌面程序可以只向业务后端提交支付意图:
record PaymentRequest(long amount, String currency, String requestId) {}
PaymentRequest payload = new PaymentRequest(
1999L,
"USD",
UUID.randomUUID().toString()
);
由 JavaFX 调用自己的后端:
HttpClient client = HttpClient.newHttpClient();
String json = """
{
"amount": 1999,
"currency": "USD",
"requestId": "%s"
}
""".formatted(UUID.randomUUID());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://your-backend.example/api/payments"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> Platform.runLater(() -> {
if (response.statusCode() >= 200 && response.statusCode() < 300) {
statusLabel.setText("Payment request accepted");
} else {
statusLabel.setText(
"Payment request failed: " + response.statusCode()
);
}
}))
.exceptionally(error -> {
Platform.runLater(() ->
statusLabel.setText("Network error: " + error.getMessage())
);
return null;
});
后端再按实际业务处理请求:
- 调用 Clover REST API 创建或查询云端资源;
- 通过受支持的连接方案通知 Clover 设备发起支付;
- 返回待处理状态,并通过轮询、回调或 Webhook 更新最终结果。
“后端已接受请求”和“支付成功”是两个不同的状态。JavaFX 界面应区分 pending、approved、declined、cancelled 以及连接失败等状态,具体名称以所用 API 为准。
排查顺序
- 确认当前使用的是 REST API、设备 SDK 还是 Clover Android SDK。
- 检查 sandbox 的 API 地址、token、merchant ID 和应用授权是否属于同一套环境。
- 查看 HTTP 状态码与响应体,确认请求实际创建了什么资源。
- 如果需要设备显示支付页面,确认请求经过了设备连接 SDK。
- 等待 SDK 报告连接成功后再发送支付请求。
- 查看连接回调、交易回调和 Android
logcat。 - 确认模拟器是否支持当前支付流程。
- 如果模拟器不支持,改用 Clover 官方支持的测试设备验证。
- sandbox 流程验证完成后,再整体切换到 production 配置,不要复用测试凭证和资源 ID。
备注:内容仅供参考。
