torque-checkout 2.0.0 → 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.
Files changed (2) hide show
  1. package/README.md +528 -195
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -5,20 +5,37 @@
5
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
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)
8
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
-
10
- ## ✨ Features
11
-
12
- - 🚀 **Lightweight & Fast** - Minimal bundle size, optimized for performance
13
- - ⚡ **Zero Configuration** - Works out of the box with environment variables
14
- - 🎯 **TypeScript First** - Full TypeScript support with comprehensive types
15
- - ⚛️ **React Hooks** - Built-in React hooks for seamless client-side integration
16
- - 🔧 **Next.js Optimized** - Server-side utilities for App Router and Pages Router
17
- - 🔒 **Production Ready** - Comprehensive error handling and validation
18
- - 📦 **Multi-Product Support** - Handle complex carts with multiple items
19
- - 🔄 **Subscription Support** - Built-in subscription management
20
-
21
- ## 📦 Installation
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
11
+
12
+ ## Table of Contents
13
+
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
27
+
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
22
39
 
23
40
  ```bash
24
41
  npm install torque-checkout
@@ -28,34 +45,46 @@ yarn add torque-checkout
28
45
  pnpm add torque-checkout
29
46
  ```
30
47
 
31
- ## 🚀 Quick Start
48
+ ### Requirements
49
+
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)
32
53
 
33
- ### 1. Get Your API Credentials
54
+ ## Quick Start
34
55
 
35
- Before using the SDK, you need to set up your Torque business profile:
56
+ ### Step 1: Get Your API Credentials
36
57
 
37
- 1. **Go to [Torque Business Dashboard](https://app.torque.fi/business/settings)**
58
+ 1. **Visit [Torque Business Dashboard](https://app.torque.fi/business/settings)**
38
59
  2. **Connect your wallet** (MetaMask, WalletConnect, etc.)
39
- 3. **Fill out your business information** (name, email, website, etc.)
40
- 4. **Set your payment wallet address** (where you want to receive payments)
41
- 5. **Save your profile** - Your API key will be generated automatically
42
- 6. **Copy your credentials** from the API Integration section:
43
- - Business ID
44
- - API Key
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`
45
67
 
46
- ### 2. Configure Environment Variables
68
+ > **Tip**: Your payment wallet address is set once in business settings. All successful payments automatically go to this wallet - no code needed!
47
69
 
48
- Create a `.env.local` file in your Next.js project:
70
+ ### Step 2: Configure Environment Variables
71
+
72
+ Create a `.env.local` file in your Next.js project root:
49
73
 
50
74
  ```env
75
+ # Required
51
76
  TORQUE_BUSINESS_ID=your_business_id_here
52
77
  TORQUE_API_KEY=your_api_key_here
53
- TORQUE_BASE_URL=https://app.torque.fi # Optional, defaults to production
78
+
79
+ # Optional (defaults to production)
80
+ TORQUE_BASE_URL=https://app.torque.fi
54
81
  ```
55
82
 
56
- ### 3. Basic Usage
83
+ > **Security**: Never commit `.env.local` to version control. Add it to `.gitignore`.
57
84
 
58
- #### Client-Side (React Component)
85
+ ### Step 3: Choose Your Integration Method
86
+
87
+ #### Option A: React Hook (Client-Side) - Recommended for Most Cases
59
88
 
60
89
  ```tsx
61
90
  'use client'
@@ -64,82 +93,78 @@ import { useTorqueCheckout } from 'torque-checkout/react'
64
93
 
65
94
  export default function CheckoutButton() {
66
95
  const { generateProductCheckout, isLoading } = useTorqueCheckout({
67
- autoRedirect: true, // Automatically redirect to checkout
68
- onSuccess: (url) => console.log('Checkout URL:', url),
69
- onError: (error) => console.error('Error:', error)
96
+ autoRedirect: true // Automatically redirects to checkout
70
97
  })
71
98
 
72
- const handleCheckout = async () => {
73
- await generateProductCheckout('prod_123', 1, {
74
- email: 'customer@example.com',
75
- firstName: 'John',
76
- lastName: 'Doe'
77
- })
78
- }
79
-
80
99
  return (
81
- <button onClick={handleCheckout} disabled={isLoading}>
82
- {isLoading ? 'Loading...' : 'Checkout'}
100
+ <button
101
+ onClick={() => generateProductCheckout('prod_123', 1, {
102
+ email: 'customer@example.com'
103
+ })}
104
+ disabled={isLoading}
105
+ >
106
+ {isLoading ? 'Loading...' : 'Buy Now'}
83
107
  </button>
84
108
  )
85
109
  }
86
110
  ```
87
111
 
88
- #### Server-Side (API Route)
112
+ #### Option B: Server-Side API Route (Most Secure)
89
113
 
90
114
  ```ts
91
115
  // app/api/checkout/route.ts
92
116
  import { handleCheckoutRequest } from 'torque-checkout/nextjs'
93
117
 
94
- export const POST = handleCheckoutRequest({
95
- onSuccess: async (url, cart) => {
96
- // Optional: Log checkout generation
97
- console.log('Checkout generated:', url)
98
- }
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
+ })
99
131
  })
