torque-checkout 2.0.0 → 2.0.2

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 +575 -192
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -5,21 +5,57 @@
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)
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
9
39
 
10
- ## ✨ Features
40
+ ```bash
41
+ npm install torque-checkout
42
+ # or
43
+ yarn add torque-checkout
44
+ # or
45
+ pnpm add torque-checkout
46
+ ```
47
+
48
+ ### Requirements
11
49
 
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
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)
20
53
 
21
- ## 📦 Installation
54
+ ## Quick Start
22
55
 
56
+ ### Get Checkout Working in 3 Steps
57
+
58
+ **1. Install & Get Credentials**
23
59
  ```bash
24
60
  npm install torque-checkout
25
61
  # or
@@ -27,35 +63,78 @@ yarn add torque-checkout
27
63
  # or
28
64
  pnpm add torque-checkout
29
65
  ```
66
+ - Visit https://app.torque.fi/business/settings
67
+ - Connect wallet, complete profile, set payment wallet
68
+ - Copy Business ID and API Key
69
+
70
+ **2. Add Environment Variables**
71
+ Create `.env.local`:
72
+ ```env
73
+ TORQUE_BUSINESS_ID=your_business_id
74
+ TORQUE_API_KEY=your_api_key
75
+ ```
76
+
77
+ **3. Add Checkout Button**
78
+ ```tsx
79
+ 'use client'
80
+ import { useTorqueCheckout } from 'torque-checkout/react'
81
+
82
+ export default function CheckoutButton() {
83
+ const { generateProductCheckout, isLoading } = useTorqueCheckout({
84
+ autoRedirect: true
85
+ })
86
+
87
+ return (
88
+ <button
89
+ onClick={() => generateProductCheckout('prod_123', 1, {
90
+ email: 'customer@example.com'
91
+ })}
92
+ disabled={isLoading}
93
+ >
94
+ {isLoading ? 'Loading...' : 'Buy Now'}
95
+ </button>
96
+ )
97
+ }
98
+ ```
99
+
100
+ **Done!** Customer clicks → Redirects to checkout → Payment goes to your wallet.
30
101
 
31
- ## 🚀 Quick Start
102
+ ---
32
103
 
33
- ### 1. Get Your API Credentials
104
+ ### Detailed Setup
34
105
 
35
- Before using the SDK, you need to set up your Torque business profile:
106
+ ### Step 1: Get Your API Credentials
36
107
 
