torque-checkout 1.1.9 → 2.0.0
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 +412 -267
- package/dist/index.d.ts +100 -6
- package/dist/index.esm.js +440 -108
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +444 -107
- package/dist/index.js.map +1 -1
- package/dist/nextjs.d.ts +111 -0
- package/dist/nextjs.esm.js +180 -0
- package/dist/nextjs.esm.js.map +1 -0
- package/dist/nextjs.js +185 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/react.d.ts +64 -0
- package/dist/react.esm.js +233 -0
- package/dist/react.esm.js.map +1 -0
- package/dist/react.js +236 -0
- package/dist/react.js.map +1 -0
- package/package.json +49 -7
package/README.md
CHANGED
|
@@ -1,42 +1,118 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Torque Checkout SDK
|
|
2
2
|
|
|
3
|
-
Official Torque checkout SDK for seamless eCommerce integrations
|
|
3
|
+
> **Official Torque checkout SDK for seamless eCommerce integrations with Next.js**
|
|
4
4
|
|
|
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://opensource.org/licenses/MIT)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## ✨ Features
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
## 📦 Installation
|
|
19
22
|
|
|
20
23
|
```bash
|
|
21
24
|
npm install torque-checkout
|
|
22
25
|
# or
|
|
23
26
|
yarn add torque-checkout
|
|
27
|
+
# or
|
|
28
|
+
pnpm add torque-checkout
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
## 🚀 Quick Start
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
import { TorqueCheckout } from 'torque-checkout'
|
|
33
|
+
### 1. Get Your API Credentials
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
Before using the SDK, you need to set up your Torque business profile:
|
|
36
|
+
|
|
37
|
+
1. **Go to [Torque Business Dashboard](https://app.torque.fi/business/settings)**
|
|
38
|
+
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
|
|
45
|
+
|
|
46
|
+
### 2. Configure Environment Variables
|
|
47
|
+
|
|
48
|
+
Create a `.env.local` file in your Next.js project:
|
|
49
|
+
|
|
50
|
+
```env
|
|
51
|
+
TORQUE_BUSINESS_ID=your_business_id_here
|
|
52
|
+
TORQUE_API_KEY=your_api_key_here
|
|
53
|
+
TORQUE_BASE_URL=https://app.torque.fi # Optional, defaults to production
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 3. Basic Usage
|
|
57
|
+
|
|
58
|
+
#### Client-Side (React Component)
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
'use client'
|
|
62
|
+
|
|
63
|
+
import { useTorqueCheckout } from 'torque-checkout/react'
|
|
64
|
+
|
|
65
|
+
export default function CheckoutButton() {
|
|
66
|
+
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)
|
|
70
|
+
})
|
|
71
|
+
|
|
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
|
+
return (
|
|
81
|
+
<button onClick={handleCheckout} disabled={isLoading}>
|
|
82
|
+
{isLoading ? 'Loading...' : 'Checkout'}
|
|
83
|
+
</button>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Server-Side (API Route)
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// app/api/checkout/route.ts
|
|
92
|
+
import { handleCheckoutRequest } from 'torque-checkout/nextjs'
|
|
93
|
+
|
|
94
|
+
export const POST = handleCheckoutRequest({
|
|
95
|
+
onSuccess: async (url, cart) => {
|
|
96
|
+
// Optional: Log checkout generation
|
|
97
|
+
console.log('Checkout generated:', url)
|
|
98
|
+
}
|
|
34
99
|
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### Direct SDK Usage
|
|
35
103
|
|
|
36
|
-
|
|
104
|
+
```ts
|
|
105
|
+
import { createTorqueCheckout } from 'torque-checkout'
|
|
106
|
+
|
|
107
|
+
const torque = createTorqueCheckout({
|
|
108
|
+
businessId: process.env.TORQUE_BUSINESS_ID!,
|
|
109
|
+
apiKey: process.env.TORQUE_API_KEY!
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Generate checkout URL
|
|
37
113
|
const checkoutUrl = await torque.generateCartCheckoutUrl({
|
|
38
114
|
items: [
|
|
39
|
-
{ productId: 'prod_123', quantity: 2
|
|
115
|
+
{ productId: 'prod_123', quantity: 2 },
|
|
40
116
|
{ productId: 'prod_456', quantity: 1 }
|
|
41
117
|
],
|
|
42
118
|
customer: {
|
|
@@ -46,48 +122,40 @@ const checkoutUrl = await torque.generateCartCheckoutUrl({
|
|
|
46
122
|
}
|
|
47
123
|
})
|
|
48
124
|
|
|
49
|
-
// Redirect customer
|
|
125
|
+
// Redirect customer
|
|
50
126
|
window.location.href = checkoutUrl
|
|
51
|
-
|
|
52
|
-
// The generated URL will look like:
|
|
53
|
-
// https://torque.fi/checkout/{businessId}/cart?items={encoded_cart_data}
|
|
54
127
|
```
|
|
55
128
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
The SDK generates checkout URLs in this format:
|
|
129
|
+
## 📚 Documentation
|
|
59
130
|
|
|
60
|
-
|
|
61
|
-
- **Multi-Product Cart**: `https://torque.fi/checkout/{businessId}/cart?items={encoded_cart_data}`
|
|
131
|
+
### Core SDK
|
|
62
132
|
|
|
63
|
-
|
|
133
|
+
#### `TorqueCheckout` Class
|
|
64
134
|
|
|
65
|
-
|
|
135
|
+
The main SDK class for generating checkout URLs and managing orders.
|
|
66
136
|
|
|
67
|
-
|
|
137
|
+
```ts
|
|
138
|
+
import { createTorqueCheckout } from 'torque-checkout'
|
|
68
139
|
|
|
69
|
-
|
|
70
|
-
|
|
140
|
+
const torque = createTorqueCheckout({
|
|
141
|
+
businessId: 'your_business_id',
|
|
142
|
+
apiKey: 'your_api_key',
|
|
143
|
+
baseUrl: 'https://app.torque.fi', // Optional
|
|
144
|
+
timeout: 30000 // Optional, default: 30000ms
|
|
145
|
+
})
|
|
71
146
|
```
|
|
72
147
|
|
|
73
|
-
|
|
74
|
-
- `businessId` (required): Your Torque business ID (get from business dashboard)
|
|
75
|
-
- `apiKey` (required): Your API authentication key (get from business dashboard)
|
|
76
|
-
- `timeout` (optional): Request timeout in milliseconds (defaults to 30000)
|
|
77
|
-
|
|
78
|
-
**Important:** Your `businessId` and `apiKey` are generated when you create your business profile in the Torque dashboard. Make sure you've set your payment wallet address in the business settings before using the SDK.
|
|
148
|
+
#### Methods
|
|
79
149
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#### `generateCartCheckoutUrl(cart: CartData): Promise<string>`
|
|
150
|
+
##### `generateCartCheckoutUrl(cart: CartData): Promise<string>`
|
|
83
151
|
|
|
84
152
|
Generate a checkout URL for a multi-product cart.
|
|
85
153
|
|
|
86
|
-
```
|
|
87
|
-
const
|
|
154
|
+
```ts
|
|
155
|
+
const url = await torque.generateCartCheckoutUrl({
|
|
88
156
|
items: [
|
|
89
|
-
{ productId: 'prod_123', quantity: 2,
|
|
90
|
-
{ productId: 'prod_456', quantity: 1 }
|
|
157
|
+
{ productId: 'prod_123', quantity: 2, price: 29.99 },
|
|
158
|
+
{ productId: 'prod_456', quantity: 1, price: 49.99 }
|
|
91
159
|
],
|
|
92
160
|
customer: {
|
|
93
161
|
email: 'customer@example.com',
|
|
@@ -102,311 +170,388 @@ const checkoutUrl = await torque.generateCartCheckoutUrl({
|
|
|
102
170
|
}
|
|
103
171
|
},
|
|
104
172
|
options: {
|
|
105
|
-
metadata: {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
},
|
|
109
|
-
expiresIn: 24 * 60 * 60 * 1000 // 24 hours
|
|
173
|
+
metadata: { orderId: 'order_123', source: 'website' },
|
|
174
|
+
expiresIn: 24 * 60 * 60 * 1000, // 24 hours
|
|
175
|
+
redirectUrl: 'https://yoursite.com/thank-you'
|
|
110
176
|
}
|
|
111
177
|
})
|
|
112
178
|
```
|
|
113
179
|
|
|
114
|
-
|
|
180
|
+
##### `generateProductCheckoutUrl(productId, quantity, customer?, options?): Promise<string>`
|
|
115
181
|
|
|
116
182
|
Generate a checkout URL for a single product.
|
|
117
183
|
|
|
118
|
-
```
|
|
119
|
-
const
|
|
184
|
+
```ts
|
|
185
|
+
const url = await torque.generateProductCheckoutUrl(
|
|
120
186
|
'prod_123',
|
|
121
187
|
2,
|
|
122
188
|
{ email: 'customer@example.com' }
|
|
123
189
|
)
|
|
124
190
|
```
|
|
125
191
|
|
|
126
|
-
|
|
192
|
+
##### `generateSubscriptionCheckoutUrl(productId, paymentPlanId, customer?, options?): Promise<string>`
|
|
127
193
|
|
|
128
|
-
|
|
194
|
+
Generate a checkout URL for a subscription product.
|
|
129
195
|
|
|
130
|
-
```
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
196
|
+
```ts
|
|
197
|
+
const url = await torque.generateSubscriptionCheckoutUrl(
|
|
198
|
+
'prod_subscription_123',
|
|
199
|
+
'plan_monthly_123',
|
|
200
|
+
{ email: 'customer@example.com' }
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
##### `validateCart(cart: CartData): Promise<CartValidation>`
|
|
205
|
+
|
|
206
|
+
Validate cart data before generating checkout.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const validation = await torque.validateCart(cart)
|
|
137
210
|
|
|
138
211
|
if (validation.valid) {
|
|
139
212
|
console.log('Cart is valid, estimated total:', validation.estimatedTotal)
|
|
140
213
|
} else {
|
|
141
|
-
console.
|
|
214
|
+
console.error('Validation errors:', validation.errors)
|
|
142
215
|
}
|
|
143
216
|
```
|
|
144
217
|
|
|
145
|
-
|
|
218
|
+
##### `getOrderStatus(orderId: string): Promise<OrderStatus>`
|
|
146
219
|
|
|
147
220
|
Get the current status of an order.
|
|
148
221
|
|
|
149
|
-
```
|
|
150
|
-
const
|
|
151
|
-
console.log('Order status:',
|
|
152
|
-
console.log('Total
|
|
222
|
+
```ts
|
|
223
|
+
const order = await torque.getOrderStatus('order_123')
|
|
224
|
+
console.log('Order status:', order.status)
|
|
225
|
+
console.log('Total:', order.totals.total)
|
|
153
226
|
```
|
|
154
227
|
|
|
155
|
-
|
|
228
|
+
### React Hooks
|
|
229
|
+
|
|
230
|
+
#### `useTorqueCheckout(options?)`
|
|
231
|
+
|
|
232
|
+
React hook for client-side checkout generation.
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import { useTorqueCheckout } from 'torque-checkout/react'
|
|
236
|
+
|
|
237
|
+
function CheckoutButton() {
|
|
238
|
+
const {
|
|
239
|
+
generateCheckout,
|
|
240
|
+
generateProductCheckout,
|
|
241
|
+
generateSubscriptionCheckout,
|
|
242
|
+
validateCart,
|
|
243
|
+
getOrderStatus,
|
|
244
|
+
isLoading,
|
|
245
|
+
error,
|
|
246
|
+
checkoutUrl
|
|
247
|
+
} = useTorqueCheckout({
|
|
248
|
+
config: {
|
|
249
|
+
businessId: 'your_business_id',
|
|
250
|
+
apiKey: 'your_api_key'
|
|
251
|
+
},
|
|
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)
|
|
256
|
+
})
|
|
156
257
|
|
|
157
|
-
|
|
258
|
+
// Use the methods...
|
|
259
|
+
}
|
|
260
|
+
```
|
|
158
261
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
262
|
+
#### `useCart()`
|
|
263
|
+
|
|
264
|
+
React hook for managing shopping cart state.
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { useCart } from 'torque-checkout/react'
|
|
268
|
+
|
|
269
|
+
function ShoppingCart() {
|
|
270
|
+
const {
|
|
271
|
+
items,
|
|
272
|
+
addItem,
|
|
273
|
+
removeItem,
|
|
274
|
+
updateQuantity,
|
|
275
|
+
clearCart,
|
|
276
|
+
getTotal,
|
|
277
|
+
getItemCount
|
|
278
|
+
} = useCart()
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div>
|
|
282
|
+
<h2>Cart ({getItemCount()} items)</h2>
|
|
283
|
+
<p>Total: ${getTotal().toFixed(2)}</p>
|
|
284
|
+
{items.map(item => (
|
|
285
|
+
<div key={`${item.productId}-${item.variant}`}>
|
|
286
|
+
{item.productId} x {item.quantity}
|
|
287
|
+
<button onClick={() => removeItem(item.productId, item.variant)}>
|
|
288
|
+
Remove
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
))}
|
|
292
|
+
</div>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
164
295
|
```
|
|
165
296
|
|
|
166
|
-
|
|
297
|
+
### Next.js Server Utilities
|
|
167
298
|
|
|
168
|
-
|
|
299
|
+
#### `generateCheckoutUrl(cart, config?)`
|
|
169
300
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
+
}
|
|
174
312
|
```
|
|
175
313
|
|
|
176
|
-
#### `
|
|
314
|
+
#### `handleCheckoutRequest(options?)`
|
|
315
|
+
|
|
316
|
+
Next.js API route handler for checkout generation.
|
|
177
317
|
|
|
178
|
-
|
|
318
|
+
```ts
|
|
319
|
+
// app/api/checkout/route.ts
|
|
320
|
+
import { handleCheckoutRequest } from 'torque-checkout/nextjs'
|
|
179
321
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
322
|
+
export const POST = handleCheckoutRequest({
|
|
323
|
+
onSuccess: async (url, cart) => {
|
|
324
|
+
// Log or process checkout
|
|
325
|
+
console.log('Checkout generated:', url)
|
|
326
|
+
},
|
|
327
|
+
onError: async (error, cart) => {
|
|
328
|
+
// Handle error
|
|
329
|
+
console.error('Checkout error:', error)
|
|
330
|
+
}
|
|
184
331
|
})
|
|
185
332
|
```
|
|
186
333
|
|
|
187
|
-
#### `
|
|
334
|
+
#### `handleWebhook(options?)`
|
|
335
|
+
|
|
336
|
+
Next.js API route handler for webhook events.
|
|
188
337
|
|
|
189
|
-
|
|
338
|
+
```ts
|
|
339
|
+
// app/api/webhooks/torque/route.ts
|
|
340
|
+
import { handleWebhook } from 'torque-checkout/nextjs'
|
|
190
341
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
342
|
+
export const POST = handleWebhook({
|
|
343
|
+
secret: process.env.TORQUE_WEBHOOK_SECRET,
|
|
344
|
+
onOrderCompleted: async (event) => {
|
|
345
|
+
// Fulfill order
|
|
346
|
+
await fulfillOrder(event.orderId!)
|
|
347
|
+
},
|
|
348
|
+
onOrderFailed: async (event) => {
|
|
349
|
+
// Handle failed order
|
|
350
|
+
await notifyCustomer(event.orderId!)
|
|
351
|
+
}
|
|
194
352
|
})
|
|
195
353
|
```
|
|
196
354
|
|
|
197
|
-
##
|
|
355
|
+
## 🎯 Complete Examples
|
|
198
356
|
|
|
199
|
-
###
|
|
357
|
+
### Next.js App Router - Full E-Commerce Integration
|
|
200
358
|
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
quantity: number // Required: Quantity to purchase
|
|
205
|
-
variant?: string // Optional: Product variant (size, color, etc.)
|
|
206
|
-
price?: number // Optional: Override product price
|
|
207
|
-
metadata?: Record<string, any> // Optional: Custom data
|
|
208
|
-
}
|
|
209
|
-
```
|
|
359
|
+
```tsx
|
|
360
|
+
// app/products/[id]/page.tsx
|
|
361
|
+
'use client'
|
|
210
362
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Same structure as shippingAddress
|
|
363
|
+
import { useTorqueCheckout, useCart } from 'torque-checkout/react'
|
|
364
|
+
import { useState } from 'react'
|
|
365
|
+
|
|
366
|
+
export default function ProductPage({ params }: { params: { id: string } }) {
|
|
367
|
+
const { addItem, items, getItemCount } = useCart()
|
|
368
|
+
const { generateProductCheckout, isLoading } = useTorqueCheckout({
|
|
369
|
+
autoRedirect: true
|
|
370
|
+
})
|
|
371
|
+
const [quantity, setQuantity] = useState(1)
|
|
372
|
+
|
|
373
|
+
const handleAddToCart = () => {
|
|
374
|
+
addItem({
|
|
375
|
+
productId: params.id,
|
|
376
|
+
quantity,
|
|
377
|
+
price: 29.99 // Your product price
|
|
378
|
+
})
|
|
228
379
|
}
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
380
|
|
|
232
|
-
|
|
381
|
+
const handleCheckout = async () => {
|
|
382
|
+
await generateProductCheckout(
|
|
383
|
+
params.id,
|
|
384
|
+
quantity,
|
|
385
|
+
{
|
|
386
|
+
email: 'customer@example.com' // Get from user session
|
|
387
|
+
}
|
|
388
|
+
)
|
|
389
|
+
}
|
|
233
390
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
391
|
+
return (
|
|
392
|
+
<div>
|
|
393
|
+
<h1>Product {params.id}</h1>
|
|
394
|
+
<div>
|
|
395
|
+
<label>Quantity:</label>
|
|
396
|
+
<input
|
|
397
|
+
type="number"
|
|
398
|
+
value={quantity}
|
|
399
|
+
onChange={(e) => setQuantity(Number(e.target.value))}
|
|
400
|
+
min="1"
|
|
401
|
+
/>
|
|
402
|
+
</div>
|
|
403
|
+
<button onClick={handleAddToCart}>Add to Cart</button>
|
|
404
|
+
<button onClick={handleCheckout} disabled={isLoading}>
|
|
405
|
+
{isLoading ? 'Processing...' : 'Checkout'}
|
|
406
|
+
</button>
|
|
407
|
+
<p>Cart: {getItemCount()} items</p>
|
|
408
|
+
</div>
|
|
409
|
+
)
|
|
240
410
|
}
|
|
241
411
|
```
|
|
242
412
|
|
|
243
|
-
|
|
413
|
+
### Next.js Pages Router - API Route
|
|
244
414
|
|
|
245
|
-
|
|
415
|
+
```ts
|
|
416
|
+
// pages/api/checkout.ts
|
|
417
|
+
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
418
|
+
import { createTorqueCheckoutFromEnv } from 'torque-checkout'
|
|
419
|
+
|
|
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
|
+
}
|
|
246
427
|
|
|
247
|
-
```typescript
|
|
248
|
-
// Shopify app integration
|
|
249
|
-
app.post('/checkout', async (req, res) => {
|
|
250
|
-
const { cart, customer } = req.body
|
|
251
|
-
|
|
252
428
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
email: customer.email,
|
|
262
|
-
firstName: customer.first_name,
|
|
263
|
-
lastName: customer.last_name,
|
|
264
|
-
shippingAddress: customer.shipping_address
|
|
265
|
-
},
|
|
266
|
-
options: {
|
|
267
|
-
metadata: {
|
|
268
|
-
orderId: cart.order_id,
|
|
269
|
-
source: 'shopify'
|
|
270
|
-
}
|
|
271
|
-
}
|
|
429
|
+
const torque = createTorqueCheckoutFromEnv()
|
|
430
|
+
const checkoutUrl = await torque.generateCartCheckoutUrl(req.body)
|
|
431
|
+
|
|
432
|
+
res.status(200).json({ checkoutUrl })
|
|
433
|
+
} catch (error: any) {
|
|
434
|
+
res.status(500).json({
|
|
435
|
+
error: error.message,
|
|
436
|
+
code: error.code
|
|
272
437
|
})
|
|
273
|
-
|
|
274
|
-
res.json({ checkoutUrl })
|
|
275
|
-
} catch (error) {
|
|
276
|
-
res.status(500).json({ error: error.message })
|
|
277
438
|
}
|
|
278
|
-
}
|
|
439
|
+
}
|
|
279
440
|
```
|
|
280
441
|
|
|
281
|
-
###
|
|
442
|
+
### Server Component (Next.js 13+)
|
|
282
443
|
|
|
283
|
-
```
|
|
284
|
-
//
|
|
285
|
-
|
|
444
|
+
```tsx
|
|
445
|
+
// app/checkout/page.tsx
|
|
446
|
+
import { createTorqueCheckoutFromEnv } from 'torque-checkout'
|
|
447
|
+
import { redirect } from 'next/navigation'
|
|
286
448
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
$checkout_data = [
|
|
300
|
-
'businessId' => get_option('torque_business_id'),
|
|
301
|
-
'cart' => ['items' => $cart_items],
|
|
302
|
-
'customerData' => [
|
|
303
|
-
'email' => $order->get_billing_email(),
|
|
304
|
-
'firstName' => $order->get_billing_first_name(),
|
|
305
|
-
'lastName' => $order->get_billing_last_name()
|
|
306
|
-
]
|
|
307
|
-
];
|
|
308
|
-
|
|
309
|
-
// Use JavaScript SDK or direct API call
|
|
310
|
-
$response = wp_remote_post('https://dashboard.torque.fi/api/checkout/generate-link', [
|
|
311
|
-
'body' => json_encode($checkout_data),
|
|
312
|
-
'headers' => ['Content-Type' => 'application/json'],
|
|
313
|
-
'timeout' => 30
|
|
314
|
-
]);
|
|
315
|
-
|
|
316
|
-
if (!is_wp_error($response)) {
|
|
317
|
-
$body = json_decode(wp_remote_retrieve_body($response), true);
|
|
318
|
-
if ($body['checkoutUrl']) {
|
|
319
|
-
wp_redirect($body['checkoutUrl']);
|
|
320
|
-
exit;
|
|
321
|
-
}
|
|
449
|
+
export default async function CheckoutPage({
|
|
450
|
+
searchParams
|
|
451
|
+
}: {
|
|
452
|
+
searchParams: { productId: string; quantity?: string }
|
|
453
|
+
}) {
|
|
454
|
+
const torque = createTorqueCheckoutFromEnv()
|
|
455
|
+
|
|
456
|
+
const checkoutUrl = await torque.generateProductCheckoutUrl(
|
|
457
|
+
searchParams.productId,
|
|
458
|
+
Number(searchParams.quantity) || 1,
|
|
459
|
+
{
|
|
460
|
+
email: 'customer@example.com' // Get from session
|
|
322
461
|
}
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
redirect(checkoutUrl)
|
|
323
465
|
}
|
|
324
466
|
```
|
|
325
467
|
|
|
326
|
-
##
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
468
|
+
## 🔧 TypeScript Types
|
|
469
|
+
|
|
470
|
+
The SDK provides comprehensive TypeScript types:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
import type {
|
|
474
|
+
CartItem,
|
|
475
|
+
CartData,
|
|
476
|
+
CustomerData,
|
|
477
|
+
CartOptions,
|
|
478
|
+
CheckoutResponse,
|
|
479
|
+
OrderStatus,
|
|
480
|
+
CartValidation,
|
|
481
|
+
Subscription,
|
|
482
|
+
SubscriptionProduct,
|
|
483
|
+
PaymentPlan,
|
|
484
|
+
TorqueConfig,
|
|
485
|
+
TorqueCheckoutError
|
|
486
|
+
} from 'torque-checkout'
|
|
337
487
|
```
|
|
338
488
|
|
|
339
|
-
|
|
489
|
+
## 🛡️ Error Handling
|
|
340
490
|
|
|
341
|
-
|
|
342
|
-
const testCart = {
|
|
343
|
-
items: [
|
|
344
|
-
{ productId: 'test_prod_1', quantity: 1, price: 10.00 }
|
|
345
|
-
],
|
|
346
|
-
customer: {
|
|
347
|
-
email: 'test@example.com'
|
|
348
|
-
}
|
|
349
|
-
}
|
|
491
|
+
The SDK uses custom error classes for better error handling:
|
|
350
492
|
|
|
351
|
-
|
|
352
|
-
|
|
493
|
+
```ts
|
|
494
|
+
import { TorqueCheckoutError } from 'torque-checkout'
|
|
353
495
|
|
|
354
|
-
## Error Handling
|
|
355
|
-
|
|
356
|
-
The SDK throws descriptive errors for various failure scenarios:
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
496
|
try {
|
|
360
|
-
const
|
|
497
|
+
const url = await torque.generateCartCheckoutUrl(cart)
|
|
361
498
|
} catch (error) {
|
|
362
|
-
if (error
|
|
363
|
-
console.error('
|
|
364
|
-
|
|
365
|
-
console.error('
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
499
|
+
if (error instanceof TorqueCheckoutError) {
|
|
500
|
+
console.error('Error code:', error.code)
|
|
501
|
+
console.error('Status code:', error.statusCode)
|
|
502
|
+
console.error('Details:', error.details)
|
|
503
|
+
|
|
504
|
+
// Handle specific error codes
|
|
505
|
+
switch (error.code) {
|
|
506
|
+
case 'VALIDATION_ERROR':
|
|
507
|
+
// Handle validation errors
|
|
508
|
+
break
|
|
509
|
+
case 'BUSINESS_NOT_FOUND':
|
|
510
|
+
// Handle business not found
|
|
511
|
+
break
|
|
512
|
+
case 'TIMEOUT':
|
|
513
|
+
// Handle timeout
|
|
514
|
+
break
|
|
515
|
+
default:
|
|
516
|
+
// Handle other errors
|
|
517
|
+
}
|
|
370
518
|
}
|
|
371
519
|
}
|
|
372
520
|
```
|
|
373
521
|
|
|
374
|
-
##
|
|
522
|
+
## 🔐 Security Best Practices
|
|
375
523
|
|
|
376
|
-
|
|
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
|
|
377
529
|
|
|
378
|
-
|
|
379
|
-
// Track cart abandonment
|
|
380
|
-
await torque.trackCartView('cart_123', cartData)
|
|
530
|
+
## 📖 API Reference
|
|
381
531
|
|
|
382
|
-
|
|
383
|
-
await torque.trackCheckoutComplete('order_123', {
|
|
384
|
-
paymentMethod: 'card',
|
|
385
|
-
total: 99.99,
|
|
386
|
-
items: 3
|
|
387
|
-
})
|
|
388
|
-
```
|
|
532
|
+
See the [full API documentation](https://docs.torque.fi/api) for complete reference.
|
|
389
533
|
|
|
390
|
-
##
|
|
534
|
+
## 🤝 Support
|
|
391
535
|
|
|
392
|
-
**
|
|
536
|
+
- **Documentation**: [https://docs.torque.fi](https://docs.torque.fi)
|
|
537
|
+
- **Email**: hello@torque.fi
|
|
538
|
+
- **GitHub Issues**: [https://github.com/torque-fi/torque-checkout/issues](https://github.com/torque-fi/torque-checkout/issues)
|
|
393
539
|
|
|
394
|
-
|
|
395
|
-
- **Automatic transfers**: All successful checkouts send funds to your wallet
|
|
396
|
-
- **Secure**: No need to handle payment addresses in your code
|
|
397
|
-
- **Update anytime**: Change your payment wallet in business settings
|
|
540
|
+
## 📄 License
|
|
398
541
|
|
|
399
|
-
|
|
542
|
+
MIT © [Torque](https://torque.fi)
|
|
400
543
|
|
|
401
|
-
|
|
402
|
-
- Configurable timeouts
|
|
403
|
-
- Efficient error handling
|
|
404
|
-
- Minimal bundle size
|
|
544
|
+
## 🎉 Getting Started Checklist
|
|
405
545
|
|
|
406
|
-
|
|
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)
|
|
548
|
+
- [ ] Set payment wallet address in business settings
|
|
549
|
+
- [ ] Copy Business ID and API Key
|
|
550
|
+
- [ ] Add environment variables to `.env.local`
|
|
551
|
+
- [ ] Import and use the SDK in your code
|
|
552
|
+
- [ ] Test checkout flow
|
|
553
|
+
- [ ] Deploy to production! 🚀
|
|
407
554
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
- **NPM Package**: [https://www.npmjs.com/package/torque-checkout](https://www.npmjs.com/package/torque-checkout)
|
|
412
|
-
- **Support**: hello@torque.fi
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
**Made with ❤️ by [Torque](https://torque.fi)**
|