Widget
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).
Intro
- Built-in wallet selector for IC and EVM wallets:
- IC: Plug, Oisy, Internet Identity (II), NFID
- EVM: MetaMask, Coinbase, Brave, Rabby, Phantom, OKX, WalletConnect (Rainbow currently disabled)
- Uses the 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.
Quickstart
Usage examples
'use client'
import { IcpayTipJar, IcpaySuccess } from '@ic-pay/icpay-widget/react'
export default function Page() {
const config = {
publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK,
amountsUsd: [1,5,10],
defaultAmountUsd: 5,
// Optional filters for multichain UX:
// chainTypes: ['ic','evm'],
// chainShortcodes: ['ic','base'],
// tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth'],
}
return (
<IcpayTipJar
config={config}
onSuccess={(detail: IcpaySuccess) => console.log('Tip completed', detail)}
/>
)
}
WalletSelect helper for custom flows
If you prefer to manage wallets yourself while using ICPay components, you can use the helper exported by the widget package:
import { createWalletSelect } from '@ic-pay/icpay-widget'
const walletSelect = createWalletSelect({ icHost: 'https://icp-api.io' })
// Prompt the user to connect; optionally specify a provider id:
// 'plug' | 'oisy' | 'ii' | 'nfid' | 'metamask' | 'coinbase' | 'brave' | 'rabby' | 'phantom' | 'okx'
await walletSelect.connect('metamask')
// Use the signed actor in your integrations
const actor = walletSelect.getActor({ canisterId, idl, requiresSigning: true, anon: false })
// Later
await walletSelect.disconnect()
Create a payment
Each component exposes a config property. The widget internally creates an SDK instance and can convert USD to token amounts automatically.
Premium content examples
'use client'
import { IcpayPremiumContent, IcpaySuccess } from '@ic-pay/icpay-widget/react'
export default function Page() {
const config = {
publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK,
priceUsd: 5,
}
return (
<IcpayPremiumContent
config={config}
onSuccess={(detail: IcpaySuccess) => console.log('Unlocked', detail)}
/>
)
}
Advanced configuration
useOwnWallet: set to true to manage wallet connection yourself; provideactorProvider+connectedWallet.plugNPlay: theme and provider toggles for built-in wallet UX.progressBar: enable/disable the built-in progress bar (default: enabled).metadata: object of custom fields to attach to created payment intents (merged with per-call metadata).derivationOrigin: optional override for wallet derivation origin (used by Internet Identity). If omitted, the widget derives it from the current site origin and collapses subdomains to the apex domain (e.g.,demo.example.com→https://example.com).debug: enable verbose console logging in widget and SDK.disablePaymentButton/disableAfterSuccess: disable the pay button immediately, or after a success.- Multichain filters and selection:
chainName?(string),chainId?(number)chainTypes?: Array<'ic' | 'evm'>chainShortcodes?: string[](e.g.,['ic','base'])tokenShortcodes?: string[](e.g.,['ic_icp','ic_ckusdc','base_eth'])
Advanced configuration examples
'use client'
import { useEffect, useRef } from 'react'
import '@ic-pay/icpay-widget'
export default function Page() {
const ref = useRef<any>(null)
useEffect(() => {
if (!ref.current) return
ref.current.config = {
publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK,
useOwnWallet: true,
connectedWallet: { owner: '<principal>' },
// Example if you manage wallets yourself using the helper below:
// const walletSelect = createWalletSelect()
// actorProvider: (canisterId, idl) => walletSelect.getActor({ canisterId, idl, requiresSigning: true, anon: false }),
plugNPlay: { enabled: false },
progressBar: { enabled: true },
theme: { primaryColor: '#0ea5e9' },
// Optional: override derivation origin used by Internet Identity
// By default, the widget will derive and use the apex of the current domain
derivationOrigin: 'https://example.com',
// Optional multichain filters to constrain balances/choices shown
chainTypes: ['ic','evm'],
chainShortcodes: ['ic','base'],
tokenShortcodes: ['ic_icp','ic_ckusdc','base_eth'],
}
}, [])
return <icpay-tip-jar ref={ref as any} />
}
SDK config via widget
The widget passes a subset of options directly to the SDK:
publishableKey,apiUrl,icHost,actorProvider,connectedWallet,evmProvider.
See all SDK options in the SDK configuration.
Secret key access via server
Secret keys are never used in the widget. If you need server-only data (history, invoices, etc.), call your backend that uses the SDK with secretKey. See SDK: Secret key access.
Options reference
Common widget config (full):
publishableKey(string) — requiredapiUrl?,icHost?,derivationOrigin?,evmProvider?actorProvider?,connectedWallet?,useOwnWallet?,debug?plugNPlay?enabled?theme?{ modalBackground?, modalBorderRadius?, buttonBackground?, buttonHoverBackground?, textColor?, primaryColor? }adapters?- Enable/disable specific wallets and pass adapter-specific config.
- Structure:
{ <adapterId>: { enabled?: boolean; config?: Record<string, any> } }
- Supported adapter ids (EVM):
metamask,coinbase,brave,rabby,phantom,okx,walletconnect - Supported adapter ids (IC):
plug,oisy,ii,nfid - Notes:
walletconnectrequiresconfig.projectId(get one atcloud.walletconnect.com).walletconnectoptional config:umdUrls?: string | string[],globalVar?: string.- Other adapters typically don’t need config; set
{ enabled: false }to hide.
openOisyInNewTab?(boolean) — Iftrue, Oisy opens in a new tab for connect/transfer. Default:false(popup).theme?{ primaryColor?, secondaryColor?, accentColor?, textColor?, mutedTextColor?, surfaceColor?, surfaceAltColor?, borderColor?, fontFamily? }
progressBar?{ enabled?: boolean }metadata?(object) — custom fields merged into payment intent metadata for every widgetdisablePaymentButton?(boolean)disableAfterSuccess?(boolean) // Multichain filters:chainTypes?: Array<'ic' | 'evm'>chainShortcodes?: string[]tokenShortcodes?: string[]
onrampDisabled?(boolean) — global kill switch to hide Transak card option (default: true)onramp?{ environment?: 'STAGING' | 'PRODUCTION'; apiKey?: string | null; width?: number|string; height?: number|string; autoOpen?: boolean; creditCardLabel?: string; enabled?: boolean }
WalletConnect (adapter)
WalletConnect enables connecting mobile wallets (e.g., MetaMask Mobile) without using in‑app browsers:
- Desktop: shows a QR code. Scan it with your wallet app to connect.
- Mobile: deep‑links directly into the wallet app and returns to your site when approved.
Required config (project-level):
plugNPlay.adapters.walletconnect.enabled = trueplugNPlay.adapters.walletconnect.config.projectId = '<your_walletconnect_project_id>'- Get one at
https://cloud.walletconnect.com
- Get one at
Optional config:
plugNPlay.adapters.walletconnect.config.umdUrls?: string | string[]- One or more URLs to the self‑hosted WalletConnect UMD (we recommend serving it yourself).
- If omitted, the widget will try self‑hosted paths relative to the embed script and WordPress plugin defaults:
.../wc/index.umd.js/wp-content/plugins/icpay-payments/assets/js/wc/index.umd.js/wp-content/plugins/instant-crypto-payments-for-woocommerce/assets/js/wc/index.umd.js
plugNPlay.adapters.walletconnect.config.globalVar?: string- Custom global variable name if your UMD exposes a non‑standard global.
Notes:
- For WordPress/WooCommerce, the UMD is set to the correct plugin asset paths automatically.
Example:
plugNPlay: {
adapters: {
walletconnect: {
enabled: true,
config: {
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
// Optional, recommended: prefer your self‑hosted UMD
umdUrls: ['/wc/index.umd.js'],
// globalVar: 'EthereumProvider', // only if your UMD uses a custom global
}
}
}
}
Component-specific (highlights):
- premium-content:
priceUsd,imageUrl?,buttonLabel?,onSuccess(tx) - tip-jar:
amountsUsd?,defaultAmountUsd?,buttonLabel?,onSuccess(tx) - article-paywall:
priceUsd,title?,preview?,lockedContent?,buttonLabel?,onSuccess(tx) - coffee-shop:
items[{ name, priceUsd }],defaultItemIndex?,buttonLabel?,onSuccess(tx) - donation-thermometer:
goalUsd,defaultAmountUsd?,amountsUsd?,buttonLabel?,onSuccess(tx)
Link SDK options that also apply: SDK configuration.
Widget errors
Widgets surface SDK errors to the UI and dispatch an icpay-error event. The widget includes helpers to classify and display errors.
window.addEventListener('icpay-error', (e) => {
console.log('Widget error', e.detail)
})
Helpers available in the widget package:
import { handleWidgetError, getErrorMessage, getErrorSeverity, getErrorAction } from '@ic-pay/icpay-widget'
Troubleshooting wallet popups (Oisy)
- By default, Oisy opens in a popup. If your site/app sets an opener-isolation policy (e.g.,
Cross-Origin-Opener-Policy: same-origin) the popup cannot establish a channel and may show “Waiting for the dApp interaction…”. - Fix on Next.js: add a headers rule with
Cross-Origin-Opener-Policy: same-origin-allow-popupsfor pages that render the widget (or site-wide if acceptable). Example innext.config.ts:
export default {
async headers() {
return [
{ source: '/:path*', headers: [
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin-allow-popups' },
]}
]
}
}
- If you cannot adjust headers, enable the widget option
openOisyInNewTab: trueso Oisy opens in a new tab instead of a popup.
Events
Widgets forward SDK events to window via CustomEvent. Subscribe globally:
window.addEventListener('icpay-sdk-transaction-created', (e) => { /* ... */ })
window.addEventListener('icpay-sdk-transaction-completed', (e) => { /* ... */ })
For the full list of event names and payloads, see SDK events.
Onramp (Transak)
All widgets expose a “Pay with credit card” action when config.onramp is set. The onramp flow:
- Creates an ICPay payment intent flagged as
onramp_icand returns TransaksessionId. - Opens Transak and listens for
TRANSAK_ORDER_SUCCESSFULfrom the iframe. - Closes the Transak modal on success and begins notifying ICPay until completion.
The progress bar updates its first step to reflect “Transak Started – Awaiting Transak information” during onramp.