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:
JavaScript/TypeScript SDK for checkout integration
Pre-built React hooks for seamless integration
Server-side helpers for Next.js
Complete TypeScript examples with types
Installation
npm install torque-checkout
# or
yarn add torque-checkout
# or
pnpm add torque-checkoutRequirements: 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
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 # OptionalUsing Configuration Object
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
// 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
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
const checkoutUrl = await torque.generateProductCheckoutUrl(
'j1234567890abcdef', // Product ID
1, // Quantity
{
email: 'customer@example.com'
}
)Subscription Checkout
const checkoutUrl = await torque.generateSubscriptionCheckoutUrl(
'j1234567890abcdef', // Subscription product ID
'plan-monthly', // Payment plan ID
{
email: 'customer@example.com',
firstName: 'John'
}
)Validate Cart
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
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.
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.
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
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
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 configurationVALIDATION_ERROR- Cart validation failedNETWORK_ERROR- Network request failedTIMEOUT- Request timeoutMISSING_ENV_VARS- Missing environment variablesError Handling Pattern
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
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
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
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
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.