spaps-sdk 1.2.0 → 1.3.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/dist/index.d.mts +100 -7
- package/dist/index.d.ts +100 -7
- package/dist/index.js +173 -5
- package/dist/index.mjs +173 -5
- package/package.json +6 -4
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as spaps_types from 'spaps-types';
|
|
2
|
-
import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
|
|
3
|
-
export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, Price, Product, ProductSyncResult, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, createSecureMessageRequestSchema, secureMessageMetadataSchema, secureMessageSchema } from 'spaps-types';
|
|
2
|
+
import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
|
|
3
|
+
export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, Price, Product, ProductSyncResult, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, createSecureMessageRequestSchema, secureMessageMetadataSchema, secureMessageSchema } from 'spaps-types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Permission checking utilities for SPAPS SDK
|
|
@@ -244,14 +244,13 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
244
244
|
password: string;
|
|
245
245
|
}) => Promise<any>;
|
|
246
246
|
/**
|
|
247
|
-
* Verify a magic link token
|
|
248
|
-
*
|
|
247
|
+
* Verify a magic link token and authenticate the user.
|
|
248
|
+
* Returns full auth response with tokens and auto-stores them.
|
|
249
249
|
*/
|
|
250
250
|
verifyMagicLink: (payload: {
|
|
251
251
|
token: string;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}>;
|
|
252
|
+
type?: string;
|
|
253
|
+
}) => Promise<AuthResponse>;
|
|
255
254
|
solana: {
|
|
256
255
|
linkWallet: (payload: {
|
|
257
256
|
wallet_address: string;
|
|
@@ -402,6 +401,72 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
402
401
|
revokeAll: () => Promise<any>;
|
|
403
402
|
touch: () => Promise<any>;
|
|
404
403
|
};
|
|
404
|
+
/**
|
|
405
|
+
* CFO (QuickBooks) namespace for managing multi-account connections
|
|
406
|
+
*/
|
|
407
|
+
cfo: {
|
|
408
|
+
/**
|
|
409
|
+
* Get all QuickBooks connections for the current user
|
|
410
|
+
*/
|
|
411
|
+
getConnections: () => Promise<{
|
|
412
|
+
connections: Array<{
|
|
413
|
+
id: string;
|
|
414
|
+
status: string;
|
|
415
|
+
companyName?: string;
|
|
416
|
+
realmId?: string;
|
|
417
|
+
connectedAt?: string;
|
|
418
|
+
}>;
|
|
419
|
+
}>;
|
|
420
|
+
/**
|
|
421
|
+
* Get connection status (returns primary connection for backward compatibility)
|
|
422
|
+
*/
|
|
423
|
+
getStatus: () => Promise<{
|
|
424
|
+
connection: {
|
|
425
|
+
id: string;
|
|
426
|
+
status: string;
|
|
427
|
+
companyName?: string;
|
|
428
|
+
realmId?: string;
|
|
429
|
+
} | null;
|
|
430
|
+
totalConnections: number;
|
|
431
|
+
}>;
|
|
432
|
+
/**
|
|
433
|
+
* Start adding a new QuickBooks connection
|
|
434
|
+
* Returns auth URL to redirect user to QuickBooks OAuth
|
|
435
|
+
*/
|
|
436
|
+
addConnection: () => Promise<{
|
|
437
|
+
authUrl: string;
|
|
438
|
+
connectionId: string;
|
|
439
|
+
existingConnections: number;
|
|
440
|
+
}>;
|
|
441
|
+
/**
|
|
442
|
+
* Disconnect a specific QuickBooks account
|
|
443
|
+
*/
|
|
444
|
+
disconnect: (connectionId: string) => Promise<{
|
|
445
|
+
success: boolean;
|
|
446
|
+
message: string;
|
|
447
|
+
}>;
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* DayRate (Dynamic Scheduling) namespace
|
|
451
|
+
* For booking half-day sessions with dynamic pricing
|
|
452
|
+
*/
|
|
453
|
+
dayrate: {
|
|
454
|
+
/**
|
|
455
|
+
* Get available slots with current pricing
|
|
456
|
+
* Public endpoint - no authentication required
|
|
457
|
+
*/
|
|
458
|
+
getAvailability: () => Promise<DayrateAvailabilityResponse>;
|
|
459
|
+
/**
|
|
460
|
+
* Create a single-slot booking and get Stripe checkout URL
|
|
461
|
+
* Returns a 10-minute reservation with checkout link
|
|
462
|
+
*/
|
|
463
|
+
createBooking: (payload: DayrateBookingRequest) => Promise<DayrateBookingResponse>;
|
|
464
|
+
/**
|
|
465
|
+
* Create a multi-slot booking and get Stripe checkout URL
|
|
466
|
+
* Reserves multiple slots with a single checkout session
|
|
467
|
+
*/
|
|
468
|
+
createMultiBooking: (payload: DayrateMultiBookingRequest) => Promise<DayrateMultiBookingResponse>;
|
|
469
|
+
};
|
|
405
470
|
createCheckoutSession(priceId: string, successUrl: string, cancelUrl?: string): Promise<{
|
|
406
471
|
data: CheckoutSession;
|
|
407
472
|
}>;
|
|
@@ -462,6 +527,12 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
462
527
|
isAuthenticated(): boolean;
|
|
463
528
|
getAccessToken(): string | undefined;
|
|
464
529
|
setAccessToken(token: string): void;
|
|
530
|
+
setRefreshToken(token: string): void;
|
|
531
|
+
/**
|
|
532
|
+
* Restore tokens (for SSO/OAuth flows)
|
|
533
|
+
* Sets both access and refresh tokens
|
|
534
|
+
*/
|
|
535
|
+
restoreTokens(accessToken: string, refreshToken?: string): void;
|
|
465
536
|
isLocalMode(): boolean;
|
|
466
537
|
/**
|
|
467
538
|
* Check if current user has admin privileges
|
|
@@ -478,6 +549,10 @@ declare class TokenManager {
|
|
|
478
549
|
private static readonly ACCESS_TOKEN_KEY;
|
|
479
550
|
private static readonly REFRESH_TOKEN_KEY;
|
|
480
551
|
private static readonly USER_KEY;
|
|
552
|
+
/**
|
|
553
|
+
* Cross-platform base64 decode (works in both Node.js and browser)
|
|
554
|
+
*/
|
|
555
|
+
private static base64Decode;
|
|
481
556
|
private static getStorage;
|
|
482
557
|
static storeTokens(tokens: AuthResponse): void;
|
|
483
558
|
static getAccessToken(): string | null;
|
|
@@ -486,6 +561,24 @@ declare class TokenManager {
|
|
|
486
561
|
static clearTokens(): void;
|
|
487
562
|
static isTokenExpired(token: string): boolean;
|
|
488
563
|
static autoRefreshToken(sdk: SPAPSClient): Promise<boolean>;
|
|
564
|
+
/**
|
|
565
|
+
* Parse auth tokens from URL fragment (for OAuth callbacks)
|
|
566
|
+
* URL format: https://app.com/callback#access_token=xxx&refresh_token=xxx
|
|
567
|
+
*/
|
|
568
|
+
static parseTokensFromUrlFragment(url?: string): {
|
|
569
|
+
access_token?: string;
|
|
570
|
+
refresh_token?: string;
|
|
571
|
+
user_id?: string;
|
|
572
|
+
} | null;
|
|
573
|
+
/**
|
|
574
|
+
* Handle OAuth callback - parse tokens from URL and store them
|
|
575
|
+
* Returns user info decoded from JWT, or null if no tokens found
|
|
576
|
+
*/
|
|
577
|
+
static handleOAuthCallback(sdk: SPAPSClient): {
|
|
578
|
+
id: string;
|
|
579
|
+
email?: string;
|
|
580
|
+
role?: string;
|
|
581
|
+
} | null;
|
|
489
582
|
}
|
|
490
583
|
declare class WalletUtils {
|
|
491
584
|
static detectChainType(address: string): 'solana' | 'ethereum' | 'bitcoin' | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as spaps_types from 'spaps-types';
|
|
2
|
-
import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
|
|
3
|
-
export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, Price, Product, ProductSyncResult, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, createSecureMessageRequestSchema, secureMessageMetadataSchema, secureMessageSchema } from 'spaps-types';
|
|
2
|
+
import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
|
|
3
|
+
export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, Price, Product, ProductSyncResult, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, createSecureMessageRequestSchema, secureMessageMetadataSchema, secureMessageSchema } from 'spaps-types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Permission checking utilities for SPAPS SDK
|
|
@@ -244,14 +244,13 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
244
244
|
password: string;
|
|
245
245
|
}) => Promise<any>;
|
|
246
246
|
/**
|
|
247
|
-
* Verify a magic link token
|
|
248
|
-
*
|
|
247
|
+
* Verify a magic link token and authenticate the user.
|
|
248
|
+
* Returns full auth response with tokens and auto-stores them.
|
|
249
249
|
*/
|
|
250
250
|
verifyMagicLink: (payload: {
|
|
251
251
|
token: string;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}>;
|
|
252
|
+
type?: string;
|
|
253
|
+
}) => Promise<AuthResponse>;
|
|
255
254
|
solana: {
|
|
256
255
|
linkWallet: (payload: {
|
|
257
256
|
wallet_address: string;
|
|
@@ -402,6 +401,72 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
402
401
|
revokeAll: () => Promise<any>;
|
|
403
402
|
touch: () => Promise<any>;
|
|
404
403
|
};
|
|
404
|
+
/**
|
|
405
|
+
* CFO (QuickBooks) namespace for managing multi-account connections
|
|
406
|
+
*/
|
|
407
|
+
cfo: {
|
|
408
|
+
/**
|
|
409
|
+
* Get all QuickBooks connections for the current user
|
|
410
|
+
*/
|
|
411
|
+
getConnections: () => Promise<{
|
|
412
|
+
connections: Array<{
|
|
413
|
+
id: string;
|
|
414
|
+
status: string;
|
|
415
|
+
companyName?: string;
|
|
416
|
+
realmId?: string;
|
|
417
|
+
connectedAt?: string;
|
|
418
|
+
}>;
|
|
419
|
+
}>;
|
|
420
|
+
/**
|
|
421
|
+
* Get connection status (returns primary connection for backward compatibility)
|
|
422
|
+
*/
|
|
423
|
+
getStatus: () => Promise<{
|
|
424
|
+
connection: {
|
|
425
|
+
id: string;
|
|
426
|
+
status: string;
|
|
427
|
+
companyName?: string;
|
|
428
|
+
realmId?: string;
|
|
429
|
+
} | null;
|
|
430
|
+
totalConnections: number;
|
|
431
|
+
}>;
|
|
432
|
+
/**
|
|
433
|
+
* Start adding a new QuickBooks connection
|
|
434
|
+
* Returns auth URL to redirect user to QuickBooks OAuth
|
|
435
|
+
*/
|
|
436
|
+
addConnection: () => Promise<{
|
|
437
|
+
authUrl: string;
|
|
438
|
+
connectionId: string;
|
|
439
|
+
existingConnections: number;
|
|
440
|
+
}>;
|
|
441
|
+
/**
|
|
442
|
+
* Disconnect a specific QuickBooks account
|
|
443
|
+
*/
|
|
444
|
+
disconnect: (connectionId: string) => Promise<{
|
|
445
|
+
success: boolean;
|
|
446
|
+
message: string;
|
|
447
|
+
}>;
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* DayRate (Dynamic Scheduling) namespace
|
|
451
|
+
* For booking half-day sessions with dynamic pricing
|
|
452
|
+
*/
|
|
453
|
+
dayrate: {
|
|
454
|
+
/**
|
|
455
|
+
* Get available slots with current pricing
|
|
456
|
+
* Public endpoint - no authentication required
|
|
457
|
+
*/
|
|
458
|
+
getAvailability: () => Promise<DayrateAvailabilityResponse>;
|
|
459
|
+
/**
|
|
460
|
+
* Create a single-slot booking and get Stripe checkout URL
|
|
461
|
+
* Returns a 10-minute reservation with checkout link
|
|
462
|
+
*/
|
|
463
|
+
createBooking: (payload: DayrateBookingRequest) => Promise<DayrateBookingResponse>;
|
|
464
|
+
/**
|
|
465
|
+
* Create a multi-slot booking and get Stripe checkout URL
|
|
466
|
+
* Reserves multiple slots with a single checkout session
|
|
467
|
+
*/
|
|
468
|
+
createMultiBooking: (payload: DayrateMultiBookingRequest) => Promise<DayrateMultiBookingResponse>;
|
|
469
|
+
};
|
|
405
470
|
createCheckoutSession(priceId: string, successUrl: string, cancelUrl?: string): Promise<{
|
|
406
471
|
data: CheckoutSession;
|
|
407
472
|
}>;
|
|
@@ -462,6 +527,12 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
|
|
|
462
527
|
isAuthenticated(): boolean;
|
|
463
528
|
getAccessToken(): string | undefined;
|
|
464
529
|
setAccessToken(token: string): void;
|
|
530
|
+
setRefreshToken(token: string): void;
|
|
531
|
+
/**
|
|
532
|
+
* Restore tokens (for SSO/OAuth flows)
|
|
533
|
+
* Sets both access and refresh tokens
|
|
534
|
+
*/
|
|
535
|
+
restoreTokens(accessToken: string, refreshToken?: string): void;
|
|
465
536
|
isLocalMode(): boolean;
|
|
466
537
|
/**
|
|
467
538
|
* Check if current user has admin privileges
|
|
@@ -478,6 +549,10 @@ declare class TokenManager {
|
|
|
478
549
|
private static readonly ACCESS_TOKEN_KEY;
|
|
479
550
|
private static readonly REFRESH_TOKEN_KEY;
|
|
480
551
|
private static readonly USER_KEY;
|
|
552
|
+
/**
|
|
553
|
+
* Cross-platform base64 decode (works in both Node.js and browser)
|
|
554
|
+
*/
|
|
555
|
+
private static base64Decode;
|
|
481
556
|
private static getStorage;
|
|
482
557
|
static storeTokens(tokens: AuthResponse): void;
|
|
483
558
|
static getAccessToken(): string | null;
|
|
@@ -486,6 +561,24 @@ declare class TokenManager {
|
|
|
486
561
|
static clearTokens(): void;
|
|
487
562
|
static isTokenExpired(token: string): boolean;
|
|
488
563
|
static autoRefreshToken(sdk: SPAPSClient): Promise<boolean>;
|
|
564
|
+
/**
|
|
565
|
+
* Parse auth tokens from URL fragment (for OAuth callbacks)
|
|
566
|
+
* URL format: https://app.com/callback#access_token=xxx&refresh_token=xxx
|
|
567
|
+
*/
|
|
568
|
+
static parseTokensFromUrlFragment(url?: string): {
|
|
569
|
+
access_token?: string;
|
|
570
|
+
refresh_token?: string;
|
|
571
|
+
user_id?: string;
|
|
572
|
+
} | null;
|
|
573
|
+
/**
|
|
574
|
+
* Handle OAuth callback - parse tokens from URL and store them
|
|
575
|
+
* Returns user info decoded from JWT, or null if no tokens found
|
|
576
|
+
*/
|
|
577
|
+
static handleOAuthCallback(sdk: SPAPSClient): {
|
|
578
|
+
id: string;
|
|
579
|
+
email?: string;
|
|
580
|
+
role?: string;
|
|
581
|
+
} | null;
|
|
489
582
|
}
|
|
490
583
|
declare class WalletUtils {
|
|
491
584
|
static detectChainType(address: string): 'solana' | 'ethereum' | 'bitcoin' | null;
|
package/dist/index.js
CHANGED
|
@@ -470,12 +470,24 @@ var SPAPSClient = class {
|
|
|
470
470
|
return data;
|
|
471
471
|
},
|
|
472
472
|
/**
|
|
473
|
-
* Verify a magic link token
|
|
474
|
-
*
|
|
473
|
+
* Verify a magic link token and authenticate the user.
|
|
474
|
+
* Returns full auth response with tokens and auto-stores them.
|
|
475
475
|
*/
|
|
476
476
|
verifyMagicLink: async (payload) => {
|
|
477
|
-
const res = await this.client.post("/api/auth/verify-magic-link",
|
|
478
|
-
|
|
477
|
+
const res = await this.client.post("/api/auth/verify-magic-link", {
|
|
478
|
+
token: payload.token,
|
|
479
|
+
type: payload.type || "magiclink"
|
|
480
|
+
});
|
|
481
|
+
const body = res.data;
|
|
482
|
+
if (body?.success === false) throw new Error(body?.error?.message || "Magic link verification failed");
|
|
483
|
+
const data = {
|
|
484
|
+
access_token: body.tokens?.access_token || body.access_token,
|
|
485
|
+
refresh_token: body.tokens?.refresh_token || body.refresh_token,
|
|
486
|
+
user: body.user
|
|
487
|
+
};
|
|
488
|
+
this.accessToken = data.access_token;
|
|
489
|
+
this.refreshToken = data.refresh_token;
|
|
490
|
+
return data;
|
|
479
491
|
},
|
|
480
492
|
solana: {
|
|
481
493
|
linkWallet: async (payload) => {
|
|
@@ -791,6 +803,70 @@ var SPAPSClient = class {
|
|
|
791
803
|
return this.unwrapApiResponse(res, "Failed to touch session");
|
|
792
804
|
}
|
|
793
805
|
};
|
|
806
|
+
/**
|
|
807
|
+
* CFO (QuickBooks) namespace for managing multi-account connections
|
|
808
|
+
*/
|
|
809
|
+
cfo = {
|
|
810
|
+
/**
|
|
811
|
+
* Get all QuickBooks connections for the current user
|
|
812
|
+
*/
|
|
813
|
+
getConnections: async () => {
|
|
814
|
+
const res = await this.client.get("/api/cfo/user-connections", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
815
|
+
return this.unwrapApiResponse(res, "Failed to get connections");
|
|
816
|
+
},
|
|
817
|
+
/**
|
|
818
|
+
* Get connection status (returns primary connection for backward compatibility)
|
|
819
|
+
*/
|
|
820
|
+
getStatus: async () => {
|
|
821
|
+
const res = await this.client.get("/api/cfo/user-status", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
822
|
+
return this.unwrapApiResponse(res, "Failed to get status");
|
|
823
|
+
},
|
|
824
|
+
/**
|
|
825
|
+
* Start adding a new QuickBooks connection
|
|
826
|
+
* Returns auth URL to redirect user to QuickBooks OAuth
|
|
827
|
+
*/
|
|
828
|
+
addConnection: async () => {
|
|
829
|
+
const res = await this.client.post("/api/cfo/user-add-connection", {}, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
830
|
+
return this.unwrapApiResponse(res, "Failed to start connection");
|
|
831
|
+
},
|
|
832
|
+
/**
|
|
833
|
+
* Disconnect a specific QuickBooks account
|
|
834
|
+
*/
|
|
835
|
+
disconnect: async (connectionId) => {
|
|
836
|
+
const res = await this.client.delete(`/api/cfo/user-connections/${connectionId}`, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
837
|
+
return this.unwrapApiResponse(res, "Failed to disconnect");
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
/**
|
|
841
|
+
* DayRate (Dynamic Scheduling) namespace
|
|
842
|
+
* For booking half-day sessions with dynamic pricing
|
|
843
|
+
*/
|
|
844
|
+
dayrate = {
|
|
845
|
+
/**
|
|
846
|
+
* Get available slots with current pricing
|
|
847
|
+
* Public endpoint - no authentication required
|
|
848
|
+
*/
|
|
849
|
+
getAvailability: async () => {
|
|
850
|
+
const res = await this.client.get("/api/dayrate/availability");
|
|
851
|
+
return this.unwrapApiResponse(res, "Failed to get availability");
|
|
852
|
+
},
|
|
853
|
+
/**
|
|
854
|
+
* Create a single-slot booking and get Stripe checkout URL
|
|
855
|
+
* Returns a 10-minute reservation with checkout link
|
|
856
|
+
*/
|
|
857
|
+
createBooking: async (payload) => {
|
|
858
|
+
const res = await this.client.post("/api/dayrate/book", payload);
|
|
859
|
+
return this.unwrapApiResponse(res, "Failed to create booking");
|
|
860
|
+
},
|
|
861
|
+
/**
|
|
862
|
+
* Create a multi-slot booking and get Stripe checkout URL
|
|
863
|
+
* Reserves multiple slots with a single checkout session
|
|
864
|
+
*/
|
|
865
|
+
createMultiBooking: async (payload) => {
|
|
866
|
+
const res = await this.client.post("/api/dayrate/book-multi", payload);
|
|
867
|
+
return this.unwrapApiResponse(res, "Failed to create multi-booking");
|
|
868
|
+
}
|
|
869
|
+
};
|
|
794
870
|
// Stripe Methods
|
|
795
871
|
async createCheckoutSession(priceId, successUrl, cancelUrl) {
|
|
796
872
|
return this.client.post("/api/stripe/create-checkout-session", {
|
|
@@ -914,6 +990,19 @@ var SPAPSClient = class {
|
|
|
914
990
|
setAccessToken(token) {
|
|
915
991
|
this.accessToken = token;
|
|
916
992
|
}
|
|
993
|
+
setRefreshToken(token) {
|
|
994
|
+
this.refreshToken = token;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Restore tokens (for SSO/OAuth flows)
|
|
998
|
+
* Sets both access and refresh tokens
|
|
999
|
+
*/
|
|
1000
|
+
restoreTokens(accessToken, refreshToken) {
|
|
1001
|
+
this.accessToken = accessToken;
|
|
1002
|
+
if (refreshToken) {
|
|
1003
|
+
this.refreshToken = refreshToken;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
917
1006
|
isLocalMode() {
|
|
918
1007
|
return this._isLocalMode;
|
|
919
1008
|
}
|
|
@@ -968,6 +1057,17 @@ var TokenManager = class _TokenManager {
|
|
|
968
1057
|
static ACCESS_TOKEN_KEY = "sweet_potato_access_token";
|
|
969
1058
|
static REFRESH_TOKEN_KEY = "sweet_potato_refresh_token";
|
|
970
1059
|
static USER_KEY = "sweet_potato_user";
|
|
1060
|
+
/**
|
|
1061
|
+
* Cross-platform base64 decode (works in both Node.js and browser)
|
|
1062
|
+
*/
|
|
1063
|
+
static base64Decode(str) {
|
|
1064
|
+
if (typeof Buffer !== "undefined") {
|
|
1065
|
+
return Buffer.from(str, "base64").toString("utf8");
|
|
1066
|
+
} else if (typeof atob !== "undefined") {
|
|
1067
|
+
return atob(str);
|
|
1068
|
+
}
|
|
1069
|
+
throw new Error("No base64 decode method available");
|
|
1070
|
+
}
|
|
971
1071
|
static getStorage() {
|
|
972
1072
|
if (typeof globalThis !== "undefined" && globalThis.localStorage) return globalThis.localStorage;
|
|
973
1073
|
if (typeof globalThis !== "undefined" && globalThis.window?.localStorage) return globalThis.window.localStorage;
|
|
@@ -1008,7 +1108,7 @@ var TokenManager = class _TokenManager {
|
|
|
1008
1108
|
try {
|
|
1009
1109
|
const parts = token.split(".");
|
|
1010
1110
|
if (parts.length !== 3 || !parts[1]) return true;
|
|
1011
|
-
const payload = JSON.parse(
|
|
1111
|
+
const payload = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
1012
1112
|
const now = Math.floor(Date.now() / 1e3);
|
|
1013
1113
|
return payload.exp < now;
|
|
1014
1114
|
} catch {
|
|
@@ -1032,6 +1132,74 @@ var TokenManager = class _TokenManager {
|
|
|
1032
1132
|
return false;
|
|
1033
1133
|
}
|
|
1034
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Parse auth tokens from URL fragment (for OAuth callbacks)
|
|
1137
|
+
* URL format: https://app.com/callback#access_token=xxx&refresh_token=xxx
|
|
1138
|
+
*/
|
|
1139
|
+
static parseTokensFromUrlFragment(url) {
|
|
1140
|
+
let hash;
|
|
1141
|
+
if (url) {
|
|
1142
|
+
try {
|
|
1143
|
+
hash = new URL(url).hash;
|
|
1144
|
+
} catch {
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
} else if (typeof window !== "undefined") {
|
|
1148
|
+
hash = window.location.hash;
|
|
1149
|
+
} else {
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
if (!hash || hash.length < 2) return null;
|
|
1153
|
+
const params = new URLSearchParams(hash.substring(1));
|
|
1154
|
+
const access_token = params.get("access_token");
|
|
1155
|
+
const refresh_token = params.get("refresh_token");
|
|
1156
|
+
if (!access_token) return null;
|
|
1157
|
+
return {
|
|
1158
|
+
access_token,
|
|
1159
|
+
refresh_token: refresh_token || void 0,
|
|
1160
|
+
user_id: params.get("user_id") || void 0
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* Handle OAuth callback - parse tokens from URL and store them
|
|
1165
|
+
* Returns user info decoded from JWT, or null if no tokens found
|
|
1166
|
+
*/
|
|
1167
|
+
static handleOAuthCallback(sdk) {
|
|
1168
|
+
const tokens = _TokenManager.parseTokensFromUrlFragment();
|
|
1169
|
+
if (!tokens?.access_token) {
|
|
1170
|
+
console.debug("[SPAPS] handleOAuthCallback: No access token found in URL fragment");
|
|
1171
|
+
return null;
|
|
1172
|
+
}
|
|
1173
|
+
if (typeof window !== "undefined") {
|
|
1174
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
1175
|
+
}
|
|
1176
|
+
try {
|
|
1177
|
+
const parts = tokens.access_token.split(".");
|
|
1178
|
+
if (parts.length !== 3 || !parts[1]) {
|
|
1179
|
+
console.warn("[SPAPS] handleOAuthCallback: Invalid JWT format - expected 3 parts");
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
const payload = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
1183
|
+
const user = {
|
|
1184
|
+
id: payload.user_id || payload.sub,
|
|
1185
|
+
email: payload.email,
|
|
1186
|
+
role: payload.role || "user"
|
|
1187
|
+
};
|
|
1188
|
+
_TokenManager.storeTokens({
|
|
1189
|
+
access_token: tokens.access_token,
|
|
1190
|
+
refresh_token: tokens.refresh_token || "",
|
|
1191
|
+
user
|
|
1192
|
+
});
|
|
1193
|
+
sdk.setAccessToken(tokens.access_token);
|
|
1194
|
+
if (tokens.refresh_token) {
|
|
1195
|
+
sdk.setRefreshToken(tokens.refresh_token);
|
|
1196
|
+
}
|
|
1197
|
+
return user;
|
|
1198
|
+
} catch (err) {
|
|
1199
|
+
console.warn("[SPAPS] handleOAuthCallback: Failed to decode JWT", err);
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1035
1203
|
};
|
|
1036
1204
|
var WalletUtils = class _WalletUtils {
|
|
1037
1205
|
static detectChainType(address) {
|
package/dist/index.mjs
CHANGED
|
@@ -443,12 +443,24 @@ var SPAPSClient = class {
|
|
|
443
443
|
return data;
|
|
444
444
|
},
|
|
445
445
|
/**
|
|
446
|
-
* Verify a magic link token
|
|
447
|
-
*
|
|
446
|
+
* Verify a magic link token and authenticate the user.
|
|
447
|
+
* Returns full auth response with tokens and auto-stores them.
|
|
448
448
|
*/
|
|
449
449
|
verifyMagicLink: async (payload) => {
|
|
450
|
-
const res = await this.client.post("/api/auth/verify-magic-link",
|
|
451
|
-
|
|
450
|
+
const res = await this.client.post("/api/auth/verify-magic-link", {
|
|
451
|
+
token: payload.token,
|
|
452
|
+
type: payload.type || "magiclink"
|
|
453
|
+
});
|
|
454
|
+
const body = res.data;
|
|
455
|
+
if (body?.success === false) throw new Error(body?.error?.message || "Magic link verification failed");
|
|
456
|
+
const data = {
|
|
457
|
+
access_token: body.tokens?.access_token || body.access_token,
|
|
458
|
+
refresh_token: body.tokens?.refresh_token || body.refresh_token,
|
|
459
|
+
user: body.user
|
|
460
|
+
};
|
|
461
|
+
this.accessToken = data.access_token;
|
|
462
|
+
this.refreshToken = data.refresh_token;
|
|
463
|
+
return data;
|
|
452
464
|
},
|
|
453
465
|
solana: {
|
|
454
466
|
linkWallet: async (payload) => {
|
|
@@ -764,6 +776,70 @@ var SPAPSClient = class {
|
|
|
764
776
|
return this.unwrapApiResponse(res, "Failed to touch session");
|
|
765
777
|
}
|
|
766
778
|
};
|
|
779
|
+
/**
|
|
780
|
+
* CFO (QuickBooks) namespace for managing multi-account connections
|
|
781
|
+
*/
|
|
782
|
+
cfo = {
|
|
783
|
+
/**
|
|
784
|
+
* Get all QuickBooks connections for the current user
|
|
785
|
+
*/
|
|
786
|
+
getConnections: async () => {
|
|
787
|
+
const res = await this.client.get("/api/cfo/user-connections", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
788
|
+
return this.unwrapApiResponse(res, "Failed to get connections");
|
|
789
|
+
},
|
|
790
|
+
/**
|
|
791
|
+
* Get connection status (returns primary connection for backward compatibility)
|
|
792
|
+
*/
|
|
793
|
+
getStatus: async () => {
|
|
794
|
+
const res = await this.client.get("/api/cfo/user-status", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
795
|
+
return this.unwrapApiResponse(res, "Failed to get status");
|
|
796
|
+
},
|
|
797
|
+
/**
|
|
798
|
+
* Start adding a new QuickBooks connection
|
|
799
|
+
* Returns auth URL to redirect user to QuickBooks OAuth
|
|
800
|
+
*/
|
|
801
|
+
addConnection: async () => {
|
|
802
|
+
const res = await this.client.post("/api/cfo/user-add-connection", {}, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
803
|
+
return this.unwrapApiResponse(res, "Failed to start connection");
|
|
804
|
+
},
|
|
805
|
+
/**
|
|
806
|
+
* Disconnect a specific QuickBooks account
|
|
807
|
+
*/
|
|
808
|
+
disconnect: async (connectionId) => {
|
|
809
|
+
const res = await this.client.delete(`/api/cfo/user-connections/${connectionId}`, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
|
|
810
|
+
return this.unwrapApiResponse(res, "Failed to disconnect");
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
/**
|
|
814
|
+
* DayRate (Dynamic Scheduling) namespace
|
|
815
|
+
* For booking half-day sessions with dynamic pricing
|
|
816
|
+
*/
|
|
817
|
+
dayrate = {
|
|
818
|
+
/**
|
|
819
|
+
* Get available slots with current pricing
|
|
820
|
+
* Public endpoint - no authentication required
|
|
821
|
+
*/
|
|
822
|
+
getAvailability: async () => {
|
|
823
|
+
const res = await this.client.get("/api/dayrate/availability");
|
|
824
|
+
return this.unwrapApiResponse(res, "Failed to get availability");
|
|
825
|
+
},
|
|
826
|
+
/**
|
|
827
|
+
* Create a single-slot booking and get Stripe checkout URL
|
|
828
|
+
* Returns a 10-minute reservation with checkout link
|
|
829
|
+
*/
|
|
830
|
+
createBooking: async (payload) => {
|
|
831
|
+
const res = await this.client.post("/api/dayrate/book", payload);
|
|
832
|
+
return this.unwrapApiResponse(res, "Failed to create booking");
|
|
833
|
+
},
|
|
834
|
+
/**
|
|
835
|
+
* Create a multi-slot booking and get Stripe checkout URL
|
|
836
|
+
* Reserves multiple slots with a single checkout session
|
|
837
|
+
*/
|
|
838
|
+
createMultiBooking: async (payload) => {
|
|
839
|
+
const res = await this.client.post("/api/dayrate/book-multi", payload);
|
|
840
|
+
return this.unwrapApiResponse(res, "Failed to create multi-booking");
|
|
841
|
+
}
|
|
842
|
+
};
|
|
767
843
|
// Stripe Methods
|
|
768
844
|
async createCheckoutSession(priceId, successUrl, cancelUrl) {
|
|
769
845
|
return this.client.post("/api/stripe/create-checkout-session", {
|
|
@@ -887,6 +963,19 @@ var SPAPSClient = class {
|
|
|
887
963
|
setAccessToken(token) {
|
|
888
964
|
this.accessToken = token;
|
|
889
965
|
}
|
|
966
|
+
setRefreshToken(token) {
|
|
967
|
+
this.refreshToken = token;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Restore tokens (for SSO/OAuth flows)
|
|
971
|
+
* Sets both access and refresh tokens
|
|
972
|
+
*/
|
|
973
|
+
restoreTokens(accessToken, refreshToken) {
|
|
974
|
+
this.accessToken = accessToken;
|
|
975
|
+
if (refreshToken) {
|
|
976
|
+
this.refreshToken = refreshToken;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
890
979
|
isLocalMode() {
|
|
891
980
|
return this._isLocalMode;
|
|
892
981
|
}
|
|
@@ -941,6 +1030,17 @@ var TokenManager = class _TokenManager {
|
|
|
941
1030
|
static ACCESS_TOKEN_KEY = "sweet_potato_access_token";
|
|
942
1031
|
static REFRESH_TOKEN_KEY = "sweet_potato_refresh_token";
|
|
943
1032
|
static USER_KEY = "sweet_potato_user";
|
|
1033
|
+
/**
|
|
1034
|
+
* Cross-platform base64 decode (works in both Node.js and browser)
|
|
1035
|
+
*/
|
|
1036
|
+
static base64Decode(str) {
|
|
1037
|
+
if (typeof Buffer !== "undefined") {
|
|
1038
|
+
return Buffer.from(str, "base64").toString("utf8");
|
|
1039
|
+
} else if (typeof atob !== "undefined") {
|
|
1040
|
+
return atob(str);
|
|
1041
|
+
}
|
|
1042
|
+
throw new Error("No base64 decode method available");
|
|
1043
|
+
}
|
|
944
1044
|
static getStorage() {
|
|
945
1045
|
if (typeof globalThis !== "undefined" && globalThis.localStorage) return globalThis.localStorage;
|
|
946
1046
|
if (typeof globalThis !== "undefined" && globalThis.window?.localStorage) return globalThis.window.localStorage;
|
|
@@ -981,7 +1081,7 @@ var TokenManager = class _TokenManager {
|
|
|
981
1081
|
try {
|
|
982
1082
|
const parts = token.split(".");
|
|
983
1083
|
if (parts.length !== 3 || !parts[1]) return true;
|
|
984
|
-
const payload = JSON.parse(
|
|
1084
|
+
const payload = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
985
1085
|
const now = Math.floor(Date.now() / 1e3);
|
|
986
1086
|
return payload.exp < now;
|
|
987
1087
|
} catch {
|
|
@@ -1005,6 +1105,74 @@ var TokenManager = class _TokenManager {
|
|
|
1005
1105
|
return false;
|
|
1006
1106
|
}
|
|
1007
1107
|
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Parse auth tokens from URL fragment (for OAuth callbacks)
|
|
1110
|
+
* URL format: https://app.com/callback#access_token=xxx&refresh_token=xxx
|
|
1111
|
+
*/
|
|
1112
|
+
static parseTokensFromUrlFragment(url) {
|
|
1113
|
+
let hash;
|
|
1114
|
+
if (url) {
|
|
1115
|
+
try {
|
|
1116
|
+
hash = new URL(url).hash;
|
|
1117
|
+
} catch {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
} else if (typeof window !== "undefined") {
|
|
1121
|
+
hash = window.location.hash;
|
|
1122
|
+
} else {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
if (!hash || hash.length < 2) return null;
|
|
1126
|
+
const params = new URLSearchParams(hash.substring(1));
|
|
1127
|
+
const access_token = params.get("access_token");
|
|
1128
|
+
const refresh_token = params.get("refresh_token");
|
|
1129
|
+
if (!access_token) return null;
|
|
1130
|
+
return {
|
|
1131
|
+
access_token,
|
|
1132
|
+
refresh_token: refresh_token || void 0,
|
|
1133
|
+
user_id: params.get("user_id") || void 0
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Handle OAuth callback - parse tokens from URL and store them
|
|
1138
|
+
* Returns user info decoded from JWT, or null if no tokens found
|
|
1139
|
+
*/
|
|
1140
|
+
static handleOAuthCallback(sdk) {
|
|
1141
|
+
const tokens = _TokenManager.parseTokensFromUrlFragment();
|
|
1142
|
+
if (!tokens?.access_token) {
|
|
1143
|
+
console.debug("[SPAPS] handleOAuthCallback: No access token found in URL fragment");
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
if (typeof window !== "undefined") {
|
|
1147
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
const parts = tokens.access_token.split(".");
|
|
1151
|
+
if (parts.length !== 3 || !parts[1]) {
|
|
1152
|
+
console.warn("[SPAPS] handleOAuthCallback: Invalid JWT format - expected 3 parts");
|
|
1153
|
+
return null;
|
|
1154
|
+
}
|
|
1155
|
+
const payload = JSON.parse(_TokenManager.base64Decode(parts[1]));
|
|
1156
|
+
const user = {
|
|
1157
|
+
id: payload.user_id || payload.sub,
|
|
1158
|
+
email: payload.email,
|
|
1159
|
+
role: payload.role || "user"
|
|
1160
|
+
};
|
|
1161
|
+
_TokenManager.storeTokens({
|
|
1162
|
+
access_token: tokens.access_token,
|
|
1163
|
+
refresh_token: tokens.refresh_token || "",
|
|
1164
|
+
user
|
|
1165
|
+
});
|
|
1166
|
+
sdk.setAccessToken(tokens.access_token);
|
|
1167
|
+
if (tokens.refresh_token) {
|
|
1168
|
+
sdk.setRefreshToken(tokens.refresh_token);
|
|
1169
|
+
}
|
|
1170
|
+
return user;
|
|
1171
|
+
} catch (err) {
|
|
1172
|
+
console.warn("[SPAPS] handleOAuthCallback: Failed to decode JWT", err);
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1008
1176
|
};
|
|
1009
1177
|
var WalletUtils = class _WalletUtils {
|
|
1010
1178
|
static detectChainType(address) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps-sdk",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking, role-based access control, and dayrate scheduling",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"sdk",
|
|
32
32
|
"client",
|
|
33
33
|
"sweet-potato",
|
|
34
|
-
"wallet-auth"
|
|
34
|
+
"wallet-auth",
|
|
35
|
+
"dayrate",
|
|
36
|
+
"scheduling"
|
|
35
37
|
],
|
|
36
38
|
"author": "buildooor",
|
|
37
39
|
"license": "UNLICENSED",
|
|
@@ -44,7 +46,7 @@
|
|
|
44
46
|
"email": "buildooor@gmail.com"
|
|
45
47
|
},
|
|
46
48
|
"dependencies": {
|
|
47
|
-
"spaps-types": "^1.0.
|
|
49
|
+
"spaps-types": "^1.0.59",
|
|
48
50
|
"axios": "^1.6.0",
|
|
49
51
|
"cross-fetch": "^4.0.0"
|
|
50
52
|
},
|