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

# 如何对接 RESTful API

> 介绍如何通过 Clink RESTful API 接入 Customer Portal API 和 Browser Pay，构建 Agent 支付能力。

本页面向需要自己编写后端和用户界面的开发者，介绍如何通过 Clink 的 RESTful API 构建 Agent 支付能力。你可以使用 Customer Portal API（Clink API 文档中的 **Agent Portal API**）管理用户的钱包、支付方式、购买授权和订单，再使用 Browser Pay 完成商户页面上的支付。

这条路径适合希望自己控制产品界面、用户体验和业务流程的平台。它不要求你的 Agent 直接连接用户 MCP，也不要求使用 Vault SDK。Clink 负责用户支付数据和授权边界，你的平台负责用户身份、业务编排、商户浏览器和订单状态。

> [!NOTE]
>
> 本教程使用 Sandbox UAT 示例。请先按[如何获取平台凭据](https://docs.clinkbill.com/cn/guides/agent/platform_credentials.md)向 Clink 申请平台 `client_id` 和 `client_secret`。示例中的邮箱、用户 ID 和 Token 都是占位值。

## 1. 准备环境和平台凭据

Clink 为每个环境分别配置凭据。不要混用 Sandbox UAT 和 Production 的 Token、用户连接、支付方式或卡关联。

| 环境 | API Base URL |
| - | - |
| Sandbox UAT | `https://uat-api.clinkbill.com` |
| Production | `https://api.clinkbill.com` |

RESTful API 使用 Pre-registration 签发的平台 `client_id` 和 `client_secret`，与 MCP 共用同一个平台 client。申请方式、Clink 登记的内容和 RSA 公钥见[如何获取平台凭据](https://docs.clinkbill.com/cn/guides/agent/platform_credentials.md)。开通时要登记首次用户授权使用的 `redirect_uri`，以及 Portal Scope `portal:read portal:write`。

除平台凭据外，后端还要保存 `CLINK_API_BASE_URL`。用户 Token、解密后的卡字段和支付凭证只能保存在可信服务端，不要放进浏览器代码、URL、日志、Agent 输入或普通业务消息。

## 2. 获取平台 Token

使用平台凭据调用 OAuth token endpoint。平台 Token 只用于服务端管理用户连接和换取 Portal Token，不能直接调用用户 Portal API，也不能代替用户授权。

```http theme={null}
POST {API_BASE_URL}/agent/cwallet/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={CLINK_CLIENT_ID}
&client_secret={CLINK_CLIENT_SECRET}
&scope=agent-platform:account
```

按响应中的 `expires_in` 缓存平台 Token，到期后重新获取。

```json theme={null}
{
  "token_type": "Bearer",
  "access_token": "<PLATFORM_CLIENT_TOKEN>",
  "expires_in": 3600,
  "scope": "agent-platform:account"
}
```

## 3. 为用户获取 Portal Token

Customer Portal API 使用代表单个 Clink 用户的 Portal Token。先在你的系统中验证用户邮箱，并将 Clink 用户与稳定的平台用户 ID 关联。

新用户，或已在当前平台有有效授权的用户，只需要 `email`。已有 Clink 用户第一次授权当前平台时，还需要重定向和 PKCE 字段。建议每次请求前都生成随机 `state` 和 PKCE `code_verifier`，计算 S256 `code_challenge`，并将它们临时绑定到平台用户：

```http theme={null}
POST {API_BASE_URL}/agent/cwallet/platform/portal/tokens
Authorization: Bearer {PLATFORM_CLIENT_TOKEN}
Content-Type: application/json

{
  "email": "user@example.com",
  "external_user_id": "platform-user-123",
  "redirect_uri": "https://agent.example.com/oauth/clink/callback",
  "state": "{RANDOM_STATE}",
  "code_challenge": "{S256_CODE_CHALLENGE}",
  "code_challenge_method": "S256"
}
```

### 已授权用户

如果用户是新用户，或已经为当前平台完成授权，接口会返回 Portal Token：

```json theme={null}
{
  "code": 200,
  "msg": "Success",
  "data": {
    "status": "authorized",
    "customer_id": "ccus_...",
    "token_type": "Bearer",
    "access_token": "<PORTAL_USER_TOKEN>",
    "expires_in": 3600,
    "scope": "portal:read portal:write"
  }
}
```

将 Portal Token 加密保存在服务端，并只绑定到返回的 `customer_id`。调用 Customer Portal API 时使用：

```http theme={null}
Authorization: Bearer {PORTAL_USER_TOKEN}
```

### 需要用户授权

如果 Clink 找到了用户，但当前平台还没有有效的授权连接，响应会是：

```json theme={null}
{
  "code": 200,
  "msg": "Success",
  "data": {
    "status": "authorization_required",
    "customer_id": "ccus_...",
    "authorization_url": "https://uat-api.clinkbill.com/agent/cwallet/oauth/authorize?..."
  }
}
```

将 `authorization_url` 原样交给对应用户。用户完成 OAuth 回调后，确认 `state` 属于发起请求的平台用户并校验 `iss`，再重新调用 Portal Token 接口取得 Token。不要跳过授权、修改授权 URL，也不要把授权链接交给模型改写。用户取消或拒绝授权时，重新发起一次授权请求。

Portal Token 只访问当前用户的数据。它不能调用 `/mcp`；MCP 用户 Token 也不能调用 `/agent/platform/**`。

### Token 过期和重新获取

Portal Token 只有 access token，没有 refresh token。过期后，用平台 Token 重新调用 Portal Token 接口换取；如果当前平台对该用户的授权已失效，接口会再次返回 `authorization_required`。平台 Token 始终是获取用户 Token 的权限来源，只能保存在服务端。

## 4. 在服务端持久化用户 Token

Portal Token 是代表单个用户的服务端凭据。获取或刷新后，平台必须按平台用户和环境加密持久化保存 Token、过期时间、`customer_id` 和已授予的 Scope；不要只保存在当前请求或 Agent 进程内存中。

* 使用平台自己的稳定用户 ID 作为主键，不要用邮箱作为主键，也不要跨用户或环境复用 Token。
* 调用 Portal API 前从服务端安全存储读取 Token，并检查 `expires_in`；过期后用平台 Token 重新换取。Portal Token 没有 refresh token。
* Token 刷新或重新换取时原子更新存储，避免并发请求覆盖新 Token。授权失效时删除本地失效 Token，重新引导用户完成授权。
* Token 只能在可信服务端使用，不要写入浏览器代码、URL、日志、Agent 输入、普通业务消息或客户端存储。

示例数据结构：

```typescript theme={null}
type UserPortalToken = {
  platformUserId: string;
  environment: "sandbox" | "production";
  customerId: string;
  accessToken: string; // 加密存储
  expiresAt: Date;
  scope: string;
};
```

服务重启后，应能根据平台用户 ID 和环境读取这条记录并继续调用 Portal API。用户重新授权后，用新 Token 原子替换旧记录。

## 5. 读取账户和支付方式

使用 Portal Token 验证当前用户状态：

```http theme={null}
GET {API_BASE_URL}/agent/platform/me
Authorization: Bearer {PORTAL_USER_TOKEN}
```

读取用户已保存的支付方式：

```http theme={null}
GET {API_BASE_URL}/agent/platform/payment-methods
Authorization: Bearer {PORTAL_USER_TOKEN}
```

响应不会包含 PAN 或 CVV。你的界面只展示 Clink 返回的品牌、尾号和可用于 Agent Pay 的标识。不要根据卡片是否存在推断用户已经授权购买。

如果用户只需要添加卡片，创建托管绑卡会话。请求没有 body，会话始终为 Portal Token 对应的用户创建：

```http theme={null}
POST {API_BASE_URL}/agent/platform/bind-sessions
Authorization: Bearer {PORTAL_USER_TOKEN}
```

```json theme={null}
{
  "session_id": "vs_...",
  "url": "https://agent.clinkbill.dev/vault/vs_...",
  "expires_at": "2026-11-09T10:30:00Z"
}
```

将响应中的短期 `url` 原样展示给用户，并把它当作凭证处理：不要转发、截图或写入日志。用户完成 Clink 托管页面后，再重新读取支付方式。绑卡会话不创建购买授权；如果要一次完成绑卡和授权，见下一步的“绑卡并签约”。不要在平台页面收集真实卡号或 CVV。

## 6. 创建购买授权

Browser Pay 需要先有适用于本次购买的、已激活的购买授权（指令）。`payment_instrument_id` 决定签约模式：

| 模式 | `payment_instrument_id` | 创建后状态 | 用户在激活链接中完成 |
| - | - | - | - |
| 指定卡签约 | 传入用户已有的支付方式 | `CREATED` | 确认卡片并完成 Passkey 验证 |
| 绑卡并签约 | 省略 | `PENDING` | 在同一次 Passkey 流程中绑卡并授权 |

```http theme={null}
POST {API_BASE_URL}/agent/platform/authorizations
Authorization: Bearer {PORTAL_USER_TOKEN}
Content-Type: application/json

{
  "request_id": "platform-ref-001",
  "title": "Office supplies budget",
  "description": "Monthly office supplies purchase",
  "payment_instrument_id": "pi_...",
  "is_recurring": false,
  "mandates": [
    {
      "description": "Purchase office supplies",
      "amount_limit": "100.00",
      "currency_code": "USD",
      "merchant_category_code": "5732"
    }
  ]
}
```

响应返回 `instruction_id`、`status`、`activation_url` 和 `activation_expires_at`。激活步骤：

1. 把 `activation_url` 只发给目标用户，在用户自己的浏览器中打开。它是激活链接，不是 Portal Token，但拿到链接的人可以在有效期内取得该用户的 Clink 浏览器登录态，不要转发、截图或写入日志。
2. 轮询 `GET /agent/platform/authorizations/{instructionId}`，直到 `status` 为 `ACTIVE`。
3. 从指令中读取覆盖本次购买的 `mandate_id`，下一步获取凭证时使用。

用户没有完成激活时，指令保持 `CREATED` 或 `PENDING`，不会授权任何消费。`request_id` 是幂等键：24 小时内用完全一致的参数重试会返回同一条指令；同一 `request_id` 携带不同参数返回 `409`。完整字段见 [Portal 接口参考](https://docs.clinkbill.com/cn/api-reference/agent-portal/portal_apis.md#创建指令)。

需要查看当前用户的授权时：

```http theme={null}
GET {API_BASE_URL}/agent/platform/authorizations
Authorization: Bearer {PORTAL_USER_TOKEN}
```

每次支付前重新检查金额、币种、商户、有效期、剩余额度和授权状态。不要把一个授权跨用户或跨环境复用。

## 7. 使用 Browser Pay 获取支付凭证

商户浏览器应先读取最终结账页的金额、币种、商户信息和订单标识。平台后端再使用已激活指令的 `instruction_id`、覆盖本次购买的 `mandate_id` 和这些真实结账信息，请求一次性支付凭证。

Browser Pay 的凭证接口属于 Portal API：

```http theme={null}
POST {API_BASE_URL}/agent/platform/credential
Authorization: Bearer {PORTAL_USER_TOKEN}
Content-Type: application/json

{
  "instruction_id": "inst_...",
  "mandate_id": "mand_...",
  "client_reference_id": "platform-ref-001",
  "amount": "12.50",
  "currency_code": "USD",
  "merchant_url": "https://shop.example.com/checkout",
  "merchant_name": "Example Store",
  "merchant_country_code": "US",
  "merchant_order_id": "shop-order-1"
}
```

`amount` 使用主货币单位的十进制字符串，`merchant_url` 必须是 HTTPS 结账页地址。`client_reference_id` 是本次购买的幂等键：请保存它，回报结果时使用同一个值；相同参数重试会返回同一凭证，不会重复签发。商品明细 `products` 和收货地址 `shipping_address` 等可选字段见 [Portal 接口参考](https://docs.clinkbill.com/cn/api-reference/agent-portal/portal_apis.md#获取凭证)。

响应模式取决于平台是否在 Clink 登记了公钥：

| 模式 | 响应 | 平台处理 |
| - | - | - |
| 已登记公钥（`matched: true`） | 密封的 `key` / `payload` 和 `expires_at` | 只在可信服务端用 RSA 私钥解密，解密方式见[加密凭证解密](https://docs.clinkbill.com/cn/guides/agent/browser_pay/decryption.md)。 |
| 未登记公钥（`matched: false`） | 明文 DPAN 和动态 CVV | 仅在服务端启用明文模式时返回；开关默认关闭，此时接口返回 `71211012`（`credential_public_key_not_configured`）。生产环境建议登记公钥。 |

解密后或明文返回的卡字段只在受控的结账执行器中短暂使用，不要写入日志、数据库、模型上下文或浏览器 local storage，也不要在结账后保留。以解密后 payload 中的有效期为准判断凭证可用时间，`expires_at` 只是估算。凭证响应超时时，用同一个 `client_reference_id` 重试取回原凭证，不要重新创建授权。

## 8. 在商户页面完成支付

你的浏览器执行器负责：

1. 打开商户结账页并读取最终金额、币种和商户信息。
2. 确认这些信息与购买授权和支付凭证的请求完全一致。
3. 在受信任的支付填充组件中使用凭证，不让模型或普通日志看到真实卡字段。
4. 提交商户订单并读取商户页面或订单接口的最终结果。
5. 在结果明确后调用支付结果接口。

Clink API 返回成功、PSP 返回 2xx 或凭证签发成功，都不能单独证明商户订单已支付。只有商户页面或订单接口的最终结果才能决定下一步。

## 9. 确认支付结果

确认商户结果后，用同一个 `client_reference_id` 回报结果。这是确认交易结果的唯一入口：

```http theme={null}
POST {API_BASE_URL}/agent/platform/credential-results
Authorization: Bearer {PORTAL_USER_TOKEN}
Content-Type: application/json

{
  "client_reference_id": "platform-ref-001",
  "outcome": "SUCCEEDED",
  "paid_amount": "12.50",
  "currency_code": "USD",
  "failure_reason": null
}
```

| `outcome` | 什么时候使用 |
| - | - |
| `SUCCEEDED` | 商户明确确认支付成功。 |
| `FAILED` | 卡已提交后，商户明确显示失败。 |
| `NOT_SUBMITTED` | 卡从未提交。 |
| `UNKNOWN` | 卡已提交，但无法确定结果。Clink 返回 `PENDING` 和 `report_deadline`，请在截止时间前补报最终结果。 |

回报规则：

* 最终结果需要在凭证签发后 24 小时内（`report_deadline` 之前）回报，超时返回 `410`。
* `FAILED` 和 `NOT_SUBMITTED` 时可以带 `failure_reason`，例如 `CARD_DECLINED`、`AMOUNT_CHANGED` 或 `BUYER_CANCELLED`。
* `paid_amount` 和 `currency_code` 必须成对出现；与授权金额不一致时，响应返回 `paid_amount_mismatch: true`。
* 已记录成功后再回报失败（或反过来）返回 `409`（`result_already_reported`）。

结果未知时，先查询原商户订单，再补报最终结果；不要换卡、创建第二个授权或重复提交同一笔支付。完整规则见 [Portal 接口参考](https://docs.clinkbill.com/cn/api-reference/agent-portal/portal_apis.md#回报支付结果)。

订单完成后，可以读取订单和退款历史：

```http theme={null}
GET {API_BASE_URL}/agent/platform/orders/{orderId}
Authorization: Bearer {PORTAL_USER_TOKEN}
```

Customer Portal API 可以读取退款记录，但不能发起退款。退款需要使用 Clink 提供的其他业务流程。

## 相关文档

* [如何获取平台凭据](https://docs.clinkbill.com/cn/guides/agent/platform_credentials.md)：申请 `client_id` 和 `client_secret`，以及三种注册方式。
* [Agent Portal 总览](https://docs.clinkbill.com/cn/api-reference/agent-portal/overview.md)：能力边界、Token 类型和 API 列表。
* [Portal token](https://docs.clinkbill.com/cn/api-reference/agent-portal/portal_token.md)：换取用户 Portal Token 的请求、响应和授权状态。
* [Portal 接口](https://docs.clinkbill.com/cn/api-reference/agent-portal/portal_apis.md)：账户、支付方式、购买授权、凭证、结果、订单和设置接口。
* [Browser Pay 指南](https://docs.clinkbill.com/cn/guides/agent/browser_pay)：商户浏览器结账、凭证保护和结果恢复。
* [加密凭证解密](https://docs.clinkbill.com/cn/guides/agent/browser_pay/decryption.md)：解密 `key` / `payload` 的示例。
* [如何对接 MCP](https://docs.clinkbill.com/cn/guides/agent/platform_integration/mcp_integration.md)：通过 MCP + OAuth 接入 Browser Pay。
* [如何对接 Vault SDK](https://docs.clinkbill.com/cn/guides/agent/platform_integration/vault_sdk.md)：Agent 只接触占位卡的 SDK 接入方式。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.