npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@clink-ai/clink-elements

v0.0.4

Published

[English](./README.md)

Readme

Clink Elements SDK

English

将安全的、预构建的支付 UI 组件嵌入任意网站。Clink Elements SDK 是一个轻量、框架无关的 JavaScript 库,集成多种支付方式。

前置条件

  • 拥有 Clink 商户账号及 publish keypk_...
  • 通过 Clink 服务端 API 创建的 checkout session ID

安装

npm / yarn / pnpm

npm install @clink-ai/clink-elements
# 或
yarn add @clink-ai/clink-elements
# 或
pnpm add @clink-ai/clink-elements
import { loadClinkElements } from '@clink-ai/clink-elements';

CDN(Script 标签)

<script src="https://unpkg.com/@clink-ai/clink-elements/dist/index.iife.js"></script>

通过 <script> 标签加载时,SDK 挂载在全局变量 ClinkElements 下:

const { loadClinkElements } = ClinkElements;

快速开始

使用打包工具(ESM)

<!-- 可选: 货币切换组件 -->
<div id="currency-select"></div>
<!-- 必须: 支付表单组件 -->
<div id="payment-method"></div>
<!-- 您自定义的 submit 按钮 -->
<button id="pay-button" disabled>支付</button>
import { loadClinkElements } from '@clink-ai/clink-elements';

const clink = await loadClinkElements({
  publishKey: 'pk_live_xxxxxxxx',
  environment: 'production',
  sessionId: 'xxxx',
});

const paymentMethod = clink.createElement('paymentMethod');
const currencySelect = clink.createElement('currencySelect');

paymentMethod.mount('#payment-method');
currencySelect.mount('#currency-select');

clink.on('submit-enabled', (enabled) => {
  document.getElementById('pay-button').disabled = !enabled;
});

clink.on('session-success', () => {
  alert('支付成功!');
});

document.getElementById('pay-button').addEventListener('click', () => {
  clink.submit();
});

使用 Script 标签(IIFE)

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/@clink-ai/clink-elements/dist/index.iife.js"></script>
</head>
<body>
  <div id="currency-select"></div>
  <div id="payment-method"></div>
  <button id="pay-button" disabled>支付</button>

  <script>
    (async function () {
      var clink = await ClinkElements.loadClinkElements({
        publishKey: 'pk_live_xxxxxxxx',
        environment: 'production',
        sessionId: 'cs_xxxxxxxx',
      });

      var paymentMethod = clink.createElement('paymentMethod');
      var currencySelect = clink.createElement('currencySelect');

      paymentMethod.mount('#payment-method');
      currencySelect.mount('#currency-select');

      clink.on('submit-enabled', function (enabled) {
        document.getElementById('pay-button').disabled = !enabled;
      });

      clink.on('session-success', function () {
        alert('支付成功!');
      });

      document.getElementById('pay-button').addEventListener('click', function () {
        clink.submit();
      });
    })();
  </script>
</body>
</html>

API 参考

loadClinkElements(options)

异步工厂函数,验证商户凭证后返回 ClinkElements 实例。

async function loadClinkElements(
  options: LoadClinkElementsOptions
): Promise<ClinkElements>;

若 publish key 或 session ID 无效,抛出 ClinkApiError

LoadClinkElementsOptions

| 属性 | 类型 | 必填 | 说明 | |---|---|---|---| | publishKey | string | 是 | 商户 publish key(以 pk_ 开头) | | environment | 'sandbox' \| 'production' | 是 | 目标环境 | | sessionId | string | 是 | 服务端 API 创建的 checkout session ID | | presetOptions | PresetOptions | 否 | UI 自定义配置 |

PresetOptions

