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; provide actorProvider + 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.comhttps://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) — required
  • apiUrl?, 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:
        • walletconnect requires config.projectId (get one at cloud.walletconnect.com).
        • walletconnect optional config: umdUrls?: string | string[], globalVar?: string.
        • Other adapters typically don’t need config; set { enabled: false } to hide.
  • openOisyInNewTab? (boolean) — If true, 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 widget
  • disablePaymentButton? (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 = true
  • plugNPlay.adapters.walletconnect.config.projectId = '<your_walletconnect_project_id>'
    • Get one at https://cloud.walletconnect.com

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-popups for pages that render the widget (or site-wide if acceptable). Example in next.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: true so 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:

  1. Creates an ICPay payment intent flagged as onramp_ic and returns Transak sessionId.
  2. Opens Transak and listens for TRANSAK_ORDER_SUCCESSFUL from the iframe.
  3. 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.

Was this page helpful?