37
- 1. **Go to [Torque Business Dashboard](https://app.torque.fi/business/settings)**
108
+ 1. **Visit [Torque Business Dashboard](https://app.torque.fi/business/settings)**
38
109
  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
110
+ 3. **Complete your business profile**:
111
+ - Business name, email, website
112
+ - **Payment wallet address** (where payments will be sent)
113
+ 4. **Save your profile** - API credentials are generated automatically
114
+ 5. **Copy your credentials** from the API Integration section:
115
+ - `Business ID`
116
+ - `API Key`
117
+
118
+ > **Tip**: Your payment wallet address is set once in business settings. All successful payments automatically go to this wallet - no code needed!
45
119
 
46
- ### 2. Configure Environment Variables
120
+ ### Step 2: Configure Environment Variables
47
121
 
48
- Create a `.env.local` file in your Next.js project:
122
+ Create a `.env.local` file in your Next.js project root:
49
123
 
50
124
  ```env
125
+ # Required
51
126
  TORQUE_BUSINESS_ID=your_business_id_here
52
127
  TORQUE_API_KEY=your_api_key_here
53
- TORQUE_BASE_URL=https://app.torque.fi # Optional, defaults to production
128
+
129
+ # Optional (defaults to production)
130
+ TORQUE_BASE_URL=https://app.torque.fi
54
131
  ```
55
132
 
56
- ### 3. Basic Usage
133
+ > **Security**: Never commit `.env.local` to version control. Add it to `.gitignore`.
134
+
135
+ ### Step 3: Choose Your Integration Method
57
136
 
58
- #### Client-Side (React Component)
137
+ #### Option A: React Hook (Client-Side) - Recommended for Most Cases
59
138
 
60
139
  ```tsx
61
140
  'use client'
@@ -64,82 +143,78 @@ import { useTorqueCheckout } from 'torque-checkout/react'
64
143
 
65
144
  export default function CheckoutButton() {
66
145
  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)
146
+ autoRedirect: true // Automatically redirects to checkout
70
147
  })
71
148
 
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
149
  return (
81
- <button onClick={handleCheckout} disabled={isLoading}>
82
- {isLoading ? 'Loading...' : 'Checkout'}
150
+ <button
151
+ onClick={() => generateProductCheckout('prod_123', 1, {
152
+ email: 'customer@example.com'
153
+ })}
154
+ disabled={isLoading}
155
+ >
156
+ {isLoading ? 'Loading...' : 'Buy Now'}
83
157
  </button>
84
158
  )
85
159
  }
86
160
  ```
87
161
 
88
- #### Server-Side (API Route)
162
+ #### Option B: Server-Side API Route (Most Secure)
89
163
 
90
164
  ```ts
91
165
  // app/api/checkout/route.ts
92
166
  import { handleCheckoutRequest } from 'torque-checkout/nextjs'
93
167
 
94
- export const POST = handleCheckoutRequest({
95
- onSuccess: async (url, cart) => {
96
- // Optional: Log checkout generation
97
- console.log('Checkout generated:', url)
98
- }
168
+ export const POST = handleCheckoutRequest()
169
+ ```
170
+
171
+ Then call from your frontend:
172
+
173
+ ```tsx
174
+ const response = await fetch('/api/checkout', {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({
178
+ items: [{ productId: 'prod_123', quantity: 1 }],
179
+ customer: { email: 'customer@example.com' }
180
+ })
99
181
  })
182
+
183
+ const { checkoutUrl } = await response.json()
184
+ window.location.href = checkoutUrl
100
185
  ```
101
186
 
102
- #### Direct SDK Usage
187
+ #### Option C: Direct SDK Usage
103
188
 
104
189
  ```ts
105
- import { createTorqueCheckout } from 'torque-checkout'
190
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
106
191
 
107
- const torque = createTorqueCheckout({
108
- businessId: process.env.TORQUE_BUSINESS_ID!,
109
- apiKey: process.env.TORQUE_API_KEY!
110
- })
192
+ const torque = createTorqueCheckoutFromEnv()
111
193
 
112
- // Generate checkout URL
113
194
  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
- }
195
+ items: [{ productId: 'prod_123', quantity: 1 }],
196
+ customer: { email: 'customer@example.com' }
123
197
  })
124
198
 
125
- // Redirect customer
126
199
  window.location.href = checkoutUrl
127
200
  ```
128
201
 
129
- ## 📚 Documentation
202
+ ## Documentation
130
203
 
131
204
  ### Core SDK
132
205
 
133
- #### `TorqueCheckout` Class
134
-
135
- The main SDK class for generating checkout URLs and managing orders.
206
+ #### Initialization
136
207
 
137
208
  ```ts
138
- import { createTorqueCheckout } from 'torque-checkout'
209
+ import { createTorqueCheckout, createTorqueCheckoutFromEnv } from 'torque-checkout'
139
210
 
211
+ // Method 1: From environment variables (recommended)
212
+ const torque = createTorqueCheckoutFromEnv()
213
+
214
+ // Method 2: Explicit configuration
140
215
  const torque = createTorqueCheckout({
141
- businessId: 'your_business_id',
142
- apiKey: 'your_api_key',
216
+ businessId: process.env.TORQUE_BUSINESS_ID!,
217
+ apiKey: process.env.TORQUE_API_KEY!,
143
218
  baseUrl: 'https://app.torque.fi', // Optional
144
219
  timeout: 30000 // Optional, default: 30000ms
145
220
  })
@@ -152,16 +227,23 @@ const torque = createTorqueCheckout({
152
227
  Generate a checkout URL for a multi-product cart.
153
228
 
154
229
  ```ts
155
- const url = await torque.generateCartCheckoutUrl({
230
+ const checkoutUrl = await torque.generateCartCheckoutUrl({
156
231
  items: [
157
- { productId: 'prod_123', quantity: 2, price: 29.99 },
158
- { productId: 'prod_456', quantity: 1, price: 49.99 }
232
+ {
233
+ productId: 'prod_123',
234
+ quantity: 2,
235
+ price: 29.99, // Optional: override product price
236
+ variant: 'large', // Optional: product variant
237
+ metadata: { customField: 'value' } // Optional: custom data
238
+ },
239
+ { productId: 'prod_456', quantity: 1 }
159
240
  ],
160
241
  customer: {
161
- email: 'customer@example.com',
162
- firstName: 'John',
163
- lastName: 'Doe',
164
- shippingAddress: {
242
+ email: 'customer@example.com', // Required
243
+ firstName: 'John', // Optional
244
+ lastName: 'Doe', // Optional
245
+ phone: '+1234567890', // Optional
246
+ shippingAddress: { // Optional: pre-fill shipping
165
247
  street: '123 Main St',
166
248
  city: 'New York',
167
249
  state: 'NY',
@@ -170,66 +252,75 @@ const url = await torque.generateCartCheckoutUrl({
170
252
  }
171
253
  },
172
254
  options: {
173
- metadata: { orderId: 'order_123', source: 'website' },
174
- expiresIn: 24 * 60 * 60 * 1000, // 24 hours
175
- redirectUrl: 'https://yoursite.com/thank-you'
255
+ metadata: { orderId: 'order_123', source: 'website' }, // Optional
256
+ expiresIn: 24 * 60 * 60 * 1000, // Optional: 24 hours
257
+ redirectUrl: 'https://yoursite.com/thank-you' // Optional
176
258
  }
177
259
  })
178
260
  ```
179
261
 
180
- ##### `generateProductCheckoutUrl(productId, quantity, customer?, options?): Promise<string>`
262
+ ##### `generateProductCheckoutUrl(productId, quantity?, customer?, options?): Promise<string>`
181
263
 
182
- Generate a checkout URL for a single product.
264
+ Quick method for single product checkout.
183
265
 
184
266
  ```ts
185
- const url = await torque.generateProductCheckoutUrl(
186
- 'prod_123',
187
- 2,
188
- { email: 'customer@example.com' }
267
+ const checkoutUrl = await torque.generateProductCheckoutUrl(
268
+ 'prod_123', // Product ID
269
+ 2, // Quantity (default: 1)
270
+ { email: 'customer@example.com' }, // Customer data (optional)
271
+ { redirectUrl: 'https://yoursite.com/success' } // Options (optional)
189
272
  )
190
273
  ```
191
274
 
192
275
  ##### `generateSubscriptionCheckoutUrl(productId, paymentPlanId, customer?, options?): Promise<string>`
193
276
 
194
- Generate a checkout URL for a subscription product.
277
+ Generate checkout for subscription products.
195
278
 
196
279
  ```ts
197
- const url = await torque.generateSubscriptionCheckoutUrl(
198
- 'prod_subscription_123',
199
- 'plan_monthly_123',
200
- { email: 'customer@example.com' }
280
+ const checkoutUrl = await torque.generateSubscriptionCheckoutUrl(
281
+ 'prod_subscription_123', // Subscription product ID
282
+ 'plan_monthly_123', // Payment plan ID
283
+ { email: 'customer@example.com' },
284
+ { redirectUrl: 'https://yoursite.com/subscription-success' }
201
285
  )
202
286
  ```
203
287
 
204
288
  ##### `validateCart(cart: CartData): Promise<CartValidation>`
205
289
 
206
- Validate cart data before generating checkout.
290
+ Validate cart before checkout to catch errors early.
207
291
 
208
292
  ```ts
209
- const validation = await torque.validateCart(cart)
293
+ const validation = await torque.validateCart({
294
+ items: [{ productId: 'prod_123', quantity: 2 }]
295
+ })
210
296
 
211
297
  if (validation.valid) {
212
- console.log('Cart is valid, estimated total:', validation.estimatedTotal)
298
+ console.log('Cart is valid')
299
+ console.log('Estimated total:', validation.estimatedTotal)
213
300
  } else {
214
301
  console.error('Validation errors:', validation.errors)
302
+ console.warn('Warnings:', validation.warnings)
215
303
  }
216
304
  ```
217
305
 
218
306
  ##### `getOrderStatus(orderId: string): Promise<OrderStatus>`
219
307
 
220
- Get the current status of an order.
308
+ Check order status after checkout.
221
309
 
222
310
  ```ts
223
311
  const order = await torque.getOrderStatus('order_123')
224
- console.log('Order status:', order.status)
312
+
313
+ console.log('Status:', order.status) // e.g., 'completed', 'pending', 'failed'
225
314
  console.log('Total:', order.totals.total)
315
+ console.log('Payment status:', order.paymentStatus)
316
+ console.log('Items:', order.items)
226
317
  ```
227
318
 
228
319
  ### React Hooks
229
320
 
230
321
  #### `useTorqueCheckout(options?)`
231
322
 
232
- React hook for client-side checkout generation.
323
+ React hook for client-side checkout with built-in state management.
233
324
 
234
325
  ```tsx
235
326
  import { useTorqueCheckout } from 'torque-checkout/react'
@@ -245,23 +336,47 @@ function CheckoutButton() {
245
336
  error,
246
337
  checkoutUrl
247
338
  } = useTorqueCheckout({
248
- config: {
249
- businessId: 'your_business_id',
250
- apiKey: 'your_api_key'
339
+ // Optional: explicit config (or use env vars)
340
+ // config: {
341
+ // businessId: 'your_business_id',
342
+ // apiKey: 'your_api_key'
343
+ // },
344
+ autoRedirect: true, // Auto-redirect to checkout URL
345
+ onSuccess: (url) => {
346
+ console.log('Checkout URL generated:', url)
347
+ // Optional: track analytics
251
348
  },
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)
349
+ onError: (error) => {
350
+ console.error('Checkout error:', error)
351
+ // Optional: show error toast
352
+ }
256
353
  })
257
354
 
258
- // Use the methods...
355
+ const handleCheckout = async () => {
356
+ const url = await generateProductCheckout('prod_123', 1, {
357
+ email: 'customer@example.com'
358
+ })
359
+ // If autoRedirect is false, you can handle the URL manually
360
+ if (url && !autoRedirect) {
361
+ window.location.href = url
362
+ }
363
+ }
364
+
365
+ return (
366
+ <div>
367
+ <button onClick={handleCheckout} disabled={isLoading}>
368
+ {isLoading ? 'Processing...' : 'Checkout'}
369
+ </button>
370
+ {error && <p className="error">{error.message}</p>}
371
+ {checkoutUrl && <p>Checkout URL: {checkoutUrl}</p>}
372
+ </div>
373
+ )
259
374
  }
260
375
  ```
261
376
 
262
377
  #### `useCart()`
263
378
 
264
- React hook for managing shopping cart state.
379
+ Shopping cart state management hook.
265
380
 
266
381
  ```tsx
267
382
  import { useCart } from 'torque-checkout/react'
@@ -279,16 +394,26 @@ function ShoppingCart() {
279
394
 
280
395
  return (
281
396
  <div>
282
- <h2>Cart ({getItemCount()} items)</h2>
283
- <p>Total: ${getTotal().toFixed(2)}</p>
397
+ <h2>Shopping Cart ({getItemCount()} items)</h2>
398
+
284
399
  {items.map(item => (
285
- <div key={`${item.productId}-${item.variant}`}>
286
- {item.productId} x {item.quantity}
400
+ <div key={`${item.productId}-${item.variant || ''}`}>
401
+ <span>Product {item.productId}</span>
402
+ <span>Quantity: {item.quantity}</span>
403
+ <button onClick={() => updateQuantity(item.productId, item.quantity + 1, item.variant)}>
404
+ +
405
+ </button>
406
+ <button onClick={() => updateQuantity(item.productId, item.quantity - 1, item.variant)}>
407
+ -
408
+ </button>
287
409
  <button onClick={() => removeItem(item.productId, item.variant)}>
288
410
  Remove
289
411
  </button>
290
412
  </div>
291
413
  ))}
414
+
415
+ <p>Total: ${getTotal().toFixed(2)}</p>
416
+ <button onClick={clearCart}>Clear Cart</button>
292
417
  </div>
293
418
  )
294
419
  }
@@ -296,65 +421,80 @@ function ShoppingCart() {
296
421
 
297
422
  ### Next.js Server Utilities
298
423
 
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
424
  #### `handleCheckoutRequest(options?)`
315
425
 
316
- Next.js API route handler for checkout generation.
426
+ Complete API route handler with error handling.
317
427
 
318
428
  ```ts
319
429
  // app/api/checkout/route.ts
320
430
  import { handleCheckoutRequest } from 'torque-checkout/nextjs'
321
431
 
322
432
  export const POST = handleCheckoutRequest({
323
- onSuccess: async (url, cart) => {
324
- // Log or process checkout
325
- console.log('Checkout generated:', url)
433
+ onSuccess: async (checkoutUrl, cart) => {
434
+ // Optional: Log, track analytics, send notifications
435
+ console.log('Checkout generated:', checkoutUrl)
436
+ await logCheckoutEvent(cart)
326
437
  },
327
438
  onError: async (error, cart) => {
328
- // Handle error
439
+ // Optional: Error logging, notifications
329
440
  console.error('Checkout error:', error)
441
+ await logError(error, cart)
330
442
  }
331
443
  })
332
444
  ```
333
445
 
334
446
  #### `handleWebhook(options?)`
335
447
 
336
- Next.js API route handler for webhook events.
448
+ Webhook handler for order events.
337
449
 
338
450
  ```ts
339
451
  // app/api/webhooks/torque/route.ts
340
452
  import { handleWebhook } from 'torque-checkout/nextjs'
341
453
 
342
454
  export const POST = handleWebhook({
343
- secret: process.env.TORQUE_WEBHOOK_SECRET,
455
+ secret: process.env.TORQUE_WEBHOOK_SECRET, // Optional: verify signatures
344
456
  onOrderCompleted: async (event) => {
345
457
  // Fulfill order
346
458
  await fulfillOrder(event.orderId!)
459
+ await sendConfirmationEmail(event.data.customerEmail)
347
460
  },
348
461
  onOrderFailed: async (event) => {
349
- // Handle failed order
462
+ // Handle failed payment
350
463
  await notifyCustomer(event.orderId!)
464
+ },
465
+ onSubscriptionCreated: async (event) => {
466
+ // Activate subscription
467
+ await activateSubscription(event.subscriptionId!)
351
468
  }
352
469
  })
353
470
  ```
354
471
 
355
- ## 🎯 Complete Examples
472
+ #### `generateCheckoutUrl(cart, config?)`
473
+
474
+ Server-side checkout URL generation.
356
475
 
357
- ### Next.js App Router - Full E-Commerce Integration
476
+ ```ts
477
+ // app/api/checkout/route.ts
478
+ import { generateCheckoutUrl } from 'torque-checkout/nextjs'
479
+
480
+ export async function POST(request: Request) {
481
+ const cart = await request.json()
482
+
483
+ try {
484
+ const checkoutUrl = await generateCheckoutUrl(cart)
485
+ return Response.json({ checkoutUrl })
486
+ } catch (error: any) {
487
+ return Response.json(
488
+ { error: error.message, code: error.code },
489
+ { status: error.statusCode || 500 }
490
+ )
491
+ }
492
+ }
493
+ ```
494
+
495
+ ## Complete Examples
496
+
497
+ ### E-Commerce Product Page
358
498
 
359
499
  ```tsx
360
500
  // app/products/[id]/page.tsx
@@ -364,26 +504,26 @@ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
364
504
  import { useState } from 'react'
365
505
 
366
506
  export default function ProductPage({ params }: { params: { id: string } }) {
367
- const { addItem, items, getItemCount } = useCart()
507
+ const cart = useCart()
368
508
  const { generateProductCheckout, isLoading } = useTorqueCheckout({
369
509
  autoRedirect: true
370
510
  })
371
511
  const [quantity, setQuantity] = useState(1)
372
512
 
373
513
  const handleAddToCart = () => {
374
- addItem({
514
+ cart.addItem({
375
515
  productId: params.id,
376
516
  quantity,
377
- price: 29.99 // Your product price
517
+ price: 29.99
378
518
  })
379
519
  }
380
520
 
381
- const handleCheckout = async () => {
521
+ const handleBuyNow = async () => {
382
522
  await generateProductCheckout(
383
523
  params.id,
384
524
  quantity,
385
525
  {
386
- email: 'customer@example.com' // Get from user session
526
+ email: 'customer@example.com' // Get from auth/session
387
527
  }
388
528
  )
389
529
  }
@@ -391,6 +531,8 @@ export default function ProductPage({ params }: { params: { id: string } }) {
391
531
  return (
392
532
  <div>
393
533
  <h1>Product {params.id}</h1>
534
+ <p>$29.99</p>
535
+
394
536
  <div>
395
537
  <label>Quantity:</label>
396
538
  <input
@@ -400,51 +542,82 @@ export default function ProductPage({ params }: { params: { id: string } }) {
400
542
  min="1"
401
543
  />
402
544
  </div>
403
- <button onClick={handleAddToCart}>Add to Cart</button>
404
- <button onClick={handleCheckout} disabled={isLoading}>
405
- {isLoading ? 'Processing...' : 'Checkout'}
545
+
546
+ <button onClick={handleAddToCart}>
547
+ Add to Cart ({cart.getItemCount()} items)
548
+ </button>
549
+
550
+ <button onClick={handleBuyNow} disabled={isLoading}>
551
+ {isLoading ? 'Processing...' : 'Buy Now'}
406
552
  </button>
407
- <p>Cart: {getItemCount()} items</p>
408
553
  </div>
409
554
  )
410
555
  }
411
556
  ```
412
557
 
413
- ### Next.js Pages Router - API Route
558
+ ### Shopping Cart with Checkout
414
559
 
415
- ```ts
416
- // pages/api/checkout.ts
417
- import type { NextApiRequest, NextApiResponse } from 'next'
418
- import { createTorqueCheckoutFromEnv } from 'torque-checkout'
560
+ ```tsx
561
+ // app/cart/page.tsx
562
+ 'use client'
419
563
 
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
- }
564
+ import { useTorqueCheckout, useCart } from 'torque-checkout/react'
427
565
 
428
- try {
429
- const torque = createTorqueCheckoutFromEnv()
430
- const checkoutUrl = await torque.generateCartCheckoutUrl(req.body)
566
+ export default function CartPage() {
567
+ const cart = useCart()
568
+ const { generateCheckout, isLoading } = useTorqueCheckout()
431
569
 
432
- res.status(200).json({ checkoutUrl })
433
- } catch (error: any) {
434
- res.status(500).json({
435
- error: error.message,
436
- code: error.code
570
+ const handleCheckout = async () => {
571
+ const url = await generateCheckout({
572
+ items: cart.items,
573
+ customer: {
574
+ email: 'customer@example.com' // Get from auth
575
+ },
576
+ options: {
577
+ redirectUrl: window.location.origin + '/thank-you'
578
+ }
437
579
  })
580
+
581
+ if (url) {
582
+ window.location.href = url
583
+ }
584
+ }
585
+
586
+ if (cart.items.length === 0) {
587
+ return <p>Your cart is empty</p>
438
588
  }
589
+
590
+ return (
591
+ <div>
592
+ <h1>Shopping Cart</h1>
593
+ {cart.items.map(item => (
594
+ <div key={`${item.productId}-${item.variant || ''}`}>
595
+ <span>Product {item.productId}</span>
596
+ <span>Qty: {item.quantity}</span>
597
+ <span>${((item.price || 0) * item.quantity).toFixed(2)}</span>
598
+ <button onClick={() => cart.removeItem(item.productId, item.variant)}>
599
+ Remove
600
+ </button>
601
+ </div>
602
+ ))}
603
+ <div>
604
+ <strong>Total: ${cart.getTotal().toFixed(2)}</strong>
605
+ </div>
606
+ <button onClick={handleCheckout} disabled={isLoading}>
607
+ {isLoading ? 'Processing...' : 'Proceed to Checkout'}
608
+ </button>
609
+ </div>
610
+ )
439
611
  }
440
612
  ```
441
613
 
442
- ### Server Component (Next.js 13+)
614
+ ### Server Component Checkout (Next.js 13+)
443
615
 
444
616
  ```tsx
445
617
  // app/checkout/page.tsx
446
618
  import { createTorqueCheckoutFromEnv } from 'torque-checkout'
447
619
  import { redirect } from 'next/navigation'
620
+ import { cookies } from 'next/headers'
448
621
 
449
622
  export default async function CheckoutPage({
450
623
  searchParams
@@ -453,40 +626,104 @@ export default async function CheckoutPage({
453
626
  }) {
454
627
  const torque = createTorqueCheckoutFromEnv()
455
628
 
629
+ // Get customer email from session/cookies
630
+ const customerEmail = cookies().get('user_email')?.value || 'guest@example.com'
631
+
456
632
  const checkoutUrl = await torque.generateProductCheckoutUrl(
457
633
  searchParams.productId,
458
634
  Number(searchParams.quantity) || 1,
459
- {
460
- email: 'customer@example.com' // Get from session
461
- }
635
+ { email: customerEmail }
462
636
  )
463
637
 
464
638
  redirect(checkoutUrl)
465
639
  }
466
640
  ```
467
641
 
468
- ## 🔧 TypeScript Types
642
+ ### Pages Router API Route
643
+
644
+ ```ts
645
+ // pages/api/checkout.ts
646
+ import type { NextApiRequest, NextApiResponse } from 'next'
647
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
469
648
 
470
- The SDK provides comprehensive TypeScript types:
649
+ export default async function handler(
650
+ req: NextApiRequest,
651
+ res: NextApiResponse
652
+ ) {
653
+ if (req.method !== 'POST') {
654
+ return res.status(405).json({ error: 'Method not allowed' })
655
+ }
656
+
657
+ try {
658
+ const torque = createTorqueCheckoutFromEnv()
659
+ const checkoutUrl = await torque.generateCartCheckoutUrl(req.body)
660
+
661
+ res.status(200).json({ checkoutUrl })
662
+ } catch (error: any) {
663
+ res.status(error.statusCode || 500).json({
664
+ error: error.message,
665
+ code: error.code
666
+ })
667
+ }
668
+ }
669
+ ```
670
+
671
+ ## TypeScript
672
+
673
+ Full TypeScript support with comprehensive types:
471
674
 
472
675
  ```ts
473
676
  import type {
677
+ // Cart types
474
678
  CartItem,
475
679
  CartData,
476
- CustomerData,
477
680
  CartOptions,
478
- CheckoutResponse,
479
- OrderStatus,
480
681
  CartValidation,
682
+
683
+ // Customer types
684
+ CustomerData,
685
+
686
+ // Order types
687
+ OrderStatus,
688
+ CheckoutResponse,
689
+
690
+ // Subscription types
481
691
  Subscription,
482
692
  SubscriptionProduct,
483
693
  PaymentPlan,
694
+ CreateSubscriptionData,
695
+ UpdateSubscriptionData,
696
+
697
+ // Config types
484
698
  TorqueConfig,
485
699
  TorqueCheckoutError
486
700
  } from 'torque-checkout'
487
701
  ```
488
702
 
489
- ## 🛡️ Error Handling
703
+ ### Type-Safe Usage
704
+
705
+ ```ts
706
+ import { createTorqueCheckout } from 'torque-checkout'
707
+ import type { CartData, CustomerData } from 'torque-checkout'
708
+
709
+ const torque = createTorqueCheckout({
710
+ businessId: process.env.TORQUE_BUSINESS_ID!,
711
+ apiKey: process.env.TORQUE_API_KEY!
712
+ })
713
+
714
+ const cart: CartData = {
715
+ items: [
716
+ { productId: 'prod_123', quantity: 1 }
717
+ ],
718
+ customer: {
719
+ email: 'customer@example.com'
720
+ } as CustomerData
721
+ }
722
+
723
+ const url: string = await torque.generateCartCheckoutUrl(cart)
724
+ ```
725
+
726
+ ## Error Handling
490
727
 
491
728
  The SDK uses custom error classes for better error handling:
492
729
 
@@ -504,54 +741,200 @@ try {
504
741
  // Handle specific error codes
505
742
  switch (error.code) {
506
743
  case 'VALIDATION_ERROR':
507
- // Handle validation errors
744
+ // Show validation errors to user
745
+ showErrors(error.details?.errors)
508
746
  break
509
747
  case 'BUSINESS_NOT_FOUND':
510
- // Handle business not found
748
+ // Check business ID configuration
749
+ console.error('Invalid business ID')
511
750
  break
512
751
  case 'TIMEOUT':
513
- // Handle timeout
752
+ // Retry or show timeout message
753
+ retryCheckout()
754
+ break
755
+ case 'NETWORK_ERROR':
756
+ // Check internet connection
757
+ showNetworkError()
514
758
  break
515
759
  default:
516
- // Handle other errors
760
+ // Generic error handling
761
+ showGenericError(error.message)
517
762
  }
763
+ } else {
764
+ // Unknown error
765
+ console.error('Unexpected error:', error)
766
+ }
767
+ }
768
+ ```
769
+
770
+ ### Common Error Codes
771
+
772
+ | Code | Description | Solution |
773
+ |------|-------------|----------|
774
+ | `VALIDATION_ERROR` | Cart validation failed | Check cart items and customer data |
775
+ | `BUSINESS_NOT_FOUND` | Invalid business ID | Verify `TORQUE_BUSINESS_ID` |
776
+ | `INVALID_API_KEY` | Invalid API key | Verify `TORQUE_API_KEY` |
777
+ | `TIMEOUT` | Request timed out | Check network, increase timeout |
778
+ | `NETWORK_ERROR` | Network request failed | Check internet connection |
779
+ | `ORDER_NOT_FOUND` | Order doesn't exist | Verify order ID |
780
+
781
+ ## Security Best Practices
782
+
783
+ 1. **Never expose API keys in client-side code**
784
+ ```ts
785
+ // BAD - Don't do this
786
+ const torque = createTorqueCheckout({
787
+ businessId: 'business_123',
788
+ apiKey: 'sk_live_...' // Exposed in browser!
789
+ })
790
+
791
+ // GOOD - Use server-side API routes
792
+ // app/api/checkout/route.ts
793
+ export const POST = handleCheckoutRequest()
794
+ ```
795
+
796
+ 2. **Use environment variables**
797
+ ```env
798
+ # .env.local (never commit)
799
+ TORQUE_BUSINESS_ID=your_business_id
800
+ TORQUE_API_KEY=your_api_key
801
+ ```
802
+
803
+ 3. **Validate user input**
804
+ ```ts
805
+ // Always validate before sending to API
806
+ if (!cart.items || cart.items.length === 0) {
807
+ throw new Error('Cart is empty')
808
+ }
809
+ ```
810
+
811
+ 4. **Use HTTPS in production**
812
+ - Always use HTTPS for checkout URLs
813
+ - Never send sensitive data over HTTP
814
+
815
+ 5. **Verify webhook signatures**
816
+ ```ts
817
+ export const POST = handleWebhook({
818
+ secret: process.env.TORQUE_WEBHOOK_SECRET
819
+ })
820
+ ```
821
+
822
+ 6. **Set payment wallet in business settings**
823
+ - Payments automatically go to your configured wallet
824
+ - No need to handle wallet addresses in code
825
+
826
+ ## Troubleshooting
827
+
828
+ ### Issue: "TorqueCheckout not initialized"
829
+
830
+ **Solution**: Make sure environment variables are set:
831
+ ```bash
832
+ # Check if variables are loaded
833
+ echo $TORQUE_BUSINESS_ID
834
+ echo $TORQUE_API_KEY
835
+ ```
836
+
837
+ Or provide explicit config:
838
+ ```ts
839
+ const torque = createTorqueCheckout({
840
+ businessId: 'your_business_id',
841
+ apiKey: 'your_api_key'
842
+ })
843
+ ```
844
+
845
+ ### Issue: "Request timeout"
846
+
847
+ **Solution**: Increase timeout or check network:
848
+ ```ts
849
+ const torque = createTorqueCheckout({
850
+ businessId: process.env.TORQUE_BUSINESS_ID!,
851
+ apiKey: process.env.TORQUE_API_KEY!,
852
+ timeout: 60000 // 60 seconds
853
+ })
854
+ ```
855
+
856
+ ### Issue: "Business not found"
857
+
858
+ **Solution**:
859
+ 1. Verify business ID in [Business Settings](https://app.torque.fi/business/settings)
860
+ 2. Check environment variable is correct
861
+ 3. Ensure business profile is saved and active
862
+
863
+ ### Issue: React hooks not working
864
+
865
+ **Solution**: Make sure you're using the React entry point:
866
+ ```tsx
867
+ // Correct
868
+ import { useTorqueCheckout } from 'torque-checkout/react'
869
+
870
+ // Wrong
871
+ import { useTorqueCheckout } from 'torque-checkout'
872
+ ```
873
+
874
+ ### Issue: TypeScript errors
875
+
876
+ **Solution**: Make sure TypeScript can find the types:
877
+ ```json
878
+ // tsconfig.json
879
+ {
880
+ "compilerOptions": {
881
+ "moduleResolution": "node",
882
+ "esModuleInterop": true
518
883
  }
519
884
  }
520
885
  ```
521
886
 
522
- ## 🔐 Security Best Practices
887
+ ## Migration Guide
888
+
889
+ ### From v1.x to v2.0.0
523
890
 
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
891
+ v2.0.0 is **backward compatible** with v1.x. No code changes required!
892
+
893
+ **New features available (optional):**
894
+ - React hooks: `useTorqueCheckout()`, `useCart()`
895
+ - Next.js utilities: `handleCheckoutRequest()`, `handleWebhook()`
896
+ - Environment variable support: `createTorqueCheckoutFromEnv()`
897
+
898
+ **Migration example:**
899
+
900
+ ```ts
901
+ // v1.x (still works)
902
+ import { TorqueCheckout } from 'torque-checkout'
903
+ const torque = new TorqueCheckout({ businessId, apiKey })
904
+
905
+ // v2.0.0 (new, optional)
906
+ import { createTorqueCheckoutFromEnv } from 'torque-checkout'
907
+ const torque = createTorqueCheckoutFromEnv() // Uses env vars
908
+ ```
529
909
 
530
- ## 📖 API Reference
910
+ ## API Reference
531
911
 
532
- See the [full API documentation](https://docs.torque.fi/api) for complete reference.
912
+ For complete API documentation, visit:
913
+ - **Full API Docs**: [https://docs.torque.fi/api](https://docs.torque.fi/api)
914
+ - **Integration Guide**: [https://docs.torque.fi/integrations](https://docs.torque.fi/integrations)
533
915
 
534
- ## 🤝 Support
916
+ ## Support
535
917
 
536
918
  - **Documentation**: [https://docs.torque.fi](https://docs.torque.fi)
537
919
  - **Email**: hello@torque.fi
538
920
  - **GitHub Issues**: [https://github.com/torque-fi/torque-checkout/issues](https://github.com/torque-fi/torque-checkout/issues)
921
+ - **Business Dashboard**: [https://app.torque.fi/business/settings](https://app.torque.fi/business/settings)
539
922
 
540
- ## 📄 License
923
+ ## License
541
924
 
542
925
  MIT © [Torque](https://torque.fi)
543
926
 
544
- ## 🎉 Getting Started Checklist
927
+ ## Getting Started Checklist
545
928
 
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)
929
+ - [ ] Install: `npm install torque-checkout`
930
+ - [ ] Create business profile: [app.torque.fi/business/settings](https://app.torque.fi/business/settings)
548
931
  - [ ] Set payment wallet address in business settings
549
932
  - [ ] Copy Business ID and API Key
550
933
  - [ ] Add environment variables to `.env.local`
551
- - [ ] Import and use the SDK in your code
934
+ - [ ] Import and use the SDK
552
935
  - [ ] Test checkout flow
553
- - [ ] Deploy to production! 🚀
936
+ - [ ] Deploy to production!
554
937
 
555
938
  ---
556
939
 
557
- **Made with ❤️ by [Torque](https://torque.fi)**
940
+ **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.2",
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",