Skip to main content
OnDB supports paid read operations. When a query requires payment, the SDK throws PaymentRequiredError containing a payment quote. You then pay and re-query using executeWithPayment().

How It Works

1. App executes query via queryBuilder().execute()
       |
2. Server determines payment is required
       |
3. SDK throws PaymentRequiredError with quote
       |
4. App processes payment using quote details
       |
5. App re-queries with executeWithPayment(quoteId, txHash)
       |
6. Server verifies payment and returns data

Basic Example

import { createClient, PaymentRequiredError } from '@ondb/sdk';

try {
  const result = await client.queryBuilder()
    .collection('premium_data')
    .selectAll()
    .execute();

  // Query succeeded without payment
  console.log('Records:', result.records);
} catch (error) {
  if (error instanceof PaymentRequiredError) {
    const paymentInfo = error.paymentRequired;

    console.log('Payment required!');
    console.log('Error:', paymentInfo.error);
    console.log('Payment options:', paymentInfo.accepts.length);
    // Each option in accepts[] contains: network, maxAmountRequired, payTo, tokenSymbol, etc.
  }
}

Pay and Re-Query

After catching PaymentRequiredError, process the payment and use executeWithPayment() to retry:
import { createClient, PaymentRequiredError } from '@ondb/sdk';

async function queryWithPayment() {
  const query = client.queryBuilder()
    .collection('premium_data')
    .whereField('category').equals('exclusive')
    .selectFields(['title', 'content', 'price'])
    .limit(10);

  try {
    // Try the query first
    const result = await query.execute();
    return result.records;
  } catch (error) {
    if (error instanceof PaymentRequiredError) {
      const requirement = error.paymentRequired.accepts[0];
      console.log(`Payment required: ${requirement.maxAmountRequired} ${requirement.tokenSymbol}`);

      // Process payment using the requirement details
      const txHash = await processPayment(requirement);

      // Re-query with payment proof
      const paidResult = await query.executeWithPayment(
        requirement.extra?.quoteId || '',
        txHash,
        requirement.network
      );

      return paidResult.records;
    }
    throw error;
  }
}

executeWithPayment

The executeWithPayment() method on QueryBuilder retries a query with payment proof:
const result = await client.queryBuilder()
  .collection('premium_data')
  .selectAll()
  .executeWithPayment<MyType>(
    quoteId,      // From PaymentRequiredError.quote.quoteId
    txHash,       // Transaction hash from your payment
    network       // Optional: network identifier
  );

Parameters

ParameterTypeDescription
quoteIdstringQuote ID from the PaymentRequiredError
paymentProofstringTransaction hash from the payment
networkstringOptional network identifier

Return Value

Returns QueryResponse<T> with the same structure as execute().

Error Types

import {
  PaymentRequiredError,
  PaymentVerificationError,
  OnDBError
} from '@ondb/sdk';

try {
  const result = await client.queryBuilder()
    .collection('paid_content')
    .selectAll()
    .execute();
} catch (error) {
  if (error instanceof PaymentRequiredError) {
    // Payment is required - paymentRequired has all details
    const paymentInfo = error.paymentRequired;
    console.log('Error:', paymentInfo.error);
    const requirement = paymentInfo.accepts[0];
    console.log('Cost:', requirement.maxAmountRequired, requirement.tokenSymbol);
    console.log('Pay to:', requirement.payTo);
  } else if (error instanceof PaymentVerificationError) {
    // Payment was made but verification failed
    console.log('Verification failed for tx:', error.txHash);
  } else if (error instanceof OnDBError) {
    console.log('OnDB error:', error.code, error.statusCode);
  }
}

Type-Safe Payment Handling

import { PaymentRequiredError, QueryResponse } from '@ondb/sdk';

interface PremiumContent {
  title: string;
  content: string;
  price: number;
}

async function fetchPremiumContent(): Promise<PremiumContent[] | null> {
  const query = client.queryBuilder()
    .collection('premium')
    .selectAll();

  try {
    const result = await query.execute<PremiumContent>();
    return result.records;
  } catch (error) {
    if (error instanceof PaymentRequiredError) {
      const requirement = error.paymentRequired.accepts[0];
      const txHash = await processPayment(requirement);

      const paidResult = await query.executeWithPayment<PremiumContent>(
        requirement.extra?.quoteId || '',
        txHash,
        requirement.network
      );
      return paidResult.records;
    }
    throw error;
  }
}

Use Cases

Use CaseDescription
Premium ContentCharge for access to exclusive data
API MonetizationMonetize data access with per-query pricing
Cost RecoveryRecover computational costs for expensive queries
Tiered AccessFree basic queries, paid for detailed fields
Data MarketplaceBuild marketplaces where data providers set prices

Next Steps

Cost Estimation

Estimate operation costs

Write Payments

Write operation payments