Developer Guide

Developer integration guide

Complete developer documentation for integrating with Torque, including SDK usage, TypeScript examples, error handling, & testing guides.

Overview

This guide provides comprehensive resources for developers integrating with Torque:

Torque Checkout SDK
Torque Checkout SDK

JavaScript/TypeScript SDK for checkout integration

React Hooks
React Hooks

Pre-built React hooks for seamless integration

Next.js Utilities
Next.js Utilities

Server-side helpers for Next.js

TypeScript Examples
TypeScript Examples

Complete TypeScript examples with types

Installation

npm install torque-checkout
# or
yarn add torque-checkout
# or
pnpm add torque-checkout

Requirements: Node.js >= 16.0.0, Next.js >= 13.0.0 (for Next.js utilities), React >= 16.8.0 (for React hooks, optional)

SDK Usage

Using Environment Variables

setup-env.ts
import { createTorqueCheckoutFromEnv } from 'torque-checkout'

// Automatically reads TORQUE_BUSINESS_ID & TORQUE_API_KEY from env
const torque = createTorqueCheckoutFromEnv()

Environment Variables:

TORQUE_BUSINESS_ID=your_business_id
TORQUE_API_KEY=your_api_key
TORQUE_BASE_URL=https://app.torque.fi  # Optional

Using Configuration Object

setup-config.ts
import { createTorqueCheckout } from 'torque-checkout'

const torque = createTorqueCheckout({
  businessId: 'your_business_id',
  apiKey: 'your_api_key',
  baseUrl: 'https://app.torque.fi', // Optional
  timeout: 30000 // Optional, default 30s
})

TypeScript Types

types.ts
// Cart Item
interface CartItem {
  productId: string // Torque product ID (required)
  quantity: number // Positive integer (required)
  variant?: string // Optional variant ID
  metadata?: Record<string, any> // Optional merchant metadata
}

// Customer Data
interface CustomerData {
  email: string
  firstName?: string
  lastName?: string
  phone?: string
  shippingAddress?: {
    street: string
    city: string
    state: string
    zipCode: string
    country: string
  }
  billingAddress?: {
    street: string
    city: string
    state: string
    zipCode: string
    country: string
  }
}

// Cart Data
interface CartData {
  items: CartItem[]
  customer?: CustomerData
  options?: {
    domain?: string
    expiresIn?: number // Milliseconds, default 24 hours
    metadata?: Record<string, any>
    redirectUrl?: string
  }
}

// Product
interface Product {
  id: string // Torque product ID
  name: string
  description?: string
  price: number // In cents
  currency: string
  isSubscription: boolean
  paymentPlans?: PaymentPlan[]
  image?: string
  images?: string[]
  status: 'active' | 'inactive' | 'draft' | 'archived'
}

// Subscription
interface Subscription {
  id: string
  businessId: string
  productId: string
  customerEmail: string
  customerName: string
  paymentPlanId: string
  status: 'active' | 'cancelled' | 'paused' | 'expired' | 'past_due'
  currentPeriodStart: number
  currentPeriodEnd: number
  nextBillingDate: number
  nextPaymentAmount: number
  currency: string
}

// Error
interface TorqueCheckoutError extends Error {
  code?: string
  statusCode?: number
  details?: any
}

SDK Methods

Generate Checkout URL

Cart Checkout

cart-checkout.ts
const checkoutUrl = await torque.generateCartCheckoutUrl({
  items: [
    {
      productId: 'j1234567890abcdef', // Torque product ID
      quantity: 1
    }
  ],
  customer: {
    email: 'customer@example.com',
    firstName: 'John',
    lastName: 'Doe'
  },
  options: {
    expiresIn: 24 * 60 * 60 * 1000, // 24 hours
    metadata: {
      orderId: 'order_123'
    }
  }
})

Single Product Checkout

product-checkout.ts
const checkoutUrl = await torque.generateProductCheckoutUrl(
  'j1234567890abcdef', // Product ID
  1, // Quantity
  {
    email: 'customer@example.com'
  }
)

Subscription Checkout

subscription-checkout.ts
const checkoutUrl = await torque.generateSubscriptionCheckoutUrl(
  'j1234567890abcdef', // Subscription product ID
  'plan-monthly', // Payment plan ID
  {
    email: 'customer@example.com',
    firstName: 'John'
  }
)

Validate Cart

