Skip to content

Payments API Reference

Payment packages enable cryptocurrency payments, x402 protocol integration, and credit systems.

Packages

Package Description
@nirholas/payments-x402 x402 HTTP payment protocol
@nirholas/payments-processor Payment processing
@nirholas/credits-core Credit management system
@nirholas/credits-billing Billing & subscriptions
@nirholas/credits-usage Usage tracking

x402 Payment Protocol

The x402 package implements the HTTP 402 Payment Required protocol for pay-per-request APIs.

Installation

pnpm add @nirholas/payments-x402

Server-Side Integration

Express Middleware

import express from 'express'
import { createX402Middleware, X402Config } from '@nirholas/payments-x402'

const app = express()

const x402Config: X402Config = {
  facilitator: 'https://facilitator.x402.org',
  payTo: '0xYourWalletAddress',
  network: 'base',

  routes: {
    '/api/premium/*': {
      maxAmountRequired: '0.001',   // ETH
      currency: 'ETH',
      description: 'Premium API access',
    },
    '/api/ai/*': {
      maxAmountRequired: '0.10',     // USDC
      currency: 'USDC',
      description: 'AI endpoint access',
    },
  },
}

app.use(createX402Middleware(x402Config))

app.get('/api/premium/data', (req, res) => {
  // This endpoint requires payment
  res.json({ data: 'Premium content' })
})

Next.js Integration

// middleware.ts
import { createX402NextMiddleware } from '@nirholas/payments-x402/next'
import { NextRequest } from 'next/server'

export const config = {
  matcher: '/api/:path*',
}

const x402 = createX402NextMiddleware({
  facilitator: process.env.X402_FACILITATOR_URL!,
  payTo: process.env.PAYMENT_ADDRESS!,
  network: 'base',
})

export function middleware(request: NextRequest) {
  return x402(request)
}

Configuration Options

interface X402Config {
  // Required
  facilitator: string           // Facilitator service URL
  payTo: string                 // Wallet address to receive payments
  network: SupportedNetwork     // Blockchain network

  // Optional
  routes?: RouteConfig          // Per-route pricing
  defaultPrice?: string         // Default price for all routes
  currency?: 'ETH' | 'USDC'     // Default currency
  testMode?: boolean            // Enable test mode

  // Callbacks
  onPaymentReceived?: (payment: Payment) => void
  onPaymentFailed?: (error: PaymentError) => void

  // Advanced
  customValidator?: (payment: Payment) => Promise<boolean>
  cachePayments?: boolean
  paymentTimeout?: number       // Seconds
}

type SupportedNetwork = 
  | 'ethereum'
  | 'base'
  | 'arbitrum'
  | 'optimism'
  | 'polygon'

Client-Side Integration

Automatic Payment Client

import { X402Client } from '@nirholas/payments-x402/client'

const client = new X402Client({
  privateKey: process.env.PRIVATE_KEY!, // Or wallet provider
  network: 'base',
})

// Automatically handles 402 responses
const response = await client.fetch('https://api.example.com/premium/data')
const data = await response.json()

Manual Payment Flow

import { parseX402Challenge, createPayment } from '@nirholas/payments-x402/client'

// 1. Make request, get 402 response
const response = await fetch('https://api.example.com/premium/data')

if (response.status === 402) {
  // 2. Parse payment challenge
  const challenge = parseX402Challenge(response)

  // 3. Create and sign payment
  const payment = await createPayment({
    challenge,
    wallet, // ethers/viem wallet
  })

  // 4. Retry with payment header
  const paidResponse = await fetch('https://api.example.com/premium/data', {
    headers: {
      'X-402-Payment': payment.signature,
    },
  })
}

Payment Types

interface Payment {
  id: string
  amount: string
  currency: string
  network: string
  from: string
  to: string
  signature: string
  timestamp: number
  expiresAt: number
  metadata?: Record<string, unknown>
}

interface X402Challenge {
  payTo: string
  amount: string
  currency: string
  network: string
  facilitator: string
  nonce: string
  expiresAt: number
  description?: string
}

interface PaymentReceipt {
  paymentId: string
  transactionHash?: string
  confirmed: boolean
  confirmedAt?: number
}

Payment Processor

Installation

pnpm add @nirholas/payments-processor

Processor Configuration

import { PaymentProcessor } from '@nirholas/payments-processor'

const processor = new PaymentProcessor({
  networks: {
    ethereum: {
      rpcUrl: process.env.ETH_RPC_URL!,
      walletAddress: process.env.ETH_WALLET!,
    },
    base: {
      rpcUrl: process.env.BASE_RPC_URL!,
      walletAddress: process.env.BASE_WALLET!,
    },
  },
  webhookUrl: 'https://your-app.com/webhooks/payments',
  confirmations: {
    ethereum: 12,
    base: 1,
  },
})

Processing Payments

// Create payment request
const request = await processor.createPaymentRequest({
  amount: '100.00',
  currency: 'USDC',
  network: 'base',
  metadata: {
    orderId: 'order-123',
    customer: 'customer-456',
  },
  expiresIn: 3600, // 1 hour
})

console.log(request.paymentAddress)
console.log(request.qrCode) // Base64 QR code

// Check payment status
const status = await processor.getPaymentStatus(request.id)
if (status.confirmed) {
  console.log(`Payment confirmed: ${status.transactionHash}`)
}

// List payments
const payments = await processor.listPayments({
  status: 'confirmed',
  startDate: new Date('2024-01-01'),
  limit: 100,
})

