SDK

The ICPay SDK provides a typed API for payments on the Internet Computer. It supports public client-side operations with a publishable key and private server-side operations with a secret key.

Intro

  • Public operations: fetch account info, verified ledgers, pricing, start payments from a connected wallet.
  • Private operations: account details, payment history, transactions. Use only on servers with your secret key.

Get your keys

Create an account in the icpay.org to obtain your Publishable Key (client) and Secret Key (server). Keep your Secret Key private.

Quickstart

Initialize the SDK with your publishable key on the client. Only when using createPayment or createPaymentUsd, you must provide an actorProvider and a connected wallet.

Client setup (TypeScript)

import { Icpay, IcpayError } from '@ic-pay/icpay-sdk'

const icpay = new Icpay({
  publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK!,

  // actorProvider + connected wallet are required only when calling
  // createPayment or createPaymentUsd (e.g. Plug, Internet Identity, Oisy)
})

try {
  // Add your code here
} catch (e) {
  if (e instanceof IcpayError) {
    // Handle gracefully
  }
}

Create a payment

There are two common ways to pay: fixed token amount or USD amount automatically converted to tokens.

Send fixed token amount

import { Icpay } from '@ic-pay/icpay-sdk'

const icpay = new Icpay({
  publishableKey: process.env.NEXT_PUBLIC_ICPAY_PK!,
  actorProvider: (canisterId, idl) => /* return agent-js actor from your wallet */ (window as any).pnp.getActor(),
  connectedWallet: { owner: '<principal-id>' },
})

// We can send either symbol or ledgerCanisterId field to the createPayment and createPaymentUsd (symbol is preferred)
const ledgers = await icpay.getVerifiedLedgers()
const icpLedger = ledgers.find(l => l.symbol === 'ICP')!
let ledgerCanisterId = icpLedger.canisterId
// or
// ledgerCanisterId = await icpay.getLedgerCanisterIdBySymbol('ICP')

const tx = await icpay.createPayment({
  ledgerCanisterId, // ICP ledger or any ICRC ledger
  symbol: 'ICP', // Its either symbol or ledgerCanisterId (symbol is preferred)
  amount: '150000000', // smallest unit (e.g. 1.5 ICP with 8 decimals)
  metadata: { myProductId: '511234', myOrderId: '511234', anyInternalField: 'allowed' },
})

Send by USD amount (auto conversion)

const res = await icpay.createPaymentUsd({
  usdAmount: 5,
  ledgerCanisterId,
  symbol: 'ICP', // Its either symbol or ledgerCanisterId (symbol is preferred)
  metadata: { context: 'tip-jar', myProductId: '511234', myOrderId: '511234', anyInternalField: 'allowed' },
})

Events are emitted throughout the flow when enableEvents is true. See Events section below.

Configuration

IcpayConfig options:

  • publishableKey (string): Public key for client operations.
  • secretKey (string): Server-only key for private endpoints.
  • environment ('development' | 'production'): Default 'production'.
  • apiUrl (string): Default https://api.icpay.org.
  • connectedWallet (object): Connected wallet/principal for signing.
  • icHost (string): IC network host for agent-js. Default https://icp0.io.
  • actorProvider (fn): (canisterId, idl) => Actor used to sign ICRC transfers.
  • debug (boolean): Verbose logging in SDK internals.
  • enableEvents (boolean): Emit SDK lifecycle events.

Price helpers and ledger info:

// Get list of Verified Ledgers
const ledgers = await icpay.getVerifiedLedgers()
// Get Ledger Canister Id by Symbol string. eg. ckUSDC or ICP
const bySymbol = await icpay.getLedgerCanisterIdBySymbol('ICP')
// Get all information (eg. decimals, current price, ...) about a ledger by passing a Ledger Canister Id (eg. ICP ledger id is 'ryjl3-tyaaa-aaaaa-aaaba-cai')
const info = await icpay.getLedgerInfo('ryjl3-tyaaa-aaaaa-aaaba-cai')
// Fetch all ledgers with current price for 1 token unit
const priced = await icpay.getAllLedgersWithPrices()
// Get number of token that is needed for a fixed price in USD
const calc = await icpay.calculateTokenAmountFromUSD({ usdAmount: 10, ledgerCanisterId: info.canisterId })

Events

Enable with enableEvents: true. Subscribe via on or standard addEventListener.

Event names:

  • icpay-sdk-error
  • icpay-sdk-transaction-created
  • icpay-sdk-transaction-updated
  • icpay-sdk-transaction-completed
  • icpay-sdk-transaction-failed
  • icpay-sdk-transaction-mismatched
  • icpay-sdk-connect-wallet
  • icpay-sdk-method-start
  • icpay-sdk-method-success
  • icpay-sdk-method-error
const unsubscribe = icpay.on('icpay-sdk-transaction-completed', (detail) => {
  // e.g., show success toast, update UI
})

icpay.on('icpay-sdk-transaction-mismatched', (detail) => {
  // detail.requestedAmount, detail.paidAmount
})

// Later
unsubscribe()

Types reference (payments)

Payment objects now include split-aware fields when applicable:

type SdkPayment = {
  id: string
  accountId: string
  paymentIntentId: string
  transactionId: string | null
  transactionSplitId?: string | null
  canisterTxId: number | null
  amount: string
  ledgerCanisterId: string
  ledgerTxId?: string | null
  accountCanisterId?: number | null
  basePaymentAccountId?: string | null
  status: 'pending' | 'completed' | 'failed' | 'canceled' | 'refunded' | 'mismatched'
  // Optional clarity fields on webhook payloads and some responses
  requestedAmount?: string | null // from payment_intent.amount
  paidAmount?: string | null // from transaction.amount
  invoiceId: string | null
  metadata: Record<string, unknown>
  createdAt: string
  updatedAt: string
}

Public responses (publishable key) expose the same fields via PaymentPublic where returned.

Alternatively, DOM-style listeners:

const onCompleted = (evt: any) => {
  const detail = evt && typeof evt === 'object' ? (evt as any).detail ?? evt : evt
  // e.g., show success toast, update UI
}

icpay.addEventListener('icpay-sdk-transaction-completed', onCompleted)

// Later
icpay.removeEventListener('icpay-sdk-transaction-completed', onCompleted)

Event reference

EventWhen it firesPayload (shape)
icpay-sdk-errorAny SDK error is emitted (if enableEvents is true)IcpayError instance (code, message, details?, retryable?, userAction?)
icpay-sdk-transaction-createdAfter creating a payment intent before transfer{ paymentIntentId, amount, ledgerCanisterId, expectedSenderPrincipal }
icpay-sdk-transaction-updatedWhile polling/awaiting status; intermediate updatesTransactionResponse-like: { transactionId, status, amount, recipientCanister, timestamp, description?, metadata?, payment? }
icpay-sdk-transaction-completed

Was this page helpful?