validate-cart.ts
const validation = await torque.validateCart({
  items: [
    {
      productId: 'j1234567890abcdef',
      quantity: 1
    }
  ]
})

if (!validation.valid) {
  console.error('Validation errors:', validation.errors)
  console.warn('Warnings:', validation.warnings)
} else {
  console.log('Estimated total:', validation.estimatedTotal)
}

Get Order Status

order-status.ts
const orderStatus = await torque.getOrderStatus('order_id')

console.log('Order status:', orderStatus.status)
console.log('Payment status:', orderStatus.paymentStatus)
console.log('Total:', orderStatus.totals.total)

React Hooks

useTorqueCheckout

Hook for generating checkout URLs in React components.

use-torque-checkout.tsx
import { useTorqueCheckout } from 'torque-checkout/react'

function CheckoutButton() {
  const { 
    generateProductCheckout, 
    isLoading, 
    error 
  } = useTorqueCheckout({
    autoRedirect: true, // Automatically redirect to checkout
    onSuccess: (url) => {
      console.log('Checkout URL generated:', url)
    },
    onError: (error) => {
      console.error('Checkout error:', error)
    }
  })

  const handleCheckout = async () => {
    const url = await generateProductCheckout(
      'j1234567890abcdef', // Product ID
      1, // Quantity
      {
        email: 'customer@example.com'
      }
    )
    
    if (url) {
      // URL generated successfully (or auto-redirected)
    }
  }

  return (
    <button 
      onClick={handleCheckout}
      disabled={isLoading}
    >
      {isLoading ? 'Loading...' : 'Checkout'}
    </button>
  )
}

useCart

Hook for managing shopping cart state.

use-cart.tsx
import { useCart } from 'torque-checkout/react'

function ShoppingCart() {
  const { 
    items, 
    addItem, 
    removeItem, 
    updateQuantity, 
    clearCart,
    getItemCount 
  } = useCart()

  return (
    <div>
      <h2>Cart ({getItemCount()} items)</h2>
      {items.map(item => (
        <div key={`${item.productId}-${item.variant}`}>
          <span>Product: {item.productId}</span>
          <span>Quantity: {item.quantity}</span>
          <button onClick={() => updateQuantity(item.productId, item.quantity + 1)}>
            +
          </button>
          <button onClick={() => updateQuantity(item.productId, item.quantity - 1)}>
            -
          </button>
          <button onClick={() => removeItem(item.productId, item.variant)}>
            Remove
          </button>
        </div>
      ))}
      <button onClick={clearCart}>Clear Cart</button>
    </div>
  )
}

Next.js Integration

Server-Side Checkout

app/api/checkout/route.ts
import { generateCheckoutUrl } from 'torque-checkout/nextjs'

export async function POST(request: Request) {
  const cart = await request.json()
  
  try {
    const checkoutUrl = await generateCheckoutUrl(cart)
    return Response.json({ checkoutUrl })
  } catch (error) {
    return Response.json(
      { error: error.message },
      { status: 500 }
    )
  }
}

Webhook Handler

app/api/webhooks/torque/route.ts
import { handleWebhook } from 'torque-checkout/nextjs'

export const POST = handleWebhook({
  secret: process.env.TORQUE_WEBHOOK_SECRET,
  onOrderCompleted: async (event) => {
    // Fulfill order
    await fulfillOrder(event.orderId!)
  },
  onOrderFailed: async (event) => {
    // Handle failed order
    await handleFailedOrder(event.orderId!)
  },
  onSubscriptionCreated: async (event) => {
    // Handle new subscription
    await handleNewSubscription(event.subscriptionId!)
  }
})

Error Handling

Error Codes

INVALID_CONFIG- Invalid SDK configuration
VALIDATION_ERROR- Cart validation failed
NETWORK_ERROR- Network request failed
TIMEOUT- Request timeout
MISSING_ENV_VARS- Missing environment variables

Error Handling Pattern

error-handling.ts
try {
  const checkoutUrl = await torque.generateCartCheckoutUrl(cart)
  // Success
} catch (error) {
  if (error instanceof TorqueCheckoutError) {
    switch (error.code) {
      case 'VALIDATION_ERROR':
        console.error('Validation errors:', error.details?.errors)
        break
      case 'NETWORK_ERROR':
        console.error('Network error:', error.message)
        break
      case 'TIMEOUT':
        console.error('Request timeout')
        break
      default:
        console.error('Error:', error.message)
    }
  } else {
    console.error('Unknown error:', error)
  }
}

