From 57653d0e01e094828919ab1922f7099801037f06 Mon Sep 17 00:00:00 2001 From: Kristijan Date: Thu, 15 Jan 2026 23:36:32 +0100 Subject: [PATCH] Version 1.2.26 - 2026-01-15 23:36:32 --- package.json | 2 +- src/app/ledgers/page.mdx | 25 +++++----- src/app/page.mdx | 10 +++- src/app/relay-payments/page.mdx | 13 +++++ src/app/sdk/page.mdx | 27 +++++++++- .../widget/components/amount-input/page.mdx | 2 +- .../components/article-paywall/page.mdx | 2 +- .../widget/components/coffee-shop/page.mdx | 2 +- .../components/donation-thermometer/page.mdx | 2 +- src/app/widget/components/pay-button/page.mdx | 8 +-- .../components/premium-content/page.mdx | 2 +- src/app/widget/components/tip-jar/page.mdx | 2 +- src/app/widget/page.mdx | 21 ++++---- src/app/x402/page.mdx | 50 +++++++++++++------ 14 files changed, 118 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index 9e46b21..e0876d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tailwind-plus-icpay", - "version": "1.2.25", + "version": "1.2.26", "private": true, "packageManager": "pnpm@9.12.3", "scripts": { diff --git a/src/app/ledgers/page.mdx b/src/app/ledgers/page.mdx index 9c7b29a..d90b4cb 100644 --- a/src/app/ledgers/page.mdx +++ b/src/app/ledgers/page.mdx @@ -13,7 +13,7 @@ export const sections = [ # Tokens -ICPay supports verified tokens across chains (IC ICRC and EVM). You can fetch lists (with or without price data), resolve canister IDs by symbol, and read detailed metadata. {{ className: 'lead' }} +ICPay supports verified tokens across chains (IC ICRC, EVM, and Solana). You can fetch lists (with or without price data), resolve IDs by symbol/shortcode, and read detailed metadata. {{ className: 'lead' }} ## Overview @@ -69,6 +69,7 @@ type SdkLedger = { Notes: - `shortcode` is a human-friendly token identifier (e.g., `ic_icp`, `ic_ckusdc`) that can be used in UX and filtering. - `chainId` refers to the underlying chain (ICPay backend UUID) associated to the token. +- On non‑IC chains, `canisterId` represents the on‑chain token address (e.g., ERC‑20 address on EVM, SPL mint address on Solana). For native SOL, the mint can be empty and is handled internally. ## Using with SDK @@ -88,9 +89,9 @@ const calc = await icpay.calculateTokenAmountFromUSD({ usdAmount: 10, ledgerSymb ## Using with Widget Widgets fetch verified tokens automatically. You can constrain choices via multichain filters in the widget config: -- `chainTypes?: Array<'ic' | 'evm'>` -- `chainShortcodes?: string[]` (e.g., `['ic','base']`) -- `tokenShortcodes?: string[]` (e.g., `['ic_icp','ic_ckusdc']`) +- `chainTypes?: Array<'ic' | 'evm' | 'sol'>` +- `chainShortcodes?: string[]` (e.g., `['ic','base','sol']`) +- `tokenShortcodes?: string[]` (e.g., `['ic_icp','ic_ckusdc','sol_usdc']`) @@ -106,9 +107,9 @@ export default function Page() { publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK, amountsUsd: [1,5,10], // Optional filters to limit networks/tokens: - // chainTypes: ['ic','evm'], - // chainShortcodes: ['ic','base'], - // tokenShortcodes: ['ic_icp','ic_ckusdc'], + // chainTypes: ['ic','evm','sol'], + // chainShortcodes: ['ic','base','sol'], + // tokenShortcodes: ['ic_icp','ic_ckusdc','sol_usdc'], } }, []) return @@ -125,8 +126,8 @@ onMounted(() => { el.value.config = { publishableKey: import.meta.env.VITE_ICPAY_PK, amountsUsd: [1,5,10], - // chainTypes: ['ic'], - // tokenShortcodes: ['ic_icp'], + // chainTypes: ['ic','sol'], + // tokenShortcodes: ['ic_icp','sol_usdc'], } }) @@ -145,7 +146,7 @@ export class AppComponent implements AfterViewInit { this.el.nativeElement.config = { publishableKey: (window as any).env?.ICPAY_PK || 'pk_test_xxx', amountsUsd: [1,5,10], - // tokenShortcodes: ['ic_icp','ic_ckusdc'], + // tokenShortcodes: ['ic_icp','ic_ckusdc','sol_usdc'], } } } @@ -158,8 +159,8 @@ export class AppComponent implements AfterViewInit { el.config = { publishableKey: 'pk_test_xxx', amountsUsd: [1,5,10], - // chainTypes: ['ic'], - // tokenShortcodes: ['ic_icp'], + // chainTypes: ['ic','sol'], + // tokenShortcodes: ['ic_icp','sol_usdc'], } ``` diff --git a/src/app/page.mdx b/src/app/page.mdx index 82417ed..bbedb96 100644 --- a/src/app/page.mdx +++ b/src/app/page.mdx @@ -17,7 +17,7 @@ export const sections = [ # Welcome to ICPay Docs -Use the ICPay SDK to accept payments from users' wallets and manage your account operations. Choose between our Public SDK for frontend payment flows, Private SDK for server-side account management, or Widget Components for quick payment UI integration. {{ className: 'lead' }} +Use the ICPay SDK to accept payments from users' wallets and manage your account operations. Choose between our Public SDK for frontend payment flows, Private SDK for server-side account management, or Widget Components for quick payment UI integration. ICPay is multichain: Internet Computer, EVM, and Solana. {{ className: 'lead' }}
+
+### What’s new +- Solana chain and tokens are now supported across SDK and Widget. +- Relay payments: accept and forward funds directly to your per‑chain recipient addresses. See [Relay payments](/relay-payments). +- X402 v2: ICPay includes its own facilitator for X402 flows (IC and EVM). See [X402 payments](/x402). + diff --git a/src/app/relay-payments/page.mdx b/src/app/relay-payments/page.mdx index 3f7af1c..84991f5 100644 --- a/src/app/relay-payments/page.mdx +++ b/src/app/relay-payments/page.mdx @@ -90,6 +90,19 @@ await client.createPaymentUsd({ }); ``` +### Solana example + +```ts +await client.createPaymentUsd({ + usdAmount: 10, + tokenShortcode: 'sol_usdc', + metadata: { context: 'donation' }, + recipientAddresses: { + sol: '7sQ8Yt7o3Qd3...YourSolanaAddressBase58', + }, +}) +``` + ### Choosing to charge a Relay Fee 1. Go to ICPay Web → Settings → ICPay Fees. diff --git a/src/app/sdk/page.mdx b/src/app/sdk/page.mdx index ab2dd5c..a3041a1 100644 --- a/src/app/sdk/page.mdx +++ b/src/app/sdk/page.mdx @@ -19,7 +19,7 @@ export const sections = [ # SDK -The ICPay SDK provides a typed API for payments on the Base blockchain and Internet Computer. It supports public client-side operations with a publishable key and private server-side operations with a secret key. {{ className: 'lead' }} +The ICPay SDK provides a typed API for payments across Internet Computer, EVM chains, and Solana. It supports public client-side operations with a publishable key and private server-side operations with a secret key. {{ className: 'lead' }} ## Intro @@ -108,6 +108,27 @@ const out = await icpay.createPaymentX402Usd({ Events are emitted throughout the flow when `enableEvents` is true. See Events section below. +### Solana support +- The SDK can initiate Solana payments (SPL tokens and native SOL) when a Solana provider is available (`window.solana`, Phantom/Backpack, or pass `solanaProvider` in config). +- Prefer `tokenShortcode` (e.g., `'sol_usdc'`) — the backend resolves mint/program details. + +```ts +import { Icpay } from '@ic-pay/icpay-sdk' + +const icpay = new Icpay({ + publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK!, + // Prefer an explicit provider if you manage wallets: + // solanaProvider: window.phantom?.solana || window.solana, +}) + +const res = await icpay.createPaymentUsd({ + usdAmount: 5, + tokenShortcode: 'sol_usdc', + // Optionally relay directly to your Solana address: + recipientAddresses: { sol: '' }, +}) +``` + {/* ### Onramp (Transak) @@ -154,6 +175,8 @@ const controller = icpay.notifyPaymentIntentOnRamp({ paymentIntentId, intervalMs - `connectedWallet` (object): Connected wallet/principal for signing. - `icHost` (string): IC network host for agent-js. Default `https://icp-api.io`. - `actorProvider` (fn): `(canisterId, idl) => Actor` used to sign ICRC transfers. +- `evmProvider` (any): Preferred EVM provider for EVM flows (defaults to `window.ethereum`). +- `solanaProvider` (any): Preferred Solana provider for Solana flows (defaults to `window.solana` / `window.phantom.solana`). - `debug` (boolean): Verbose logging in SDK internals. - `enableEvents` (boolean): Emit SDK lifecycle events. @@ -223,7 +246,7 @@ const principal = icpay.getAccountAddress() - Account and chains: - `getAccountInfo()` → Public account info: id/live/canister/branding. - `getVerifiedLedgers()` → Verified ledgers with price metadata. - - `getChains()` → Enabled chains (IC/EVM) with RPC/explorer data. + - `getChains()` → Enabled chains (IC/EVM/Solana) with RPC/explorer data. - `getLedgerCanisterIdBySymbol(symbol)` → Resolve ICPay-verified symbol to canister id. - Ledger info and prices: - `getLedgerInfo(ledgerCanisterId)` → Detailed ledger metadata, price, decimals. diff --git a/src/app/widget/components/amount-input/page.mdx b/src/app/widget/components/amount-input/page.mdx index 69c4c90..8032312 100644 --- a/src/app/widget/components/amount-input/page.mdx +++ b/src/app/widget/components/amount-input/page.mdx @@ -126,7 +126,7 @@ export class AppComponent implements AfterViewInit { - `minUsd?: number`, `maxUsd?: number`, `stepUsd?: number` - `buttonLabel?: string` - `progressBar?: { enabled?: boolean }` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* ## Onramp (Transak) diff --git a/src/app/widget/components/article-paywall/page.mdx b/src/app/widget/components/article-paywall/page.mdx index d2496a9..b1d6764 100644 --- a/src/app/widget/components/article-paywall/page.mdx +++ b/src/app/widget/components/article-paywall/page.mdx @@ -129,7 +129,7 @@ export class AppComponent implements AfterViewInit { - `lockedContent?: string` - `buttonLabel?: string` (supports `{amount}` and `{symbol}` placeholders) - `onSuccess?: (tx) => void` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* ## Onramp (Transak) diff --git a/src/app/widget/components/coffee-shop/page.mdx b/src/app/widget/components/coffee-shop/page.mdx index e9e3e5e..9a525be 100644 --- a/src/app/widget/components/coffee-shop/page.mdx +++ b/src/app/widget/components/coffee-shop/page.mdx @@ -122,7 +122,7 @@ export class AppComponent implements AfterViewInit { - `defaultItemIndex?: number` - `buttonLabel?: string` (supports `{amount}` and `{symbol}` placeholders) - `onSuccess?: (tx) => void` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* onramp-start */} ## Onramp (Transak) diff --git a/src/app/widget/components/donation-thermometer/page.mdx b/src/app/widget/components/donation-thermometer/page.mdx index 08f90be..95832be 100644 --- a/src/app/widget/components/donation-thermometer/page.mdx +++ b/src/app/widget/components/donation-thermometer/page.mdx @@ -125,7 +125,7 @@ export class AppComponent implements AfterViewInit { - `amountsUsd?: number[]` - `buttonLabel?: string` (supports `{amount}` and `{symbol}` placeholders) - `onSuccess?: (tx) => void` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* onramp-start */} ## Onramp (Transak) diff --git a/src/app/widget/components/pay-button/page.mdx b/src/app/widget/components/pay-button/page.mdx index 572a081..5bea6e4 100644 --- a/src/app/widget/components/pay-button/page.mdx +++ b/src/app/widget/components/pay-button/page.mdx @@ -26,9 +26,9 @@ export default function Page() { publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK, amountUsd: 49.99, // Optional: constrain wallets/tokens shown - // chainTypes: ['ic','evm'], - // chainShortcodes: ['ic','base'], - // tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth'], + // chainTypes: ['ic','evm','sol'], + // chainShortcodes: ['ic','base','sol'], + // tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth','sol_usdc'], // progressBar: { enabled: true }, } return ( @@ -115,7 +115,7 @@ export class AppComponent implements AfterViewInit { - `amountUsd?: number` - `buttonLabel?: string` (used if `amountUsd` not set) - `progressBar?: { enabled?: boolean }` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* onramp-start */} ## Onramp (Transak) diff --git a/src/app/widget/components/premium-content/page.mdx b/src/app/widget/components/premium-content/page.mdx index aac8ca5..7bb0f75 100644 --- a/src/app/widget/components/premium-content/page.mdx +++ b/src/app/widget/components/premium-content/page.mdx @@ -101,7 +101,7 @@ export class AppComponent implements AfterViewInit { - `imageUrl?: string` - `buttonLabel?: string` (supports `{amount}` and `{symbol}` placeholders) - `onSuccess?: (tx) => void` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` {/* onramp-start */} ## Onramp (Transak) diff --git a/src/app/widget/components/tip-jar/page.mdx b/src/app/widget/components/tip-jar/page.mdx index 7e113e0..d51e7f3 100644 --- a/src/app/widget/components/tip-jar/page.mdx +++ b/src/app/widget/components/tip-jar/page.mdx @@ -122,4 +122,4 @@ export class AppComponent implements AfterViewInit { - `defaultAmountUsd?: number` - `buttonLabel?: string` (supports `{amount}` and `{symbol}` placeholders) - `onSuccess?: (tx) => void` -- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` \ No newline at end of file +- Common options: `tokenShortcodes?: string[]`, `chainShortcodes?: string[]`, `chainTypes?: Array<'ic' | 'evm' | 'sol'>`, `progressBar?`, `theme?`, `useOwnWallet?`, `plugNPlay?`, `debug?`, `disablePaymentButton?`, `disableAfterSuccess?` \ No newline at end of file diff --git a/src/app/widget/page.mdx b/src/app/widget/page.mdx index c199cc4..b93f781 100644 --- a/src/app/widget/page.mdx +++ b/src/app/widget/page.mdx @@ -23,9 +23,10 @@ export const sections = [ ICPay Widget is a set of framework-agnostic Web Components for payments. Drop in `icpay-premium-content`, `icpay-tip-jar`, `icpay-article-paywall`, `icpay-coffee-shop`, `icpay-donation-thermometer`, `icpay-pay-button` or `icpay-amount-input` to accept payments instantly. For React, wrappers are provided to use idiomatic props (e.g., `onSuccess`). {{ className: 'lead' }} ## Intro -- Built-in wallet selector for IC and EVM wallets: +- Built-in wallet selector for IC, EVM, and Solana wallets: - IC: Plug, Oisy, Internet Identity (II), NFID - - EVM: MetaMask, Coinbase, Brave, Rabby, Phantom, OKX, WalletConnect (Rainbow currently disabled) + - EVM: MetaMask, Coinbase, Brave, Rabby, OKX, WalletConnect (Rainbow currently disabled) + - Solana: Phantom, Backpack (and compatible `window.solana` providers) - Uses the [SDK](/sdk) under the hood; you can pass SDK options through the widget config. - Emits global events so your app can react to progress and results. @@ -43,9 +44,9 @@ export default function Page() { amountsUsd: [1,5,10], defaultAmountUsd: 5, // Optional filters for multichain UX: - // chainTypes: ['ic','evm'], - // chainShortcodes: ['ic','base'], - // tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth'], + // chainTypes: ['ic','evm','sol'], + // chainShortcodes: ['ic','base','sol'], + // tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth','sol_usdc'], } return ( ` - - `chainShortcodes?: string[]` (e.g., `['ic','base']`) - - `tokenShortcodes?: string[]` (e.g., `['ic_icp','ic_ckusdc','base_eth']`) + - `chainTypes?: Array<'ic' | 'evm' | 'sol'>` + - `chainShortcodes?: string[]` (e.g., `['ic','base','sol']`) + - `tokenShortcodes?: string[]` (e.g., `['ic_icp','ic_ckusdc','base_eth','sol_usdc']`) @@ -462,7 +463,7 @@ export class AppComponent implements AfterViewInit { The widget passes a subset of options directly to the SDK: -- `publishableKey`, `apiUrl`, `icHost`, `actorProvider`, `connectedWallet`, `evmProvider`. +- `publishableKey`, `apiUrl`, `icHost`, `actorProvider`, `connectedWallet`, `evmProvider`, `solanaProvider`. See all SDK options in the [SDK configuration](/sdk#configuration). @@ -498,7 +499,7 @@ Common widget config (full): - `disablePaymentButton?` (boolean) - `disableAfterSuccess?` (boolean) // Multichain filters: -- `chainTypes?: Array<'ic' | 'evm'>` +- `chainTypes?: Array<'ic' | 'evm' | 'sol'>` - `chainShortcodes?: string[]` - `tokenShortcodes?: string[]` {/* onramp-start */} diff --git a/src/app/x402/page.mdx b/src/app/x402/page.mdx index 21827c7..3d6d478 100644 --- a/src/app/x402/page.mdx +++ b/src/app/x402/page.mdx @@ -1,7 +1,7 @@ export const metadata = { - title: 'X402 payments', + title: 'X402 v2 payments', description: - 'How ICPay uses X402: building the authorization header with EIP-712, requesting a wallet signature, and settling the 402 payment via the SDK.', + 'How ICPay implements X402 v2 with its own facilitator: acceptance objects, EIP-712 (EVM), Solana signing, header format, and settlement via the SDK.', } export const sections = [ @@ -16,15 +16,18 @@ export const sections = [ { title: 'Troubleshooting', id: 'troubleshooting' }, ] -# X402 payments +# X402 v2 payments -ICPay supports X402, a “Payment Required” pattern for card/onramp-friendly flows. Instead of sending tokens directly from the user’s wallet to a contract, the wallet signs an authorization payload which is sent to ICPay’s settlement endpoint. ICPay verifies and settles the payment, then returns the intent/payment status. {{ className: 'lead' }} +ICPay supports X402 v2, a “Payment Required” pattern for card/onramp-friendly flows. Instead of sending tokens directly from the user’s wallet to a contract, the wallet signs an authorization payload which is sent to ICPay’s facilitator and settlement endpoints. ICPay verifies and settles the payment, then returns the intent/payment status. {{ className: 'lead' }} -## Summary of X402 in icpay +## Summary of X402 v2 in ICPay -- **Server produces Accepts:** The API returns one or more payment “acceptance” records describing what to sign (scheme/network/amount/recipient). -- **Client signs authorization:** The SDK builds EIP-712 typed data (EIP-3009 Permit or TransferWithAuthorization), requests a wallet signature, and packages this into an X402 header. -- **Settlement:** The SDK sends a settle request with the X402 header; ICPay validates and processes payment, then your app waits until a terminal status. +- **Server produces Accepts:** The API returns one or more payment “acceptance” records describing what to sign (scheme/network/amount/recipient). EVM acceptances carry EIP‑3009 metadata; Solana acceptances carry message/transaction hints. +- **Client signs authorization:** The SDK builds the correct authorization for the network: + - EVM: EIP‑712 typed data (EIP‑3009 Permit or TransferWithAuthorization). + - Solana: Signs an X402 v2 message or a prebuilt transaction (as required). +- **Settlement:** The SDK sends a settle request with the X402 header to ICPay’s facilitator; ICPay validates and processes payment, then your app waits until a terminal status. +- **Facilitator:** ICPay operates its own X402 facilitator endpoints for EVM and Solana so you don’t need to run custom infra. ## High-level flow @@ -43,19 +46,19 @@ An X402 acceptance describes the exact authorization you must sign: ```ts type X402Acceptance = { scheme: string // usually 'exact' - network: string // EVM chain ID (decimal string), e.g. '84532' + network: string // EVM: chain ID (decimal string: '84532'); Solana: 'solana:mainnet' | 'solana:devnet' maxAmountRequired: string // amount in smallest unit (wei-like) resource?: string | null // optional description?: string | null mimeType?: string | null - payTo: string // EVM address (recipient / verifying contract) + payTo: string // EVM: recipient/verifying contract; Solana: program address or destination maxTimeoutSeconds?: number asset?: string // token address (may match payTo) extra?: { intentId?: string provider?: string ledgerId?: string - facilitatorUrl?: string | null + facilitatorUrl?: string | null // ICPay facilitator URL used by the SDK name?: string // token name for domain eip3009Version?: string // e.g. '1' primaryType?: 'Permit' | 'TransferWithAuthorization' // optional hint @@ -63,7 +66,9 @@ type X402Acceptance = { } ``` -## Typed data and signature (EIP-712) +## Signing (EVM EIP-712 and Solana) + +EVM: The SDK constructs an EIP-712 domain and message from the acceptance, then asks the wallet to sign: - Domain: @@ -83,6 +88,14 @@ The SDK constructs an EIP-712 domain and message from the acceptance, then asks The signature is collected using `eth_signTypedData_v4`. +Solana: + +- The SDK includes the payer’s `publicKey` when initiating the X402 intent so the server can build a signable payload. +- Depending on the acceptance, the SDK either: + - Requests a `signMessage` signature over the X402 v2 message with domain prefix `ICPAY_X402_V2:`; or + - Requests a transaction signature over a prebuilt base64 transaction that includes the necessary program instructions. +- The SDK then settles via ICPay’s Solana facilitator, which can relay the transaction on your behalf when required. + ## X402 header format After signing, the SDK builds a base64-encoded JSON header that looks like: @@ -122,6 +135,15 @@ await publicApiClient.post('/sdk/public/payments/x402/settle', { ICPay verifies the signature and authorizes the payment. The response contains status info (e.g., `succeeded` / `failed`) and may include a `txHash`. The SDK then proceeds to await a terminal status via notify/long-polling if needed. +## Facilitator + +ICPay operates its own X402 facilitator for EVM and Solana: + +- EVM: Verifies EIP‑712 authorization and performs settlement against the configured contracts. +- Solana: Verifies the X402 v2 message or relays the signed transaction to the network using a platform relayer when necessary. + +These endpoints are used automatically by the SDK; no additional configuration is required beyond having an EVM/Solana wallet provider available. + ## SDK usage example ```ts @@ -164,8 +186,8 @@ You can still pass `symbol`, `ledgerCanisterId`, or `chainId`, but `tokenShortco ### Scope and networks -- X402 is currently an EVM-based flow in the SDK (signing via `eth_signTypedData_v4`). -- For the Internet Computer (IC) network, the SDK uses native ICRC-1 transfers (see `createPayment` / `createPaymentUsd`) and then notifies/awaits terminal status. X402 is not used on IC at this time. +- EVM and Solana are supported in X402 v2 flows (EIP‑712 on EVM; message/transaction signing on Solana). +- For the Internet Computer (IC) network, the SDK uses native ICRC‑1 transfers (see `createPayment` / `createPaymentUsd`) and then notifies/awaits terminal status. X402 is not used on IC at this time. ## Events and statuses