interface PresetOptions {
  locale?: 'de-DE' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'ko-KR' | 'pt-PT' | 'zh-CN' | 'zh-Hant';
  theme?: 'light' | 'dark';
  primaryColor?: string;
  radius?: {
    components?: number;
    card?: number;
  };
  currencySelect?:
    | { hideIfOneCurrency?: boolean }
    | { oneCurrencyStyle?: Record<string, string | number> };
  section?: {
    hideExpire?: boolean;
    hideError?: boolean;
    hideSkeleton?: boolean;
    hideSuccess?: boolean;
    hidePending?: boolean;
  };
  sdkButtons?: SdkButtonOptions;
}

| 属性 | 类型 | 默认值 | 说明 | |---|---|---|---| | locale | LocaleKey | 'en-US' | 显示语言,支持:'de-DE''en-US''es-ES''fr-FR''ja-JP''ko-KR''pt-PT''zh-CN''zh-Hant' | | theme | 'light' \| 'dark' | 'light' | 颜色主题 | | primaryColor | string | — | 主色调(任意 CSS 颜色值,如 '#1677FF') | | radius.components | number | 6 | 输入框和按钮的圆角半径(px) | | radius.card | number | 6 | 卡片容器的圆角半径(px) | | currencySelect | object | — | 货币选择器行为(详情) | | section | object | — | 控制 UI 状态的可见性(详情) | | sdkButtons | SdkButtonOptions | — | Apple Pay、Google Pay、Link 和 PayPal 按钮样式 |

注意: currencySelect.hideIfOneCurrencycurrencySelect.oneCurrencyStyle 互斥 — 只能设置其中一个。

第三方 SDK 按钮

按钮配置按用户可识别的支付方式组织,不区分 Stripe Connect、Stripe 或原生实现。SDK 会根据 session 自动选择底层实现并映射同一份配置。

presetOptions.sdkButtons 及其公共类型从 SDK 0.0.3 开始提供。

interface SdkButtonOptions {
  applePay?: ApplePayButtonOptions;
  googlePay?: GooglePayButtonOptions;
  link?: LinkButtonOptions;
  paypal?: PayPalButtonOptions;
}

interface ApplePayButtonOptions {
  buttonHeight?: number;
  buttonRadius?: number;
  buttonTheme?: 'black' | 'white' | 'white-outline';
  buttonType?: 'buy' | 'check-out' | 'donate' | 'order' | 'plain';
}

interface GooglePayButtonOptions {
  buttonHeight?: number;
  buttonRadius?: number;
  buttonTheme?: 'black' | 'white';
  buttonType?:
    | 'book'
    | 'buy'
    | 'checkout'
    | 'donate'
    | 'order'
    | 'pay'
    | 'plain'
    | 'subscribe';
}

interface LinkButtonOptions {
  buttonHeight?: number;
  buttonRadius?: number;
}

interface PayPalButtonOptions {
  buttonHeight?: number;
  buttonRadius?: number;
  buttonType?: 'buynow' | 'checkout' | 'donate' | 'pay' | 'subscribe';
}

| 按钮 | 默认值 | 说明 | |---|---|---| | Apple Pay | 高度 45、圆角 6、类型 buy | 未指定主题时,浅色主题使用 black,深色主题使用 white | | Google Pay | 高度 45、圆角 6、主题 black、类型 pay | 原生 Google Pay 与 Stripe Connect 使用相同公共配置 | | Link | 高度 45、圆角 6 | 支持 Stripe Express Checkout 高度和圆角配置 | | PayPal | 高度 45、圆角 6、PayPal SDK 默认类型 | Clink 通过外层样式应用高度和圆角,可通过 buttonType 设置按钮文案 |

