icpay

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ICPay Skill

ICPay 技能文档

Instruction manual for working with the ICPay project: SDK, widget, payment links, accounts, webhooks, relay payments, X402 v2, refunds, split payments, email notifications, demo site, and integrations (WordPress, WooCommerce, Shopify).
ICPay项目操作手册:涵盖SDK、Widget、支付链接、账户、Webhook、中继支付、X402 v2、退款、分账支付、邮件通知、演示站点以及各类集成(WordPress、WooCommerce、Shopify)。

Feature overview

功能概述

  • Relay payments — Per-chain recipient addresses (EVM, IC, Solana); funds forwarded to your specified addresses; optional relay fee in account settings.
  • X402 v2 — HTTP 402 “Payment Required” flow for IC, EVM, and Solana; sign authorization, ICPay facilitator settles; card/onramp-friendly.
  • Currency — Payment links have
    fiatCurrencyId
    ; user/account profile can set default fiat for payment (USD, EUR, etc.).
  • QR and mobile — WalletConnect QR for desktop; deep links for mobile browsers so users can pay with mobile phone wallet apps.
  • Wallet adapters — EVM: MetaMask, Coinbase, Brave, Rabby, OKX, WalletConnect. Solana: Phantom, Backpack. IC: Plug, Internet Identity (II), Oisy, NFID. Configurable enable/disable per adapter.
  • Split payments — Optional: multiple merchants share revenue via split rules (target account + percentage in basis points).
  • Refunds — Refund completed payments; execute-refunds worker; webhook
    payment.refunded
    ; email notification for refund completed.
  • Email notifications — Payment completed and refund completed emails to account; configurable templates; process-notifications worker.
  • Webhooks — Merchant endpoint receives payment/refund events; HMAC-SHA256 verification.
  • demo.icpay.org — Live demo/playground for building and testing custom widgets (all components, configurable options).
  • betterstripe.com — Sandbox environment: same features as icpay.org but on testnets (Solana devnet, Base Sepolia, Ark network testnet, and other testnets) for developers.
  • Filter tokens/chains — Widget config:
    tokenShortcodes
    ,
    chainShortcodes
    ,
    chainTypes
    to show only specific tokens or chains.
  • 中继支付 — 支持各链收款地址(EVM、IC、Solana);资金将转至您指定的地址;可在账户设置中配置可选的中继手续费。
  • X402 v2 — 针对IC、EVM和Solana的HTTP 402“需要支付”流程;签署授权后,由ICPay服务商完成结算;支持卡片/入金通道。
  • 货币设置 — 支付链接包含
    fiatCurrencyId
    ;用户/账户资料可设置支付默认法币(美元、欧元等)。
  • 二维码与移动端支持 — 桌面端显示WalletConnect二维码;移动端浏览器提供深度链接,用户可通过手机钱包应用完成支付。
  • 钱包适配器 — EVM链:MetaMask、Coinbase、Brave、Rabby、OKX、WalletConnect。Solana链:Phantom、Backpack。IC链:Plug、Internet Identity (II)、Oisy、NFID。可配置每个适配器的启用/禁用状态。
  • 分账支付 — 可选功能:多个商户通过分账规则共享收益(目标账户+基点百分比)。
  • 退款 — 可对已完成的支付发起退款;由execute-refunds worker处理;退款完成时触发Webhook
    payment.refunded
    并发送邮件通知。
  • 邮件通知 — 账户将收到支付完成和退款完成的邮件;支持配置模板;由process-notifications worker处理。
  • Webhook — 商户端点接收支付/退款事件;采用HMAC-SHA256验证。
  • demo.icpay.org — 用于构建和测试自定义Widget的在线演示/沙箱环境(包含所有组件和可配置选项)。
  • betterstripe.com — 沙箱环境:功能与icpay.org完全一致,但基于测试网(Solana devnet、Base Sepolia、Ark网络测试网及其他测试网),供开发者使用。
  • 筛选代币/链 — Widget配置项:
    tokenShortcodes
    chainShortcodes
    chainTypes
    ,用于仅显示特定代币或链。

Public Project layout

公开项目结构

  • icpay-sdk
    @ic-pay/icpay-sdk
    : typed client for browser/server; create payments, wallet helpers, events.
  • icpay-widget — Web Components + React wrappers: pay-button, amount-input, tip-jar, paywall, etc.
  • icpay-docs — Documentation site (docs.icpay.org); MDX under
    src/app/
    .
Use pnpm for install/build across the repo. Main technical reference: repo root
technical.md
.
  • icpay-sdk
    @ic-pay/icpay-sdk
    :适用于浏览器/服务器的类型化客户端;支持创建支付、钱包辅助功能、事件监听。
  • icpay-widget — Web组件 + React封装:包含支付按钮、金额输入框、小费罐、付费墙等组件。
  • icpay-docs — 文档站点(docs.icpay.org);基于
    src/app/
    下的MDX文件构建。
在整个仓库中使用pnpm进行安装/构建。主要技术参考文档:仓库根目录下的
technical.md

Keys and auth

密钥与认证

  • Publishable key (
    pk_live_*
    /
    pk_test_*
    ): safe for client; used by widget and public SDK calls.
  • Secret key: server-only; required for protected API (payments list, account info, webhook verification). Never expose in browser.
  • Auth:
    Authorization: Bearer <key>
    for both keys. API base:
    https://api.icpay.org
    (or env
    API_URL
    ).
  • 可发布密钥 (
    pk_live_*
    /
    pk_test_*
    ):可安全用于客户端;供Widget和公开SDK调用使用。
  • 密钥:仅用于服务器端;受保护的API(支付列表、账户信息、Webhook验证)需要此密钥。绝不能在浏览器中暴露。
  • 认证方式:两种密钥均使用
    Authorization: Bearer <key>
    请求头。API基础地址:
    https://api.icpay.org
    (或通过环境变量
    API_URL
    配置)。

SDK (
@ic-pay/icpay-sdk
)

SDK (
@ic-pay/icpay-sdk
)

Install:
pnpm add @ic-pay/icpay-sdk
.
Browser (publishable key):
ts
import { Icpay } from '@ic-pay/icpay-sdk';

const icpay = new Icpay({
  publishableKey: 'pk_live_xxx',
  apiUrl: 'https://api.icpay.org',
  debug: false,
});
Create payment (USD): Prefer
tokenShortcode
(e.g.
ic_icp
,
base_usdc
). For
createPaymentUsd
/
createPayment
you must provide wallet context:
actorProvider
+
connectedWallet
(IC) or
evmProvider
+ connected address (EVM).
ts
const tx = await icpay.createPaymentUsd({
  amountUsd: 5,
  metadata: { orderId: 'ORDER-123' },
});
X402 v2 (IC, EVM, Solana): Use
createPaymentX402Usd(request)
for sign-and-settle flows; SDK builds EIP-712 (EVM) or Solana message/transaction, sends to ICPay facilitator, returns terminal status. Fallback to regular
createPaymentUsd
when X402 not available. When you have an existing payment intent (e.g. from a pay link), pass
paymentIntentId
or
paymentIntent
in config or in the request so the SDK sends it to the x402 intent endpoint and the API reuses that intent instead of creating a second one.
Server (secret key): Use for
icpay.protected.*
:
getPaymentById
,
listPayments
,
getPaymentHistory
,
getDetailedAccountInfo
,
getVerifiedLedgersPrivate
, etc.
Prefer SDK methods over raw fetch. Handle errors via
IcpayError
; subscribe to SDK events for lifecycle (see SDK events below).
安装:
pnpm add @ic-pay/icpay-sdk
浏览器端(使用可发布密钥):
ts
import { Icpay } from '@ic-pay/icpay-sdk';

const icpay = new Icpay({
  publishableKey: 'pk_live_xxx',
  apiUrl: 'https://api.icpay.org',
  debug: false,
});
创建美元支付订单: 优先使用
tokenShortcode
(例如
ic_icp
base_usdc
)。调用
createPaymentUsd
/
createPayment
时,必须提供钱包上下文:IC链需
actorProvider
+
connectedWallet
,EVM链需
evmProvider
+ 已连接地址。
ts
const tx = await icpay.createPaymentUsd({
  amountUsd: 5,
  metadata: { orderId: 'ORDER-123' },
});
X402 v2(IC、EVM、Solana): 使用
createPaymentX402Usd(request)
实现签署-结算流程;SDK构建EIP-712(EVM)或Solana消息/交易,发送至ICPay服务商,返回终端状态。当X402不可用时,回退至常规
createPaymentUsd
方法。若已有支付意向(例如来自支付链接),请在配置或请求中传入**
paymentIntentId
paymentIntent
**,SDK会将其发送至x402意向端点,API将复用该意向而非创建新的意向。
服务器端(使用密钥): 用于
icpay.protected.*
相关方法:
getPaymentById
listPayments
getPaymentHistory
getDetailedAccountInfo
getVerifiedLedgersPrivate
等。
优先使用SDK方法而非原生fetch请求。通过
IcpayError
处理错误;订阅SDK事件以监听生命周期(详见下方SDK事件)。

Widget (
@ic-pay/icpay-widget
)

Widget (
@ic-pay/icpay-widget
)

