> ## Documentation Index
> Fetch the complete documentation index at: https://developers.uqpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Java SDK

> 安装并配置 UQPAY 官方 Java SDK，集成支付、账户与发卡 API。

UQPAY Java SDK 以带类型的方式访问 UQPAY API，内置自动 OAuth2 鉴权、webhook 签名校验、基于 PGP 的授权决策以及沙盒模拟器。

<Card title="GitHub" icon="java" href="https://github.com/uqpay/uqpay-sdk-java">
  com.uqpay.sdk:uqpay-sdk-java
</Card>

<Info>
  SDK 需要 **Java 11** 或更高版本，配合 **Maven 3.6+** 或 **Gradle 7+**。
</Info>

## 安装

<Tabs>
  <Tab title="Maven">
    ```xml theme={null}
    <dependency>
        <groupId>com.uqpay.sdk</groupId>
        <artifactId>uqpay-sdk-java</artifactId>
        <version>1.0.0</version>
    </dependency>
    ```
  </Tab>

  <Tab title="Gradle">
    ```groovy theme={null}
    implementation 'com.uqpay.sdk:uqpay-sdk-java:1.0.0'
    ```
  </Tab>
</Tabs>

## 快速上手

```java theme={null}
import com.uqpay.sdk.v2.UqpayClient;

// 沙盒（测试用）
UqpayClient client = UqpayClient.sandbox("your-client-id", "your-api-key");

// 生产
UqpayClient client = UqpayClient.production("your-client-id", "your-api-key");
```

## 鉴权

SDK 自动处理 OAuth2 鉴权。它会用 `clientId` 和 `apiKey` 换取 access token，缓存并在过期前自动刷新，你无需手动管理 token。

## 配置

### 自定义 HTTP client

```java theme={null}
import okhttp3.OkHttpClient;
import com.uqpay.sdk.v2.config.Configuration;
import com.uqpay.sdk.v2.config.Environment;

OkHttpClient httpClient = new OkHttpClient.Builder()
        .connectTimeout(Duration.ofSeconds(60))
        .readTimeout(Duration.ofSeconds(60))
        .build();

Configuration config = Configuration.builder()
        .clientId("your-client-id")
        .apiKey("your-api-key")
        .environment(Environment.PRODUCTION)
        .httpClient(httpClient)
        .build();

UqpayClient client = new UqpayClient(config);
```

### 环境

| 常量                       | 说明           |
| ------------------------ | ------------ |
| `Environment.SANDBOX`    | 沙盒环境，用于测试和开发 |
| `Environment.PRODUCTION` | 生产环境，用于真实交易  |

## 分页

所有 list 方法返回分页响应，提供 `getData()`、`getTotalPages()` 与 `getTotalItems()`：

```java theme={null}
ListCardsRequest req = new ListCardsRequest();
req.setPageNumber(1);
req.setPageSize(50);
ListCardsResponse page = client.issuing().getCards().list(req);

System.out.println(page.getData());       // List<Card>
System.out.println(page.getTotalPages()); // 总页数
System.out.println(page.getTotalItems()); // 总条数
```

## Webhooks

校验 UQPAY 发来的 webhook 签名：

```java theme={null}
import com.uqpay.sdk.v2.webhook.WebhookVerifier;
import com.uqpay.sdk.v2.webhook.Event;
import com.uqpay.sdk.v2.common.UqpayWebhookException;

WebhookVerifier verifier = new WebhookVerifier("your-webhook-secret");

try {
    Event event = verifier.verifyAndParse(
        requestBodyString,
        request.getHeader("x-signature"),
        request.getHeader("x-timestamp")
    );

    if (event.isCardTransactionEvent()) {
        // 处理卡交易的授权、清算等
    } else if (Event.EVENT_NAME_CARDHOLDER_KYC.equals(event.getEventName())) {
        // 处理 KYC 状态更新
    }
} catch (UqpayWebhookException e) {
    // 签名无效或时间戳超过阈值
    response.setStatus(400);
}
```

校验器检查 HMAC-SHA512 签名，默认拒绝 timestamp 超过 300 秒的请求。

## 授权决策（PGP）

处理卡交易的实时授权决策。UQPAY 会向你的端点发送 PGP 加密的交易通知体，SDK 解密后调用你的处理器，并返回加密响应。

```java theme={null}
import com.uqpay.sdk.v2.issuing.AuthDecisionService;
import com.uqpay.sdk.v2.issuing.model.AuthDecisionConfig;
import com.uqpay.sdk.v2.issuing.model.AuthDecisionRequest;
import com.uqpay.sdk.v2.issuing.model.AuthDecisionResponse;

// 启动时一次性配置 PGP 密钥
AuthDecisionConfig config = new AuthDecisionConfig(
    "./keys/my-private.asc",
    System.getenv("PGP_PASSPHRASE"),
    "./keys/uqpay-public.asc"
);

AuthDecisionService authDecision = client.issuing().getAuthDecision();
authDecision.configure(config);

// 处理一次决策请求
AuthDecisionHandler handler = (AuthDecisionRequest tx) -> {
    if (tx.getBillingAmount() > 10000) {
        return AuthDecisionResponse.decline(tx.getTransactionId(), "51");
    }
    return AuthDecisionResponse.approve(tx.getTransactionId());
};

// 在 HTTP 处理器中传入原始加密请求体字符串：
String encryptedResponse = authDecision.processRequest(requestBodyString, handler);
response.getWriter().write(encryptedResponse);
```

## 错误处理

SDK 使用一组异常层级：

| 异常                      | 说明                              |
| ----------------------- | ------------------------------- |
| `UqpayException`        | 所有 SDK 错误的基类                    |
| `UqpayApiException`     | API 返回错误响应（包含 HTTP 状态、错误码、错误消息） |
| `UqpayAuthException`    | 鉴权失败（凭证无效、token 刷新失败）           |
| `UqpayNetworkException` | 网络层失败（超时、连接被拒）                  |
| `UqpayWebhookException` | webhook 签名校验失败                  |

```java theme={null}
try {
    Balance balance = client.banking().getBalances().get("SGD");
} catch (UqpayApiException e) {
    System.err.printf("API error: status=%d, code=%s, message=%s%n",
            e.getStatusCode(), e.getCode(), e.getMessage());
} catch (UqpayAuthException e) {
    System.err.println("Authentication failed: " + e.getMessage());
} catch (UqpayNetworkException e) {
    System.err.println("Network error: " + e.getMessage());
} catch (UqpayException e) {
    System.err.println("SDK error: " + e.getMessage());
}
```