const clink = await loadClinkElements({
  publishKey: 'pk_live_xxxxxxxx',
  environment: 'production',
  sessionId: 'cs_xxxxxxxx',
  presetOptions: {
    locale: 'zh-CN',
    theme: 'light',
    sdkButtons: {
      applePay: {
        buttonHeight: 48,
        buttonRadius: 8,
        buttonTheme: 'black',
        buttonType: 'buy',
      },
      googlePay: {
        buttonHeight: 48,
        buttonRadius: 8,
        buttonTheme: 'black',
        buttonType: 'pay',
      },
      link: { buttonHeight: 48, buttonRadius: 8 },
      paypal: { buttonHeight: 48, buttonRadius: 8, buttonType: 'pay' },
    },
  },
});
  • 所有按钮固定占满容器宽度,不提供任意 CSS、边框或尺寸模式透传。
  • buttonHeight 必须为有限正数;buttonRadius 会限制在 0buttonHeight / 2,非法值回退到默认值。
  • PayPal 的高度和圆角由 Clink 通过外层样式统一应用到按钮、加载态和错误态。
  • presetOptions.localesetLocale() 统一控制全部按钮语言。Apple Pay 未显式设置 buttonTheme 时继续跟随 presetOptions.themesetTheme()
  • 金额、币种、商户号、网关密钥、支付网络、tokenization 和 PayPal 拉起模式始终由 session 与 SDK 内部控制。

ClinkElements

loadClinkElements() 返回的主实例。

createElement(type)

createElement(type: 'paymentMethod' | 'currencySelect'): ClinkElement

创建支付元素,返回可挂载到 DOM 的 ClinkElement 实例。

  • 'paymentMethod' — 主支付表单(银行卡输入、钱包按钮等)
  • 'currencySelect' — 货币选择下拉框

约束:

  • 'paymentMethod' 必须'currencySelect' 之前创建
  • 每种类型在同一实例中只能创建一次

on(event, callback) / off(event, callback)

on<K extends EventType>(event: K, callback: EventCallback<K>): void
off<K extends EventType>(event: K, callback: EventCallback<K>): void

订阅或取消订阅事件。在 TypeScript 中回调参数会自动推断类型。

submit()

submit(): void

触发支付提交。SDK 内部自动处理所有支付流程(3DS 认证、二维码支付、第三方跳转)。

监听 session-successsession-pending 获取支付结果。

setLocale(locale)

setLocale(locale: 'de-DE' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'ko-KR' | 'pt-PT' | 'zh-CN' | 'zh-Hant'): void

运行时切换显示语言,作用于所有已挂载的元素。

setTheme(theme)

setTheme(theme: 'light' | 'dark'): void

运行时切换颜色主题,作用于所有已挂载的元素。

promoCodeChange(data)

promoCodeChange(
  data: { type: 'apply'; code: string } | { type: 'clear' }
): void

应用或清除优惠码。完整流程参见优惠码章节。

destroy()

destroy(): void

卸载所有元素、移除事件监听、清理资源。可安全多次调用(幂等)。页面卸载时务必调用此方法。

ClinkElement

单个 UI 元素。

mount(target)

mount(target: HTMLElement | string): void

将元素挂载到 DOM。接受 CSS 选择器字符串(如 '#payment-method')或 HTMLElement 引用。

若目标元素不存在或元素已挂载,将抛出异常。

unmount()

unmount(): void

从 DOM 中移除元素并恢复容器的原始样式。未挂载时调用无副作用。

事件

使用 clink.on(event, callback) 监听事件:

| 事件 | 回调数据 | 说明 | |---|---|---| | submit-enabled | boolean | 支付按钮是否应启用 | | submit-visible | boolean | 支付按钮是否应显示(部分钱包使用内置按钮) | | sdk-button-initialized | SdkButtonInitializedData | 当前 session 的第三方支付按钮完成一轮初始化 | | session-init-success | undefined | 会话初始化完成,元素已就绪 | | session-success | undefined | 支付成功 | | session-pending | undefined | 支付待确认(异步支付方式) | | amount-change | { amount: DueTodayAmountInfo } | 订单金额或价格明细发生变化 | | promo-code-error | { message: string } | 优惠码验证失败 | | error | { error: Error } | 发生错误(参见错误处理) |

sdk-button-initialized 从 SDK 0.0.3 开始提供。

示例

clink.on('submit-enabled', (enabled) => {
  payButton.disabled = !enabled;
});

clink.on('submit-visible', (visible) => {
  payButton.style.display = visible ? 'block' : 'none';
});