Install:
pnpm add @ic-pay/icpay-widget @ic-pay/icpay-sdk
.
Components:
icpay-pay-button
,
icpay-amount-input
,
icpay-tip-jar
,
icpay-premium-content
,
icpay-article-paywall
,
icpay-coffee-shop
,
icpay-donation-thermometer
,
icpay-progress-bar
.
HTML (bundler):
html
<script type="module"> import '@ic-pay/icpay-widget'; </script>
<icpay-pay-button
  id="pay"
  publishableKey="YOUR_PK"
  tokenShortcodes="base_usdc"
  amountUsd="5"
></icpay-pay-button>
Set
config
on the element (object with
publishableKey
,
tokenShortcode
,
amountUsd
, etc.). Listen for
icpay-pay
,
icpay-error
on the element or
window
.
React: Use wrappers from
@ic-pay/icpay-widget/react
(e.g.
IcpayPayButton
,
IcpayTipJar
) with
config
prop and
onSuccess
/
onError
.
Hosted embed (no bundler): Script from
https://widget.icpay.org/v{VERSION}/embed.min.js
; then
ICPay.create('pay-button', { publishableKey, amountUsd, defaultSymbol, ... }).mount('#el')
.
Filter tokens/chains: In config set
tokenShortcodes
(e.g.
['ic_icp','base_usdc']
),
chainShortcodes
(e.g.
['ic','base']
), or
chainTypes
(e.g.
['ic','evm','sol']
) to restrict which tokens or chains are shown in the widget.
Relay payments: Set
recipientAddresses: { evm?: string, ic?: string, sol?: string }
in config; funds are relayed to those addresses. Optional relay fee is set per account in dashboard (Settings → ICPay Fees → Relay Fee).
QR and deep links: Payment links support
showWalletConnectQr
(default true) and
showBaseWalletQr
; WalletConnect shows QR on desktop and deep links on mobile so users can open wallet apps.
Handle success/error via events, not console. Theming: CSS variables on
:root
or component (e.g.
--icpay-primary
,
--icpay-surface
). See widget-reference.md for options, wallet adapters, and component-specific config.
安装:
pnpm add @ic-pay/icpay-widget @ic-pay/icpay-sdk
组件列表:
icpay-pay-button
icpay-amount-input
icpay-tip-jar
icpay-premium-content
icpay-article-paywall
icpay-coffee-shop
icpay-donation-thermometer
icpay-progress-bar
HTML(使用打包工具):
html
<script type="module"> import '@ic-pay/icpay-widget'; </script>
<icpay-pay-button
  id="pay"
  publishableKey="YOUR_PK"
  tokenShortcodes="base_usdc"
  amountUsd="5"
></icpay-pay-button>
可在元素上设置
config
对象(包含
publishableKey
tokenShortcode
amountUsd
等参数)。监听元素或
window
上的
icpay-pay
icpay-error
事件。
React环境: 使用
@ic-pay/icpay-widget/react
中的封装组件(例如
IcpayPayButton
IcpayTipJar
),传入
config
属性及
onSuccess
/
onError
回调。
托管嵌入(无需打包工具): 引入脚本
https://widget.icpay.org/v{VERSION}/embed.min.js
;然后调用
ICPay.create('pay-button', { publishableKey, amountUsd, defaultSymbol, ... }).mount('#el')
筛选代币/链: 在配置中设置
tokenShortcodes
(例如
['ic_icp','base_usdc']
)、
chainShortcodes
(例如
['ic','base']
)或
chainTypes
(例如
['ic','evm','sol']
),以限制Widget中显示的代币或链。
中继支付: 在配置中设置
recipientAddresses: { evm?: string, ic?: string, sol?: string }
;资金将转至这些地址。可在仪表盘的账户设置中配置可选的中继手续费(Settings → ICPay Fees → Relay Fee)。
二维码与深度链接: 支付链接支持
showWalletConnectQr
(默认开启)和
showBaseWalletQr
;WalletConnect在桌面端显示二维码,在移动端提供深度链接,方便用户打开钱包应用。
通过事件处理成功/错误,而非依赖控制台输出。主题定制:在
:root
或组件上使用CSS变量(例如
--icpay-primary
--icpay-surface
)。详见widget-reference.md获取配置选项、钱包适配器及组件专属配置说明。

SDK events (icpay-sdk)

SDK事件(icpay-sdk)

The SDK emits named events so agents and apps can react to payment lifecycle and method outcomes without polling. Subscribe with
icpay.on(type, (detail) => { ... })
; unsubscribe with
icpay.off(type, listener)
. Events can be disabled via config:
{ enableEvents: false }
(default is
true
). In browsers the SDK uses
EventTarget
/
CustomEvent
; in Node it uses an in-memory emitter.
SDK会触发命名事件,以便代理和应用无需轮询即可响应支付生命周期和方法执行结果。使用
icpay.on(type, (detail) => { ... })
订阅事件;使用
icpay.off(type, listener)
取消订阅。可通过配置
{ enableEvents: false }
禁用事件(默认开启)。浏览器环境下SDK使用
EventTarget
/
CustomEvent
;Node环境下使用内存事件发射器。

Success event (crucial for apps)

成功事件(对应用至关重要)

icpay-sdk-transaction-completed
— Fired when a payment has successfully completed. This is the primary event apps should listen to to fulfill orders, unlock content, or show confirmation.
  • When it fires: After the payment is confirmed (on-chain and/or backend reconciliation). Emitted from
    createPayment
    ,
    createPaymentUsd
    ,
    createPaymentX402Usd
    , and from polling/notify flows when status becomes
    completed
    .
  • Payload (detail): A TransactionResponse-shaped object:
    • transactionId
      (number) — Canister/backend transaction id.
    • status: 'completed'
      .
    • amount
      (string) — Amount in smallest unit.
    • recipientCanister
      (string).
    • timestamp
      (Date).
    • description?
      ,
      metadata?
      (e.g. your
      orderId
      ).
    • payment?
      — When present, includes
      paymentId
      ,
      paymentIntentId
      ,
      status
      ,
      canisterTxId
      ,
      transactionId
      (from
      PublicNotifyResponse
      ).
  • What to do: Use
    detail.paymentIntentId
    or
    detail.payment?.paymentIntentId
    and
    detail.payment?.paymentId
    for idempotency. Fulfill the order, persist success, show a success UI. Do not rely only on the widget callback; listening to this event ensures you capture completion even if the user navigates or the widget is unmounted.
Example (SDK instance):
ts
const unbind = icpay.on('icpay-sdk-transaction-completed', (detail) => {
  const paymentIntentId = detail.payment?.paymentIntentId ?? detail.paymentIntentId;
  const paymentId = detail.payment?.paymentId;
  // Fulfill order, update DB, show success (idempotent by paymentId/paymentIntentId)
});
// later: unbind();
Example (window — e.g. when using the widget, which forwards SDK events to
window
):
ts
function handleSuccess(e: CustomEvent) {
  const detail = e.detail ?? e;
  const paymentIntentId = detail.payment?.paymentIntentId ?? detail.paymentIntentId;
  const paymentId = detail.payment?.paymentId;
  // Fulfill order, update DB, show success (idempotent by paymentId/paymentIntentId)
}
window.addEventListener('icpay-sdk-transaction-completed', handleSuccess as EventListener);
// later: window.removeEventListener('icpay-sdk-transaction-completed', handleSuccess as EventListener);
icpay-sdk-transaction-completed
— 支付成功完成时触发。这是应用应监听的核心事件,用于完成订单、解锁内容或显示确认信息。
  • 触发时机: 支付确认后(链上确认及/或后端对账完成)。在
    createPayment
    createPaymentUsd
    createPaymentX402Usd
    方法执行后,或轮询/通知流程中状态变为
    completed
    时触发。
  • 负载(detail): 符合TransactionResponse格式的对象:
    • transactionId
      (数字) — 容器/后端交易ID。
    • status: 'completed'
    • amount
      (字符串) — 最小单位的金额。
    • recipientCanister
      (字符串)。
    • timestamp
      (Date类型)。
    • description?
      metadata?
      (例如自定义的
      orderId
      )。
  • 处理逻辑: 使用
    detail.paymentIntentId
    detail.payment?.paymentIntentId
    detail.payment?.paymentId
    实现幂等性。完成订单、更新数据库、显示成功界面。不要仅依赖Widget回调;监听此事件可确保即使用户导航离开或Widget被卸载,仍能捕获支付完成状态。
示例(SDK实例):
ts
const unbind = icpay.on('icpay-sdk-transaction-completed', (detail) => {
  const paymentIntentId = detail.payment?.paymentIntentId ?? detail.paymentIntentId;
  const paymentId = detail.payment?.paymentId;
  // 完成订单、更新数据库、显示成功界面(通过paymentId/paymentIntentId实现幂等)
});
// 后续取消订阅:unbind();
示例(window对象 — 例如使用Widget时,Widget会将SDK事件转发至window):
ts
function handleSuccess(e: CustomEvent) {
  const detail = e.detail ?? e;
  const paymentIntentId = detail.payment?.paymentIntentId ?? detail.paymentIntentId;
  const paymentId = detail.payment?.paymentId;
  // 完成订单、更新数据库、显示成功界面(通过paymentId/paymentIntentId实现幂等)
}
window.addEventListener('icpay-sdk-transaction-completed', handleSuccess as EventListener);
// 后续取消订阅:window.removeEventListener('icpay-sdk-transaction-completed', handleSuccess as EventListener);

Transaction lifecycle events

