跳转到主要内容
本页面向需要自己编写后端和用户界面的开发者,介绍如何通过 Clink 的 RESTful API 构建 Agent 支付能力。你可以使用 Customer Portal API(Clink API 文档中的 Agent Portal API)管理用户的钱包、支付方式、购买授权和订单,再使用 Browser Pay 完成商户页面上的支付。 这条路径适合希望自己控制产品界面、用户体验和业务流程的平台。它不要求你的 Agent 直接连接用户 MCP,也不要求使用 Vault SDK。Clink 负责用户支付数据和授权边界,你的平台负责用户身份、业务编排、商户浏览器和订单状态。
本教程使用 Sandbox UAT 示例。请先按如何获取平台凭据向 Clink 申请平台 client_id 和 client_secret。示例中的邮箱、用户 ID 和 Token 都是占位值。

1. 准备环境和平台凭据

Clink 为每个环境分别配置凭据。不要混用 Sandbox UAT 和 Production 的 Token、用户连接、支付方式或卡关联。 RESTful API 使用 Pre-registration 签发的平台 client_id 和 client_secret,与 MCP 共用同一个平台 client。申请方式、Clink 登记的内容和 RSA 公钥见如何获取平台凭据。开通时要登记首次用户授权使用的 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,也不能代替用户授权。
按响应中的 expires_in 缓存平台 Token,到期后重新获取。

3. 为用户获取 Portal Token

Customer Portal API 使用代表单个 Clink 用户的 Portal Token。先在你的系统中验证用户邮箱,并将 Clink 用户与稳定的平台用户 ID 关联。 新用户,或已在当前平台有有效授权的用户,只需要 email。已有 Clink 用户第一次授权当前平台时,还需要重定向和 PKCE 字段。建议每次请求前都生成随机 state 和 PKCE code_verifier,计算 S256 code_challenge,并将它们临时绑定到平台用户:

已授权用户

如果用户是新用户,或已经为当前平台完成授权,接口会返回 Portal Token:
将 Portal Token 加密保存在服务端,并只绑定到返回的 customer_id。调用 Customer Portal API 时使用:

需要用户授权

如果 Clink 找到了用户,但当前平台还没有有效的授权连接,响应会是:
将 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 输入、普通业务消息或客户端存储。
示例数据结构:
服务重启后,应能根据平台用户 ID 和环境读取这条记录并继续调用 Portal API。用户重新授权后,用新 Token 原子替换旧记录。

5. 读取账户和支付方式

使用 Portal Token 验证当前用户状态:
读取用户已保存的支付方式:
响应不会包含 PAN 或 CVV。你的界面只展示 Clink 返回的品牌、尾号和可用于 Agent Pay 的标识。不要根据卡片是否存在推断用户已经授权购买。 如果用户只需要添加卡片,创建托管绑卡会话。请求没有 body,会话始终为 Portal Token 对应的用户创建:
将响应中的短期 url 原样展示给用户,并把它当作凭证处理:不要转发、截图或写入日志。用户完成 Clink 托管页面后,再重新读取支付方式。绑卡会话不创建购买授权;如果要一次完成绑卡和授权,见下一步的“绑卡并签约”。不要在平台页面收集真实卡号或 CVV。

6. 创建购买授权

Browser Pay 需要先有适用于本次购买的、已激活的购买授权(指令)。payment_instrument_id 决定签约模式:
响应返回 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 接口参考。 需要查看当前用户的授权时:
每次支付前重新检查金额、币种、商户、有效期、剩余额度和授权状态。不要把一个授权跨用户或跨环境复用。

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

商户浏览器应先读取最终结账页的金额、币种、商户信息和订单标识。平台后端再使用已激活指令的 instruction_id、覆盖本次购买的 mandate_id 和这些真实结账信息,请求一次性支付凭证。 Browser Pay 的凭证接口属于 Portal API:
amount 使用主货币单位的十进制字符串,merchant_url 必须是 HTTPS 结账页地址。client_reference_id 是本次购买的幂等键:请保存它,回报结果时使用同一个值;相同参数重试会返回同一凭证,不会重复签发。商品明细 products 和收货地址 shipping_address 等可选字段见 Portal 接口参考。 响应模式取决于平台是否在 Clink 登记了公钥: 解密后或明文返回的卡字段只在受控的结账执行器中短暂使用,不要写入日志、数据库、模型上下文或浏览器 local storage,也不要在结账后保留。以解密后 payload 中的有效期为准判断凭证可用时间,expires_at 只是估算。凭证响应超时时,用同一个 client_reference_id 重试取回原凭证,不要重新创建授权。

8. 在商户页面完成支付

你的浏览器执行器负责:
  1. 打开商户结账页并读取最终金额、币种和商户信息。
  2. 确认这些信息与购买授权和支付凭证的请求完全一致。
  3. 在受信任的支付填充组件中使用凭证,不让模型或普通日志看到真实卡字段。
  4. 提交商户订单并读取商户页面或订单接口的最终结果。
  5. 在结果明确后调用支付结果接口。
Clink API 返回成功、PSP 返回 2xx 或凭证签发成功,都不能单独证明商户订单已支付。只有商户页面或订单接口的最终结果才能决定下一步。

9. 确认支付结果

确认商户结果后,用同一个 client_reference_id 回报结果。这是确认交易结果的唯一入口:
回报规则:
  • 最终结果需要在凭证签发后 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 接口参考。 订单完成后,可以读取订单和退款历史:
Customer Portal API 可以读取退款记录,但不能发起退款。退款需要使用 Clink 提供的其他业务流程。

相关文档