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.
- package/README.md +575 -192
- 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
|
[](https://www.npmjs.com/package/torque-checkout)
|
|
8
|
+
[](https://www.npmjs.com/package/torque-checkout)
|
|
8
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](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
|
-
|
|
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
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
102
|
+
---
|
|
32
103
|
|
|
33
|
-
###
|
|
104
|
+
### Detailed Setup
|
|
34
105
|
|
|
35
|
-
|
|
106
|
+
### Step 1: Get Your API Credentials
|
|
36
107
|
|
|
37
|
-
1. **
|
|
108
|
+
1. **Visit [Torque Business Dashboard](https://app.torque.fi/business/settings)**
|
|
38
109
|
2. **Connect your wallet** (MetaMask, WalletConnect, etc.)
|
|
39
|
-
3. **
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
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
|
|
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
|
-
|
|
128
|
+
|
|
129
|
+
# Optional (defaults to production)
|
|
130
|
+
TORQUE_BASE_URL=https://app.torque.fi
|
|
54
131
|
```
|
|
55
132
|
|
|
56
|
-
|
|
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
|
|
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
|
|
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
|
|
82
|
-
{
|
|
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
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 {
|
|
190
|
+
import { createTorqueCheckoutFromEnv } from 'torque-checkout'
|
|
106
191
|
|
|
107
|
-
const torque =
|
|
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
|
-
|
|
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
|
-
##
|
|
202
|
+
## Documentation
|
|
130
203
|
|
|
131
204
|
### Core SDK
|
|
132
205
|
|
|
133
|
-
####
|
|
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:
|
|
142
|
-
apiKey:
|
|
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
|
|
230
|
+
const checkoutUrl = await torque.generateCartCheckoutUrl({
|
|
156
231
|
items: [
|
|
157
|
-
{
|
|
158
|
-
|
|
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
|
-
|
|
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
|
|
262
|
+
##### `generateProductCheckoutUrl(productId, quantity?, customer?, options?): Promise<string>`
|
|
181
263
|
|
|
182
|
-
|
|
264
|
+
Quick method for single product checkout.
|
|
183
265
|
|
|
184
266
|
```ts
|
|
185
|
-
const
|
|
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
|
|
277
|
+
Generate checkout for subscription products.
|
|
195
278
|
|
|
196
279
|
```ts
|
|
197
|
-
const
|
|
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
|
|
290
|
+
Validate cart before checkout to catch errors early.
|
|
207
291
|
|
|
208
292
|
```ts
|
|
209
|
-
const validation = await torque.validateCart(
|
|
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
|
|
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
|
-
|
|
308
|
+
Check order status after checkout.
|
|
221
309
|
|
|
222
310
|
```ts
|
|
223
311
|
const order = await torque.getOrderStatus('order_123')
|
|
224
|
-
|
|
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
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
349
|
+
onError: (error) => {
|
|
350
|
+
console.error('Checkout error:', error)
|
|
351
|
+
// Optional: show error toast
|
|
352
|
+
}
|
|
256
353
|
})
|
|
257
354
|
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
397
|
+
<h2>Shopping Cart ({getItemCount()} items)</h2>
|
|
398
|
+
|
|
284
399
|
{items.map(item => (
|
|
285
|
-
<div key={`${item.productId}-${item.variant}`}>
|
|
286
|
-
{item.productId}
|
|
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
|
-
|
|
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 (
|
|
324
|
-
// Log
|
|
325
|
-
console.log('Checkout generated:',
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
472
|
+
#### `generateCheckoutUrl(cart, config?)`
|
|
473
|
+
|
|
474
|
+
Server-side checkout URL generation.
|
|
356
475
|
|
|
357
|
-
|
|
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
|
|
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
|
|
517
|
+
price: 29.99
|
|
378
518
|
})
|
|
379
519
|
}
|
|
380
520
|
|
|
381
|
-
const
|
|
521
|
+
const handleBuyNow = async () => {
|
|
382
522
|
await generateProductCheckout(
|
|
383
523
|
params.id,
|
|
384
524
|
quantity,
|
|
385
525
|
{
|
|
386
|
-
email: 'customer@example.com' // Get from
|
|
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
|
-
|
|
404
|
-
<button onClick={
|
|
405
|
-
|
|
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
|
-
###
|
|
558
|
+
### Shopping Cart with Checkout
|
|
414
559
|
|
|
415
|
-
```
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
import { createTorqueCheckoutFromEnv } from 'torque-checkout'
|
|
560
|
+
```tsx
|
|
561
|
+
// app/cart/page.tsx
|
|
562
|
+
'use client'
|
|
419
563
|
|
|
420
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
566
|
+
export default function CartPage() {
|
|
567
|
+
const cart = useCart()
|
|
568
|
+
const { generateCheckout, isLoading } = useTorqueCheckout()
|
|
431
569
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
744
|
+
// Show validation errors to user
|
|
745
|
+
showErrors(error.details?.errors)
|
|
508
746
|
break
|
|
509
747
|
case 'BUSINESS_NOT_FOUND':
|
|
510
|
-
//
|
|
748
|
+
// Check business ID configuration
|
|
749
|
+
console.error('Invalid business ID')
|
|
511
750
|
break
|
|
512
751
|
case 'TIMEOUT':
|
|
513
|
-
//
|
|
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
|
-
//
|
|
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
|
-
##
|
|
887
|
+
## Migration Guide
|
|
888
|
+
|
|
889
|
+
### From v1.x to v2.0.0
|
|
523
890
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
##
|
|
910
|
+
## API Reference
|
|
531
911
|
|
|
532
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
923
|
+
## License
|
|
541
924
|
|
|
542
925
|
MIT © [Torque](https://torque.fi)
|
|
543
926
|
|
|
544
|
-
##
|
|
927
|
+
## Getting Started Checklist
|
|
545
928
|
|
|
546
|
-
- [ ] Install
|
|
547
|
-
- [ ] Create
|
|
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
|
|
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
|
|
940
|
+
**Made with love by [Torque](https://torque.fi)**
|