交易生命周期事件

  • icpay-sdk-transaction-created
    — Payment intent created; user still has to send funds. Detail:
    { paymentIntentId, amount, ledgerCanisterId, expectedSenderPrincipal?, accountCanisterId? }
    . Not emitted for onramp-only flows.
  • icpay-sdk-transaction-updated
    — Status changed (e.g. pending → processing). Detail: same shape as TransactionResponse (or with extra
    status
    ,
    requestedAmount
    ,
    paidAmount
    when relevant). Use for progress UI.
  • icpay-sdk-transaction-failed
    — Payment failed (rejected, timeout, or backend marked failed). Detail: TransactionResponse-like; check
    status: 'failed'
    and optional
    reason
    for messaging.
  • icpay-sdk-transaction-mismatched
    — Paid amount does not match requested amount. Detail: includes
    requestedAmount
    ,
    paidAmount
    plus TransactionResponse fields. Followed by
    icpay-sdk-transaction-updated
    with
    status: 'mismatched'
    . Use to prompt user to correct or refund/partial-fulfill per business rules.
  • icpay-sdk-transaction-created
    — 支付意向创建完成;用户仍需完成资金划转。负载:
    { paymentIntentId, amount, ledgerCanisterId, expectedSenderPrincipal?, accountCanisterId? }
    。仅入金流程不会触发此事件。
  • icpay-sdk-transaction-updated
    — 状态变更时触发(例如pending → processing)。负载格式与TransactionResponse一致(或包含额外的
    status
    requestedAmount
    paidAmount
    字段)。用于显示进度界面。
  • icpay-sdk-transaction-failed
    — 支付失败时触发(被拒绝、超时或后端标记为失败)。负载格式类似TransactionResponse;检查
    status: 'failed'
    及可选的
    reason
    字段以生成提示信息。
  • icpay-sdk-transaction-mismatched
    — 支付金额与请求金额不符时触发。负载包含
    requestedAmount
    paidAmount
    TransactionResponse字段。随后会触发**
    icpay-sdk-transaction-updated
    **事件,状态为
    mismatched
    。用于提示用户修正或根据业务规则发起退款/部分履约。

Method lifecycle events (generic)

方法生命周期事件(通用)

Every SDK method that uses the internal emitter fires:
  • icpay-sdk-method-start
    — Method invoked. Detail:
    { name: string, args?: any }
    (e.g.
    name: 'createPayment'
    ,
    args: { request: { amountUsd, ... } }
    ).
  • icpay-sdk-method-success
    — Method finished successfully. Detail:
    { name: string, result?: any }
    (e.g.
    name: 'createPayment'
    ,
    result
    is the return value or a summary).
  • icpay-sdk-method-error
    — Method threw. Detail:
    { name: string, error: any }
    .
Method names include:
notifyPayment
,
getAccountInfo
,
quoteAtxpRequest
,
payAtxpRequest
,
executeAtxpRequest
,
getVerifiedLedgers
,
getChains
,
getLedgerCanisterIdBySymbol
,
triggerTransactionSync
,
showWalletModal
,
connectWallet
,
getWalletProviders
,
isWalletProviderAvailable
,
getAccountAddress
,
getLedgerBalance
,
createPayment
,
createPaymentUsd
,
createPaymentX402Usd
,
pollTransactionStatus
,
notifyLedgerTransaction
,
getTransactionStatusPublic
,
sendFundsToLedger
,
getTransactionByFilter
,
getExternalWalletBalances
,
getSingleLedgerBalance
,
calculateTokenAmountFromUSD
,
getLedgerInfo
,
getAllLedgersWithPrices
, and protected API method names when using
icpay.protected.*
.
For payment success, prefer
icpay-sdk-transaction-completed
over
icpay-sdk-method-success
for
createPayment
/
createPaymentUsd
/
createPaymentX402Usd
, because the transaction-completed event carries the final payment state and is emitted at the right semantic time.
所有使用内部事件发射器的SDK方法都会触发以下事件:
  • icpay-sdk-method-start
    — 方法被调用时触发。负载:
    { name: string, args?: any }
    (例如
    name: 'createPayment'
    ,
    args: { request: { amountUsd, ... } }
    )。
  • icpay-sdk-method-success
    — 方法执行成功时触发。负载:
    { name: string, result?: any }
    (例如
    name: 'createPayment'
    ,
    result
    为返回值或摘要信息)。
  • icpay-sdk-method-error
    — 方法执行抛出错误时触发。负载:
    { name: string, error: any }
方法名称包括:
notifyPayment
getAccountInfo
quoteAtxpRequest
payAtxpRequest
executeAtxpRequest
getVerifiedLedgers
getChains
getLedgerCanisterIdBySymbol
triggerTransactionSync
showWalletModal
connectWallet
getWalletProviders
isWalletProviderAvailable
getAccountAddress
getLedgerBalance
createPayment
createPaymentUsd
createPaymentX402Usd
pollTransactionStatus
notifyLedgerTransaction
getTransactionStatusPublic
sendFundsToLedger
getTransactionByFilter
getExternalWalletBalances
getSingleLedgerBalance
calculateTokenAmountFromUSD
getLedgerInfo
getAllLedgersWithPrices
,以及使用
icpay.protected.*
时的受保护API方法名称。
对于支付成功事件,优先使用**
icpay-sdk-transaction-completed
**而非
createPayment
/
createPaymentUsd
/
createPaymentX402Usd
对应的
icpay-sdk-method-success
事件,因为交易完成事件携带最终支付状态,并在语义正确的时机触发。

Error event

错误事件

  • icpay-sdk-error
    — Any SDK error (including from method-error). Detail: an IcpayError-like object (
    code
    ,
    message
    ,
    details?
    ). Use for logging and user-facing error messages.
  • icpay-sdk-error
    — 任何SDK错误触发(包含方法错误)。负载:类似IcpayError的对象(包含
    code
    message
    details?
    字段)。用于日志记录和用户友好的错误提示。

Optional / internal

可选/内部事件

  • icpay-sdk-onramp-intent-created
    — Emitted when an onramp-only flow creates an intent (e.g. Transak). Detail:
    { paymentIntentId, amountUsd?, onramp? }
    . Useful for UI that shows “redirect to onramp” state.
  • icpay-sdk-onramp-intent-created
    — 仅入金流程创建意向时触发(例如Transak)。负载:
    { paymentIntentId, amountUsd?, onramp? }
    。用于显示“跳转至入金通道”状态的界面。

Summary table

事件汇总表

EventWhenDetail (main fields)
icpay-sdk-transaction-completedPayment succeededTransactionResponse +
payment?
use for fulfillment
icpay-sdk-transaction-createdIntent createdpaymentIntentId, amount, ledgerCanisterId, …
icpay-sdk-transaction-updatedStatus changedTransactionResponse (+ status/requestedAmount/paidAmount when relevant)
icpay-sdk-transaction-failedPayment failedTransactionResponse, optional reason
icpay-sdk-transaction-mismatchedAmount mismatchTransactionResponse + requestedAmount, paidAmount
icpay-sdk-method-startMethod calledname, args
icpay-sdk-method-successMethod resolvedname, result
icpay-sdk-method-errorMethod rejectedname, error
icpay-sdk-errorAny SDK errorIcpayError-like
icpay-sdk-onramp-intent-createdOnramp intent createdpaymentIntentId, amountUsd?, onramp?
事件触发时机负载(核心字段)
icpay-sdk-transaction-completed支付成功TransactionResponse +
payment?
用于履约处理
icpay-sdk-transaction-created意向创建完成paymentIntentId, amount, ledgerCanisterId, …
icpay-sdk-transaction-updated状态变更TransactionResponse (+ 相关的status/requestedAmount/paidAmount字段)
icpay-sdk-transaction-failed支付失败TransactionResponse, 可选reason字段
icpay-sdk-transaction-mismatched金额不符TransactionResponse + requestedAmount, paidAmount
icpay-sdk-method-start方法调用name, args
icpay-sdk-method-success方法执行成功name, result
icpay-sdk-method-error方法执行出错name, error
icpay-sdk-error任何SDK错误类似IcpayError的对象
icpay-sdk-onramp-intent-created入金意向创建paymentIntentId, amountUsd?, onramp?

Payment links

支付链接

Payment links are per-account entities with a unique shortcode. Public pay page:
https://icpay.org/pay/<shortcode>
.
  • Create (user/dashboard): API
    PaymentLinksService.createForAccount(accountId, dto)
    ; DTO includes
    name
    ,
    description
    ,
    amountUsd
    ,
    collectEmail
    ,
    requireEmail
    ,
    widgetOptions
    ,
    showWalletConnectQr
    , etc. Shortcode is generated (unique). User endpoints:
    POST /user/payment-links
    with JWT (see Flow: I am an agent).
  • Create (POS / publishable key):
    POST /sdk/public/payment-links
    with publishable key in
    Authorization: Bearer <pk_...>
    . Body: CreatePosPaymentLinkDto
    amountUsd
    (required, min 0.01), optional
    name
    ,
    description
    ,
    tokenShortcodes
    (array; when exactly one, the payment intent is created with that token; otherwise intent has no token and user selects on pay page),
    showWalletConnectQr
    (default false for POS),
    showBaseWalletQr
    (default true). Creates the link and an associated payment intent; response includes
    shortcode
    and
    paymentIntentId
    . Use the pay page URL
    https://icpay.org/pay/<shortcode>
    (optionally
    ?paymentIntentId=<id>
    for pre-filled intent). POS-only: recipient addresses are not set on the link.
  • Public fetch:
    GET /public/payment-links/:shortcode
    returns
    { link, account }
    (link config + account publishableKey/branding). Used by
    icpay-web
    pay page.
  • Merchant UI:
    icpay-web
    → Payment Links → create/edit; link to
    /pay/<shortcode>
    shown after create.
