PAYATHON 2026

如何在 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 的差异

两套环境的数据结构和业务步骤通常相近,但下面这些配置必须分开:

配置项SandboxProduction
API 地址Sandbox API 域名Production API 域名
OAuth 地址Sandbox 授权地址Production 授权地址
App ID、App SecretSandbox 应用凭证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 为准。

排查顺序

  1. 确认当前使用的是 REST API、设备 SDK 还是 Clover Android SDK。
  2. 检查 sandbox 的 API 地址、token、merchant ID 和应用授权是否属于同一套环境。
  3. 查看 HTTP 状态码与响应体,确认请求实际创建了什么资源。
  4. 如果需要设备显示支付页面,确认请求经过了设备连接 SDK。
  5. 等待 SDK 报告连接成功后再发送支付请求。
  6. 查看连接回调、交易回调和 Android logcat。
  7. 确认模拟器是否支持当前支付流程。
  8. 如果模拟器不支持,改用 Clover 官方支持的测试设备验证。
  9. sandbox 流程验证完成后,再整体切换到 production 配置,不要复用测试凭证和资源 ID。

备注:内容仅供参考。