spaps-sdk 1.0.2 โ 1.1.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 +204 -605
- package/dist/index.d.mts +302 -0
- package/dist/index.d.ts +302 -0
- package/dist/index.js +379 -854
- package/dist/index.mjs +370 -840
- package/package.json +40 -34
- package/.env.example +0 -23
- package/admin-utils.ts +0 -243
package/README.md
CHANGED
|
@@ -1,700 +1,299 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @spaps/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://www.typescriptlang.org/)
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
> Sweet Potato Authentication & Payment Service SDK
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
Zero-config client for SPAPS authentication, payments, and permission checking. Works automatically with local development mode.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
## ๐ New in v1.1.0: Admin API Methods!
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
### Authentication
|
|
14
|
-
- ๐ **Multi-wallet Authentication**: Support for Solana, Ethereum, Bitcoin, and Base wallets
|
|
15
|
-
- ๐ **Traditional Authentication**: Email/password and magic link authentication
|
|
16
|
-
- ๐ **Token Management**: Automatic token refresh and secure storage utilities
|
|
17
|
-
- ๐งฐ **Wallet Utilities**: Address validation, chain detection
|
|
18
|
-
|
|
19
|
-
### Payments & Subscriptions
|
|
20
|
-
- ๐ณ **Stripe Checkout Sessions**: One-time payments and subscriptions
|
|
21
|
-
- ๐ฆ **Product Management**: List, create, update products and prices
|
|
22
|
-
- ๐ **Subscription Management**: Create, update, cancel subscriptions
|
|
23
|
-
- ๐ช **Customer Portal**: Self-service billing management
|
|
24
|
-
- ๐ฃ **Webhook Handling**: Secure event processing
|
|
25
|
-
|
|
26
|
-
### Developer Experience
|
|
27
|
-
- ๐ฏ **TypeScript First**: Full TypeScript support with comprehensive type definitions
|
|
28
|
-
- ๐ก๏ธ **Error Handling**: Robust error handling with custom error types
|
|
29
|
-
- ๐ **Retry Logic**: Built-in retry mechanism with exponential backoff
|
|
30
|
-
- โ๏ธ **React Integration**: Ready-to-use hooks and components
|
|
31
|
-
- ๐ **Extensive Examples**: Real-world usage patterns
|
|
32
|
-
- ๐งช **Well Tested**: Comprehensive test suite with high coverage
|
|
33
|
-
|
|
34
|
-
## Installation
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm install spaps-sdk
|
|
38
|
-
# or
|
|
39
|
-
yarn add spaps-sdk
|
|
40
|
-
# or
|
|
41
|
-
pnpm add spaps-sdk
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
> **๐ Important:** SPAPS runs on port **3456**. This is standardized across all tools.
|
|
45
|
-
|
|
46
|
-
### Requirements
|
|
47
|
-
|
|
48
|
-
- Node.js 14+ or modern browser
|
|
49
|
-
- TypeScript 4.5+ (optional, for TypeScript projects)
|
|
50
|
-
|
|
51
|
-
## Quick Start
|
|
52
|
-
|
|
53
|
-
### Basic Setup
|
|
9
|
+
Built-in admin API methods for Stripe product management:
|
|
54
10
|
|
|
55
11
|
```typescript
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
// Initialize the SDK
|
|
59
|
-
const sdk = new SweetPotatoSDK({
|
|
60
|
-
apiUrl: 'http://localhost:3456', // Always port 3456 for local dev
|
|
61
|
-
// apiUrl: 'https://api.sweetpotato.dev', // Production (not available yet)
|
|
62
|
-
apiKey: process.env.SPAPS_API_KEY || 'test_key_local_dev_only',
|
|
63
|
-
timeout: 30000, // Optional: 30 second timeout
|
|
64
|
-
retries: 3 // Optional: 3 retry attempts
|
|
65
|
-
});
|
|
12
|
+
import { SPAPSClient } from 'spaps-sdk';
|
|
66
13
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Authentication Examples
|
|
72
|
-
|
|
73
|
-
### Wallet Authentication (Complete Flow)
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
// Complete implementation with proper signature handling
|
|
77
|
-
const walletAddress = '0x742d35CC6354C7Cb24b5d2C2C7f9Ff5Ef8B4d5f6';
|
|
78
|
-
|
|
79
|
-
const authResponse = await sdk.auth.authenticateWallet(
|
|
80
|
-
walletAddress,
|
|
81
|
-
async (message) => {
|
|
82
|
-
// The SDK provides the message to sign
|
|
83
|
-
// You must return the signed message
|
|
84
|
-
|
|
85
|
-
// Browser (MetaMask):
|
|
86
|
-
if (typeof window !== 'undefined' && window.ethereum) {
|
|
87
|
-
return await window.ethereum.request({
|
|
88
|
-
method: 'personal_sign',
|
|
89
|
-
params: [message, walletAddress]
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Node.js (ethers.js):
|
|
94
|
-
// const { Wallet } = require('ethers');
|
|
95
|
-
// const wallet = new Wallet(privateKey);
|
|
96
|
-
// return await wallet.signMessage(message);
|
|
97
|
-
},
|
|
98
|
-
'ethereum', // Chain type: 'ethereum', 'solana', 'bitcoin', 'base'
|
|
99
|
-
'username123' // Optional: username for new users
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Store tokens for future use
|
|
103
|
-
TokenManager.storeTokens(authResponse);
|
|
104
|
-
console.log('User:', authResponse.user);
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Email/Password Authentication
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
const authResponse = await sdk.auth.signInWithPassword({
|
|
111
|
-
email: 'user@example.com',
|
|
112
|
-
password: 'securePassword123!'
|
|
14
|
+
const spaps = new SPAPSClient({
|
|
15
|
+
apiKey: 'your-api-key',
|
|
16
|
+
apiUrl: 'https://api.sweetpotato.dev'
|
|
113
17
|
});
|
|
114
18
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
### Magic Link Authentication
|
|
19
|
+
// Authenticate as admin
|
|
20
|
+
await spaps.signIn('admin@example.com', 'password');
|
|
119
21
|
|
|
120
|
-
|
|
121
|
-
await
|
|
122
|
-
|
|
123
|
-
|
|
22
|
+
// Admin API methods
|
|
23
|
+
await spaps.admin.createProduct({
|
|
24
|
+
name: 'Premium Plan',
|
|
25
|
+
category: 'subscription',
|
|
26
|
+
description: 'Advanced features'
|
|
124
27
|
});
|
|
125
|
-
// User will receive email with authentication link
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Payment Examples
|
|
129
28
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// Using existing price
|
|
134
|
-
const checkoutSession = await sdk.payments.createPaymentCheckout({
|
|
135
|
-
price_id: 'price_1234567890',
|
|
136
|
-
success_url: 'https://myapp.com/success',
|
|
137
|
-
cancel_url: 'https://myapp.com/cancel',
|
|
138
|
-
metadata: {
|
|
139
|
-
order_id: 'order_123',
|
|
140
|
-
user_type: 'premium'
|
|
141
|
-
}
|
|
29
|
+
await spaps.admin.updateProduct('prod_123', {
|
|
30
|
+
name: 'Premium Plan Pro'
|
|
142
31
|
});
|
|
143
32
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Dynamic Pricing
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// Create payment with custom price
|
|
152
|
-
const checkoutSession = await sdk.payments.createPaymentCheckout({
|
|
153
|
-
product_name: 'Premium Feature Access',
|
|
154
|
-
amount: 2999, // $29.99 in cents
|
|
33
|
+
await spaps.admin.createPrice({
|
|
34
|
+
product_id: 'prod_123',
|
|
35
|
+
unit_amount: 2999,
|
|
155
36
|
currency: 'usd',
|
|
156
|
-
|
|
157
|
-
success_url: 'https://myapp.com/success',
|
|
158
|
-
cancel_url: 'https://myapp.com/cancel'
|
|
37
|
+
interval: 'month'
|
|
159
38
|
});
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Subscription Creation
|
|
163
39
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
price_id: 'price_monthly_subscription',
|
|
167
|
-
success_url: 'https://myapp.com/subscription-success',
|
|
168
|
-
cancel_url: 'https://myapp.com/subscription-cancel',
|
|
169
|
-
trial_period_days: 14,
|
|
170
|
-
metadata: {
|
|
171
|
-
subscription_type: 'pro',
|
|
172
|
-
source: 'website'
|
|
173
|
-
}
|
|
174
|
-
});
|
|
40
|
+
// Super admin operations
|
|
41
|
+
await spaps.admin.syncProducts();
|
|
175
42
|
```
|
|
176
43
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// List all products
|
|
181
|
-
const products = await sdk.payments.listProducts({
|
|
182
|
-
category: 'subscription',
|
|
183
|
-
active: true,
|
|
184
|
-
limit: 20
|
|
185
|
-
});
|
|
44
|
+
## ๐ Permission Utilities
|
|
186
45
|
|
|
187
|
-
|
|
188
|
-
const product = await sdk.payments.getProduct('prod_123');
|
|
189
|
-
|
|
190
|
-
// Create new product (admin only)
|
|
191
|
-
const newProduct = await sdk.payments.createProduct({
|
|
192
|
-
name: 'New Premium Plan',
|
|
193
|
-
description: 'Enhanced features for power users',
|
|
194
|
-
category: 'subscription',
|
|
195
|
-
metadata: {
|
|
196
|
-
tier: 'premium',
|
|
197
|
-
features: 'advanced-analytics,priority-support'
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Subscription Management
|
|
46
|
+
Built-in permission checking and role-based access control:
|
|
203
47
|
|
|
204
48
|
```typescript
|
|
205
|
-
|
|
206
|
-
const subscriptions = await sdk.payments.listSubscriptions({
|
|
207
|
-
status: 'active'
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Update subscription
|
|
211
|
-
await sdk.payments.updateSubscription('sub_123', {
|
|
212
|
-
price_id: 'price_new_plan',
|
|
213
|
-
metadata: { upgrade_reason: 'user_requested' }
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Cancel subscription at period end
|
|
217
|
-
await sdk.payments.updateSubscription('sub_123', {
|
|
218
|
-
cancel_at_period_end: true
|
|
219
|
-
});
|
|
49
|
+
import { isAdminAccount, canAccessAdmin, getUserRole } from 'spaps-sdk';
|
|
220
50
|
|
|
221
|
-
//
|
|
222
|
-
const
|
|
223
|
-
return_url: 'https://myapp.com/account'
|
|
224
|
-
});
|
|
225
|
-
window.location.href = portalSession.url;
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Token Management
|
|
51
|
+
// Check admin status
|
|
52
|
+
const isAdmin = isAdminAccount('buildooor@gmail.com'); // true
|
|
229
53
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
async function setupAutoRefresh() {
|
|
235
|
-
const refreshSuccess = await TokenManager.autoRefreshToken(sdk);
|
|
236
|
-
|
|
237
|
-
if (refreshSuccess) {
|
|
238
|
-
console.log('Authentication restored from stored tokens');
|
|
239
|
-
} else {
|
|
240
|
-
console.log('No valid tokens found, user needs to authenticate');
|
|
241
|
-
}
|
|
54
|
+
// Check admin access with detailed result
|
|
55
|
+
const adminCheck = canAccessAdmin(user);
|
|
56
|
+
if (adminCheck.allowed) {
|
|
57
|
+
// Show admin UI
|
|
242
58
|
}
|
|
243
59
|
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
const refreshToken = TokenManager.getRefreshToken();
|
|
248
|
-
if (refreshToken) {
|
|
249
|
-
const newTokens = await sdk.auth.refreshToken(refreshToken);
|
|
250
|
-
TokenManager.storeTokens(newTokens);
|
|
251
|
-
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.error('Token refresh failed:', error);
|
|
254
|
-
// Redirect to login
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
```
|
|
60
|
+
// Get user role
|
|
61
|
+
const role = getUserRole('user@example.com'); // 'user' | 'admin' | 'guest'
|
|
258
62
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
async function logout() {
|
|
263
|
-
try {
|
|
264
|
-
await sdk.auth.logout();
|
|
265
|
-
TokenManager.clearTokens();
|
|
266
|
-
console.log('Successfully logged out');
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.error('Logout failed:', error);
|
|
269
|
-
}
|
|
63
|
+
// Check if authenticated user is admin
|
|
64
|
+
if (spaps.isAdmin(currentUser)) {
|
|
65
|
+
// Show admin features
|
|
270
66
|
}
|
|
271
67
|
```
|
|
272
68
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
### Get Current User
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
async function getCurrentUser() {
|
|
279
|
-
try {
|
|
280
|
-
const user = await sdk.auth.getCurrentUser();
|
|
281
|
-
console.log('Current user:', user);
|
|
282
|
-
return user;
|
|
283
|
-
} catch (error) {
|
|
284
|
-
console.error('Failed to get user:', error);
|
|
285
|
-
// User might not be authenticated
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
```
|
|
69
|
+
See [PERMISSIONS.md](./PERMISSIONS.md) for React examples and complete documentation.
|
|
289
70
|
|
|
290
|
-
|
|
71
|
+
## Installation
|
|
291
72
|
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
} else {
|
|
297
|
-
console.log('User needs to log in');
|
|
298
|
-
}
|
|
299
|
-
}
|
|
73
|
+
```bash
|
|
74
|
+
npm install @spaps/sdk
|
|
75
|
+
# or
|
|
76
|
+
yarn add @spaps/sdk
|
|
300
77
|
```
|
|
301
78
|
|
|
302
|
-
##
|
|
303
|
-
|
|
304
|
-
### Wallet Address Validation
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import { WalletUtils } from '@sweet-potato/auth-sdk';
|
|
308
|
-
|
|
309
|
-
// Detect chain type
|
|
310
|
-
const chainType = WalletUtils.detectChainType('0x742d35Cc6634C0532925a3b8D6Ac6d8F49c3589F');
|
|
311
|
-
console.log(chainType); // 'ethereum'
|
|
79
|
+
## Quick Start
|
|
312
80
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
81
|
+
```javascript
|
|
82
|
+
import { SPAPSClient } from '@spaps/sdk';
|
|
83
|
+
// or
|
|
84
|
+
const { SPAPSClient } = require('@spaps/sdk');
|
|
316
85
|
|
|
317
|
-
// Auto-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
86
|
+
// Auto-detects local mode - no API key needed for localhost!
|
|
87
|
+
const spaps = new SPAPSClient({
|
|
88
|
+
apiUrl: 'http://localhost:3300' // Optional, auto-detected
|
|
89
|
+
});
|
|
321
90
|
|
|
322
|
-
|
|
91
|
+
// Login
|
|
92
|
+
const { data } = await spaps.login('user@example.com', 'password');
|
|
93
|
+
console.log('User:', data.user);
|
|
323
94
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
console.log('Token is expired, refreshing...');
|
|
329
|
-
await refreshTokens();
|
|
95
|
+
// Check authentication
|
|
96
|
+
if (spaps.isAuthenticated()) {
|
|
97
|
+
const user = await spaps.getUser();
|
|
98
|
+
console.log('Current user:', user.data);
|
|
330
99
|
}
|
|
331
100
|
```
|
|
332
101
|
|
|
333
|
-
##
|
|
102
|
+
## Features
|
|
334
103
|
|
|
335
|
-
|
|
336
|
-
|
|
104
|
+
### ๐ Zero Configuration
|
|
105
|
+
- **Auto-detects local mode** - No API key needed for localhost
|
|
106
|
+
- **Auto-refreshes tokens** - Handles expired tokens automatically
|
|
107
|
+
- **TypeScript support** - Full type definitions included
|
|
337
108
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
} catch (error) {
|
|
344
|
-
if (error instanceof SweetPotatoAPIError) {
|
|
345
|
-
console.error('API Error:', {
|
|
346
|
-
message: error.message,
|
|
347
|
-
code: error.code,
|
|
348
|
-
status: error.status,
|
|
349
|
-
details: error.details
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Handle specific error codes
|
|
353
|
-
switch (error.code) {
|
|
354
|
-
case 'INVALID_CREDENTIALS':
|
|
355
|
-
console.log('Please check your email and password');
|
|
356
|
-
break;
|
|
357
|
-
case 'RATE_LIMITED':
|
|
358
|
-
console.log('Too many attempts, please wait');
|
|
359
|
-
break;
|
|
360
|
-
default:
|
|
361
|
-
console.log('An unexpected error occurred');
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
```
|
|
109
|
+
### ๐ Authentication Methods
|
|
110
|
+
```javascript
|
|
111
|
+
// Email/Password
|
|
112
|
+
await spaps.login(email, password);
|
|
113
|
+
await spaps.register(email, password);
|
|
366
114
|
|
|
367
|
-
|
|
115
|
+
// Wallet Authentication
|
|
116
|
+
await spaps.walletSignIn(walletAddress, signature, message, 'solana');
|
|
368
117
|
|
|
369
|
-
|
|
118
|
+
// Token Management
|
|
119
|
+
await spaps.refresh();
|
|
120
|
+
await spaps.logout();
|
|
370
121
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
async function makeCustomRequest() {
|
|
374
|
-
try {
|
|
375
|
-
const response = await sdk.request(
|
|
376
|
-
'GET',
|
|
377
|
-
'/api/custom-endpoint',
|
|
378
|
-
null,
|
|
379
|
-
true // requires authentication
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
console.log('Custom response:', response.data);
|
|
383
|
-
} catch (error) {
|
|
384
|
-
console.error('Custom request failed:', error);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
122
|
+
// Get User
|
|
123
|
+
const user = await spaps.getUser();
|
|
387
124
|
```
|
|
388
125
|
|
|
389
|
-
###
|
|
126
|
+
### ๐ณ Stripe Integration
|
|
127
|
+
```javascript
|
|
128
|
+
// Create checkout session
|
|
129
|
+
const session = await spaps.createCheckoutSession(priceId, successUrl);
|
|
130
|
+
window.location.href = session.data.url;
|
|
390
131
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (isHealthy) {
|
|
395
|
-
console.log('API is healthy');
|
|
396
|
-
} else {
|
|
397
|
-
console.log('API is down or unreachable');
|
|
398
|
-
}
|
|
399
|
-
}
|
|
132
|
+
// Manage subscription
|
|
133
|
+
const subscription = await spaps.getSubscription();
|
|
134
|
+
await spaps.cancelSubscription();
|
|
400
135
|
```
|
|
401
136
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const sdk = createSweetPotatoSDK({
|
|
409
|
-
apiUrl: process.env.REACT_APP_API_URL!,
|
|
410
|
-
apiKey: process.env.REACT_APP_API_KEY!
|
|
411
|
-
});
|
|
137
|
+
### ๐ Usage Tracking
|
|
138
|
+
```javascript
|
|
139
|
+
// Check balance
|
|
140
|
+
const balance = await spaps.getUsageBalance();
|
|
141
|
+
console.log(`Credits: ${balance.data.balance}`);
|
|
412
142
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const [loading, setLoading] = useState(true);
|
|
416
|
-
|
|
417
|
-
useEffect(() => {
|
|
418
|
-
async function restoreAuth() {
|
|
419
|
-
try {
|
|
420
|
-
const restored = await TokenManager.autoRefreshToken(sdk);
|
|
421
|
-
if (restored) {
|
|
422
|
-
const currentUser = await sdk.auth.getCurrentUser();
|
|
423
|
-
setUser(currentUser);
|
|
424
|
-
}
|
|
425
|
-
} catch (error) {
|
|
426
|
-
console.error('Failed to restore authentication:', error);
|
|
427
|
-
} finally {
|
|
428
|
-
setLoading(false);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
restoreAuth();
|
|
433
|
-
}, []);
|
|
434
|
-
|
|
435
|
-
const login = async (email: string, password: string) => {
|
|
436
|
-
const result = await sdk.auth.signInWithPassword({ email, password });
|
|
437
|
-
TokenManager.storeTokens(result);
|
|
438
|
-
setUser(result.user);
|
|
439
|
-
return result;
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const logout = async () => {
|
|
443
|
-
await sdk.auth.logout();
|
|
444
|
-
TokenManager.clearTokens();
|
|
445
|
-
setUser(null);
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
user,
|
|
450
|
-
loading,
|
|
451
|
-
login,
|
|
452
|
-
logout,
|
|
453
|
-
isAuthenticated: !!user
|
|
454
|
-
};
|
|
455
|
-
}
|
|
143
|
+
// Record usage
|
|
144
|
+
await spaps.recordUsage('api-call', 1);
|
|
456
145
|
```
|
|
457
146
|
|
|
458
|
-
## Configuration
|
|
147
|
+
## Configuration
|
|
459
148
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
149
|
+
### Production Mode
|
|
150
|
+
```javascript
|
|
151
|
+
const spaps = new SPAPSClient({
|
|
152
|
+
apiUrl: 'https://api.sweetpotato.com',
|
|
153
|
+
apiKey: 'spaps_your_api_key_here',
|
|
154
|
+
timeout: 10000 // Optional timeout in ms
|
|
155
|
+
});
|
|
467
156
|
```
|
|
468
157
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
import type {
|
|
475
|
-
User,
|
|
476
|
-
UserWallet,
|
|
477
|
-
AuthResponse,
|
|
478
|
-
NonceResponse,
|
|
479
|
-
WalletSignInRequest,
|
|
480
|
-
TraditionalLoginRequest,
|
|
481
|
-
ApiResponse,
|
|
482
|
-
SweetPotatoAPIError
|
|
483
|
-
} from '@sweet-potato/auth-sdk';
|
|
158
|
+
### Local Development Mode (Auto-detected)
|
|
159
|
+
```javascript
|
|
160
|
+
const spaps = new SPAPSClient();
|
|
161
|
+
// Automatically uses http://localhost:3300 with no API key
|
|
484
162
|
```
|
|
485
163
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
- `Promise`
|
|
492
|
-
- `async/await`
|
|
493
|
-
|
|
494
|
-
## Error Codes
|
|
495
|
-
|
|
496
|
-
Common error codes you might encounter:
|
|
497
|
-
|
|
498
|
-
- `INVALID_CREDENTIALS`: Invalid email/password or wallet signature
|
|
499
|
-
- `MISSING_REQUIRED_FIELDS`: Required fields are missing from request
|
|
500
|
-
- `INVALID_SIGNATURE`: Wallet signature verification failed
|
|
501
|
-
- `RATE_LIMITED`: Too many requests, rate limit exceeded
|
|
502
|
-
- `UNAUTHORIZED`: Invalid or expired tokens
|
|
503
|
-
- `INVALID_APPLICATION`: Invalid API key or application configuration
|
|
504
|
-
- `INTERNAL_SERVER_ERROR`: Server-side error occurred
|
|
505
|
-
|
|
506
|
-
## API Reference
|
|
507
|
-
|
|
508
|
-
### SDK Classes
|
|
509
|
-
|
|
510
|
-
#### `SweetPotatoSDK`
|
|
511
|
-
Main SDK class that provides access to all services.
|
|
512
|
-
|
|
513
|
-
#### `AuthService`
|
|
514
|
-
- `authenticateWallet()` - Complete wallet authentication flow
|
|
515
|
-
- `signInWithPassword()` - Email/password login
|
|
516
|
-
- `signUp()` - Create new account
|
|
517
|
-
- `requestMagicLink()` - Send magic link email
|
|
518
|
-
- `verifyMagicLink()` - Verify magic link token
|
|
519
|
-
- `refreshToken()` - Refresh access token
|
|
520
|
-
- `logout()` - Sign out and invalidate tokens
|
|
521
|
-
- `getCurrentUser()` - Get authenticated user
|
|
522
|
-
- `isAuthenticated()` - Check auth status
|
|
523
|
-
|
|
524
|
-
#### `PaymentsService`
|
|
525
|
-
- `createCheckoutSession()` - Create Stripe checkout
|
|
526
|
-
- `createPaymentCheckout()` - Simplified one-time payment
|
|
527
|
-
- `createSubscriptionCheckout()` - Simplified subscription
|
|
528
|
-
- `listProducts()` - Get available products
|
|
529
|
-
- `getProduct()` - Get product details
|
|
530
|
-
- `createProduct()` - Create product (admin)
|
|
531
|
-
- `updateProduct()` - Update product (admin)
|
|
532
|
-
- `syncProducts()` - Sync from Stripe (admin)
|
|
533
|
-
- `listSubscriptions()` - User's subscriptions
|
|
534
|
-
- `updateSubscription()` - Modify subscription
|
|
535
|
-
- `cancelSubscription()` - Cancel subscription
|
|
536
|
-
- `createCustomerPortalSession()` - Billing portal
|
|
537
|
-
|
|
538
|
-
#### `TokenManager`
|
|
539
|
-
Static utility class for browser token management.
|
|
540
|
-
- `storeTokens()` - Save tokens to localStorage
|
|
541
|
-
- `getAccessToken()` - Retrieve access token
|
|
542
|
-
- `getRefreshToken()` - Retrieve refresh token
|
|
543
|
-
- `clearTokens()` - Remove all tokens
|
|
544
|
-
- `isTokenExpired()` - Check token expiration
|
|
545
|
-
- `autoRefreshToken()` - Auto-refresh if needed
|
|
546
|
-
|
|
547
|
-
#### `WalletUtils`
|
|
548
|
-
Static utility class for wallet operations.
|
|
549
|
-
- `detectChainType()` - Identify blockchain from address
|
|
550
|
-
- `isValidAddress()` - Validate wallet address format
|
|
551
|
-
|
|
552
|
-
## What's NOT in the SDK
|
|
553
|
-
|
|
554
|
-
> **โ ๏ธ Important:** These features require direct API calls with admin authentication.
|
|
555
|
-
|
|
556
|
-
### Whitelist Management
|
|
557
|
-
โ **Not available:** `sdk.whitelist.check()`, `sdk.admin.addToWhitelist()`
|
|
558
|
-
|
|
559
|
-
Whitelist operations require admin JWT authentication and must be done via direct API calls:
|
|
560
|
-
|
|
561
|
-
```typescript
|
|
562
|
-
// Authenticate as admin first
|
|
563
|
-
const adminAuth = await sdk.auth.signInWithPassword({
|
|
564
|
-
email: 'admin@example.com',
|
|
565
|
-
password: 'admin_password'
|
|
566
|
-
});
|
|
164
|
+
### Environment Variables
|
|
165
|
+
```bash
|
|
166
|
+
# .env
|
|
167
|
+
SPAPS_API_URL=https://api.sweetpotato.com
|
|
168
|
+
SPAPS_API_KEY=spaps_your_api_key_here
|
|
567
169
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
method: 'POST',
|
|
571
|
-
headers: {
|
|
572
|
-
'Authorization': `Bearer ${adminAuth.access_token}`,
|
|
573
|
-
'X-API-Key': apiKey,
|
|
574
|
-
'Content-Type': 'application/json'
|
|
575
|
-
},
|
|
576
|
-
body: JSON.stringify({
|
|
577
|
-
email: 'vip@example.com',
|
|
578
|
-
tier: 'premium',
|
|
579
|
-
bypass_payment: true
|
|
580
|
-
})
|
|
581
|
-
});
|
|
170
|
+
# Next.js
|
|
171
|
+
NEXT_PUBLIC_SPAPS_API_URL=https://api.sweetpotato.com
|
|
582
172
|
```
|
|
583
173
|
|
|
584
|
-
|
|
585
|
-
โ **Not available:** `sdk.admin.*` methods
|
|
174
|
+
## Helper Methods
|
|
586
175
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
- Direct database operations
|
|
591
|
-
- Product sync from Stripe
|
|
176
|
+
```javascript
|
|
177
|
+
// Check if authenticated
|
|
178
|
+
spaps.isAuthenticated() // boolean
|
|
592
179
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
2. Use the JWT token for direct API calls
|
|
596
|
-
3. Include both JWT and API key in headers
|
|
180
|
+
// Get current access token
|
|
181
|
+
spaps.getAccessToken() // string | undefined
|
|
597
182
|
|
|
598
|
-
|
|
183
|
+
// Set access token manually
|
|
184
|
+
spaps.setAccessToken(token)
|
|
599
185
|
|
|
600
|
-
|
|
186
|
+
// Check if in local mode
|
|
187
|
+
spaps.isLocalMode() // boolean
|
|
601
188
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
- โ
CORS enabled for all origins
|
|
606
|
-
- โ
No real API keys required
|
|
607
|
-
- โ
Mock Stripe integration ready
|
|
608
|
-
- โ
Runs on port **3456** by default
|
|
189
|
+
// Health check
|
|
190
|
+
await spaps.health()
|
|
191
|
+
```
|
|
609
192
|
|
|
610
|
-
|
|
611
|
-
# Start local server
|
|
612
|
-
npx spaps local
|
|
193
|
+
## Import Styles
|
|
613
194
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
195
|
+
All these work:
|
|
196
|
+
```javascript
|
|
197
|
+
// ES6 Import
|
|
198
|
+
import { SPAPSClient } from '@spaps/sdk';
|
|
199
|
+
import SPAPSClient from '@spaps/sdk';
|
|
617
200
|
|
|
618
|
-
|
|
201
|
+
// CommonJS
|
|
202
|
+
const { SPAPSClient } = require('@spaps/sdk');
|
|
203
|
+
const SPAPSClient = require('@spaps/sdk');
|
|
619
204
|
|
|
620
|
-
|
|
205
|
+
// Alternative names
|
|
206
|
+
import { SPAPS } from '@spaps/sdk';
|
|
207
|
+
import { SweetPotatoSDK } from '@spaps/sdk';
|
|
208
|
+
```
|
|
621
209
|
|
|
622
|
-
|
|
210
|
+
## Admin API Methods
|
|
623
211
|
|
|
624
|
-
|
|
212
|
+
The SDK includes comprehensive admin methods for Stripe product management:
|
|
625
213
|
|
|
626
|
-
###
|
|
214
|
+
### Product Management
|
|
627
215
|
|
|
628
|
-
**Wrong Port**
|
|
629
216
|
```typescript
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
217
|
+
// Create product (Admin required)
|
|
218
|
+
const product = await spaps.admin.createProduct({
|
|
219
|
+
name: 'Premium Plan',
|
|
220
|
+
category: 'subscription',
|
|
221
|
+
description: 'Advanced features for power users',
|
|
222
|
+
images: ['https://example.com/product-image.jpg'],
|
|
223
|
+
metadata: { feature_set: 'premium' },
|
|
224
|
+
active: true
|
|
225
|
+
});
|
|
633
226
|
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
227
|
+
// Update product (Admin required)
|
|
228
|
+
const updatedProduct = await spaps.admin.updateProduct('prod_123', {
|
|
229
|
+
name: 'Premium Plan Pro',
|
|
230
|
+
description: 'Updated features'
|
|
231
|
+
});
|
|
637
232
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
// In local mode, CORS is enabled for all origins
|
|
641
|
-
// In production, ensure your domain is whitelisted
|
|
642
|
-
```
|
|
233
|
+
// Archive product (Admin required)
|
|
234
|
+
await spaps.admin.deleteProduct('prod_123');
|
|
643
235
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
236
|
+
// Get products (includes admin metadata if user is admin)
|
|
237
|
+
const { data } = await spaps.admin.getProducts();
|
|
238
|
+
if (data.adminMetadata) {
|
|
239
|
+
console.log('Total revenue:', data.adminMetadata.total_revenue);
|
|
240
|
+
}
|
|
648
241
|
```
|
|
649
242
|
|
|
650
|
-
|
|
243
|
+
### Price Management
|
|
244
|
+
|
|
651
245
|
```typescript
|
|
652
|
-
//
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
246
|
+
// Create price (Admin required)
|
|
247
|
+
const price = await spaps.admin.createPrice({
|
|
248
|
+
product_id: 'prod_123',
|
|
249
|
+
unit_amount: 2999, // $29.99
|
|
250
|
+
currency: 'usd',
|
|
251
|
+
interval: 'month',
|
|
252
|
+
interval_count: 1,
|
|
253
|
+
nickname: 'Monthly Premium'
|
|
658
254
|
});
|
|
659
255
|
```
|
|
660
256
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
### From v0.x to v1.0
|
|
257
|
+
### Super Admin Operations
|
|
664
258
|
|
|
665
259
|
```typescript
|
|
666
|
-
//
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
// New (v1.0)
|
|
670
|
-
const sdk = new SweetPotatoSDK({
|
|
671
|
-
apiUrl,
|
|
672
|
-
apiKey,
|
|
673
|
-
timeout: 30000,
|
|
674
|
-
retries: 3
|
|
675
|
-
});
|
|
260
|
+
// Sync all products from Stripe (Super Admin required)
|
|
261
|
+
const syncResult = await spaps.admin.syncProducts();
|
|
262
|
+
console.log(`Synced ${syncResult.data.synced_count} products`);
|
|
676
263
|
```
|
|
677
264
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
681
|
-
|
|
682
|
-
## Security
|
|
265
|
+
### Permission Checking
|
|
683
266
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
267
|
+
```typescript
|
|
268
|
+
// Check if current user can access admin features
|
|
269
|
+
const user = await spaps.getCurrentUser();
|
|
270
|
+
if (spaps.isAdmin(user)) {
|
|
271
|
+
// Show admin UI components
|
|
272
|
+
console.log('User has admin privileges');
|
|
273
|
+
} else {
|
|
274
|
+
// Show regular user UI
|
|
275
|
+
console.log('Regular user');
|
|
276
|
+
}
|
|
277
|
+
```
|
|
690
278
|
|
|
691
|
-
##
|
|
279
|
+
## Error Handling
|
|
692
280
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
281
|
+
```javascript
|
|
282
|
+
try {
|
|
283
|
+
await spaps.admin.createProduct(productData);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (error.response?.status === 401) {
|
|
286
|
+
console.error('Authentication required');
|
|
287
|
+
} else if (error.response?.status === 403) {
|
|
288
|
+
console.error('Admin privileges required');
|
|
289
|
+
} else if (error.response?.status === 429) {
|
|
290
|
+
console.error('Rate limited');
|
|
291
|
+
} else {
|
|
292
|
+
console.error('Error:', error.message);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
697
296
|
|
|
698
297
|
## License
|
|
699
298
|
|
|
700
|
-
MIT
|
|
299
|
+
MIT
|