Payment link entity:
icpay-api/src/entities/payment-link.entity.ts
. Fields:
amountUsd
,
shortcode
,
fiatCurrencyId
(display currency for the link), collect/require for email, name, address, phone, quantity (min/max/default),
widgetOptions
(JSON),
showWalletConnectQr
,
showBaseWalletQr
,
isActive
.
支付链接是每个账户的专属实体,包含唯一的短代码。公开支付页面地址:
https://icpay.org/pay/<shortcode>
  • 创建(用户/仪表盘): 调用API
    PaymentLinksService.createForAccount(accountId, dto)
    ;DTO包含
    name
    description
    amountUsd
    collectEmail
    requireEmail
    widgetOptions
    showWalletConnectQr
    等字段。短代码自动生成(唯一)。用户端点:携带JWT调用
    POST /user/payment-links
    (详见流程:我是代理)。
  • 创建(POS / 可发布密钥): 携带可发布密钥
    Authorization: Bearer <pk_...>
    )调用
    POST /sdk/public/payment-links
    。请求体:CreatePosPaymentLinkDto — 必填
    amountUsd
    (最小值0.01),可选
    name
    description
    tokenShortcodes
    (数组;若仅包含一个值,支付意向将使用该代币创建;否则意向无指定代币,用户需在支付页面选择)、
    showWalletConnectQr
    (POS场景默认关闭)、
    showBaseWalletQr
    (默认开启)。创建链接及关联的支付意向;响应包含
    shortcode
    paymentIntentId
    。使用支付页面地址
    https://icpay.org/pay/<shortcode>
    (可附加
    ?paymentIntentId=<id>
    预填充意向)。POS专属规则:链接上不设置收款地址。
  • 公开查询: 调用
    GET /public/payment-links/:shortcode
    返回
    { link, account }
    (链接配置 + 账户可发布密钥/品牌信息)。供
    icpay-web
    支付页面使用。
  • 商户界面:
    icpay-web
    中进入Payment Links → 创建/编辑;创建完成后显示
    /pay/<shortcode>
    链接。
支付链接实体定义:
icpay-api/src/entities/payment-link.entity.ts
。字段包括:
amountUsd
shortcode
fiatCurrencyId
(链接显示货币)、邮箱收集/必填设置、名称、地址、电话、数量(最小/最大/默认值)、
widgetOptions
(JSON格式)、
showWalletConnectQr
showBaseWalletQr
isActive

Payment intent: reusing by id (widget, SDK, X402)

支付意向:通过ID复用(Widget、SDK、X402)

When you already have a payment intent id (e.g. from a pay link or POS flow) but not the full intent object, pass
paymentIntentId
so the SDK and API reuse that intent instead of creating a new one.
  • Widget: In pay-button config (or when creating the SDK via the widget), set
    paymentIntentId
    when the full
    paymentIntent
    object is not loaded. The widget passes it into the SDK config; the SDK uses it in
    getOrResolvePaymentIntent
    and when calling the x402 intent endpoint.
  • SDK config: You can pass
    paymentIntent
    (full object) or
    paymentIntentId
    (string). When only the id is set, the SDK fetches the intent by id via
    GET /sdk/public/payments/intents/:id
    and uses it for
    createPayment
    ,
    createPaymentUsd
    , and createPaymentX402Usd. This avoids creating a second intent when the user pays on a pay link (first intent created with the link, second avoided by passing the id).
  • X402 intent endpoint: When calling
    POST /sdk/public/payments/intents/x402
    , the SDK includes
    paymentIntentId
    in the request body when it has an existing intent (from config or request). The API reuses that intent when
    body.paymentIntentId
    is present: it loads the intent, verifies it belongs to the account and is in a reusable state (
    requires_payment
    or
    processing
    ), merges
    icpay_x402: true
    into metadata, and returns that intent for the x402 response instead of creating a new one. Use this so a single payment intent is used end-to-end (e.g. pay link + x402 EVM flow) and metadata (e.g.
    icpayPaymentLink
    ) is preserved.
若已有支付意向ID(例如来自支付链接或POS流程)但无完整意向对象,可传入**
paymentIntentId
**,SDK和API将复用该意向而非创建新的意向。
  • Widget: 在支付按钮配置中(或通过Widget创建SDK时),若未加载完整
    paymentIntent
    对象,可设置
    paymentIntentId
    。Widget会将其传入SDK配置;SDK会在
    getOrResolvePaymentIntent
    方法及调用x402意向端点时使用该ID。
  • SDK配置: 可传入
    paymentIntent
    (完整对象)或**
    paymentIntentId
    (字符串)。若仅设置ID,SDK会通过
    GET /sdk/public/payments/intents/:id
    查询意向,并在
    createPayment
    createPaymentUsd
    createPaymentX402Usd**方法中使用。避免用户在支付页面支付时创建第二个意向(第一个意向随链接创建,通过传入ID避免重复创建)。
  • X402意向端点: 调用
    POST /sdk/public/payments/intents/x402
    时,若SDK已有现存意向(来自配置或请求),会在请求体中包含**
    paymentIntentId
    。当请求体中存在
    body.paymentIntentId
    时,API会
    复用**该意向:加载意向、验证其归属账户及可复用状态(
    requires_payment
    processing
    )、将
    icpay_x402: true
    合并至元数据,并返回该意向用于x402响应,而非创建新意向。确保单个支付意向端到端复用(例如支付链接 + x402 EVM流程),并保留元数据(例如
    icpayPaymentLink
    )。

Accounts

账户

  • User: Register via
    POST /auth/register
    (firstName, lastName, email, password, etc.). Login:
    POST /auth/login
    ; JWT returned.
  • Account (merchant): Created per user via
    POST /user-accounts
    with JWT; body:
    CreateAccountDto
    (name, email, country, businessName, accountType, businessType). User can own multiple accounts; each account has
    publishableKey
    and secret (managed by backend).
  • Dashboard:
    icpay-web
    : signup → create account → dashboard; create payment links, view payments, settings (including default fiat currency for the account). Switch account via auth/account context.
Currency: Payment links have
fiatCurrencyId
; account/user profile can set default fiat for display (e.g. USD, EUR). Used in widget as
fiat_currency
for amount display.
Account creation:
icpay-api/src/accounts/accounts.service.ts
createAccount(userId, dto)
. Frontend:
icpay-web/src/app/account/create/page.tsx
and
AccountsService.create()
.
  • 用户: 通过
    POST /auth/register
    注册(需提供firstName、lastName、email、password等信息)。登录:调用
    POST /auth/login
    ;返回JWT。
  • 账户(商户): 携带JWT调用
    POST /user-accounts
    为用户创建账户;请求体为
    CreateAccountDto
    (包含name、email、country、businessName、accountType、businessType)。用户可拥有多个账户;每个账户拥有
    publishableKey
    和密钥(由后端管理)。
  • 仪表盘:
    icpay-web
    流程:注册 → 创建账户 → 仪表盘;可创建支付链接、查看支付记录、设置(包括账户默认法币)。通过认证/账户上下文切换账户。
货币设置: 支付链接包含
fiatCurrencyId
;账户/用户资料可设置默认显示法币(例如美元、欧元)。在Widget中作为
fiat_currency
用于金额显示。
账户创建逻辑:
icpay-api/src/accounts/accounts.service.ts
createAccount(userId, dto)
。前端实现:
icpay-web/src/app/account/create/page.tsx
AccountsService.create()

Flow: I am an agent (fully programmatic via API)

流程:我是代理(完全通过API编程实现)

