spaps-sdk 0.1.0 β 1.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/.env.example +23 -0
- package/README.md +639 -103
- package/admin-utils.ts +243 -0
- package/dist/index.js +890 -134
- package/dist/index.mjs +880 -121
- package/package.json +34 -40
- package/dist/index.d.mts +0 -82
- package/dist/index.d.ts +0 -82
package/README.md
CHANGED
|
@@ -1,164 +1,700 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Sweet Potato SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/spaps-sdk)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
A comprehensive TypeScript SDK for integrating with the Sweet Potato Authentication and Payment Service (SPAPS). This SDK provides complete functionality for user authentication, Stripe payment processing, subscription management, and more.
|
|
8
|
+
|
|
9
|
+
**[π Full API Documentation](../API_INTEGRATION_GUIDE.md) | [π οΈ Stripe Admin Guide](../STRIPE_ADMIN_GUIDE.md)**
|
|
10
|
+
|
|
11
|
+
## Features
|
|
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
|
|
6
33
|
|
|
7
34
|
## Installation
|
|
8
35
|
|
|
9
36
|
```bash
|
|
10
|
-
npm install
|
|
37
|
+
npm install spaps-sdk
|
|
11
38
|
# or
|
|
12
|
-
yarn add
|
|
39
|
+
yarn add spaps-sdk
|
|
40
|
+
# or
|
|
41
|
+
pnpm add spaps-sdk
|
|
13
42
|
```
|
|
14
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
|
+
|
|
15
51
|
## Quick Start
|
|
16
52
|
|
|
17
|
-
|
|
18
|
-
import { SPAPSClient } from '@spaps/sdk';
|
|
19
|
-
// or
|
|
20
|
-
const { SPAPSClient } = require('@spaps/sdk');
|
|
53
|
+
### Basic Setup
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
apiUrl: 'http://localhost:3300' // Optional, auto-detected
|
|
25
|
-
});
|
|
55
|
+
```typescript
|
|
56
|
+
import { SweetPotatoSDK, TokenManager } from 'spaps-sdk';
|
|
26
57
|
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
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
|
+
});
|
|
30
66
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
const user = await spaps.getUser();
|
|
34
|
-
console.log('Current user:', user.data);
|
|
35
|
-
}
|
|
67
|
+
// Auto-restore authentication state (browser only)
|
|
68
|
+
await TokenManager.autoRefreshToken(sdk);
|
|
36
69
|
```
|
|
37
70
|
|
|
38
|
-
##
|
|
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
|
+
```
|
|
39
106
|
|
|
40
|
-
###
|
|
41
|
-
- **Auto-detects local mode** - No API key needed for localhost
|
|
42
|
-
- **Auto-refreshes tokens** - Handles expired tokens automatically
|
|
43
|
-
- **TypeScript support** - Full type definitions included
|
|
107
|
+
### Email/Password Authentication
|
|
44
108
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
109
|
+
```typescript
|
|
110
|
+
const authResponse = await sdk.auth.signInWithPassword({
|
|
111
|
+
email: 'user@example.com',
|
|
112
|
+
password: 'securePassword123!'
|
|
113
|
+
});
|
|
50
114
|
|
|
51
|
-
|
|
52
|
-
|
|
115
|
+
TokenManager.storeTokens(authResponse);
|
|
116
|
+
```
|
|
53
117
|
|
|
54
|
-
|
|
55
|
-
await spaps.refresh();
|
|
56
|
-
await spaps.logout();
|
|
118
|
+
### Magic Link Authentication
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
|
|
120
|
+
```typescript
|
|
121
|
+
await sdk.auth.requestMagicLink({
|
|
122
|
+
email: 'user@example.com',
|
|
123
|
+
redirect_url: 'https://myapp.com/auth/callback'
|
|
124
|
+
});
|
|
125
|
+
// User will receive email with authentication link
|
|
60
126
|
```
|
|
61
127
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const session = await spaps.createCheckoutSession(priceId, successUrl);
|
|
66
|
-
window.location.href = session.data.url;
|
|
128
|
+
## Payment Examples
|
|
129
|
+
|
|
130
|
+
### One-Time Payment
|
|
67
131
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
await
|
|
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
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Redirect to Stripe Checkout
|
|
145
|
+
window.location.href = checkoutSession.url!;
|
|
71
146
|
```
|
|
72
147
|
|
|
73
|
-
###
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
155
|
+
currency: 'usd',
|
|
156
|
+
quantity: 1,
|
|
157
|
+
success_url: 'https://myapp.com/success',
|
|
158
|
+
cancel_url: 'https://myapp.com/cancel'
|
|
159
|
+
});
|
|
160
|
+
```
|
|
78
161
|
|
|
79
|
-
|
|
80
|
-
|
|
162
|
+
### Subscription Creation
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const subscriptionCheckout = await sdk.payments.createSubscriptionCheckout({
|
|
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
|
+
});
|
|
81
175
|
```
|
|
82
176
|
|
|
83
|
-
|
|
177
|
+
### Product Management
|
|
84
178
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
179
|
+
```typescript
|
|
180
|
+
// List all products
|
|
181
|
+
const products = await sdk.payments.listProducts({
|
|
182
|
+
category: 'subscription',
|
|
183
|
+
active: true,
|
|
184
|
+
limit: 20
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Get specific product with prices
|
|
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
|
+
}
|
|
91
199
|
});
|
|
92
200
|
```
|
|
93
201
|
|
|
94
|
-
###
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
202
|
+
### Subscription Management
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// List user subscriptions
|
|
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
|
+
});
|
|
220
|
+
|
|
221
|
+
// Create customer portal session
|
|
222
|
+
const portalSession = await sdk.payments.createCustomerPortalSession({
|
|
223
|
+
return_url: 'https://myapp.com/account'
|
|
224
|
+
});
|
|
225
|
+
window.location.href = portalSession.url;
|
|
98
226
|
```
|
|
99
227
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
228
|
+
## Token Management
|
|
229
|
+
|
|
230
|
+
### Automatic Token Refresh
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
// Set up automatic token refresh
|
|
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
|
+
}
|
|
242
|
+
}
|
|
105
243
|
|
|
106
|
-
|
|
107
|
-
|
|
244
|
+
// Manual token refresh
|
|
245
|
+
async function refreshTokens() {
|
|
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
|
+
}
|
|
108
257
|
```
|
|
109
258
|
|
|
110
|
-
|
|
259
|
+
### Logout
|
|
111
260
|
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
261
|
+
```typescript
|
|
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
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
115
272
|
|
|
116
|
-
|
|
117
|
-
spaps.getAccessToken() // string | undefined
|
|
273
|
+
## User Management
|
|
118
274
|
|
|
119
|
-
|
|
120
|
-
spaps.setAccessToken(token)
|
|
275
|
+
### Get Current User
|
|
121
276
|
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
```
|
|
289
|
+
|
|
290
|
+
### Check Authentication Status
|
|
124
291
|
|
|
125
|
-
|
|
126
|
-
|
|
292
|
+
```typescript
|
|
293
|
+
function checkAuthStatus() {
|
|
294
|
+
if (sdk.auth.isAuthenticated()) {
|
|
295
|
+
console.log('User is authenticated');
|
|
296
|
+
} else {
|
|
297
|
+
console.log('User needs to log in');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
127
300
|
```
|
|
128
301
|
|
|
129
|
-
##
|
|
302
|
+
## Utility Functions
|
|
303
|
+
|
|
304
|
+
### Wallet Address Validation
|
|
130
305
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// ES6 Import
|
|
134
|
-
import { SPAPSClient } from '@spaps/sdk';
|
|
135
|
-
import SPAPSClient from '@spaps/sdk';
|
|
306
|
+
```typescript
|
|
307
|
+
import { WalletUtils } from '@sweet-potato/auth-sdk';
|
|
136
308
|
|
|
137
|
-
//
|
|
138
|
-
const
|
|
139
|
-
|
|
309
|
+
// Detect chain type
|
|
310
|
+
const chainType = WalletUtils.detectChainType('0x742d35Cc6634C0532925a3b8D6Ac6d8F49c3589F');
|
|
311
|
+
console.log(chainType); // 'ethereum'
|
|
140
312
|
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
313
|
+
// Validate address format
|
|
314
|
+
const isValid = WalletUtils.isValidAddress('0x742d35Cc6634C0532925a3b8D6Ac6d8F49c3589F', 'ethereum');
|
|
315
|
+
console.log(isValid); // true
|
|
316
|
+
|
|
317
|
+
// Auto-detect and validate
|
|
318
|
+
const isValidAuto = WalletUtils.isValidAddress('0x742d35Cc6634C0532925a3b8D6Ac6d8F49c3589F');
|
|
319
|
+
console.log(isValidAuto); // true
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Token Utilities
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
// Check if token is expired
|
|
326
|
+
const token = TokenManager.getAccessToken();
|
|
327
|
+
if (token && TokenManager.isTokenExpired(token)) {
|
|
328
|
+
console.log('Token is expired, refreshing...');
|
|
329
|
+
await refreshTokens();
|
|
330
|
+
}
|
|
144
331
|
```
|
|
145
332
|
|
|
146
333
|
## Error Handling
|
|
147
334
|
|
|
148
|
-
```
|
|
335
|
+
```typescript
|
|
336
|
+
import { SweetPotatoAPIError } from '@sweet-potato/auth-sdk';
|
|
337
|
+
|
|
149
338
|
try {
|
|
150
|
-
await
|
|
339
|
+
await sdk.auth.signInWithPassword({
|
|
340
|
+
email: 'invalid@example.com',
|
|
341
|
+
password: 'wrongpassword'
|
|
342
|
+
});
|
|
151
343
|
} catch (error) {
|
|
152
|
-
if (error
|
|
153
|
-
console.error('
|
|
154
|
-
|
|
155
|
-
|
|
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
|
+
```
|
|
366
|
+
|
|
367
|
+
## Advanced Usage
|
|
368
|
+
|
|
369
|
+
### Custom Requests
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
// Make custom authenticated requests
|
|
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
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Health Check
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
async function checkAPIHealth() {
|
|
393
|
+
const isHealthy = await sdk.healthCheck();
|
|
394
|
+
if (isHealthy) {
|
|
395
|
+
console.log('API is healthy');
|
|
156
396
|
} else {
|
|
157
|
-
console.
|
|
397
|
+
console.log('API is down or unreachable');
|
|
158
398
|
}
|
|
159
399
|
}
|
|
160
400
|
```
|
|
161
401
|
|
|
402
|
+
## React Integration Example
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import React, { useEffect, useState } from 'react';
|
|
406
|
+
import { createSweetPotatoSDK, TokenManager, User } from '@sweet-potato/auth-sdk';
|
|
407
|
+
|
|
408
|
+
const sdk = createSweetPotatoSDK({
|
|
409
|
+
apiUrl: process.env.REACT_APP_API_URL!,
|
|
410
|
+
apiKey: process.env.REACT_APP_API_KEY!
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
export function useAuth() {
|
|
414
|
+
const [user, setUser] = useState<User | null>(null);
|
|
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
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Configuration Options
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
interface SweetPotatoConfig {
|
|
462
|
+
apiUrl: string; // Required: Your API base URL
|
|
463
|
+
apiKey: string; // Required: Your application API key
|
|
464
|
+
timeout?: number; // Optional: Request timeout in ms (default: 30000)
|
|
465
|
+
retries?: number; // Optional: Number of retries (default: 3)
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## TypeScript Types
|
|
470
|
+
|
|
471
|
+
The SDK exports comprehensive TypeScript types for all API responses and request payloads:
|
|
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';
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Browser Compatibility
|
|
487
|
+
|
|
488
|
+
The SDK supports all modern browsers with ES2018+ support. For older browsers, you may need to include polyfills for:
|
|
489
|
+
|
|
490
|
+
- `fetch` API
|
|
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
|
+
});
|
|
567
|
+
|
|
568
|
+
// Use JWT for admin API calls
|
|
569
|
+
const response = await fetch(`${apiUrl}/api/v1/whitelist`, {
|
|
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
|
+
});
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### Admin Operations
|
|
585
|
+
β **Not available:** `sdk.admin.*` methods
|
|
586
|
+
|
|
587
|
+
Admin operations require JWT authentication:
|
|
588
|
+
- Application management (create/rotate API keys)
|
|
589
|
+
- User management (admin dashboard)
|
|
590
|
+
- Direct database operations
|
|
591
|
+
- Product sync from Stripe
|
|
592
|
+
|
|
593
|
+
All admin operations follow this pattern:
|
|
594
|
+
1. Authenticate as admin user via `sdk.auth.signInWithPassword()`
|
|
595
|
+
2. Use the JWT token for direct API calls
|
|
596
|
+
3. Include both JWT and API key in headers
|
|
597
|
+
|
|
598
|
+
See [Stripe Admin Guide](../STRIPE_ADMIN_GUIDE.md) for product management instructions.
|
|
599
|
+
|
|
600
|
+
## Local Development Mode
|
|
601
|
+
|
|
602
|
+
When using `npx spaps local`:
|
|
603
|
+
- β
Auto-authentication enabled (no signup needed)
|
|
604
|
+
- β
Test users pre-created: 'user', 'admin', 'premium'
|
|
605
|
+
- β
CORS enabled for all origins
|
|
606
|
+
- β
No real API keys required
|
|
607
|
+
- β
Mock Stripe integration ready
|
|
608
|
+
- β
Runs on port **3456** by default
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
# Start local server
|
|
612
|
+
npx spaps local
|
|
613
|
+
|
|
614
|
+
# Your app can now connect to:
|
|
615
|
+
http://localhost:3456
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
## Production Notes
|
|
619
|
+
|
|
620
|
+
β οΈ **Production API is not yet available.** Currently, only local development mode is supported.
|
|
621
|
+
|
|
622
|
+
Future production URL: `https://api.sweetpotato.dev` (coming soon)
|
|
623
|
+
|
|
624
|
+
## Troubleshooting
|
|
625
|
+
|
|
626
|
+
### Common Issues
|
|
627
|
+
|
|
628
|
+
**Wrong Port**
|
|
629
|
+
```typescript
|
|
630
|
+
// β Wrong
|
|
631
|
+
apiUrl: 'http://localhost:3000'
|
|
632
|
+
apiUrl: 'http://localhost:3300'
|
|
633
|
+
|
|
634
|
+
// β
Correct - always use 3456
|
|
635
|
+
apiUrl: 'http://localhost:3456'
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**CORS Errors**
|
|
639
|
+
```typescript
|
|
640
|
+
// In local mode, CORS is enabled for all origins
|
|
641
|
+
// In production, ensure your domain is whitelisted
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Token Expired**
|
|
645
|
+
```typescript
|
|
646
|
+
// Use TokenManager for automatic refresh
|
|
647
|
+
await TokenManager.autoRefreshToken(sdk);
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Rate Limiting**
|
|
651
|
+
```typescript
|
|
652
|
+
// SDK automatically retries with exponential backoff
|
|
653
|
+
// Configure retries in SDK initialization
|
|
654
|
+
const sdk = new SweetPotatoSDK({
|
|
655
|
+
apiUrl: '...',
|
|
656
|
+
apiKey: '...',
|
|
657
|
+
retries: 5 // Increase retry count
|
|
658
|
+
});
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
## Migration Guide
|
|
662
|
+
|
|
663
|
+
### From v0.x to v1.0
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
// Old (v0.x)
|
|
667
|
+
const sdk = new SweetPotatoSDK(apiUrl, apiKey);
|
|
668
|
+
|
|
669
|
+
// New (v1.0)
|
|
670
|
+
const sdk = new SweetPotatoSDK({
|
|
671
|
+
apiUrl,
|
|
672
|
+
apiKey,
|
|
673
|
+
timeout: 30000,
|
|
674
|
+
retries: 3
|
|
675
|
+
});
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
## Contributing
|
|
679
|
+
|
|
680
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
681
|
+
|
|
682
|
+
## Security
|
|
683
|
+
|
|
684
|
+
- **Never expose API keys in client-side code**
|
|
685
|
+
- **Use environment variables for sensitive data**
|
|
686
|
+
- **Enable HTTPS in production**
|
|
687
|
+
- **Implement proper CORS policies**
|
|
688
|
+
|
|
689
|
+
Report security vulnerabilities to security@sweetpotato.dev
|
|
690
|
+
|
|
691
|
+
## Support
|
|
692
|
+
|
|
693
|
+
- π [Full Documentation](../API_INTEGRATION_GUIDE.md)
|
|
694
|
+
- π¬ Discord: [discord.gg/sweetpotato](https://discord.gg/sweetpotato)
|
|
695
|
+
- π§ Email: support@sweetpotato.dev
|
|
696
|
+
- π Issues: [GitHub Issues](https://github.com/sweet-potato/sdk/issues)
|
|
697
|
+
|
|
162
698
|
## License
|
|
163
699
|
|
|
164
|
-
MIT
|
|
700
|
+
MIT Β© Sweet Potato Team
|