clink.on('sdk-button-initialized', (status) => {
  // 例如:{ applePay: true, googlePay: false, link: true }
  console.log('第三方按钮初始化状态:', status);
});

clink.on('amount-change', ({ amount }) => {
  priceDisplay.textContent = `${amount.currency} ${amount.dueTodayAmount}`;
});

clink.on('session-success', () => {
  window.location.href = '/thank-you';
});

clink.on('error', ({ error }) => {
  console.error('支付错误:', error);
});

SdkButtonInitializedData

type SdkButtonType = 'applePay' | 'googlePay' | 'link' | 'paypal';
type SdkButtonInitializedData = Partial<Record<SdkButtonType, boolean>>;
  • Payload 字段与 presetOptions.sdkButtons 配置键保持一致,统一使用驼峰命名。
  • Payload 只包含当前 session 提供的第三方按钮;没有目标按钮时为 {}
  • true 表示 SDK 完成初始化且按钮在当前设备真实可用,false 表示设备不支持、配置错误或 SDK 加载失败。
  • 事件会等待本轮所有目标按钮进入成功或失败终态后触发。支付方式集合变化或按钮重新加载完成后会再次触发。
  • 支付弹窗取消、下单失败等初始化完成后的交互错误不会改变该状态。

DueTodayAmountInfo

amount-change 事件提供详细的价格信息:

interface DueTodayAmountInfo {
  currency: string;
  subtotalAmount: number;
  dueTodayAmount: number;
  product: Product;
  multiProducts?: MultiProduct[];
  subscription: Subscription;
  enablePromotionCode?: boolean;
  promotionCodeInfo?: PromotionCodeInfo;
  requiresTaxCalculation?: boolean;
  taxInfo?: TaxInfo;
}

interface Product {
  name?: string;
  /** 多语言产品名称,key 为 locale(如 en-US、zh-CN) */
  localizedNames: Record<string, string> | null;
  type: 'ONETIME' | 'SUBSCRIPTION';
  imageUrl?: string;
}

interface Subscription {
  units?: number;
  recurring?: 'DAY' | 'WEEK' | 'MONTH' | 'QUARTER' | 'HALF_YEAR' | 'YEAR' | 'CUSTOM';
  customDays?: number;
  isFreeTrial?: boolean;
  freeTrialDays?: number;
  freeTrialEndDate?: string;
}

interface MultiProduct {
  name: string;
  quantity: number;
  unitAmount: number;
  currency: string;
  imageUrl?: string;
}

interface PromotionCodeInfo {
  name: string;
  terms: string;
  discountAmount: number;
  currency: string;
  durationType: 'ONCE' | 'REPEATING' | 'FOREVER' | null;
  durationPeriods?: number;
}

interface TaxInfo {
  name?: string;
  rate?: string;
  amount: number | null;
  currency: string;
}

自定义配置

语言

通过 presetOptions.locale 设置初始语言,或运行时切换:

clink.setLocale('zh-CN');

主题

通过 presetOptions.theme 设置初始主题,或运行时切换:

clink.setTheme('dark');

主色调

覆盖按钮和交互元素的主色调:

const clink = await loadClinkElements({
  // ...
  presetOptions: {
    primaryColor: '#7C3AED',
  },
});

圆角半径

自定义 UI 组件和卡片容器的圆角半径:

presetOptions: {
  radius: {
    components: 12,
    card: 16,
  },
}

货币选择器行为

控制仅有一种货币时的货币选择器行为:

// 方式 A:直接隐藏
presetOptions: {
  currencySelect: { hideIfOneCurrency: true },
}

// 方式 B:自定义样式
presetOptions: {
  currencySelect: {
    oneCurrencyStyle: { opacity: 0.5, pointerEvents: 'none' },
  },
}

两个选项互斥,不可同时设置。

区块可见性

控制支付后各 UI 状态的显示:

presetOptions: {
  section: {
    hideExpire: false,
    hideError: false,
    hideSkeleton: false,
    hideSuccess: true,
    hidePending: true,
  },
}