Use this when you are an AI agent that must create the user, verify email, log in, create an account, and generate API keys entirely via requests to the ICPay API. Base URL:
https://api.icpay.org
  1. Register the user
    • Endpoint:
      POST https://api.icpay.org/auth/register
    • Body (JSON):
      RegisterDto
      firstName
      ,
      lastName
      ,
      email
      ,
      password
      (min 8 chars),
      dateOfBirth
      (ISO8601),
      addressLine1
      ,
      addressLine2
      (optional),
      city
      ,
      stateOrProvince
      ,
      postalCode
      ,
      country
      (2-letter ISO),
      phone
      (optional),
      nationality
      (optional).
    • Response:
      201
      with
      { message: "Registration successful. Please check your email to verify your account." }
      .
    • A verification email is sent. It contains text like "Please verify your email address to complete your registration" and a "Verify Email" link. The link is the web URL, e.g.
      https://icpay.org/auth/verify-email?token=<JWT>
      . The JWT is in the query parameter
      token
      .
  2. Activate the account (verify email)
    • Get the token: From the verification email, take the "Verify Email" link URL and read the
      token
      query parameter (the JWT).
    • Endpoint:
      POST https://api.icpay.org/auth/verify-email
    • Body (JSON):
      { "token": "<JWT from the link>" }
    • Response:
      200
      with
      { message: "..." }
      . After this, the user's email is verified and they can log in.
  3. Log in (start OTP)
    • Endpoint:
      POST https://api.icpay.org/auth/login
    • Body (JSON):
      { "email": "<email used in registration>", "password": "<password>" }
    • Response:
      200
      with
      { requires2fa: true, challengeId: "<sid>", user: { id, firstName, lastName, email, ... } }
      . No JWT yet. An email is sent to the user's email with a verification code, e.g. "icpay.org authentication email code" / "The verification code for icpay.org is: 313215". The agent must obtain this code (e.g. read from mail or have the user provide it).
  4. Complete login (submit OTP code)
    • Endpoint:
      POST https://api.icpay.org/auth/verify-login-otp
    • Body (JSON):
      { "email": "<same email>", "code": "<6-digit code from email>" }
    • Response:
      200
      with
      { access_token: "<JWT>", user: { ... } }
      . Use
      access_token
      as the Bearer token for all following requests.
  5. Use the Bearer token for all further requests
    • Send header:
      Authorization: Bearer <access_token>
      on every request to the API.
  6. Create one account
    • Endpoint:
      POST https://api.icpay.org/user-accounts
    • Headers:
      Authorization: Bearer <access_token>
      ,
      Content-Type: application/json
    • Body (JSON):
      CreateAccountDto
      name
      ,
      email
      , and optionally
      country
      (2-letter),
      accountType
      ,
      businessName
      ,
      businessType
      .
    • Response:
      201
      with the created account object; note
      id
      (account ID).
  7. Generate API keys (publishable + secret)
    • Endpoint:
      POST https://api.icpay.org/user-accounts/:accountId/generate-secret-key
    • Headers:
      Authorization: Bearer <access_token>
    • Body: none (or
      {}
      ).
    • Response:
      200
      with
      { secretKey: "sk_...", publicKey: "pk_..." }
      .
    • Publishable key (
      pk_...
      ):
      Safe to store in
      .env
      (e.g.
      NEXT_PUBLIC_ICPAY_PK
      ) or use directly in client code; it is public.
    • Secret key (
      sk_...
      ):
      Store only in
      .env
      (e.g.
      ICPAY_SECRET_KEY
      ). Never embed in source code or commit to git; use only on the server.
  8. Use in the app
    • Publishable key: Use with icpay-widget to build any frontend that accepts crypto payments (pay button, tip jar, paywall, etc.).
    • Secret key: Use with icpay-sdk on the server for protected operations (e.g.
      icpay.protected.getPaymentById
      ,
      listPayments
      ,
      getDetailedAccountInfo
      ) and to verify payment state. Alternatively (or in addition), register a webhook URL in the ICPay dashboard; verify
      X-ICPay-Signature
      and handle
      payment.completed
      and
      payment.refunded
      for fulfillment.
  9. Other API endpoints (all require
    Authorization: Bearer <access_token>
    )
    With the JWT from step 4, the agent can perform all account operations via the API without using icpay.org. Base URL:
    https://api.icpay.org
    . Send
    Content-Type: application/json
    where a body is used.
    Email code for sensitive actions: For payouts, split rules (create/update/delete/replace), the API requires an email verification code. First call
    POST /user/security/email-challenge/start
    (optional body
    { reason?, metadata? }
    ); the user receives an email with a code. Then include that code in the request body as
    emailSecurityCode
    or
    securityCode
    when calling the endpoint below. Alternatively use the same code from the login OTP flow if still valid for the same user.
    • User profile
      • GET /users/profile
        — Get current user profile.
      • PATCH /users/profile
        — Update profile. Body:
        SelfUpdateUserDto
        (optional:
        firstName
        ,
        lastName
        ,
        phone
        ,
        dateOfBirth
        ,
        avatarUrl
        ,
        nationality
        ,
        address
        { line1, line2, city, stateOrProvince, postalCode, country },
        fiatCurrencyId
        ). Email cannot be changed via this endpoint.
    • Switch account
      • POST /auth/switch-account
        — Body:
        { "accountId": "<uuid>" }
        . Returns new token scoped to that account; use for subsequent requests if the user has multiple accounts.
    • List user's accounts
      • GET /user-account-users/my-accounts
        — List accounts the user belongs to.
      • GET /user-account-users/my-pending-invitations
        — List pending invitations.
      • POST /user-account-users/invite
        — Invite a user to an account. Body:
        CreateInvitationDto
        accountId
        (string),
        email
        (string), optional
        role
        (e.g.
        "owner"
        |
        "admin"
        |
        "viewer"
        , default
        "viewer"
        ), optional
        permissions
        (string[]).
        invitedBy
        is not accepted from the client; it is set server-side from the authenticated user (JWT).
      • POST /user-account-users/:id/accept
        — Accept invitation.
      • POST /user-account-users/:id/decline
        — Decline invitation.
      • GET /user-account-users/account/:accountId
        — List users for an account (must be member).
    • Account (user-accounts)
      • PATCH /user-accounts/:id
        — Update account (JWT: owner or admin of the account). Body:
        UpdateAccountDto
        — all optional, only the following are intended for user/owner use (admin-only and system fields are not listed):
        name
        ,
        email
        ,
        country
        ,
        accountType
        ,
        businessName
        ,
        businessType
        ,
        isActive
        ,
        isLive
        ,
        taxId
        ,
        businessProfile
        (object:
        name
        ,
        url
        ,
        mcc
        ,
        supportEmail
        ,
        supportPhone
        ,
        supportUrl
        ),
        capabilities
        (object:
        cardPayments
        ,
        transfers
        ,
        taxReporting
        ),
        requirements
        (object:
        currentlyDue
        ,
        eventuallyDue
        ,
        pastDue
        ,
        disabledReason
        ),
        primaryDomain
        ,
        branding
        (object:
        logoUrl
        ,
        faviconUrl
        ,
        primaryColor
        ,
        secondaryColor
        ),
        address
        ,
        billingAddress
        ,
        taxInfo
        ,
        relayFeeBps
        (number, basis points),
        settings
        .
      • POST /user-accounts/:id/regenerate-secret-key
        — Regenerate API keys; returns
        { secretKey, publicKey }
        (show secret once).
      • POST /user-accounts/:id/phone-change/start
        — Start phone change; body
        { phone }
        .
      • POST /user-accounts/:id/phone-change/verify
        — Body
        { challengeId, code }
        to confirm phone change.
    • Payment links
      • GET /user/payment-links?accountId=<uuid>
        — List payment links for account.
      • GET /user/payment-links/:id
        — Get one payment link.
      • POST /user/payment-links
        — Create. Body:
        CreatePaymentLinkDto
        name
        ,
        description
        ,
        amountUsd
        , optional
        fiatCurrencyId
        ,
        accountId
        (or use query
        ?accountId=
        ), collect/require (email, name, address, phone, business, shipping), quantity (allow, default, min, max),
        maxRedemptions
        ,
        widgetOptions
        ,
        showWalletConnectQr
        ,
        showBaseWalletQr
        ,
        isActive
        . Shortcode is generated.
      • PUT /user/payment-links/:id
        — Update. Body:
        UpdatePaymentLinkDto
        (same fields as create, partial).
      • DELETE /user/payment-links/:id
        — Delete payment link.
      • GET /user/payment-links/:id/submissions
        — List submissions for the link.
    • Payments (list, get, refund)
      • GET /user/payments?accountId=<uuid>
        — List payments (with pagination).
      • GET /user/payments/summary
        — Payment summary for account.
      • GET /user/payments/stats
        — Payment stats.
      • GET /user/payments/:id
        — Get payment by ID.
      • GET /user/payments/:id/refund-precheck
        — Check if refund is allowed.
      • POST /user/payments/:id/refund
        — Create a refund for the payment. No body required (refund is full). Refunds are processed by the execute-refunds worker; webhook
        payment.refunded
        is sent when done.
    • Payouts (require email code)
      • GET /user/payouts?accountId=<uuid>
        — List payouts.
      • POST /user/payouts
        — Create payout. Body:
        accountId
        ,
        amount
        (decimal string), optional
        ledgerId
        ,
        ledgerCanisterId
        ,
        accountCanisterId
        ,
        toWalletAddress
        ,
        toWalletSubaccount
        , and
        emailSecurityCode
        or
        securityCode
        (code from email). Only OWNER or admin with payouts permission.
      • POST /user/payouts/:id/execute
        — Execute a created payout.
    • Webhook endpoints
      • GET /user/webhook-endpoints?accountId=<uuid>
        — List webhook endpoints.
      • GET /user/webhook-endpoints/:id
        — Get one endpoint.
      • POST /user/webhook-endpoints
        — Create. Body:
        CreateWebhookEndpointDto
        endpointUrl
        ,
        eventTypes
        (array, e.g.
        ["payment.completed","payment.refunded"]
        ), optional
        accountId
        ,
        isActive
        ,
        secretKey
        ,
        description
        ,
        retryCount
        ,
        timeoutSeconds
        ,
        headers
        .
      • PUT /user/webhook-endpoints/:id
        — Update. Body:
        UpdateWebhookEndpointDto
        (partial:
        endpointUrl
        ,
        eventTypes
        ,
        isActive
        ,
        description
        ,
        retryCount
        ,
        timeoutSeconds
        ,
        headers
        ).
      • DELETE /user/webhook-endpoints/:id
        — Delete webhook endpoint.
      • POST /user/webhook-endpoints/:id/test
        — Send a test event to the endpoint.
    • Webhook events
      • GET /user/webhook-events
        — List webhook events (query filters).
      • GET /user/webhook-events/:id
        — Get one event.
    • Split rules (require email code for create/update/delete)
      • GET /user/accounts/:accountId/split-rules
        — List split rules for account.
      • POST /user/accounts/:accountId/split-rules
        — Create split rule. Body:
        CreateSplitRuleDto
        targetAccountCanisterId
        (number),
        percentageBps
        (0–10000), optional
        targetAccountId
        ; and
        emailSecurityCode
        or
        securityCode
        . OWNER only.
      • PUT /user/accounts/:accountId/split-rules/:id
        — Update split rule. Body:
        UpdateSplitRuleDto
        (partial) +
        emailSecurityCode
        or
        securityCode
        . OWNER only.
      • PUT /user/accounts/:accountId/split-rules
        — Replace all rules atomically. Body:
        { rules: CreateSplitRuleDto[], emailSecurityCode?: string, securityCode?: string }
        . OWNER only.
      • DELETE /user/accounts/:accountId/split-rules/:id
        — Delete split rule. Body:
        emailSecurityCode
        or
        securityCode
        . OWNER only.
      • GET /user/accounts/:accountId/transactions/:transactionId/splits
        — Get splits for a transaction.
    • Transactions
      • GET /user-transactions?accountId=<uuid>
        — List transactions.
      • GET /user-transactions/:id
        — Get transaction by ID.
    • Notifications
      • GET /user/notification-templates
        — List notification templates.
      • GET /user/accounts/:accountId/subscriptions
        — List subscription for account.
      • POST /user/accounts/:accountId/subscriptions
        — Subscribe; body
        { templateId }
        .
      • DELETE /user/accounts/:accountId/subscriptions/:templateId
        — Unsubscribe.
    • Security (email challenge — get a code for sensitive actions)
      • POST /user/security/email-challenge/start
        — Request an email with a verification code. Optional body:
        { reason?, metadata? }
        . Response includes
        challengeId
        (optional).
      • POST /user/security/email-challenge/verify
        — Verify the code. Body:
        { code: "<6-digit>" }
        . Use the same code in payout/split/wallet requests as
        emailSecurityCode
        or
        securityCode
        .
    With these endpoints, the agent can create and manage payment links, webhooks, splits, payouts, refunds, wallets, and user profile entirely via the API, without using the icpay.org dashboard.
