torque-checkout 1.1.10 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,99 +1,199 @@
1
- # torque-checkout
1
+ # Torque Checkout SDK
2
2
 
3
- Official Torque checkout SDK for seamless eCommerce integrations. Generate multi-product checkout links, track orders, and manage customer data with our powerful API.
3
+ > **Official Torque checkout SDK for seamless eCommerce integrations with Next.js**
4
4
 
5
- ## Quick Start
5
+ The easiest way to integrate Torque checkout into your Next.js eCommerce application. Accept crypto payments with just a few lines of code.
6
6
 
7
- ### Prerequisites
7
+ [![npm version](https://img.shields.io/npm/v/torque-checkout.svg)](https://www.npmjs.com/package/torque-checkout)
8
+ [![npm downloads](https://img.shields.io/npm/dm/torque-checkout.svg)](https://www.npmjs.com/package/torque-checkout)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
11
 
9
- **Before installing the package, you must create your Torque business profile:**
12
+ ## Table of Contents
10
13
 
11
- 1. **Go to Torque Business Dashboard**: https://torque.fi/business/settings
12
- 2. **Connect your wallet** (MetaMask, WalletConnect, etc.)
13
- 3. **Fill out business information** (name, email, website, etc.)
14
- 4. **Set your payment wallet address** (where you want to receive payments)
15
- 5. **Save your profile** (API key will be generated automatically)
16
- 6. **Get your credentials** from the bottom of the business settings page
14
+ - [Features](#features)
15
+ - [Installation](#installation)
16
+ - [Quick Start](#quick-start)
17
+ - [Documentation](#documentation)
18
+ - [Examples](#examples)
19
+ - [TypeScript](#typescript)
20
+ - [Error Handling](#error-handling)
21
+ - [Security](#security)
22
+ - [Troubleshooting](#troubleshooting)
23
+ - [Migration Guide](#migration-guide)
24
+ - [Support](#support)
25
+
26
+ ## Features
17
27
 
18
- ### Installation
28
+ - **Lightweight & Fast** - Minimal bundle size (~40KB), optimized for performance
29
+ - **Zero Configuration** - Works out of the box with environment variables
30
+ - **TypeScript First** - Full TypeScript support with comprehensive types and IntelliSense
31
+ - **React Hooks** - Built-in React hooks for seamless client-side integration
32
+ - **Next.js Optimized** - Server-side utilities for App Router and Pages Router
33
+ - **Production Ready** - Comprehensive error handling, validation, and retry logic
34
+ - **Multi-Product Support** - Handle complex carts with multiple items and variants
35
+ - **Subscription Support** - Built-in subscription and recurring payment management
36
+ - **Universal** - Works in browser, Node.js, and edge environments
37
+
38
+ ## Installation
19
39
 
20
40
  ```bash
21
41
  npm install torque-checkout
22
42
  # or
23
43
  yarn add torque-checkout
44
+ # or
45
+ pnpm add torque-checkout
24
46
  ```
25
47
 
26
- ### Basic Usage
48
+ ### Requirements
27
49
 
28
- ```typescript
29
- import { TorqueCheckout } from 'torque-checkout'
50
+ - **Node.js**: >= 16.0.0
51
+ - **Next.js**: >= 13.0.0 (for Next.js utilities)
52
+ - **React**: >= 16.8.0 (for React hooks, optional)
30
53
 
31
- const torque = new TorqueCheckout({
32
- businessId: 'your_business_id',
33
- apiKey: 'your_api_key'
34
- })
54
+ ## Quick Start
35
55
 
36
- // Generate cart checkout link
37
- const checkoutUrl = await torque.generateCartCheckoutUrl({
38
- items: [
39
- { productId: 'prod_123', quantity: 2, variant: 'large' },
40
- { productId: 'prod_456', quantity: 1 }
41
- ],
42
- customer: {
43
- email: 'customer@example.com',
44
- firstName: 'John',
45
- lastName: 'Doe'
46
- }
47
- })
56
+ ### Step 1: Get Your API Credentials
48
57
 
49
- // Redirect customer to checkout
50
- window.location.href = checkoutUrl
58
+ 1. **Visit [Torque Business Dashboard](https://app.torque.fi/business/settings)**
59
+ 2. **Connect your wallet** (MetaMask, WalletConnect, etc.)
60
+ 3. **Complete your business profile**:
61
+ - Business name, email, website
62
+ - **Payment wallet address** (where payments will be sent)
63
+ 4. **Save your profile** - API credentials are generated automatically
64
+ 5. **Copy your credentials** from the API Integration section:
65
+ - `Business ID`
66
+ - `API Key`
67
+
68
+ > **Tip**: Your payment wallet address is set once in business settings. All successful payments automatically go to this wallet - no code needed!
69
+
70
+ ### Step 2: Configure Environment Variables
51
71
 
52
- // The generated URL will look like:
53
- // https://torque.fi/checkout/{businessId}/cart?items={encoded_cart_data}
72
+ Create a `.env.local` file in your Next.js project root:
73
+
74
+ ```env
75
+ # Required
76
+ TORQUE_BUSINESS_ID=your_business_id_here
77
+ TORQUE_API_KEY=your_api_key_here
78
+
79
+ # Optional (defaults to production)
80
+ TORQUE_BASE_URL=https://app.torque.fi
54
81
  ```
55
82
 
56
- ## URL Structure
83
+ > **Security**: Never commit `.env.local` to version control. Add it to `.gitignore`.
57
84
 
58
- The SDK generates checkout URLs in this format:
85
+ ### Step 3: Choose Your Integration Method
59
86
 
60
- - **Single Product**: `https://torque.fi/checkout/{businessId}/{productId}?quantity=1`
61
- - **Multi-Product Cart**: `https://torque.fi/checkout/{businessId}/cart?items={encoded_cart_data}`
87
+ #### Option A: React Hook (Client-Side) - Recommended for Most Cases
62
88
 
63
- The `items` parameter contains URL-encoded JSON with your cart data.
89
+ ```tsx
90
+ 'use client'
64
91
 
65
- ## API Reference
92
+ import { useTorqueCheckout } from 'torque-checkout/react'
66
93
 
67
- ### Constructor
94
+ export default function CheckoutButton() {
95
+ const { generateProductCheckout, isLoading } = useTorqueCheckout({
96
+ autoRedirect: true // Automatically redirects to checkout
97
+ })
68
98
 
69
- ```typescript
70
- new TorqueCheckout(config: TorqueConfig)
99
+ return (
100
+ <button
101
+ onClick={() => generateProductCheckout('prod_123', 1, {
102
+ email: 'customer@example.com'
103
+ })}
104
+ disabled={isLoading}
105
+ >
106
+ {isLoading ? 'Loading...' : 'Buy Now'}
107
+ </button>
108
+ )
109
+ }
71
110
  ```
72
111
 
73
- **Config Options:**
74
- - `businessId` (required): Your Torque business ID (get from business dashboard)
75
- - `apiKey` (required): Your API authentication key (get from business dashboard)
76
- - `timeout` (optional): Request timeout in milliseconds (defaults to 30000)
112
+ #### Option B: Server-Side API Route (Most Secure)
113
+
114
+ ```ts
115
+ // app/api/checkout/route.ts
116
+ import { handleCheckoutRequest } from 'torque-checkout/nextjs'
77
117
 
78
- **Important:** Your `businessId` and `apiKey` are generated when you create your business profile in the Torque dashboard. Make sure you've set your payment wallet address in the business settings before using the SDK.
118
+ export const POST = handleCheckoutRequest()
119
+ ```
120
+
121
+ Then call from your frontend:
122
+
123
+ ```tsx
124
+ const response = await fetch('/api/checkout', {
125
+ method: 'POST',
126
+ headers: { 'Content-Type': 'application/json' },
127
+ body: JSON.stringify({
128
+ items: [{ productId: 'prod_123', quantity: 1 }],
129
+ customer: { email: 'customer@example.com' }
130
+ })
131
+ })
132
+
133
+ const { checkoutUrl } = await response.json()
134
+ window.location.href = checkoutUrl
135
+ ```
136
+
137
+ #### Option C: Direct SDK Usage
138
+
139
+ ```ts
140
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
141
+
142
+ const torque = createTorqueCheckoutFromEnv()
143
+
144
+ const checkoutUrl = await torque.generateCartCheckoutUrl({
145
+ items: [{ productId: 'prod_123', quantity: 1 }],
146
+ customer: { email: 'customer@example.com' }
147
+ })
148
+
149
+ window.location.href = checkoutUrl
150
+ ```
79
151
 
80
- ### Methods
152
+ ## Documentation
81
153
 
82
- #### `generateCartCheckoutUrl(cart: CartData): Promise<string>`
154
+ ### Core SDK
155
+
156
+ #### Initialization
157
+
158
+ ```ts
159
+ import { createTorqueCheckout, createTorqueCheckoutFromEnv } from 'torque-checkout'
160
+
161
+ // Method 1: From environment variables (recommended)
162
+ const torque = createTorqueCheckoutFromEnv()
163
+
164
+ // Method 2: Explicit configuration
165
+ const torque = createTorqueCheckout({
166
+ businessId: process.env.TORQUE_BUSINESS_ID!,
167
+ apiKey: process.env.TORQUE_API_KEY!,
168
+ baseUrl: 'https://app.torque.fi', // Optional
169
+ timeout: 30000 // Optional, default: 30000ms
170
+ })
171
+ ```
172
+
173
+ #### Methods
174
+
175
+ ##### `generateCartCheckoutUrl(cart: CartData): Promise<string>`
83
176
 
84
177
  Generate a checkout URL for a multi-product cart.
85
178
 
86
- ```typescript
179
+ ```ts
87
180
  const checkoutUrl = await torque.generateCartCheckoutUrl({
88
181
  items: [
89
- { productId: 'prod_123', quantity: 2, variant: 'large' },
182
+ {
183
+ productId: 'prod_123',
184
+ quantity: 2,
185
+ price: 29.99, // Optional: override product price
186
+ variant: 'large', // Optional: product variant
187
+ metadata: { customField: 'value' } // Optional: custom data
188
+ },
90
189
  { productId: 'prod_456', quantity: 1 }
91
190
  ],
92
191
  customer: {
93
- email: 'customer@example.com',
94
- firstName: 'John',
95
- lastName: 'Doe',
96
- shippingAddress: {
192
+ email: 'customer@example.com', // Required
193
+ firstName: 'John', // Optional
194
+ lastName: 'Doe', // Optional
195
+ phone: '+1234567890', // Optional
196
+ shippingAddress: { // Optional: pre-fill shipping
97
197
  street: '123 Main St',
98
198
  city: 'New York',
99
199
  state: 'NY',
@@ -102,311 +202,689 @@ const checkoutUrl = await torque.generateCartCheckoutUrl({
102
202
  }
103
203
  },
104
204
  options: {
105
- metadata: {
106
- orderId: 'order_123',
107
- source: 'shopify'
108
- },
109
- expiresIn: 24 * 60 * 60 * 1000 // 24 hours
205
+ metadata: { orderId: 'order_123', source: 'website' }, // Optional
206
+ expiresIn: 24 * 60 * 60 * 1000, // Optional: 24 hours
207
+ redirectUrl: 'https://yoursite.com/thank-you' // Optional
110
208
  }
111
209
  })
112
210
  ```
113
211
 
114
- #### `generateProductCheckoutUrl(productId: string, quantity?: number, customer?: CustomerData, options?: CartOptions): Promise<string>`
212
+ ##### `generateProductCheckoutUrl(productId, quantity?, customer?, options?): Promise<string>`
115
213
 
116
- Generate a checkout URL for a single product.
214
+ Quick method for single product checkout.
117
215
 
118
- ```typescript
216
+ ```ts
119
217
  const checkoutUrl = await torque.generateProductCheckoutUrl(
120
- 'prod_123',
121
- 2,
122
- { email: 'customer@example.com' }
218
+ 'prod_123', // Product ID
219
+ 2, // Quantity (default: 1)
220
+ { email: 'customer@example.com' }, // Customer data (optional)
221
+ { redirectUrl: 'https://yoursite.com/success' } // Options (optional)
123
222
  )
124
223
  ```
125
224
 
126
- #### `validateCart(cart: CartData): Promise<CartValidation>`
225
+ ##### `generateSubscriptionCheckoutUrl(productId, paymentPlanId, customer?, options?): Promise<string>`
127
226
 
128
- Validate cart data before generating checkout links.
227
+ Generate checkout for subscription products.
129
228
 
130
- ```typescript
229
+ ```ts
230
+ const checkoutUrl = await torque.generateSubscriptionCheckoutUrl(
231
+ 'prod_subscription_123', // Subscription product ID
232
+ 'plan_monthly_123', // Payment plan ID
233
+ { email: 'customer@example.com' },
234
+ { redirectUrl: 'https://yoursite.com/subscription-success' }
235
+ )
236
+ ```
237
+
238
+ ##### `validateCart(cart: CartData): Promise<CartValidation>`
239
+
240
+ Validate cart before checkout to catch errors early.
241
+
242
+ ```ts
131
243
  const validation = await torque.validateCart({
132
- items: [
133
- { productId: 'prod_123', quantity: 2 },
134
- { productId: 'prod_456', quantity: 1 }
135
- ]
244
+ items: [{ productId: 'prod_123', quantity: 2 }]
136
245
  })
137
246
 
138
247
  if (validation.valid) {
139
- console.log('Cart is valid, estimated total:', validation.estimatedTotal)
248
+ console.log('Cart is valid')
249
+ console.log('Estimated total:', validation.estimatedTotal)
140
250
  } else {
141
- console.log('Cart validation errors:', validation.errors)
251
+ console.error('Validation errors:', validation.errors)
252
+ console.warn('Warnings:', validation.warnings)
142
253
  }
143
254
  ```
144
255
 
145
- #### `getOrderStatus(orderId: string): Promise<OrderStatus>`
256
+ ##### `getOrderStatus(orderId: string): Promise<OrderStatus>`
257
+
258
+ Check order status after checkout.
146
259
 
147
- Get the current status of an order.
260
+ ```ts
261
+ const order = await torque.getOrderStatus('order_123')
148
262
 
149
- ```typescript
150
- const orderStatus = await torque.getOrderStatus('order_123')
151
- console.log('Order status:', orderStatus.status)
152
- console.log('Total amount:', orderStatus.totals.total)
263
+ console.log('Status:', order.status) // e.g., 'completed', 'pending', 'failed'
264
+ console.log('Total:', order.totals.total)
265
+ console.log('Payment status:', order.paymentStatus)
266
+ console.log('Items:', order.items)
153
267
  ```
154
268
 
155
- #### `sendWebhookEvent(orderId: string, status: string, customerData?: any, metadata?: Record<string, any>): Promise<void>`
269
+ ### React Hooks
270
+
271
+ #### `useTorqueCheckout(options?)`
272
+
273
+ React hook for client-side checkout with built-in state management.
274
+
275
+ ```tsx
276
+ import { useTorqueCheckout } from 'torque-checkout/react'
277
+
278
+ function CheckoutButton() {
279
+ const {
280
+ generateCheckout,
281
+ generateProductCheckout,
282
+ generateSubscriptionCheckout,
283
+ validateCart,
284
+ getOrderStatus,
285
+ isLoading,
286
+ error,
287
+ checkoutUrl
288
+ } = useTorqueCheckout({
289
+ // Optional: explicit config (or use env vars)
290
+ // config: {
291
+ // businessId: 'your_business_id',
292
+ // apiKey: 'your_api_key'
293
+ // },
294
+ autoRedirect: true, // Auto-redirect to checkout URL
295
+ onSuccess: (url) => {
296
+ console.log('Checkout URL generated:', url)
297
+ // Optional: track analytics
298
+ },
299
+ onError: (error) => {
300
+ console.error('Checkout error:', error)
301
+ // Optional: show error toast
302
+ }
303
+ })
156
304
 
157
- Send webhook events to update order status.
305
+ const handleCheckout = async () => {
306
+ const url = await generateProductCheckout('prod_123', 1, {
307
+ email: 'customer@example.com'
308
+ })
309
+ // If autoRedirect is false, you can handle the URL manually
310
+ if (url && !autoRedirect) {
311
+ window.location.href = url
312
+ }
313
+ }
158
314
 
159
- ```typescript
160
- await torque.sendWebhookEvent('order_123', 'shipped', {
161
- trackingNumber: '1Z999AA1234567890',
162
- carrier: 'UPS'
163
- })
315
+ return (
316
+ <div>
317
+ <button onClick={handleCheckout} disabled={isLoading}>
318
+ {isLoading ? 'Processing...' : 'Checkout'}
319
+ </button>
320
+ {error && <p className="error">{error.message}</p>}
321
+ {checkoutUrl && <p>Checkout URL: {checkoutUrl}</p>}
322
+ </div>
323
+ )
324
+ }
164
325
  ```
165
326
 
166
- #### `trackCartView(cartId: string, cartData: CartData): Promise<void>`
327
+ #### `useCart()`
328
+
329
+ Shopping cart state management hook.
330
+
331
+ ```tsx
332
+ import { useCart } from 'torque-checkout/react'
333
+
334
+ function ShoppingCart() {
335
+ const {
336
+ items,
337
+ addItem,
338
+ removeItem,
339
+ updateQuantity,
340
+ clearCart,
341
+ getTotal,
342
+ getItemCount
343
+ } = useCart()
344
+
345
+ return (
346
+ <div>
347
+ <h2>Shopping Cart ({getItemCount()} items)</h2>
348
+
349
+ {items.map(item => (
350
+ <div key={`${item.productId}-${item.variant || ''}`}>
351
+ <span>Product {item.productId}</span>
352
+ <span>Quantity: {item.quantity}</span>
353
+ <button onClick={() => updateQuantity(item.productId, item.quantity + 1, item.variant)}>
354
+ +
355
+ </button>
356
+ <button onClick={() => updateQuantity(item.productId, item.quantity - 1, item.variant)}>
357
+ -
358
+ </button>
359
+ <button onClick={() => removeItem(item.productId, item.variant)}>
360
+ Remove
361
+ </button>
362
+ </div>
363
+ ))}
364
+
365
+ <p>Total: ${getTotal().toFixed(2)}</p>
366
+ <button onClick={clearCart}>Clear Cart</button>
367
+ </div>
368
+ )
369
+ }
370
+ ```
167
371
 
168
- Track cart views for analytics.
372
+ ### Next.js Server Utilities
169
373
 
170
- ```typescript
171
- await torque.trackCartView('cart_123', {
172
- items: [{ productId: 'prod_123', quantity: 2 }]
173
- })
174
- ```
374
+ #### `handleCheckoutRequest(options?)`
175
375
 
176
- #### `trackCheckoutComplete(orderId: string, checkoutData: any): Promise<void>`
376
+ Complete API route handler with error handling.
177
377
 
178
- Track successful checkout completions.
378
+ ```ts
379
+ // app/api/checkout/route.ts
380
+ import { handleCheckoutRequest } from 'torque-checkout/nextjs'
179
381
 
180
- ```typescript
181
- await torque.trackCheckoutComplete('order_123', {
182
- paymentMethod: 'card',
183
- total: 99.99
382
+ export const POST = handleCheckoutRequest({
383
+ onSuccess: async (checkoutUrl, cart) => {
384
+ // Optional: Log, track analytics, send notifications
385
+ console.log('Checkout generated:', checkoutUrl)
386
+ await logCheckoutEvent(cart)
387
+ },
388
+ onError: async (error, cart) => {
389
+ // Optional: Error logging, notifications
390
+ console.error('Checkout error:', error)
391
+ await logError(error, cart)
392
+ }
184
393
  })
185
394
  ```
186
395
 
187
- #### `generateCartHash(cart: CartData): string`
396
+ #### `handleWebhook(options?)`
188
397
 
189
- Generate a unique hash for cart identification and caching.
398
+ Webhook handler for order events.
190
399
 
191
- ```typescript
192
- const cartHash = torque.generateCartHash({
193
- items: [{ productId: 'prod_123', quantity: 2 }]
400
+ ```ts
401
+ // app/api/webhooks/torque/route.ts
402
+ import { handleWebhook } from 'torque-checkout/nextjs'
403
+
404
+ export const POST = handleWebhook({
405
+ secret: process.env.TORQUE_WEBHOOK_SECRET, // Optional: verify signatures
406
+ onOrderCompleted: async (event) => {
407
+ // Fulfill order
408
+ await fulfillOrder(event.orderId!)
409
+ await sendConfirmationEmail(event.data.customerEmail)
410
+ },
411
+ onOrderFailed: async (event) => {
412
+ // Handle failed payment
413
+ await notifyCustomer(event.orderId!)
414
+ },
415
+ onSubscriptionCreated: async (event) => {
416
+ // Activate subscription
417
+ await activateSubscription(event.subscriptionId!)
418
+ }
194
419
  })
195
420
  ```
196
421
 
197
- ## Data Types
422
+ #### `generateCheckoutUrl(cart, config?)`
423
+
424
+ Server-side checkout URL generation.
198
425
 
199
- ### CartItem
426
+ ```ts
427
+ // app/api/checkout/route.ts
428
+ import { generateCheckoutUrl } from 'torque-checkout/nextjs'
200
429
 
201
- ```typescript
202
- interface CartItem {
203
- productId: string // Required: Torque product ID
204
- quantity: number // Required: Quantity to purchase
205
- variant?: string // Optional: Product variant (size, color, etc.)
206
- price?: number // Optional: Override product price
207
- metadata?: Record<string, any> // Optional: Custom data
430
+ export async function POST(request: Request) {
431
+ const cart = await request.json()
432
+
433
+ try {
434
+ const checkoutUrl = await generateCheckoutUrl(cart)
435
+ return Response.json({ checkoutUrl })
436
+ } catch (error: any) {
437
+ return Response.json(
438
+ { error: error.message, code: error.code },
439
+ { status: error.statusCode || 500 }
440
+ )
441
+ }
208
442
  }
209
443
  ```
210
444
 
211
- ### CustomerData
212
-
213
- ```typescript
214
- interface CustomerData {
215
- email: string // Required: Customer email
216
- firstName?: string // Optional: First name
217
- lastName?: string // Optional: Last name
218
- phone?: string // Optional: Phone number
219
- shippingAddress?: { // Optional: Pre-fill shipping address
220
- street: string
221
- city: string
222
- state: string
223
- zipCode: string
224
- country: string
445
+ ## Complete Examples
446
+
447
+ ### E-Commerce Product Page
448
+
449
+ ```tsx
450
+ // app/products/[id]/page.tsx
451
+ 'use client'
452
+
453
+ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
454
+ import { useState } from 'react'
455
+
456
+ export default function ProductPage({ params }: { params: { id: string } }) {
457
+ const cart = useCart()
458
+ const { generateProductCheckout, isLoading } = useTorqueCheckout({
459
+ autoRedirect: true
460
+ })
461
+ const [quantity, setQuantity] = useState(1)
462
+
463
+ const handleAddToCart = () => {
464
+ cart.addItem({
465
+ productId: params.id,
466
+ quantity,
467
+ price: 29.99
468
+ })
225
469
  }
226
- billingAddress?: { // Optional: Pre-fill billing address
227
- // Same structure as shippingAddress
470
+
471
+ const handleBuyNow = async () => {
472
+ await generateProductCheckout(
473
+ params.id,
474
+ quantity,
475
+ {
476
+ email: 'customer@example.com' // Get from auth/session
477
+ }
478
+ )
228
479
  }
480
+
481
+ return (
482
+ <div>
483
+ <h1>Product {params.id}</h1>
484
+ <p>$29.99</p>
485
+
486
+ <div>
487
+ <label>Quantity:</label>
488
+ <input
489
+ type="number"
490
+ value={quantity}
491
+ onChange={(e) => setQuantity(Number(e.target.value))}
492
+ min="1"
493
+ />
494
+ </div>
495
+
496
+ <button onClick={handleAddToCart}>
497
+ Add to Cart ({cart.getItemCount()} items)
498
+ </button>
499
+
500
+ <button onClick={handleBuyNow} disabled={isLoading}>
501
+ {isLoading ? 'Processing...' : 'Buy Now'}
502
+ </button>
503
+ </div>
504
+ )
229
505
  }
230
506
  ```
231
507
 
232
- ### CartOptions
508
+ ### Shopping Cart with Checkout
233
509
 
234
- ```typescript
235
- interface CartOptions {
236
- domain?: string // Optional: Custom domain for checkout
237
- expiresIn?: number // Optional: Link expiration time (ms)
238
- metadata?: Record<string, any> // Optional: Custom metadata
239
- redirectUrl?: string // Optional: Post-checkout redirect URL
240
- }
241
- ```
510
+ ```tsx
511
+ // app/cart/page.tsx
512
+ 'use client'
242
513
 
243
- ## Platform Integrations
514
+ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
244
515
 
245
- ### Shopify
516
+ export default function CartPage() {
517
+ const cart = useCart()
518
+ const { generateCheckout, isLoading } = useTorqueCheckout()
246
519
 
247
- ```typescript
248
- // Shopify app integration
249
- app.post('/checkout', async (req, res) => {
250
- const { cart, customer } = req.body
251
-
252
- try {
253
- const checkoutUrl = await torque.generateCartCheckoutUrl({
254
- items: cart.items.map(item => ({
255
- productId: item.product_id,
256
- quantity: item.quantity,
257
- variant: item.variant_title,
258
- price: item.price
259
- })),
520
+ const handleCheckout = async () => {
521
+ const url = await generateCheckout({
522
+ items: cart.items,
260
523
  customer: {
261
- email: customer.email,
262
- firstName: customer.first_name,
263
- lastName: customer.last_name,
264
- shippingAddress: customer.shipping_address
524
+ email: 'customer@example.com' // Get from auth
265
525
  },
266
526
  options: {
267
- metadata: {
268
- orderId: cart.order_id,
269
- source: 'shopify'
270
- }
527
+ redirectUrl: window.location.origin + '/thank-you'
271
528
  }
272
529
  })
273
530
 
274
- res.json({ checkoutUrl })
275
- } catch (error) {
276
- res.status(500).json({ error: error.message })
531
+ if (url) {
532
+ window.location.href = url
533
+ }
277
534
  }
278
- })
535
+
536
+ if (cart.items.length === 0) {
537
+ return <p>Your cart is empty</p>
538
+ }
539
+
540
+ return (
541
+ <div>
542
+ <h1>Shopping Cart</h1>
543
+ {cart.items.map(item => (
544
+ <div key={`${item.productId}-${item.variant || ''}`}>
545
+ <span>Product {item.productId}</span>
546
+ <span>Qty: {item.quantity}</span>
547
+ <span>${((item.price || 0) * item.quantity).toFixed(2)}</span>
548
+ <button onClick={() => cart.removeItem(item.productId, item.variant)}>
549
+ Remove
550
+ </button>
551
+ </div>
552
+ ))}
553
+ <div>
554
+ <strong>Total: ${cart.getTotal().toFixed(2)}</strong>
555
+ </div>
556
+ <button onClick={handleCheckout} disabled={isLoading}>
557
+ {isLoading ? 'Processing...' : 'Proceed to Checkout'}
558
+ </button>
559
+ </div>
560
+ )
561
+ }
279
562
  ```
280
563
 
281
- ### WooCommerce
564
+ ### Server Component Checkout (Next.js 13+)
282
565
 
283
- ```php
284
- // WooCommerce hook integration
285
- add_action('woocommerce_checkout_order_processed', 'redirect_to_torque_checkout', 10, 3);
566
+ ```tsx
567
+ // app/checkout/page.tsx
568
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
569
+ import { redirect } from 'next/navigation'
570
+ import { cookies } from 'next/headers'
286
571
 
287
- function redirect_to_torque_checkout($order_id, $posted_data, $order) {
288
- $cart_items = [];
289
-
290
- foreach ($order->get_items() as $item) {
291
- $cart_items[] = [
292
- 'productId' => $item->get_product_id(),
293
- 'quantity' => $item->get_quantity(),
294
- 'variant' => $item->get_variation_id() ? $item->get_name() : null,
295
- 'price' => $item->get_total() / $item->get_quantity()
296
- ];
297
- }
298
-
299
- $checkout_data = [
300
- 'businessId' => get_option('torque_business_id'),
301
- 'cart' => ['items' => $cart_items],
302
- 'customerData' => [
303
- 'email' => $order->get_billing_email(),
304
- 'firstName' => $order->get_billing_first_name(),
305
- 'lastName' => $order->get_billing_last_name()
306
- ]
307
- ];
308
-
309
- // Use JavaScript SDK or direct API call
310
- $response = wp_remote_post('https://dashboard.torque.fi/api/checkout/generate-link', [
311
- 'body' => json_encode($checkout_data),
312
- 'headers' => ['Content-Type' => 'application/json'],
313
- 'timeout' => 30
314
- ]);
315
-
316
- if (!is_wp_error($response)) {
317
- $body = json_decode(wp_remote_retrieve_body($response), true);
318
- if ($body['checkoutUrl']) {
319
- wp_redirect($body['checkoutUrl']);
320
- exit;
321
- }
322
- }
572
+ export default async function CheckoutPage({
573
+ searchParams
574
+ }: {
575
+ searchParams: { productId: string; quantity?: string }
576
+ }) {
577
+ const torque = createTorqueCheckoutFromEnv()
578
+
579
+ // Get customer email from session/cookies
580
+ const customerEmail = cookies().get('user_email')?.value || 'guest@example.com'
581
+
582
+ const checkoutUrl = await torque.generateProductCheckoutUrl(
583
+ searchParams.productId,
584
+ Number(searchParams.quantity) || 1,
585
+ { email: customerEmail }
586
+ )
587
+
588
+ redirect(checkoutUrl)
323
589
  }
324
590
  ```
325
591
 
326
- ## Testing
592
+ ### Pages Router API Route
327
593
 
328
- ### Test Environment
594
+ ```ts
595
+ // pages/api/checkout.ts
596
+ import type { NextApiRequest, NextApiResponse } from 'next'
597
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
329
598
 
330
- Use our sandbox environment for testing:
599
+ export default async function handler(
600
+ req: NextApiRequest,
601
+ res: NextApiResponse
602
+ ) {
603
+ if (req.method !== 'POST') {
604
+ return res.status(405).json({ error: 'Method not allowed' })
605
+ }
331
606
 
332
- ```typescript
333
- const torque = new TorqueCheckout({
334
- businessId: 'test_business_123',
335
- apiKey: 'test_api_key'
336
- })
607
+ try {
608
+ const torque = createTorqueCheckoutFromEnv()
609
+ const checkoutUrl = await torque.generateCartCheckoutUrl(req.body)
610
+
611
+ res.status(200).json({ checkoutUrl })
612
+ } catch (error: any) {
613
+ res.status(error.statusCode || 500).json({
614
+ error: error.message,
615
+ code: error.code
616
+ })
617
+ }
618
+ }
619
+ ```
620
+
621
+ ## TypeScript
622
+
623
+ Full TypeScript support with comprehensive types:
624
+
625
+ ```ts
626
+ import type {
627
+ // Cart types
628
+ CartItem,
629
+ CartData,
630
+ CartOptions,
631
+ CartValidation,
632
+
633
+ // Customer types
634
+ CustomerData,
635
+
636
+ // Order types
637
+ OrderStatus,
638
+ CheckoutResponse,
639
+
640
+ // Subscription types
641
+ Subscription,
642
+ SubscriptionProduct,
643
+ PaymentPlan,
644
+ CreateSubscriptionData,
645
+ UpdateSubscriptionData,
646
+
647
+ // Config types
648
+ TorqueConfig,
649
+ TorqueCheckoutError
650
+ } from 'torque-checkout'
337
651
  ```
338
652
 
339
- ### Test Data
653
+ ### Type-Safe Usage
654
+
655
+ ```ts
656
+ import { createTorqueCheckout } from 'torque-checkout'
657
+ import type { CartData, CustomerData } from 'torque-checkout'
340
658
 
341
- ```typescript
342
- const testCart = {
659
+ const torque = createTorqueCheckout({
660
+ businessId: process.env.TORQUE_BUSINESS_ID!,
661
+ apiKey: process.env.TORQUE_API_KEY!
662
+ })
663
+
664
+ const cart: CartData = {
343
665
  items: [
344
- { productId: 'test_prod_1', quantity: 1, price: 10.00 }
666
+ { productId: 'prod_123', quantity: 1 }
345
667
  ],
346
668
  customer: {
347
- email: 'test@example.com'
348
- }
669
+ email: 'customer@example.com'
670
+ } as CustomerData
349
671
  }
350
672
 
351
- const checkoutUrl = await torque.generateCartCheckoutUrl(testCart)
673
+ const url: string = await torque.generateCartCheckoutUrl(cart)
352
674
  ```
353
675
 
354
676
  ## Error Handling
355
677
 
356
- The SDK throws descriptive errors for various failure scenarios:
678
+ The SDK uses custom error classes for better error handling:
679
+
680
+ ```ts
681
+ import { TorqueCheckoutError } from 'torque-checkout'
357
682
 
358
- ```typescript
359
683
  try {
360
- const checkoutUrl = await torque.generateCartCheckoutUrl(cart)
684
+ const url = await torque.generateCartCheckoutUrl(cart)
361
685
  } catch (error) {
362
- if (error.message.includes('BUSINESS_NOT_FOUND')) {
363
- console.error('Invalid business ID')
364
- } else if (error.message.includes('PRODUCTS_NOT_FOUND')) {
365
- console.error('Some products not found')
366
- } else if (error.message.includes('Request timeout')) {
367
- console.error('API request timed out')
686
+ if (error instanceof TorqueCheckoutError) {
687
+ console.error('Error code:', error.code)
688
+ console.error('Status code:', error.statusCode)
689
+ console.error('Details:', error.details)
690
+
691
+ // Handle specific error codes
692
+ switch (error.code) {
693
+ case 'VALIDATION_ERROR':
694
+ // Show validation errors to user
695
+ showErrors(error.details?.errors)
696
+ break
697
+ case 'BUSINESS_NOT_FOUND':
698
+ // Check business ID configuration
699
+ console.error('Invalid business ID')
700
+ break
701
+ case 'TIMEOUT':
702
+ // Retry or show timeout message
703
+ retryCheckout()
704
+ break
705
+ case 'NETWORK_ERROR':
706
+ // Check internet connection
707
+ showNetworkError()
708
+ break
709
+ default:
710
+ // Generic error handling
711
+ showGenericError(error.message)
712
+ }
368
713
  } else {
369
- console.error('Unexpected error:', error.message)
714
+ // Unknown error
715
+ console.error('Unexpected error:', error)
370
716
  }
371
717
  }
372
718
  ```
373
719
 
374
- ## Analytics & Tracking
720
+ ### Common Error Codes
721
+
722
+ | Code | Description | Solution |
723
+ |------|-------------|----------|
724
+ | `VALIDATION_ERROR` | Cart validation failed | Check cart items and customer data |
725
+ | `BUSINESS_NOT_FOUND` | Invalid business ID | Verify `TORQUE_BUSINESS_ID` |
726
+ | `INVALID_API_KEY` | Invalid API key | Verify `TORQUE_API_KEY` |
727
+ | `TIMEOUT` | Request timed out | Check network, increase timeout |
728
+ | `NETWORK_ERROR` | Network request failed | Check internet connection |
729
+ | `ORDER_NOT_FOUND` | Order doesn't exist | Verify order ID |
730
+
731
+ ## Security Best Practices
732
+
733
+ 1. **Never expose API keys in client-side code**
734
+ ```ts
735
+ // BAD - Don't do this
736
+ const torque = createTorqueCheckout({
737
+ businessId: 'business_123',
738
+ apiKey: 'sk_live_...' // Exposed in browser!
739
+ })
740
+
741
+ // GOOD - Use server-side API routes
742
+ // app/api/checkout/route.ts
743
+ export const POST = handleCheckoutRequest()
744
+ ```
745
+
746
+ 2. **Use environment variables**
747
+ ```env
748
+ # .env.local (never commit)
749
+ TORQUE_BUSINESS_ID=your_business_id
750
+ TORQUE_API_KEY=your_api_key
751
+ ```
752
+
753
+ 3. **Validate user input**
754
+ ```ts
755
+ // Always validate before sending to API
756
+ if (!cart.items || cart.items.length === 0) {
757
+ throw new Error('Cart is empty')
758
+ }
759
+ ```
760
+
761
+ 4. **Use HTTPS in production**
762
+ - Always use HTTPS for checkout URLs
763
+ - Never send sensitive data over HTTP
764
+
765
+ 5. **Verify webhook signatures**
766
+ ```ts
767
+ export const POST = handleWebhook({
768
+ secret: process.env.TORQUE_WEBHOOK_SECRET
769
+ })
770
+ ```
771
+
772
+ 6. **Set payment wallet in business settings**
773
+ - Payments automatically go to your configured wallet
774
+ - No need to handle wallet addresses in code
775
+
776
+ ## Troubleshooting
777
+
778
+ ### Issue: "TorqueCheckout not initialized"
779
+
780
+ **Solution**: Make sure environment variables are set:
781
+ ```bash
782
+ # Check if variables are loaded
783
+ echo $TORQUE_BUSINESS_ID
784
+ echo $TORQUE_API_KEY
785
+ ```
375
786
 
376
- Track customer behavior and optimize conversions:
787
+ Or provide explicit config:
788
+ ```ts
789
+ const torque = createTorqueCheckout({
790
+ businessId: 'your_business_id',
791
+ apiKey: 'your_api_key'
792
+ })
793
+ ```
377
794
 
378
- ```typescript
379
- // Track cart abandonment
380
- await torque.trackCartView('cart_123', cartData)
795
+ ### Issue: "Request timeout"
381
796
 
382
- // Track successful checkouts
383
- await torque.trackCheckoutComplete('order_123', {
384
- paymentMethod: 'card',
385
- total: 99.99,
386
- items: 3
797
+ **Solution**: Increase timeout or check network:
798
+ ```ts
799
+ const torque = createTorqueCheckout({
800
+ businessId: process.env.TORQUE_BUSINESS_ID!,
801
+ apiKey: process.env.TORQUE_API_KEY!,
802
+ timeout: 60000 // 60 seconds
387
803
  })
388
804
  ```
389
805
 
390
- ## Payment Destination
806
+ ### Issue: "Business not found"
807
+
808
+ **Solution**:
809
+ 1. Verify business ID in [Business Settings](https://app.torque.fi/business/settings)
810
+ 2. Check environment variable is correct
811
+ 3. Ensure business profile is saved and active
391
812
 
392
- **Payments are transferred to the wallet address set in business profile.**
813
+ ### Issue: React hooks not working
393
814
 
394
- - **Set once**: Configure your payment wallet in business settings
395
- - **Automatic transfers**: All successful checkouts send funds to your wallet
396
- - **Secure**: No need to handle payment addresses in your code
397
- - **Update anytime**: Change your payment wallet in business settings
815
+ **Solution**: Make sure you're using the React entry point:
816
+ ```tsx
817
+ // Correct
818
+ import { useTorqueCheckout } from 'torque-checkout/react'
398
819
 
399
- ## Performance
820
+ // Wrong
821
+ import { useTorqueCheckout } from 'torque-checkout'
822
+ ```
823
+
824
+ ### Issue: TypeScript errors
400
825
 
401
- - Built-in request caching with cart hashing
402
- - Configurable timeouts
403
- - Efficient error handling
404
- - Minimal bundle size
826
+ **Solution**: Make sure TypeScript can find the types:
827
+ ```json
828
+ // tsconfig.json
829
+ {
830
+ "compilerOptions": {
831
+ "moduleResolution": "node",
832
+ "esModuleInterop": true
833
+ }
834
+ }
835
+ ```
836
+
837
+ ## Migration Guide
838
+
839
+ ### From v1.x to v2.0.0
840
+
841
+ v2.0.0 is **backward compatible** with v1.x. No code changes required!
842
+
843
+ **New features available (optional):**
844
+ - React hooks: `useTorqueCheckout()`, `useCart()`
845
+ - Next.js utilities: `handleCheckoutRequest()`, `handleWebhook()`
846
+ - Environment variable support: `createTorqueCheckoutFromEnv()`
847
+
848
+ **Migration example:**
849
+
850
+ ```ts
851
+ // v1.x (still works)
852
+ import { TorqueCheckout } from 'torque-checkout'
853
+ const torque = new TorqueCheckout({ businessId, apiKey })
854
+
855
+ // v2.0.0 (new, optional)
856
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
857
+ const torque = createTorqueCheckoutFromEnv() // Uses env vars
858
+ ```
859
+
860
+ ## API Reference
861
+
862
+ For complete API documentation, visit:
863
+ - **Full API Docs**: [https://docs.torque.fi/api](https://docs.torque.fi/api)
864
+ - **Integration Guide**: [https://docs.torque.fi/integrations](https://docs.torque.fi/integrations)
405
865
 
406
866
  ## Support
407
867
 
408
868
  - **Documentation**: [https://docs.torque.fi](https://docs.torque.fi)
409
- - **API Reference**: [https://docs.torque.fi/api](https://docs.torque.fi/api)
410
- - **Integration Guide**: [https://docs.torque.fi/integrations](https://docs.torque.fi/integrations)
411
- - **NPM Package**: [https://www.npmjs.com/package/torque-checkout](https://www.npmjs.com/package/torque-checkout)
412
- - **Support**: hello@torque.fi
869
+ - **Email**: hello@torque.fi
870
+ - **GitHub Issues**: [https://github.com/torque-fi/torque-checkout/issues](https://github.com/torque-fi/torque-checkout/issues)
871
+ - **Business Dashboard**: [https://app.torque.fi/business/settings](https://app.torque.fi/business/settings)
872
+
873
+ ## License
874
+
875
+ MIT © [Torque](https://torque.fi)
876
+
877
+ ## Getting Started Checklist
878
+
879
+ - [ ] Install: `npm install torque-checkout`
880
+ - [ ] Create business profile: [app.torque.fi/business/settings](https://app.torque.fi/business/settings)
881
+ - [ ] Set payment wallet address in business settings
882
+ - [ ] Copy Business ID and API Key
883
+ - [ ] Add environment variables to `.env.local`
884
+ - [ ] Import and use the SDK
885
+ - [ ] Test checkout flow
886
+ - [ ] Deploy to production!
887
+
888
+ ---
889
+
890
+ **Made with love by [Torque](https://torque.fi)**