132
+
133
+ const { checkoutUrl } = await response.json()
134
+ window.location.href = checkoutUrl
100
135
  ```
101
136
 
102
- #### Direct SDK Usage
137
+ #### Option C: Direct SDK Usage
103
138
 
104
139
  ```ts
105
- import { createTorqueCheckout } from 'torque-checkout'
140
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
106
141
 
107
- const torque = createTorqueCheckout({
108
- businessId: process.env.TORQUE_BUSINESS_ID!,
109
- apiKey: process.env.TORQUE_API_KEY!
110
- })
142
+ const torque = createTorqueCheckoutFromEnv()
111
143
 
112
- // Generate checkout URL
113
144
  const checkoutUrl = await torque.generateCartCheckoutUrl({
114
- items: [
115
- { productId: 'prod_123', quantity: 2 },
116
- { productId: 'prod_456', quantity: 1 }
117
- ],
118
- customer: {
119
- email: 'customer@example.com',
120
- firstName: 'John',
121
- lastName: 'Doe'
122
- }
145
+ items: [{ productId: 'prod_123', quantity: 1 }],
146
+ customer: { email: 'customer@example.com' }
123
147
  })
124
148
 
125
- // Redirect customer
126
149
  window.location.href = checkoutUrl
127
150
  ```
128
151
 
129
- ## 📚 Documentation
152
+ ## Documentation
130
153
 
131
154
  ### Core SDK
132
155
 
133
- #### `TorqueCheckout` Class
134
-
135
- The main SDK class for generating checkout URLs and managing orders.
156
+ #### Initialization
136
157
 
137
158
  ```ts
138
- import { createTorqueCheckout } from 'torque-checkout'
159
+ import { createTorqueCheckout, createTorqueCheckoutFromEnv } from 'torque-checkout'
139
160
 
161
+ // Method 1: From environment variables (recommended)
162
+ const torque = createTorqueCheckoutFromEnv()
163
+
164
+ // Method 2: Explicit configuration
140
165
  const torque = createTorqueCheckout({
141
- businessId: 'your_business_id',
142
- apiKey: 'your_api_key',
166
+ businessId: process.env.TORQUE_BUSINESS_ID!,
167
+ apiKey: process.env.TORQUE_API_KEY!,
143
168
  baseUrl: 'https://app.torque.fi', // Optional
144
169
  timeout: 30000 // Optional, default: 30000ms
145
170
  })