适用于AI代理需完全通过ICPay API请求创建用户、验证邮箱、登录、创建账户并生成API密钥的场景。基础URL:
https://api.icpay.org
  1. 注册用户
    • 端点:
      POST https://api.icpay.org/auth/register
    • 请求体(JSON):
      RegisterDto
      firstName
      lastName
      email
      password
      (至少8位)、
      dateOfBirth
      (ISO8601格式)、
      addressLine1
      addressLine2
      (可选)、
      city
      stateOrProvince
      postalCode
      country
      (2位ISO代码)、
      phone
      (可选)、
      nationality
      (可选)。
    • 响应:
      201
      状态码,返回
      { message: "Registration successful. Please check your email to verify your account." }
    • 系统将发送验证邮件。邮件内容包含“Please verify your email address to complete your registration”及**“Verify Email”链接。链接为网页**地址,例如
      https://icpay.org/auth/verify-email?token=<JWT>
      。JWT包含在查询参数
      token
      中。
  2. 激活账户(验证邮箱)
    • 获取token: 从验证邮件中提取“Verify Email”链接,读取查询参数**
      token
      **(即JWT)。
    • 端点:
      POST https://api.icpay.org/auth/verify-email
    • 请求体(JSON):
      { "token": "<链接中的JWT>" }
    • 响应:
      200
      状态码,返回
      { message: "..." }
      。完成后,用户邮箱验证通过,可登录系统。
  3. 登录(启动OTP)
    • 端点:
      POST https://api.icpay.org/auth/login
    • 请求体(JSON):
      { "email": "<注册时使用的邮箱>", "password": "<密码>" }
    • 响应:
      200
      状态码,返回
      { requires2fa: true, challengeId: "<sid>", user: { id, firstName, lastName, email, ... } }
      。此时未返回JWT。系统将向用户邮箱发送包含验证码的邮件,例如“icpay.org authentication email code” / “The verification code for icpay.org is: 313215”。代理需获取该验证码(例如读取邮件或由用户提供)。
  4. 完成登录(提交OTP验证码)
    • 端点:
      POST https://api.icpay.org/auth/verify-login-otp
    • 请求体(JSON):
      { "email": "<同一邮箱>", "code": "<邮件中的6位验证码>" }
    • 响应:
      200
      状态码,返回
      { access_token: "<JWT>", user: { ... } }
      。后续所有请求均使用**
      access_token
      **作为Bearer令牌。
  5. 使用Bearer令牌发起后续所有请求
    • 每个API请求需携带请求头:
      Authorization: Bearer <access_token>
  6. 创建账户
    • 端点:
      POST https://api.icpay.org/user-accounts
    • 请求头:
      Authorization: Bearer <access_token>
      Content-Type: application/json
    • 请求体(JSON):
      CreateAccountDto
      name
      email
      ,可选
      country
      (2位代码)、
      accountType
      businessName
      businessType
    • 响应:
      201
      状态码,返回创建的账户对象;记录**
      id
      **(账户ID)。
  7. 生成API密钥(可发布密钥 + 密钥)
    • 端点:
      POST https://api.icpay.org/user-accounts/:accountId/generate-secret-key
    • 请求头:
      Authorization: Bearer <access_token>
    • 请求体: 无(或
      {}
      )。
    • 响应:
      200
      状态码,返回
      { secretKey: "sk_...", publicKey: "pk_..." }
    • 可发布密钥(
      pk_...
      ):
      可安全存储在
      .env
      中(例如
      NEXT_PUBLIC_ICPAY_PK
      )或直接在客户端代码中使用;属于公开密钥。
    • 密钥(
      sk_...
      ):
      仅显示一次;请立即复制。仅存储在**
      .env
      **中(例如
      ICPAY_SECRET_KEY=sk_...
      )。绝不能嵌入源代码或提交至git;仅在服务器端使用。
  8. 在应用中使用
    • 可发布密钥:icpay-widget配合使用,构建任何接受加密支付的前端(支付按钮、小费罐、付费墙等)。
    • 密钥: 在服务器端与icpay-sdk配合使用,执行受保护操作(例如
      icpay.protected.getPaymentById
      ,
      listPayments
      ,
      getDetailedAccountInfo
      )并验证支付状态。或者(或同时)在ICPay仪表盘中注册Webhook URL;验证
      X-ICPay-Signature
      并处理
      payment.completed
      (可选
      payment.refunded
      )事件以完成履约。
  9. 其他API端点(均需
    Authorization: Bearer <access_token>
    使用步骤4获取的JWT,代理可完全通过API执行所有账户操作,无需使用icpay.org。基础URL:
    https://api.icpay.org
    。当请求包含体时,需发送
    Content-Type: application/json
    敏感操作的邮箱验证码: 对于提现分账规则(创建/更新/删除/替换),API要求提供邮箱验证码。首先调用
    POST /user/security/email-challenge/start
    (可选请求体
    { reason?, metadata? }
    );用户将收到包含验证码的邮件。然后在调用以下端点时,将验证码作为
    emailSecurityCode
    securityCode
    包含在请求体中。若登录OTP验证码仍有效,也可复用该验证码。
    • 用户资料
      • GET /users/profile
        — 获取当前用户资料。
      • PATCH /users/profile
        — 更新资料。请求体:
        SelfUpdateUserDto
        (可选字段:
        firstName
        lastName
        phone
        dateOfBirth
        avatarUrl
        nationality
        address
        { line1, line2, city, stateOrProvince, postalCode, country }、
        fiatCurrencyId
        )。无法通过此端点修改邮箱。
    • 切换账户
      • POST /auth/switch-account
        — 请求体:
        { "accountId": "<uuid>" }
        。返回针对该账户的新令牌;若用户拥有多个账户,后续请求使用此令牌。
    • 列出用户账户
      • GET /user-account-users/my-accounts
        — 列出用户所属的所有账户。
      • GET /user-account-users/my-pending-invitations
        — 列出待处理的邀请。
      • POST /user-account-users/invite
        — 邀请用户加入账户。请求体:
        CreateInvitationDto
        accountId
        (字符串)、
        email
        (字符串),可选
        role
        (例如
        "owner"
        |
        "admin"
        |
        "viewer"
        ,默认
        "viewer"
        )、
        permissions
        (字符串数组)。
        invitedBy
        字段由服务器从认证用户(JWT)中设置,客户端无需传入。
      • POST /user-account-users/:id/accept
        — 接受邀请。
      • POST /user-account-users/:id/decline
        — 拒绝邀请。
      • GET /user-account-users/account/:accountId
        — 列出账户的所有用户(需为账户成员)。
    • 账户(user-accounts)
      • PATCH /user-accounts/:id
        — 更新账户(JWT需为账户所有者或管理员)。请求体:
        UpdateAccountDto
        — 所有字段可选,以下为用户/所有者可修改的字段(未列出管理员专属和系统字段):
        name
        email
        country
        accountType
        businessName
        businessType
        isActive
        isLive
        taxId
        businessProfile
        (对象:
        name
        url
        mcc
        supportEmail
        supportPhone
        supportUrl
        )、
        capabilities
        (对象:
        cardPayments
        transfers
        taxReporting
        )、
        requirements
        (对象:
        currentlyDue
        eventuallyDue
        pastDue
        disabledReason
        )、
        primaryDomain
        branding
        (对象:
        logoUrl
        faviconUrl
        primaryColor
        secondaryColor
        )、
        address
        billingAddress
        taxInfo
        relayFeeBps
        (数字,基点)、
        settings
      • POST /user-accounts/:id/regenerate-secret-key
        — 重新生成API密钥;返回
        { secretKey, publicKey }
        (密钥仅显示一次)。
      • POST /user-accounts/:id/phone-change/start
        — 启动手机号变更流程;请求体
        { phone }
      • POST /user-accounts/:id/phone-change/verify
        — 请求体
        { challengeId, code }
        确认手机号变更。
    • 支付链接
      • GET /user/payment-links?accountId=<uuid>
        — 列出账户的支付链接。
      • GET /user/payment-links/:id
        — 获取单个支付链接。
      • POST /user/payment-links
        — 创建支付链接。请求体:
        CreatePaymentLinkDto
        name
        description
        amountUsd
        ,可选
        fiatCurrencyId
        accountId
        (或通过查询参数
        ?accountId=
        传入)、收集/必填设置(邮箱、姓名、地址、电话、商家信息、配送信息)、数量(允许设置、默认值、最小值、最大值)、
        maxRedemptions
        widgetOptions
        showWalletConnectQr
        showBaseWalletQr
        isActive
        。短代码自动生成。
      • PUT /user/payment-links/:id
        — 更新支付链接。请求体:
        UpdatePaymentLinkDto
        (与创建字段相同,支持部分更新)。
      • DELETE /user/payment-links/:id
        — 删除支付链接。
      • GET /user/payment-links/:id/submissions
        — 列出支付链接的提交记录。
    • 支付(列表、详情、退款)
      • GET /user/payments?accountId=<uuid>
        — 列出支付记录(支持分页)。
      • GET /user/payments/summary
        — 获取账户支付汇总。
      • GET /user/payments/stats
        — 获取支付统计数据。
      • GET /user/payments/:id
        — 根据ID获取支付记录。
      • GET /user/payments/:id/refund-precheck
        — 检查是否允许退款。
      • POST /user/payments/:id/refund
        — 为支付记录发起退款。无需请求体(全额退款)。退款由execute-refunds worker处理;退款完成时触发Webhook
        payment.refunded
    • 提现(需邮箱验证码)
      • GET /user/payouts?accountId=<uuid>
        — 列出提现记录。
      • POST /user/payouts
        — 创建提现请求。请求体:
        accountId
        amount
        (十进制字符串),可选
        ledgerId
        ledgerCanisterId
        accountCanisterId
        toWalletAddress
        toWalletSubaccount
        ,以及**
        emailSecurityCode
        securityCode
        **(邮件中的验证码)。仅所有者或拥有提现权限的管理员可操作。
      • POST /user/payouts/:id/execute
        — 执行已创建的提现请求。
    • Webhook端点
      • GET /user/webhook-endpoints?accountId=<uuid>
        — 列出Webhook端点。
      • GET /user/webhook-endpoints/:id
        — 获取单个Webhook端点。
      • POST /user/webhook-endpoints
        — 创建Webhook端点。请求体:
        CreateWebhookEndpointDto
        endpointUrl
        eventTypes
        (数组,例如
        ["payment.completed","payment.refunded"]
        ),可选
        accountId
        isActive
        secretKey
        description
        retryCount
        timeoutSeconds
        headers
      • PUT /user/webhook-endpoints/:id
        — 更新Webhook端点。请求体:
        UpdateWebhookEndpointDto
        (部分更新:
        endpointUrl
        eventTypes
        isActive
        description
        retryCount
        timeoutSeconds
        headers
        )。
      • DELETE /user/webhook-endpoints/:id
        — 删除Webhook端点。
      • POST /user/webhook-endpoints/:id/test
        — 向端点发送测试事件。
    • Webhook事件
      • GET /user/webhook-events
        — 列出Webhook事件(支持查询筛选)。
      • GET /user/webhook-events/:id
        — 获取单个Webhook事件。
    • 分账规则(创建/更新/删除需邮箱验证码)
      • GET /user/accounts/:accountId/split-rules
        — 列出账户的分账规则。
      • POST /user/accounts/:accountId/split-rules
        — 创建分账规则。请求体:
        CreateSplitRuleDto
        targetAccountCanisterId
        (数字)、
        percentageBps
        (0–10000),可选
        targetAccountId
        ;以及**
        emailSecurityCode
        securityCode
        **。仅所有者可操作。
      • PUT /user/accounts/:accountId/split-rules/:id
        — 更新分账规则。请求体:
        UpdateSplitRuleDto
        (部分更新) +
        emailSecurityCode
        securityCode
        。仅所有者可操作。
      • PUT /user/accounts/:accountId/split-rules
        — 原子替换所有分账规则。请求体:
        { rules: CreateSplitRuleDto[], emailSecurityCode?: string, securityCode?: string }
        。仅所有者可操作。
      • DELETE /user/accounts/:accountId/split-rules/:id
        — 删除分账规则。请求体:
        emailSecurityCode
        securityCode
        。仅所有者可操作。
      • GET /user/accounts/:accountId/transactions/:transactionId/splits
        — 获取交易的分账详情。
    • 交易记录
      • GET /user-transactions?accountId=<uuid>
        — 列出交易记录。
      • GET /user-transactions/:id
        — 根据ID获取交易记录。
    • 通知
      • GET /user/notification-templates
        — 列出通知模板。
      • GET /user/accounts/:accountId/subscriptions
        — 列出账户的通知订阅。
      • POST /user/accounts/:accountId/subscriptions
        — 订阅通知;请求体
        { templateId }
      • DELETE /user/accounts/:accountId/subscriptions/:templateId
        — 取消订阅。
    • 安全(邮箱挑战 — 获取敏感操作的验证码)
      • POST /user/security/email-challenge/start
        — 请求发送包含验证码的邮件。可选请求体:
        { reason?, metadata? }
        。响应包含
        challengeId
        (可选)。
      • POST /user/security/email-challenge/verify
        — 验证验证码。请求体:
        { code: "<6位验证码>" }
        。可将同一验证码用于提现/分账/钱包请求中的
        emailSecurityCode
        securityCode
        字段。
    通过这些端点,代理可完全通过API创建和管理支付链接、Webhook、分账规则、提现、退款、钱包及用户资料,无需使用icpay.org仪表盘。

Flow: I am the human (getting started myself)

流程:我是开发者(自行入门)

Use this when you are the developer/user and want to register on icpay.org, create one account, get API keys, and accept crypto payments.
  1. Sign up — Go to https://icpay.org and complete Sign up (first name, last name, email, password, and any other required fields such as address, country).
  2. Verify email — Check the email inbox for the address you used. You will either receive:
    • A link — Click “Verify Email” (or similar) to complete verification; or
    • A code — Enter that code on the verification page when prompted. Do not skip this step; you cannot log in until email is verified.
  3. Log in — Go to https://icpay.org/auth/login and sign in with your email and password.
  4. Create one account — After login, create a business account (name, email, country, business name, business type). This is your merchant account for receiving payments.
  5. Generate API keys — In the dashboard, go to Settings (or API Keys). Click Generate (or Generate secret key). You will see:
    • Publishable key (
      pk_live_...
      or
      pk_test_...
      ) — Safe to use in frontend or put in
      .env
      (e.g.
      NEXT_PUBLIC_ICPAY_PK
      ).
    • Secret key (
      sk_...
      ) — Shown once; copy it immediately. Store it only in
      .env
      (e.g.
      ICPAY_SECRET_KEY=sk_...
      ). Never put the secret key in your source code or commit it to git.
  6. Use in your app — Use the publishable key with icpay-widget (e.g.
    @ic-pay/icpay-widget
    ) to add pay buttons, tip jars, paywalls, or other components that accept crypto. To verify that a payment succeeded, use either:
    • icpay-sdk on your server with the secret key (e.g.
      icpay.protected.getPaymentById(id)
      ), or
    • A webhook URL registered in the ICPay dashboard; verify the
      X-ICPay-Signature
      header and handle
      payment.completed
      (and optionally
      payment.refunded
      ).
适用于开发者/用户自行在icpay.org注册、创建账户、获取API密钥并接受加密支付的场景。
  1. 注册 — 访问**https://icpay.org**并完成**注册**(填写姓名、邮箱、密码及其他必填信息,例如地址、国家)。
  2. 验证邮箱 — 检查注册时使用的邮箱收件箱。您将收到:
    • 一个链接 — 点击“Verify Email”(或类似按钮)完成验证;或
    • 一个验证码 — 在验证页面输入该验证码。 请勿跳过此步骤;邮箱未验证前无法登录。
  3. 登录 — 访问**https://icpay.org/auth/login**并使用邮箱和密码登录。
  4. 创建账户 — 登录后,创建商户账户(填写名称、邮箱、国家、商家名称、商家类型)。这是您用于接收支付的商户账户。
  5. 生成API密钥 — 在仪表盘进入Settings(或API Keys)页面。点击Generate(或Generate secret key)。您将看到:
    • 可发布密钥 (
      pk_live_...
      pk_test_...
      ) — 可安全用于前端或存储在
      .env
      中(例如
      NEXT_PUBLIC_ICPAY_PK
      )。
    • 密钥 (
      sk_...
      ) — 仅显示一次;请立即复制。仅存储在**
      .env
      **中(例如
      ICPAY_SECRET_KEY=sk_...
      )。绝不能将密钥放入源代码或提交至git。
  6. 在应用中使用 — 使用可发布密钥配合icpay-widget(例如
    @ic-pay/icpay-widget
    )添加支付按钮、小费罐、付费墙或其他接受加密支付的组件。要验证支付是否成功,可选择以下两种方式之一:
    • 在服务器端使用密钥配合icpay-sdk(例如
      icpay.protected.getPaymentById(id)
      ),或
    • 在ICPay仪表盘中注册Webhook URL;验证
      X-ICPay-Signature
      请求头并处理
      payment.completed
      (可选
      payment.refunded
      )事件。

Webhooks

Webhook

  • Endpoint: Merchant registers URL in ICPay dashboard; events posted to that URL.
  • Security: Verify
    X-ICPay-Signature
    = HMAC-SHA256(raw body, webhook secret) using constant-time compare. Reject if invalid.
  • Payload: JSON body;
    event.type
    e.g.
    payment.completed
    ,
    payment.failed
    ,
    payment.refunded
    . Use event/payment IDs for idempotency.
  • Retries: Backoff and retries by backend; handle duplicate deliveries idempotently.
  • 端点: 商户在ICPay仪表盘中注册URL;事件将推送至该URL。
  • 安全验证: 使用恒时比较法验证
    X-ICPay-Signature
    是否等于HMAC-SHA256(原始请求体, Webhook密钥)。验证失败则拒绝请求。
  • 负载: JSON格式请求体;
    event.type
    例如
    payment.completed
    payment.failed
    payment.refunded
    。使用事件/支付ID实现幂等性。
  • 重试机制: 后端采用退避重试策略;需处理重复投递的幂等性。

Refunds

退款

  • Refunds are requested/executed via API or dashboard; execute-refunds worker processes them.
  • Webhook
    payment.refunded
    is sent when a refund completes. Email notification (refund completed) can be sent to the account; templates in
    notification_templates
    (e.g.
    email_refund_completed_account
    ).
  • 可通过API或仪表盘发起/执行退款;由execute-refunds worker处理。
  • 退款完成时触发Webhook
    payment.refunded
    并向账户发送邮件通知;通知模板位于
    notification_templates
    (例如
    email_refund_completed_account
    )。

Split payments (optional)

分账支付(可选)

  • Split rules let multiple merchants share revenue: per account, define target account(s) and percentage (basis points). Entity:
    SplitRule
    (accountId, targetAccountId / targetAccountCanisterId, percentageBps). Services distribute funds according to rules.
  • API: splits module (
    icpay-api/src/splits/
    ) — user/sdk controllers for split rules (create, update, list). Optional feature; when not used, 100% goes to the receiving account.
  • 分账规则允许多个商户共享收益:为账户定义目标账户及基点百分比。实体定义:
    SplitRule
    (包含accountId、targetAccountId / targetAccountCanisterId、percentageBps)。服务将根据规则分配资金。
  • API:分账模块(
    icpay-api/src/splits/
    ) — 提供用户/SDK控制器用于分账规则的创建、更新、列表查询。可选功能;未启用时,100%收益归收款账户所有。

Email notifications

邮件通知

  • Payment completed and refund completed emails can be sent to the account. process-notifications worker and notification templates in the API; user/account can have default fiat for amount in emails.
  • Configure in dashboard/account settings; templates editable via migrations or admin.
Example verification (Node):
ts
const crypto = require('node:crypto');
const sig = req.headers['x-icpay-signature'] || '';
const raw = req.body; // raw buffer
const expected = crypto.createHmac('sha256', process.env.ICPAY_WEBHOOK_SECRET).update(raw).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return res.status(401).send();
  • 账户将收到支付完成和退款完成的邮件;支持配置模板;由process-notifications worker处理。
  • 可在仪表盘/账户设置中配置;模板可通过迁移或管理员操作编辑。
验证示例(Node):
ts
const crypto = require('node:crypto');
const sig = req.headers['x-icpay-signature'] || '';
const raw = req.body; // 原始Buffer
const expected = crypto.createHmac('sha256', process.env.ICPAY_WEBHOOK_SECRET).update(raw).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) return res.status(401).send();