| 属性 | 说明 | |---|---| | hideExpire | 隐藏会话过期状态 | | hideError | 隐藏错误状态 | | hideSkeleton | 隐藏加载骨架屏 | | hideSuccess | 隐藏支付成功状态 | | hidePending | 隐藏支付待确认状态 |

优惠码

在结账流程中使用优惠码的完整步骤:

1. 检查是否启用优惠码

clink.on('amount-change', ({ amount }) => {
  if (amount.enablePromotionCode) {
    showPromoCodeInput();
  }
});

2. 应用优惠码

clink.promoCodeChange({ type: 'apply', code: 'SAVE20' });

3. 处理错误

clink.on('promo-code-error', ({ message }) => {
  showError(message);
});

4. 读取折扣信息

下一次 amount-change 事件的数据中将包含 promotionCodeInfo,携带折扣详情。

5. 清除优惠码

clink.promoCodeChange({ type: 'clear' });

错误处理

初始化错误

使用 try-catch 包裹 loadClinkElements() 处理初始化失败:

import {
  loadClinkElements,
  ClinkApiError,
  SessionExpiredError,
  SessionCompleteError,
} from '@clink-ai/clink-elements';

try {
  const clink = await loadClinkElements({ /* ... */ });
} catch (error) {
  if (error instanceof ClinkApiError) {
    console.error('凭证无效:', error.message);
  }
}

运行时错误

监听 error 事件处理初始化之后发生的错误:

clink.on('error', ({ error }) => {
  if (error instanceof SessionExpiredError) {
    showMessage('会话已过期,请刷新页面。');
  } else if (error instanceof SessionCompleteError) {
    showMessage('该笔支付已完成。');
  } else {
    showMessage('出了点问题,请重试。');
  }
});

异常类

| 类名 | 说明 | |---|---| | ClinkApiError | API 请求失败(publish key 无效、网络错误等) | | SessionExpiredError | checkout 会话已过期 | | SessionCompleteError | 该笔支付已完成 | | SessionLoadError | 加载会话数据失败 | | SessionNotSupportedError | 会话的 UI 模式与 Elements 不兼容 | | PromoCodeError | 优惠码操作失败 |

所有异常类均继承自原生 Error 类。

TypeScript 支持

SDK 附带完整的 TypeScript 类型声明,所有导出均有完整类型:

import { loadClinkElements } from '@clink-ai/clink-elements';
import type {
  ClinkElements,
  ClinkElement,
  LoadClinkElementsOptions,
  PresetOptions,
  EventType,
  EventCallback,
  EventDataMap,
  SdkButtonOptions,
  ApplePayButtonOptions,
  GooglePayButtonOptions,
  LinkButtonOptions,
  PayPalButtonOptions,
  SdkButtonType,
  SdkButtonInitializedData,
  Environment,
  ElementType,
  DueTodayAmountInfo,
  Product,
  Subscription,
  MultiProduct,
  PromotionCodeInfo,
  TaxInfo,
} from '@clink-ai/clink-elements';

事件回调的参数类型会根据事件名自动推断:

clink.on('submit-enabled', (enabled) => {
  // `enabled` 被推断为 `boolean`
});

clink.on('amount-change', ({ amount }) => {
  // `amount` 被推断为 `DueTodayAmountInfo`
});

注意事项

  • 仅限浏览器 — SDK 依赖 windowdocument,不兼容服务端渲染。请在客户端加载(如 onMounteduseEffectDOMContentLoaded 中)。
  • 元素创建顺序'paymentMethod' 必须在 'currencySelect' 之前创建,否则会抛出异常。
  • 单次创建 — 每种元素类型在同一 ClinkElements 实例中只能创建一次。
  • 资源清理 — 页面卸载时务必调用 destroy() 以避免内存泄漏。
  • 挂载目标 — 调用 mount() 时目标 DOM 元素必须已存在。
  • 沙箱测试 — 开发和测试阶段使用 environment: 'sandbox' 配合测试 publish key,上线时切换为 'production'