Credits System

Installation

pnpm add @nirholas/credits-core

Credit Manager

import { CreditManager } from '@nirholas/credits-core'

const credits = new CreditManager({
  database: process.env.DATABASE_URL!,
  defaultBalance: 100, // Starting credits for new users
})

// Get user balance
const balance = await credits.getBalance(userId)
console.log(`Credits: ${balance.available} / ${balance.pending}`)

// Add credits
await credits.addCredits(userId, 500, {
  reason: 'purchase',
  transactionId: 'tx-123',
})

// Deduct credits
const result = await credits.deductCredits(userId, 10, {
  reason: 'api-usage',
  endpoint: '/api/ai/generate',
})

if (!result.success) {
  throw new Error('Insufficient credits')
}

// Get transaction history
const history = await credits.getHistory(userId, {
  limit: 50,
  type: 'deduction',
})

Credit Types

interface CreditBalance {
  userId: string
  available: number
  pending: number
  reserved: number
  lifetime: number
  updatedAt: Date
}

interface CreditTransaction {
  id: string
  userId: string
  type: 'addition' | 'deduction' | 'reservation' | 'release'
  amount: number
  balance: number
  reason: string
  metadata?: Record<string, unknown>
  createdAt: Date
}

interface CreditPackage {
  id: string
  name: string
  credits: number
  price: number
  currency: string
  bonus?: number
  popular?: boolean
}

Billing & Subscriptions

Installation

pnpm add @nirholas/credits-billing

Subscription Management

import { BillingManager } from '@nirholas/credits-billing'

const billing = new BillingManager({
  database: process.env.DATABASE_URL!,
  stripeSecretKey: process.env.STRIPE_SECRET_KEY,
  cryptoProcessor: paymentProcessor,
})

// Create subscription
const subscription = await billing.createSubscription({
  userId: 'user-123',
  planId: 'pro',
  paymentMethod: 'crypto',
  network: 'base',
})

// Get subscription
const sub = await billing.getSubscription(userId)
console.log(`Plan: ${sub.plan.name}`)
console.log(`Status: ${sub.status}`)
console.log(`Next billing: ${sub.nextBillingDate}`)

// Cancel subscription
await billing.cancelSubscription(userId, {
  reason: 'user_requested',
  immediate: false, // Cancel at end of period
})

// Change plan
await billing.changePlan(userId, 'enterprise')

Plan Configuration

interface SubscriptionPlan {
  id: string
  name: string
  description: string
  price: {
    monthly: number
    yearly: number
    currency: string
  }
  credits: {
    monthly: number
    rollover: boolean
    maxRollover?: number
  }
  features: string[]
  limits: {
    apiCalls: number
    storage: number
    teamMembers: number
  }
}

interface Subscription {
  id: string
  userId: string
  plan: SubscriptionPlan
  status: 'active' | 'past_due' | 'canceled' | 'trialing'
  currentPeriodStart: Date
  currentPeriodEnd: Date
  nextBillingDate: Date
  canceledAt?: Date
  trialEnd?: Date
}

Usage Tracking

Installation

pnpm add @nirholas/credits-usage

Usage Tracker

import { UsageTracker } from '@nirholas/credits-usage'

const usage = new UsageTracker({
  database: process.env.DATABASE_URL!,
  redis: process.env.REDIS_URL, // Optional for real-time tracking
})

// Track usage event
await usage.track({
  userId: 'user-123',
  event: 'api_call',
  endpoint: '/api/ai/generate',
  credits: 5,
  metadata: {
    model: 'gpt-4',
    tokens: 1500,
  },
})

// Get usage summary
const summary = await usage.getSummary(userId, {
  period: 'month',
  groupBy: 'endpoint',
})

// Get real-time usage
const realtime = await usage.getRealtime(userId)
console.log(`Today: ${realtime.today} credits`)
console.log(`This hour: ${realtime.hour} credits`)

// Check limits
const limits = await usage.checkLimits(userId)
if (limits.exceeded) {
  throw new Error(`Rate limit exceeded: ${limits.reason}`)
}

Usage Types

interface UsageEvent {
  id: string
  userId: string
  event: string
  endpoint?: string
  credits: number
  metadata?: Record<string, unknown>
  timestamp: Date
}

interface UsageSummary {
  period: string
  totalCredits: number
  totalEvents: number
  byEndpoint: Record<string, number>
  byEvent: Record<string, number>
  dailyBreakdown: Array<{
    date: string
    credits: number
    events: number
  }>
}

interface UsageLimits {
  exceeded: boolean
  reason?: string
  limits: {
    daily: { current: number; max: number }
    hourly: { current: number; max: number }
    perMinute: { current: number; max: number }
  }
  resetsAt: Date
}

Error Types

class PaymentError extends Error {
  code: string
}

// x402 errors
class X402ChallengeError extends PaymentError {}
class X402PaymentExpiredError extends PaymentError {}
class X402InvalidSignatureError extends PaymentError {}
class X402InsufficientFundsError extends PaymentError {}

// Credit errors
class InsufficientCreditsError extends PaymentError {}
class CreditTransactionError extends PaymentError {}

// Billing errors
class SubscriptionNotFoundError extends PaymentError {}
class PlanNotFoundError extends PaymentError {}
class PaymentMethodError extends PaymentError {}

// Usage errors
class RateLimitExceededError extends PaymentError {}
class UsageTrackingError extends PaymentError {}