Events (Widget & SDK)

事件(Widget & SDK)

Widget:
icpay-pay
(payment done),
icpay-error
,
icpay-unlock
(paywall),
icpay-tip
,
icpay-donation
. SDK (on window):
icpay-sdk-method-start|success|error
,
icpay-sdk-transaction-created|updated|completed|failed|mismatched
,
icpay-sdk-wallet-connected|disconnected|cancelled|error
. Subscribe on the widget element or
window
to drive UI/analytics; do not rely on console.
Widget事件:
icpay-pay
(支付完成)、
icpay-error
icpay-unlock
(付费墙解锁)、
icpay-tip
icpay-donation
SDK事件(在window对象上触发):
icpay-sdk-method-start|success|error
icpay-sdk-transaction-created|updated|completed|failed|mismatched
icpay-sdk-wallet-connected|disconnected|cancelled|error
。在Widget元素或
window
上订阅事件以驱动界面/分析;请勿依赖控制台输出。

Demo / playground (demo.icpay.org)

演示/沙箱环境(demo.icpay.org)

  • https://demo.icpay.org — Live demo app (
    icpay-demo/
    ) for building and testing custom widgets. All widget types, configurable options, copy-paste snippets. Use for quick experiments and sharing configs (e.g. with publishableKey in query).
  • https://demo.icpay.org — 用于构建和测试自定义Widget的在线演示应用(
    icpay-demo/
    )。包含所有Widget类型、可配置选项及代码片段复制功能。用于快速实验和共享配置(例如在查询参数中携带publishableKey)。