Complete Examples

Complete Checkout Flow

complete-checkout.ts
import { createTorqueCheckout, type CartData } from 'torque-checkout'

async function completeCheckoutFlow() {
  // 1. Initialize SDK
  const torque = createTorqueCheckout({
    businessId: process.env.TORQUE_BUSINESS_ID!,
    apiKey: process.env.TORQUE_API_KEY!
  })

  // 2. Prepare cart
  const cart: CartData = {
    items: [
      {
        productId: 'j1234567890abcdef',
        quantity: 2,
        metadata: {
          orderId: 'order_123'
        }
      }
    ],
    customer: {
      email: 'customer@example.com',
      firstName: 'John',
      lastName: 'Doe'
    },
    options: {
      expiresIn: 24 * 60 * 60 * 1000, // 24 hours
      metadata: {
        source: 'website',
        campaign: 'summer_sale'
      }
    }
  }

  // 3. Validate cart
  const validation = await torque.validateCart(cart)
  if (!validation.valid) {
    throw new Error(`Cart validation failed: ${validation.errors.join(', ')}`)
  }

  // 4. Generate checkout URL
  const checkoutUrl = await torque.generateCartCheckoutUrl(cart)

  // 5. Redirect customer
  window.location.href = checkoutUrl
}

Subscription Management

subscription-management.ts
import { createTorqueCheckout } from 'torque-checkout'

async function manageSubscription() {
  const torque = createTorqueCheckout({
    businessId: process.env.TORQUE_BUSINESS_ID!,
    apiKey: process.env.TORQUE_API_KEY!
  })

  // Create subscription
  const subscription = await torque.createSubscription({
    businessId: 'business_id',
    productId: 'product_id',
    customerEmail: 'customer@example.com',
    customerName: 'John Doe',
    paymentPlanId: 'plan-monthly'
  })

  // Get subscription details
  const details = await torque.getSubscription(subscription.id)
  console.log('Subscription status:', details.status)

  // Pause subscription
  await torque.pauseSubscription(subscription.id, 
    Date.now() + (30 * 24 * 60 * 60 * 1000) // Resume in 30 days
  )

  // Resume subscription
  await torque.resumeSubscription(subscription.id)

  // Cancel subscription
  await torque.cancelSubscription(subscription.id)
}

Testing

Mock Data

test/mocks/torque-checkout.ts
export const mockCart: CartData = {
  items: [
    {
      productId: 'j1234567890abcdef',
      quantity: 1
    }
  ],
  customer: {
    email: 'test@example.com'
  }
}

export const mockProduct: Product = {
  id: 'j1234567890abcdef',
  name: 'Test Product',
  price: 10000, // $100.00
  currency: 'USD',
  isSubscription: false,
  status: 'active'
}

Unit Testing

test/checkout.test.ts
import { describe, it, expect, vi } from 'vitest'
import { TorqueCheckout } from 'torque-checkout'
import { mockCart } from './mocks/torque-checkout'

describe('TorqueCheckout', () => {
  it('should generate checkout URL', async () => {
    const torque = new TorqueCheckout({
      businessId: 'test_business_id',
      apiKey: 'test_api_key'
    })

    // Mock fetch
    global.fetch = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        success: true,
        checkoutUrl: 'https://app.torque.fi/checkout/...'
      })
    })

    const url = await torque.generateCartCheckoutUrl(mockCart)
    expect(url).toBe('https://app.torque.fi/checkout/...')
  })
})

Best Practices

1. Error Handling

Always implement comprehensive error handling with try-catch blocks & specific error type checking.

2. Validation

Always validate cart before generating checkout to catch errors early.

3. Loading States

Always show loading states during async operations to improve user experience.

4. Type Safety

Always use TypeScript types for better development experience & type safety.

Troubleshooting

"TorqueCheckout not initialized"

Solution: Ensure TORQUE_BUSINESS_ID & TORQUE_API_KEY are set, or provide config object to createTorqueCheckout

"Product ID must be a valid Torque product ID"

Solution: Products must be uploaded to Torque first at /business/products. Use the Torque product ID (Convex ID format) from product management.

"Cart validation failed"

Solution: Check cart items have valid productId & quantity > 0. Verify products exist in Torque.

"Request timeout"

Solution: Increase timeout in config, check network connection, verify API endpoint is accessible.

Was this helpful?