npm package: @payxor/sdk
Description: TypeScript SDK for PayXor payment and entitlement system
Author: PayXor
Latest version: 0.1.2

# @payxor/sdk

TypeScript SDK for integrating PayXor payments and entitlements into your application. Works with any wallet provider (wagmi, RainbowKit, web3-react, etc.) via viem.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [PayXorClient](#payxorclient)
  - [Constructor](#constructor)
  - [Core Methods](#core-methods)
  - [Entitlement Methods](#entitlement-methods)
  - [Token Management](#token-management)
  - [Utility Methods](#utility-methods)
- [Types](#types)
- [Supported Chains](#supported-chains)
- [Standalone Functions](#standalone-functions)
- [Examples](#examples)

## Installation

```bash
pnpm add @payxor/sdk
```

```bash
npm install @payxor/sdk
```

```bash
yarn add @payxor/sdk
```

**Dependencies:** `viem`
**Optional peer dependency:** `wagmi` (for wallet client integration)

## Quick Start

```typescript
import { PayXorClient } from "@payxor/sdk";
import { maxUint256 } from "viem";

// Initialize client with wagmi wallet client
const client = new PayXorClient({
  apiUrl: "https://api.payxor.xyz",
  walletClient: yourWagmiWalletClient,
});

// 1. Get app's supported stablecoins
const stablecoins = await client.getAppSupportedStablecoins(appId, chainId);

// 2. Check balance and approval
const balance = await client.checkBalance(stablecoins[0].address, userAddress, chainId);
const needsApproval = !(await client.hasSufficientAllowance(
  stablecoins[0].address,
  userAddress,
  requiredAmount,
  chainId
));

// 3. Approve if needed
if (needsApproval) {
  await client.approveToken(stablecoins[0].address, maxUint256, userAddress, chainId);
}

// 4. Get quote and pay
const { quote, sigBackend } = await client.getQuote({
  appId,
  productId,
  chainId,
  payer: userAddress,
  tokenAddress: stablecoins[0].address,
});

const txHash = await client.pay(quote, sigBackend, userAddress, chainId);

// 5. Confirm transaction
await client.confirmTransaction(txHash, quote.quoteId);
```

---

## PayXorClient

The main client class for interacting with PayXor.

### Constructor

```typescript
import { PayXorClient } from "@payxor/sdk";

const client = new PayXorClient({
  apiUrl: "https://api.payxor.xyz",
  walletClient: yourWagmiWalletClient,
});
```

**Options:**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `apiUrl` | `string` | `"http://localhost:3000"` | PayXor API URL |
| `walletClient` | `WalletClient` | — | Wagmi v2 wallet client (required for transactions) |
| `transport` | `Transport` | — | Viem transport (alternative to walletClient) |

> **Note:** To execute on-chain transactions (`pay`, `approveToken`), you must provide either `walletClient` or `transport`.

---

### Core Methods

#### getProductInfo

Fetch public product information without creating a quote. Use for UI display and approval amount checks.

```typescript
const product = await client.getProductInfo(appId, productId);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | `string` | App ID (hex string, e.g., `"0x..."`) |
| `productId` | `string` | Product ID (hex string) |

**Returns:** `Promise<ProductInfo>`

```typescript
interface ProductInfo {
  appId: string;
  productId: string;
  name: string;
  mode: PaymentMode;
  price: string;           // Price in smallest unit (wei)
  duration: number | null; // Session duration in seconds
  entitlementId: string | null;
}
```

---

#### getQuote

Get a signed quote from the backend. Call this when the user is ready to purchase.

```typescript
const { quote, sigBackend } = await client.getQuote({
  appId: "0x...",
  productId: "0x...",
  chainId: 8453,
  payer: "0x...",
  tokenAddress: "0x...", // Optional
});
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | `string` | App ID (hex string) |
| `productId` | `string` | Product ID (hex string) |
| `chainId` | `number` | Target chain ID |
| `payer` | `string` | User's wallet address |
| `tokenAddress` | `string` | *(Optional)* Payment token address |

**Returns:** `Promise<{ quote: Quote; sigBackend: string }>`

---

#### pay

Execute the payment transaction on-chain.

```typescript
const txHash = await client.pay(
  quote,
  sigBackend,
  account,
  chainId,
  contractAddress,    // Optional
  onTransactionHash   // Optional callback
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `quote` | `Quote` | Quote from `getQuote()` |
| `sigBackend` | `string` | Backend signature from `getQuote()` |
| `account` | `Address` | User's wallet address |
| `chainId` | `number` | Chain ID for the transaction |
| `contractAddress` | `Address` | *(Optional)* PayXor contract address |
| `onTransactionHash` | `(hash: string) => void` | *(Optional)* Callback when tx is submitted |

**Returns:** `Promise<Hash>` — Transaction hash

---

#### confirmTransaction

Confirm a transaction with the backend after payment. Updates quote status to "used".

```typescript
await client.confirmTransaction(txHash, quoteId);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `txHash` | `Hash` | Transaction hash |
| `quoteId` | `string` | Quote ID from the quote |

**Returns:** `Promise<ConfirmTxResponse>`

---

### Entitlement Methods

#### isSessionActive

Check if a user has an active time-based session.

```typescript
const isActive = await client.isSessionActive(
  payer,
  appId,
  productId,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `payer` | `Address` | User's wallet address |
| `appId` | `string` | App ID (hex string) |
| `productId` | `string` | Product ID (hex string) |
| `chainId` | `number` | Chain ID |
| `contractAddress` | `Address` | *(Optional)* PayXor contract address |
| `rpcUrl` | `string` | *(Optional)* Custom RPC URL |

**Returns:** `Promise<boolean>`

---

#### isFeatureUnlocked

Check if a user has permanently unlocked a feature.

```typescript
const isUnlocked = await client.isFeatureUnlocked(
  payer,
  appId,
  entitlementId,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `payer` | `Address` | User's wallet address |
| `appId` | `string` | App ID (hex string) |
| `entitlementId` | `string` | Entitlement ID (from product or custom) |
| `chainId` | `number` | Chain ID |
| `contractAddress` | `Address` | *(Optional)* PayXor contract address |
| `rpcUrl` | `string` | *(Optional)* Custom RPC URL |

**Returns:** `Promise<boolean>`

---

#### hasPass

Check if a user holds a specific pass.

```typescript
const hasUserPass = await client.hasPass(
  payer,
  appId,
  passId,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `payer` | `Address` | User's wallet address |
| `appId` | `string` | App ID (hex string) |
| `passId` | `string` | Pass ID (hex string) |
| `chainId` | `number` | Chain ID |
| `contractAddress` | `Address` | *(Optional)* PayXor contract address |
| `rpcUrl` | `string` | *(Optional)* Custom RPC URL |

**Returns:** `Promise<boolean>`

---

#### verifyReceipt

Verify a payment receipt by checking on-chain events.

```typescript
const isValid = await client.verifyReceipt(
  txHash,
  expectedQuoteId,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Returns:** `Promise<boolean>`

---

### Token Management

#### getAppSupportedStablecoins

Get the stablecoins configured for a specific app on a chain.

```typescript
const stablecoins = await client.getAppSupportedStablecoins(appId, chainId);
// Returns app-specific tokens configured in the dashboard
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `appId` | `string` | App ID (hex string) |
| `chainId` | `number` | Chain ID |

**Returns:** `Promise<StablecoinConfig[]>`

```typescript
interface StablecoinConfig {
  symbol: string;   // e.g., "USDC", "USDT", "DAI"
  name: string;     // e.g., "USD Coin"
  address: string;  // Token contract address
  decimals: number; // Usually 6 for USDC/USDT, 18 for DAI
}
```

---

#### getAvailableStablecoins

Get all PayXor-supported stablecoins for a chain (not app-specific).

```typescript
const allStablecoins = client.getAvailableStablecoins(chainId);
```

**Returns:** `StablecoinConfig[]`

---

#### checkBalance

Check a user's token balance.

```typescript
const balance = await client.checkBalance(
  tokenAddress,
  owner,
  chainId,
  rpcUrl // Optional
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `tokenAddress` | `Address` | ERC20 token address |
| `owner` | `Address` | Address to check |
| `chainId` | `number` | Chain ID |
| `rpcUrl` | `string` | *(Optional)* Custom RPC URL |

**Returns:** `Promise<bigint>` — Balance in smallest unit (wei)

---

#### checkAllowance

Check the current token allowance for the PayXor contract.

```typescript
const allowance = await client.checkAllowance(
  tokenAddress,
  owner,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Returns:** `Promise<bigint>` — Allowance in smallest unit (wei)

---

#### hasSufficientAllowance

Check if allowance is sufficient for a payment amount.

```typescript
const hasEnough = await client.hasSufficientAllowance(
  tokenAddress,
  owner,
  requiredAmount,
  chainId,
  contractAddress, // Optional
  rpcUrl           // Optional
);
```

**Returns:** `Promise<boolean>`

---

#### approveToken

Approve token spending for the PayXor contract.

```typescript
import { maxUint256 } from "viem";

const txHash = await client.approveToken(
  tokenAddress,
  maxUint256,      // Or specific amount
  account,
  chainId,
  contractAddress  // Optional
);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `tokenAddress` | `Address` | ERC20 token address |
| `amount` | `bigint` | Amount to approve (use `maxUint256` for unlimited) |
| `account` | `Address` | User's wallet address |
| `chainId` | `number` | Chain ID |
| `contractAddress` | `Address` | *(Optional)* PayXor contract address |

**Returns:** `Promise<Hash>` — Transaction hash

> **Note:** Requires `walletClient` to be configured.

---

### Utility Methods

#### formatTokenAmount

Format a token amount for display.

```typescript
const displayAmount = client.formatTokenAmount(1000000n, 6);
// Returns: "1"
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `amount` | `bigint` | Amount in smallest unit |
| `decimals` | `number` | Token decimals |

**Returns:** `string`

---

#### parseTokenAmount

Parse a display amount to smallest unit.

```typescript
const rawAmount = client.parseTokenAmount("100.50", 6);
// Returns: 100500000n
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `amount` | `string` | Display amount |
| `decimals` | `number` | Token decimals |

**Returns:** `bigint`

---

## Types

The SDK exports all types from `@payxor/types`:

### PaymentMode

```typescript
enum PaymentMode {
  SESSION = 0,  // Time-based access
  FEATURE = 1,  // Permanent unlock
  RECEIPT = 2,  // One-time payment proof
  PASS = 3,     // Pass/membership
}
```

### Quote

```typescript
interface Quote {
  appId: string;         // bytes32 hex
  productId: string;     // bytes32 hex
  mode: PaymentMode;
  token: string;         // Token address
  amount: string;        // Payment amount (wei)
  payee: string;         // Recipient address
  entitlementId: string; // bytes32 hex
  duration: number;      // Session duration (seconds)
  expiresAt: number;     // Quote expiration timestamp
  quoteId: string;       // Unique quote identifier
  payer: string;         // Payer address
  deadline: number;      // Payment deadline timestamp
  chainId: number;       // Target chain ID
}
```

### QuoteRequest

```typescript
interface QuoteRequest {
  appId: string;
  productId: string;
  chainId: number;
  payer: string;
  tokenAddress?: string; // Optional token selection
}
```

### ProductInfo

```typescript
interface ProductInfo {
  appId: string;
  productId: string;
  name: string;
  mode: PaymentMode;
  price: string;
  duration: number | null;
  entitlementId: string | null;
}
```

### StablecoinConfig

```typescript
interface StablecoinConfig {
  symbol: string;   // "USDC", "USDT", "DAI", "USD1"
  name: string;     // Full name
  address: string;  // Contract address
  decimals: number; // Token decimals
}
```

### ChainConfig

```typescript
interface ChainConfig {
  chainId: number;
  name: string;
  rpcUrl?: string;
  usdcAddress: string;
  payxorContractAddress?: string;
}
```

---

## Supported Chains

### Mainnets

| Chain | Chain ID | Supported Stablecoins |
|-------|----------|----------------------|
| Ethereum | 1 | USDC, USDT, DAI, USD1 |
| Arbitrum | 42161 | USDC, USDT, DAI |
| Base | 8453 | USDC, USDT, DAI |
| Polygon | 137 | USDC, USDC.e, USDT, DAI |
| Avalanche | 43114 | USDC, USDT, DAI |
| Optimism | 10 | USDC, USDT, DAI |
| BNB Chain | 56 | USDC, USDT, DAI, USD1 |
| zkSync | 324 | USDC, USDT, DAI |

### Testnets

| Chain | Chain ID |
|-------|----------|
| Ethereum Sepolia | 11155111 |
| Base Sepolia | 84532 |
| Arbitrum Sepolia | 421614 |
| Polygon Mumbai | 80001 |
| Optimism Sepolia | 11155420 |
| Avalanche Fuji | 43113 |
| BNB Chain Testnet | 97 |
| zkSync Sepolia | 300 |

---

## Standalone Functions

The SDK also exports standalone functions for use without the client:

### Chain Utilities

```typescript
import {
  getViemChain,
  getPublicClient,
  getChainConfig,
} from "@payxor/sdk";

// Get viem chain configuration
const chain = getViemChain(8453); // Base

// Create a public client for reading
const publicClient = getPublicClient(8453, customRpcUrl);

// Get chain config with contract address
const config = getChainConfig(8453);
```

### Token Utilities

```typescript
import {
  getAvailableStablecoins,
  getStablecoinsForChain,
  getMockTokensForChain,
  getAllTokensForChain,
  checkBalance,
  checkAllowance,
  approveToken,
  formatTokenAmount,
  parseTokenAmount,
} from "@payxor/sdk";
```

### Hex Utilities

```typescript
import { normalizeBytes32 } from "@payxor/sdk";

// Normalize a bytes32 value (pads to 64 hex chars)
const normalized = normalizeBytes32("0x123");
// Returns: "0x0000...0123" (64 hex chars)
```

### EIP-712 Utilities

```typescript
import { getDomain, getQuoteTypedData } from "@payxor/sdk";

// Get EIP-712 domain for signing
const domain = getDomain(chainId, contractAddress);

// Get typed data for quote signing
const typedData = getQuoteTypedData(quote, chainId, contractAddress);
```

### Chain Helper Functions

```typescript
import {
  isTestnet,
  getTestnetChains,
  getMainnetChains,
  getFaucetAddress,
  getMockTokenAddress,
} from "@payxor/sdk";

isTestnet(11155111);       // true (Sepolia)
isTestnet(8453);           // false (Base mainnet)

const testnets = getTestnetChains();
const mainnets = getMainnetChains();

// For testnets with mock tokens
const faucetAddress = getFaucetAddress(11155111);
const mockUsdcAddress = getMockTokenAddress(11155111, "MockUSDC");
```

---

## Examples

### Complete Payment Flow with wagmi

```typescript
import { PayXorClient } from "@payxor/sdk";
import { maxUint256, parseUnits } from "viem";
import { useWalletClient, useAccount, useChainId } from "wagmi";

function PaymentComponent({ appId, productId }: Props) {
  const { address } = useAccount();
  const chainId = useChainId();
  const { data: walletClient } = useWalletClient();

  const handlePayment = async () => {
    if (!walletClient || !address) return;

    const client = new PayXorClient({
      apiUrl: "https://api.payxor.xyz",
      walletClient,
    });

    // Get product info for price
    const product = await client.getProductInfo(appId, productId);

    // Get supported tokens
    const stablecoins = await client.getAppSupportedStablecoins(appId, chainId);
    const selectedToken = stablecoins[0];

    // Check balance
    const balance = await client.checkBalance(
      selectedToken.address,
      address,
      chainId
    );

    const requiredAmount = BigInt(product.price);
    if (balance < requiredAmount) {
      throw new Error("Insufficient balance");
    }

    // Check and handle approval
    const hasApproval = await client.hasSufficientAllowance(
      selectedToken.address,
      address,
      requiredAmount,
      chainId
    );

    if (!hasApproval) {
      const approveTx = await client.approveToken(
        selectedToken.address,
        maxUint256,
        address,
        chainId
      );
      // Wait for approval confirmation...
    }

    // Get quote and execute payment
    const { quote, sigBackend } = await client.getQuote({
      appId,
      productId,
      chainId,
      payer: address,
      tokenAddress: selectedToken.address,
    });

    const txHash = await client.pay(
      quote,
      sigBackend,
      address,
      chainId,
      undefined,
      (hash) => console.log("Transaction submitted:", hash)
    );

    // Confirm with backend
    await client.confirmTransaction(txHash, quote.quoteId);

    console.log("Payment complete:", txHash);
  };

  return <button onClick={handlePayment}>Pay</button>;
}
```

### Checking Entitlements

```typescript
import { PayXorClient } from "@payxor/sdk";

const client = new PayXorClient({
  apiUrl: "https://api.payxor.xyz",
});

// Check session-based access
const hasActiveSession = await client.isSessionActive(
  userAddress,
  appId,
  productId,
  chainId
);

if (hasActiveSession) {
  // Grant temporary access
}

// Check permanent feature unlock
const hasPremium = await client.isFeatureUnlocked(
  userAddress,
  appId,
  "premium-feature-id",
  chainId
);

if (hasPremium) {
  // Show premium content
}

// Check pass ownership
const hasVipPass = await client.hasPass(
  userAddress,
  appId,
  "vip-pass-id",
  chainId
);
```

### Server-Side Receipt Verification

```typescript
import { PayXorClient } from "@payxor/sdk";

const client = new PayXorClient({
  apiUrl: "https://api.payxor.xyz",
});

// Verify a payment receipt
const isValidReceipt = await client.verifyReceipt(
  txHash,
  expectedQuoteId,
  chainId
);

if (isValidReceipt) {
  // Fulfill the order
}
```

### Multi-Chain Token Selection UI

```typescript
import { PayXorClient, getMainnetChains } from "@payxor/sdk";

const client = new PayXorClient({
  apiUrl: "https://api.payxor.xyz",
  walletClient,
});

// Get all supported chains
const chains = getMainnetChains();

// For each chain, get app's supported tokens
const tokensByChain = await Promise.all(
  chains.map(async (chain) => ({
    chainId: chain.chainId,
    chainName: chain.name,
    tokens: await client.getAppSupportedStablecoins(appId, chain.chainId),
  }))
);

// Display token selector UI with chain/token options
```

---

## Notes

- Chain selection is configured during app creation in the PayXor dashboard. The SDK supports multi-chain apps by specifying `chainId` per method call.
- All `bytes32` values (appId, productId, entitlementId) should be hex strings. Use `normalizeBytes32()` to ensure proper formatting.
- For React applications, consider using `@payxor/react` which provides hooks and components built on top of this SDK.