Sandbox (betterstripe.com)

沙箱环境(betterstripe.com)

  • https://betterstripe.com — Sandbox environment for developers. Same functionality as icpay.org (dashboard, payment links, widget, API, webhooks, relay, X402, splits, refunds, email notifications) but uses testnets as well as mainnets, so you can test without mainnet funds if you need to.
  • Networks: Solana devnet, Base Sepolia, Ark network testnet, and other supported testnets. Mainnet chains (e.g. Solana mainnet, Base mainnet, IC mainnet);
  • Use case: Integrate the widget or SDK against the sandbox API and pay page; create test accounts and payment links; verify webhooks, relay, and refund flows with testnet tokens.
  • Keys: Sandbox uses test keys (e.g.
    pk_test_*
    ); keep sandbox and production keys separate. API base and pay page are sandbox-specific (betterstripe.com); switch to icpay.org and production API when going live.
  • https://betterstripe.com — 供开发者使用的沙箱环境。功能与icpay.org完全一致(仪表盘、支付链接、Widget、API、Webhook、中继支付、X402、分账、退款、邮件通知),但同时支持测试网和主网,无需主网资金即可完成测试。
  • 支持网络: Solana devnet、Base Sepolia、Ark网络测试网及其他支持的测试网。同时支持主网链(例如Solana主网、Base主网、IC主网);
  • 使用场景: 针对沙箱API和支付页面集成Widget或SDK;创建测试账户和支付链接;使用测试网代币验证Webhook、中继支付和退款流程。
  • 密钥: 沙箱使用测试密钥(例如
    pk_test_*
    );请将沙箱密钥与生产密钥分开存储。API基础地址和支付页面均为沙箱专属(betterstripe.com);上线时切换至icpay.org和生产API。

WordPress plugins

WordPress插件

Two plugins live under
icpay-integrations/
:
  1. icpay-payments — Standalone: Gutenberg block + shortcodes for all widgets; settings for publishable/secret key; webhook receiver; sync payments. Webhook URL:
    /wp-json/icpay-payments/v1/webhook
    .
  2. instant-crypto-payments-for-woocommerce — WooCommerce gateway: checkout/order-pay pay button; webhook updates order status; reuses ICPay keys from main plugin if present. Webhook URL:
    /wp-json/instant-crypto-payments-for-woocommerce/v1/wc/webhook
    .
Both verify webhooks with HMAC-SHA256. Widget script:
assets/js/icpay-embed.min.js
(built from icpay-widget). See wordpress.md for build and shortcode/block usage.
icpay-integrations/
目录下包含两个插件:
  1. icpay-payments — 独立插件:提供Gutenberg区块 + 短代码支持所有Widget;支持配置可发布/密钥;内置Webhook接收器;同步支付记录。Webhook URL:
    /wp-json/icpay-payments/v1/webhook
  2. instant-crypto-payments-for-woocommerce — WooCommerce网关:在结账/订单支付页面添加支付按钮;Webhook更新订单状态;若已安装主插件,将复用ICPay密钥。Webhook URL:
    /wp-json/instant-crypto-payments-for-woocommerce/v1/wc/webhook
两个插件均使用HMAC-SHA256验证Webhook。Widget脚本:
assets/js/icpay-embed.min.js
(由icpay-widget构建)。详见wordpress.md获取构建和短代码/区块使用说明。

Conventions

约定

  • Use pnpm for install/build.
  • Prefer SDK over direct HTTP for payment/account operations. Prefer widget events for success/error handling.
  • Token identification: Use
    tokenShortcode
    (e.g.
    ic_icp
    ,
    base_eth
    ); legacy symbol/ledgerCanisterId/chainId still supported.
  • Errors: Catch
    IcpayError
    ; check
    code
    and
    message
    ; surface user-friendly messages; log details server-side only when needed.
  • 使用pnpm进行安装/构建。
  • 优先使用SDK而非直接HTTP请求处理支付/账户操作。优先使用Widget事件处理成功/错误。
  • 代币标识: 使用
    tokenShortcode
    (例如
    ic_icp
    base_eth
    );仍支持旧版symbol/ledgerCanisterId/chainId。
  • 错误处理: 捕获
    IcpayError
    ;检查
    code
    message
    ;显示用户友好的提示信息;仅在必要时在服务器端记录详细错误。

Additional resources

额外资源