@@ -152,16 +177,23 @@ const torque = createTorqueCheckout({
152
177
  Generate a checkout URL for a multi-product cart.
153
178
 
154
179
  ```ts
155
- const url = await torque.generateCartCheckoutUrl({
180
+ const checkoutUrl = await torque.generateCartCheckoutUrl({
156
181
  items: [
157
- { productId: 'prod_123', quantity: 2, price: 29.99 },
158
- { productId: 'prod_456', quantity: 1, price: 49.99 }
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
+ },
189
+ { productId: 'prod_456', quantity: 1 }
159
190
  ],
160
191
  customer: {
161
- email: 'customer@example.com',
162
- firstName: 'John',
163
- lastName: 'Doe',
164
- 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
165
197
  street: '123 Main St',
166
198
  city: 'New York',
167
199
  state: 'NY',
@@ -170,66 +202,75 @@ const url = await torque.generateCartCheckoutUrl({
170
202
  }
171
203
  },
172
204
  options: {
173
- metadata: { orderId: 'order_123', source: 'website' },
174
- expiresIn: 24 * 60 * 60 * 1000, // 24 hours
175
- redirectUrl: 'https://yoursite.com/thank-you'
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
176
208
  }
177
209
  })
178
210
  ```
179
211
 
180
- ##### `generateProductCheckoutUrl(productId, quantity, customer?, options?): Promise<string>`
212
+ ##### `generateProductCheckoutUrl(productId, quantity?, customer?, options?): Promise<string>`
181
213
 
182
- Generate a checkout URL for a single product.
214
+ Quick method for single product checkout.
183
215
 
184
216
  ```ts
185
- const url = await torque.generateProductCheckoutUrl(
186
- 'prod_123',
187
- 2,
188
- { email: 'customer@example.com' }
217
+ const checkoutUrl = await torque.generateProductCheckoutUrl(
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)
189
222
  )
190
223
  ```
191
224
 
192
225
  ##### `generateSubscriptionCheckoutUrl(productId, paymentPlanId, customer?, options?): Promise<string>`
193
226
 
194
- Generate a checkout URL for a subscription product.
227
+ Generate checkout for subscription products.
195
228
 
196
229
  ```ts
197
- const url = await torque.generateSubscriptionCheckoutUrl(
198
- 'prod_subscription_123',
199
- 'plan_monthly_123',
200
- { email: 'customer@example.com' }
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' }
201
235
  )
202
236
  ```
203
237
 
204
238
  ##### `validateCart(cart: CartData): Promise<CartValidation>`
205
239
 
206
- Validate cart data before generating checkout.
240
+ Validate cart before checkout to catch errors early.
207
241
 
208
242
  ```ts
209
- const validation = await torque.validateCart(cart)
243
+ const validation = await torque.validateCart({
244
+ items: [{ productId: 'prod_123', quantity: 2 }]
245
+ })
210
246
 
211
247
  if (validation.valid) {
212
- console.log('Cart is valid, estimated total:', validation.estimatedTotal)
248
+ console.log('Cart is valid')
249
+ console.log('Estimated total:', validation.estimatedTotal)
213
250
  } else {
214
251
  console.error('Validation errors:', validation.errors)
252
+ console.warn('Warnings:', validation.warnings)
215
253
  }
216
254
  ```
217
255
 
218
256
  ##### `getOrderStatus(orderId: string): Promise<OrderStatus>`
219
257
 
220
- Get the current status of an order.
258
+ Check order status after checkout.
221
259
 
222
260
  ```ts
223
261
  const order = await torque.getOrderStatus('order_123')
224
- console.log('Order status:', order.status)
262
+
263
+ console.log('Status:', order.status) // e.g., 'completed', 'pending', 'failed'
225
264
  console.log('Total:', order.totals.total)
265
+ console.log('Payment status:', order.paymentStatus)
266
+ console.log('Items:', order.items)
226
267
  ```
227
268
 
228
269
  ### React Hooks
229
270
 
230
271
  #### `useTorqueCheckout(options?)`
231
272
 
232
- React hook for client-side checkout generation.
273
+ React hook for client-side checkout with built-in state management.
233
274
 
234
275
  ```tsx
235
276
  import { useTorqueCheckout } from 'torque-checkout/react'
@@ -245,23 +286,47 @@ function CheckoutButton() {
245
286
  error,
246
287
  checkoutUrl
247
288
  } = useTorqueCheckout({
248
- config: {
249
- businessId: 'your_business_id',
250
- apiKey: 'your_api_key'
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
251
298
  },
252
- // Or use environment variables (no config needed)
253
- autoRedirect: true,
254
- onSuccess: (url) => console.log('Success:', url),
255
- onError: (error) => console.error('Error:', error)
299
+ onError: (error) => {
300
+ console.error('Checkout error:', error)
301
+ // Optional: show error toast
302
+ }
256
303
  })
257
304
 
258
- // Use the methods...
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
+ }
314
+
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
+ )
259
324
  }
260
325
  ```
261
326
 
262
327
  #### `useCart()`
263
328
 
264
- React hook for managing shopping cart state.
329
+ Shopping cart state management hook.
265
330
 
266
331
  ```tsx
267
332
  import { useCart } from 'torque-checkout/react'
@@ -279,16 +344,26 @@ function ShoppingCart() {
279
344
 
280
345
  return (
281
346
  <div>
282
- <h2>Cart ({getItemCount()} items)</h2>
283
- <p>Total: ${getTotal().toFixed(2)}</p>
347
+ <h2>Shopping Cart ({getItemCount()} items)</h2>
348
+
284
349
  {items.map(item => (
285
- <div key={`${item.productId}-${item.variant}`}>
286
- {item.productId} x {item.quantity}
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>
287
359
  <button onClick={() => removeItem(item.productId, item.variant)}>
288
360
  Remove
289
361
  </button>
290
362
  </div>
291
363
  ))}
364
+
365
+ <p>Total: ${getTotal().toFixed(2)}</p>
366
+ <button onClick={clearCart}>Clear Cart</button>
292
367
  </div>
293
368
  )
294
369
  }
@@ -296,65 +371,80 @@ function ShoppingCart() {
296
371
 
297
372
  ### Next.js Server Utilities
298
373
 
299
- #### `generateCheckoutUrl(cart, config?)`
300
-
301
- Generate checkout URL on the server.
302
-
303
- ```ts
304
- // app/api/checkout/route.ts
305
- import { generateCheckoutUrl } from 'torque-checkout/nextjs'
306
-
307
- export async function POST(request: Request) {
308
- const cart = await request.json()
309
- const url = await generateCheckoutUrl(cart)
310
- return Response.json({ checkoutUrl: url })
311
- }
312
- ```
313
-
314
374
  #### `handleCheckoutRequest(options?)`
315
375
 
316
- Next.js API route handler for checkout generation.
376
+ Complete API route handler with error handling.
317
377
 
318
378
  ```ts
319
379
  // app/api/checkout/route.ts
320
380
  import { handleCheckoutRequest } from 'torque-checkout/nextjs'
321
381
 
322
382
  export const POST = handleCheckoutRequest({
323
- onSuccess: async (url, cart) => {
324
- // Log or process checkout
325
- console.log('Checkout generated:', url)
383
+ onSuccess: async (checkoutUrl, cart) => {
384
+ // Optional: Log, track analytics, send notifications
385
+ console.log('Checkout generated:', checkoutUrl)
386
+ await logCheckoutEvent(cart)
326
387
  },
327
388
  onError: async (error, cart) => {
328
- // Handle error
389
+ // Optional: Error logging, notifications
329
390
  console.error('Checkout error:', error)
391
+ await logError(error, cart)
330
392
  }
331
393
  })
332
394
  ```
333
395
 
334
396
  #### `handleWebhook(options?)`
335
397
 
336
- Next.js API route handler for webhook events.
398
+ Webhook handler for order events.
337
399
 
338
400
  ```ts
339
401
  // app/api/webhooks/torque/route.ts
340
402
  import { handleWebhook } from 'torque-checkout/nextjs'
341
403
 
342
404
  export const POST = handleWebhook({
343
- secret: process.env.TORQUE_WEBHOOK_SECRET,
405
+ secret: process.env.TORQUE_WEBHOOK_SECRET, // Optional: verify signatures
344
406
  onOrderCompleted: async (event) => {
345
407
  // Fulfill order
346
408
  await fulfillOrder(event.orderId!)
409
+ await sendConfirmationEmail(event.data.customerEmail)
347
410
  },
348
411
  onOrderFailed: async (event) => {
349
- // Handle failed order
412
+ // Handle failed payment
350
413
  await notifyCustomer(event.orderId!)
414
+ },
415
+ onSubscriptionCreated: async (event) => {
416
+ // Activate subscription
417
+ await activateSubscription(event.subscriptionId!)
351
418
  }
352
419
  })
353
420
  ```
354
421
 
355
- ## 🎯 Complete Examples
422
+ #### `generateCheckoutUrl(cart, config?)`
423
+
424
+ Server-side checkout URL generation.
425
+
426
+ ```ts
427
+ // app/api/checkout/route.ts
428
+ import { generateCheckoutUrl } from 'torque-checkout/nextjs'
429
+
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
+ }
442
+ }
443
+ ```
444
+
445
+ ## Complete Examples
356
446
 
357
- ### Next.js App Router - Full E-Commerce Integration
447
+ ### E-Commerce Product Page
358
448
 
359
449
  ```tsx
360
450
  // app/products/[id]/page.tsx
@@ -364,26 +454,26 @@ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
364
454
  import { useState } from 'react'
365
455
 
366
456
  export default function ProductPage({ params }: { params: { id: string } }) {
367
- const { addItem, items, getItemCount } = useCart()
457
+ const cart = useCart()
368
458
  const { generateProductCheckout, isLoading } = useTorqueCheckout({
369
459
  autoRedirect: true
370
460
  })
371
461
  const [quantity, setQuantity] = useState(1)
372
462
 
373
463
  const handleAddToCart = () => {
374
- addItem({
464
+ cart.addItem({
375
465
  productId: params.id,
376
466
  quantity,
377
- price: 29.99 // Your product price
467
+ price: 29.99
378
468
  })
379
469
  }
380
470
 
381
- const handleCheckout = async () => {
471
+ const handleBuyNow = async () => {
382
472
  await generateProductCheckout(
383
473
  params.id,
384
474
  quantity,
385
475
  {
386
- email: 'customer@example.com' // Get from user session
476
+ email: 'customer@example.com' // Get from auth/session
387
477
  }
388
478
  )
389
479
  }
@@ -391,6 +481,8 @@ export default function ProductPage({ params }: { params: { id: string } }) {
391
481
  return (
392
482
  <div>
393
483
  <h1>Product {params.id}</h1>
484
+ <p>$29.99</p>
485
+
394
486
  <div>
395
487
  <label>Quantity:</label>
396
488
  <input
@@ -400,51 +492,82 @@ export default function ProductPage({ params }: { params: { id: string } }) {
400
492
  min="1"
401
493
  />
402
494
  </div>
403
- <button onClick={handleAddToCart}>Add to Cart</button>
404
- <button onClick={handleCheckout} disabled={isLoading}>
405
- {isLoading ? 'Processing...' : 'Checkout'}
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'}
406
502
  </button>
407
- <p>Cart: {getItemCount()} items</p>
408
503
  </div>
409
504
  )
410
505
  }
411
506
  ```
412
507
 
413
- ### Next.js Pages Router - API Route
508
+ ### Shopping Cart with Checkout
414
509
 
415
- ```ts
416
- // pages/api/checkout.ts
417
- import type { NextApiRequest, NextApiResponse } from 'next'
418
- import { createTorqueCheckoutFromEnv } from 'torque-checkout'
510
+ ```tsx
511
+ // app/cart/page.tsx
512
+ 'use client'
419
513
 
420
- export default async function handler(
421
- req: NextApiRequest,
422
- res: NextApiResponse
423
- ) {
424
- if (req.method !== 'POST') {
425
- return res.status(405).json({ error: 'Method not allowed' })
426
- }
514
+ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
427
515
 
428
- try {
429
- const torque = createTorqueCheckoutFromEnv()
430
- const checkoutUrl = await torque.generateCartCheckoutUrl(req.body)
516
+ export default function CartPage() {
517
+ const cart = useCart()
518
+ const { generateCheckout, isLoading } = useTorqueCheckout()
431
519
 
432
- res.status(200).json({ checkoutUrl })
433
- } catch (error: any) {
434
- res.status(500).json({
435
- error: error.message,
436
- code: error.code
520
+ const handleCheckout = async () => {
521
+ const url = await generateCheckout({
522
+ items: cart.items,
523
+ customer: {
524
+ email: 'customer@example.com' // Get from auth
525
+ },
526
+ options: {
527
+ redirectUrl: window.location.origin + '/thank-you'
528
+ }
437
529
  })
530
+
531
+ if (url) {
532
+ window.location.href = url
533
+ }
438
534
  }
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
+ )
439
561
  }
440
562
  ```
441
563
 
442
- ### Server Component (Next.js 13+)
564
+ ### Server Component Checkout (Next.js 13+)
443
565
 
444
566
  ```tsx
445
567
  // app/checkout/page.tsx
446
568
  import { createTorqueCheckoutFromEnv } from 'torque-checkout'
447
569
  import { redirect } from 'next/navigation'
570
+ import { cookies } from 'next/headers'
448
571
 
449
572
  export default async function CheckoutPage({
450
573
  searchParams
@@ -453,40 +576,104 @@ export default async function CheckoutPage({
453
576
  }) {
454
577
  const torque = createTorqueCheckoutFromEnv()
455
578
 
579
+ // Get customer email from session/cookies
580
+ const customerEmail = cookies().get('user_email')?.value || 'guest@example.com'
581
+
456
582
  const checkoutUrl = await torque.generateProductCheckoutUrl(
457
583
  searchParams.productId,
458
584
  Number(searchParams.quantity) || 1,
459
- {
460
- email: 'customer@example.com' // Get from session
461
- }
585
+ { email: customerEmail }
462
586
  )
463
587
 
464
588
  redirect(checkoutUrl)
465
589
  }
466
590
  ```
467
591
 
468
- ## 🔧 TypeScript Types
592
+ ### Pages Router API Route
593
+
594
+ ```ts
595
+ // pages/api/checkout.ts
596
+ import type { NextApiRequest, NextApiResponse } from 'next'
597
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
598
+
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
+ }
606
+
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
469
622
 
470
- The SDK provides comprehensive TypeScript types:
623
+ Full TypeScript support with comprehensive types:
471
624
 
472
625
  ```ts
473
626
  import type {
627
+ // Cart types
474
628
  CartItem,
475
629
  CartData,
476
- CustomerData,
477
630
  CartOptions,
478
- CheckoutResponse,
479
- OrderStatus,
480
631
  CartValidation,
632
+
633
+ // Customer types
634
+ CustomerData,
635
+
636
+ // Order types
637
+ OrderStatus,
638
+ CheckoutResponse,
639
+
640
+ // Subscription types
481
641
  Subscription,
482
642
  SubscriptionProduct,
483
643
  PaymentPlan,
644
+ CreateSubscriptionData,
645
+ UpdateSubscriptionData,
646
+
647
+ // Config types
484
648
  TorqueConfig,
485
649
  TorqueCheckoutError
486
650
  } from 'torque-checkout'
487
651
  ```
488
652
 
489
- ## 🛡️ Error Handling
653
+ ### Type-Safe Usage
654
+
655
+ ```ts
656
+ import { createTorqueCheckout } from 'torque-checkout'
657
+ import type { CartData, CustomerData } from 'torque-checkout'
658
+
659
+ const torque = createTorqueCheckout({
660
+ businessId: process.env.TORQUE_BUSINESS_ID!,
661
+ apiKey: process.env.TORQUE_API_KEY!
662
+ })
663
+
664
+ const cart: CartData = {
665
+ items: [
666
+ { productId: 'prod_123', quantity: 1 }
667
+ ],
668
+ customer: {
669
+ email: 'customer@example.com'
670
+ } as CustomerData
671
+ }
672
+
673
+ const url: string = await torque.generateCartCheckoutUrl(cart)
674
+ ```
675
+
676
+ ## Error Handling
490
677
 
491
678
  The SDK uses custom error classes for better error handling:
492
679
 
@@ -504,54 +691,200 @@ try {
504
691
  // Handle specific error codes
505
692
  switch (error.code) {
506
693
  case 'VALIDATION_ERROR':
507
- // Handle validation errors
694
+ // Show validation errors to user
695
+ showErrors(error.details?.errors)
508
696
  break
509
697
  case 'BUSINESS_NOT_FOUND':
510
- // Handle business not found
698
+ // Check business ID configuration
699
+ console.error('Invalid business ID')
511
700
  break
512
701
  case 'TIMEOUT':
513
- // Handle timeout
702
+ // Retry or show timeout message
703
+ retryCheckout()
704
+ break
705
+ case 'NETWORK_ERROR':
706
+ // Check internet connection
707
+ showNetworkError()
514
708
  break
515
709
  default:
516
- // Handle other errors
710
+ // Generic error handling
711
+ showGenericError(error.message)
517
712
  }
713
+ } else {
714
+ // Unknown error
715
+ console.error('Unexpected error:', error)
716
+ }
717
+ }
718
+ ```
719
+
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
+ ```
786
+
787
+ Or provide explicit config:
788
+ ```ts
789
+ const torque = createTorqueCheckout({
790
+ businessId: 'your_business_id',
791
+ apiKey: 'your_api_key'
792
+ })
793
+ ```
794
+
795
+ ### Issue: "Request timeout"
796
+
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
803
+ })
804
+ ```
805
+
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
812
+
813
+ ### Issue: React hooks not working
814
+
815
+ **Solution**: Make sure you're using the React entry point:
816
+ ```tsx
817
+ // Correct
818
+ import { useTorqueCheckout } from 'torque-checkout/react'
819
+
820
+ // Wrong
821
+ import { useTorqueCheckout } from 'torque-checkout'
822
+ ```
823
+
824
+ ### Issue: TypeScript errors
825
+
826
+ **Solution**: Make sure TypeScript can find the types:
827
+ ```json
828
+ // tsconfig.json
829
+ {
830
+ "compilerOptions": {
831
+ "moduleResolution": "node",
832
+ "esModuleInterop": true
518
833
  }
519
834
  }
520
835
  ```
521
836
 
522
- ## 🔐 Security Best Practices
837
+ ## Migration Guide
838
+
839
+ ### From v1.x to v2.0.0
523
840
 
524
- 1. **Never expose API keys in client-side code** - Use environment variables and server-side API routes
525
- 2. **Validate all input** - The SDK includes validation, but always validate user input
526
- 3. **Use HTTPS** - Always use HTTPS in production
527
- 4. **Verify webhook signatures** - Use the webhook secret to verify webhook authenticity
528
- 5. **Set payment wallet in business settings** - Payments automatically go to your configured wallet
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
+ ```
529
859
 
530
- ## 📖 API Reference
860
+ ## API Reference
531
861
 
532
- See the [full API documentation](https://docs.torque.fi/api) for complete reference.
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)
533
865
 
534
- ## 🤝 Support
866
+ ## Support
535
867
 
536
868
  - **Documentation**: [https://docs.torque.fi](https://docs.torque.fi)
537
869
  - **Email**: hello@torque.fi
538
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)
539
872
 
540
- ## 📄 License
873
+ ## License
541
874
 
542
875
  MIT © [Torque](https://torque.fi)
543
876
 
544
- ## 🎉 Getting Started Checklist
877
+ ## Getting Started Checklist
545
878
 
546
- - [ ] Install the package: `npm install torque-checkout`
547
- - [ ] Create Torque business profile at [app.torque.fi/business/settings](https://app.torque.fi/business/settings)
879
+ - [ ] Install: `npm install torque-checkout`
880
+ - [ ] Create business profile: [app.torque.fi/business/settings](https://app.torque.fi/business/settings)
548
881
  - [ ] Set payment wallet address in business settings
549
882
  - [ ] Copy Business ID and API Key
550
883
  - [ ] Add environment variables to `.env.local`
551
- - [ ] Import and use the SDK in your code
884
+ - [ ] Import and use the SDK
552
885
  - [ ] Test checkout flow
553
- - [ ] Deploy to production! 🚀
886
+ - [ ] Deploy to production!
554
887
 
555
888
  ---
556
889
 
557
- **Made with ❤️ by [Torque](https://torque.fi)**
890
+ **Made with love by [Torque](https://torque.fi)**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torque-checkout",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Official Torque checkout SDK for seamless eCommerce integrations with Next.js",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",