@clink-ai/clink-elements 目前发布到 0.0.3,API 还可能调整。建议在 package.json 里锁定版本。完整链路长什么样
Elements 只替换前端那一段。创建 Session 仍然必须在商户服务端完成,因为那一步要用 Secret Key。 第 1 步要提交的是商品标识,不是浏览器算好的最终金额。金额由第 2 步在服务端重新算。四方职责
一、服务端:写一个自己的 Session 接口
这一层不是可选优化。它同时是三条边界:Secret Key 的安全边界、金额校验的边界、本地订单关联的边界。 下面用的clinkRequest 就是 Hosted Checkout 接入 里那个带认证头的 helper,它返回的是响应中的 data。
uiMode传elements,returnUrl必填。URL 里可以放{ELEMENTS_SESSION_ID},Clink 会替换成真实 Session ID- 字段名是
returnUrl。有些旧资料写成redirectUrl,那是错的——redirectUrl是requires_action场景下响应里的跳转地址 merchantReferenceId只用于关联对账,不是幂等键。同一个值调两次会得到两个 Session
Clink 返回什么
上面clinkRequest 拿到的 data,节选如下:
{ "code": 200, "msg": "success", "data": { ... } } 这层信封,clinkRequest 已经剥掉了。
expireTime 当前返回的是 "2026-07-30 12:00:00" 这种格式,不带时区标识,也不是 RFC 3339。别直接丢给 new Date() 或按本地时间解析——不同运行环境的解释会不一致。也不要按”商户账户时区”或浏览器时区去推断。 后端目前用服务进程的默认时区序列化,没有按账户做转换,所以这个字符串代表哪个时区,接口本身没有表达。要精确处理跨时区逻辑,得等后端把输出契约升级成带 offset 的 RFC 3339 或 Unix 时间戳。在那之前,判断 Session 是否还能付款请看 status,不要拿这个字符串自己算。- 响应里没有 Publishable Key,也没有
environment。 这两样来自商户自己的应用配置,见下一节 - 响应里有
url,那是托管收银台地址。Elements 接入不使用它
二、前端:调自己的接口,然后初始化
浏览器只做两件事:向商户后端要一个sessionId,然后用它初始化 SDK。
PUBLIC_CLINK_PUBLISHABLE_KEY 和 environment 是应用的部署配置,来自后台 开发者 > API Keys 里的 Publishable Key(pk_uat_ 开头)。它们可以公开出现在浏览器里,但不来自 Create Session 的响应。
- Vite
- Next.js
SDK 的参数名是
publishKey,正文里说的 Publishable Key 就是它。代码里必须写 publishKey。三、挂载、提交与第三方按钮
结账页上可能出现两类按钮,处理方式完全不同: 商户自己的支付按钮 —— 银行卡这类需要宿主触发提交的流程。点击后调clink.submit()。
SDK 内置的第三方按钮 —— Apple Pay、Google Pay、PayPal 等。它们由 paymentMethod 组件内部渲染,点击也由 SDK 接管。这时 SDK 会发 submit-visible: false,宿主页面要把自己的按钮藏起来。
submit-enabled 报告的是「能不能提交」。按钮写 disabled = !enabled,别把事件值直接塞进 disabled——那样逻辑正好反过来。
配置 SDK 按钮样式
从0.0.3 开始,可以通过 presetOptions.sdkButtons 配置 Apple Pay、Google Pay、Link 和 PayPal 的按钮样式。
- 为兼容当前所有按钮渲染方式,请将高度设置在
40–55像素之间 - 非有限值或小于等于
0的高度会回退到45;其他不受支持的正数不会回退,可能导致对应按钮初始化失败 - 非有限值或负数圆角会回退到
6;超过按钮高度一半时会自动取高度的一半 - 不受支持的主题和类型会回退到对应默认值
- 所有 SDK 按钮都是
100%宽度 - Apple Pay 没有设置主题时,浅色模式用
black,深色模式用white - PayPal 的高度和圆角由 Clink 应用在 PayPal 组件的外层容器上,不会作为 PayPal SDK 参数传入
loadClinkElements()。
获取 SDK 按钮可用状态
在挂载paymentMethod 之前监听 sdk-button-initialized,避免漏掉首次事件。
true表示按钮初始化成功,并且当前设备可以使用false表示设备不支持、配置有误、网关不支持、第三方 SDK 加载失败,或按钮在当前初始化周期内超过 10 秒仍未完成- 只返回当前 Session 中存在的按钮;没有第三方按钮时返回
{}
四、事件
按用途分两组。 更新宿主 UI
流程提示
五、优惠码
可选能力。服务端的 Coupon 和 Promotion Code 怎么建,见 优惠与促销码。这里只讲前端。{ amount },金额字段在 amount 里面。写成 (info) => info.enablePromotionCode 拿到的是 undefined,优惠码入口会一直不显示。
折扣和最终应付金额以 amount 返回的为准,不要自己算。
六、错误处理
0.0.3 会抛出三类东西,处理方式不一样。
初始化:loadClinkElements()
它不是只抛 ClinkApiError。至少有三条路径:
响应体不是合法 JSON 时,
await res.json() 还会抛原生 SyntaxError。
所以 catch 参数要按 unknown 处理,并且一定要留兜底分支:
初始化之后:部分错误是同步抛的
不是所有运行期问题都走事件。 下面这些是同步抛出的原生Error,必须用 try/catch 接:
支付流程中的错误:走事件
真正跟支付流程有关的错误由收银台 iframe 发事件转发,不会被包装成异常类。七、实现约束
这几条写错会直接导致接入失败,其余布局细节按自己的设计来就行。上线自查
- 浏览器 Network 和构建产物里没有 Secret Key,也没有 Webhook 签名密钥
- 浏览器只调用商户自己的 Session 包装接口,没有直接调 Clink 的 Create Session
- 后端不相信浏览器传来的最终金额,按商品或购物车重新计算
- 本地订单、
merchantReferenceId和sessionId三者的映射已保存 - Publishable Key 和
environment来自应用公开配置,没有被当成 Create Session 的响应字段 - Apple Pay、Google Pay、PayPal 等按钮出现时,
submit-visible能让宿主页面的按钮隐藏 -
session-success不直接触发发货,最终状态来自验签 Webhook 和后端查询 - Session 变化或页面卸载时销毁了旧实例
接下来
Hosted Checkout 接入
后端和 Webhook 那两块,Elements 和 Hosted Checkout 完全一样。
上线检查
切生产前要过的清单。