UNPKG

spaps-sdk

Version:

Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking, role-based access control, and dayrate scheduling

623 lines (617 loc) 23.5 kB
import * as spaps_types from 'spaps-types'; 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'; 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'; /** * Permission checking utilities for SPAPS SDK * Client-side role and permission management */ interface User { id: string; email?: string; wallet_address?: string; role?: string; permissions?: string[]; tier?: string; } interface AdminConfig { email: string; wallets: { ethereum: string; solana: string; }; } interface PermissionCheckResult { allowed: boolean; reason?: string; userRole: string; requiredRole?: string; } declare const DEFAULT_ADMIN_ACCOUNTS: AdminConfig; /** * Check if an identifier (email/wallet) is an admin account */ declare function isAdminAccount(identifier: string, customAdmins?: (string | AdminConfig)[]): boolean; /** * Get user role based on identifier */ declare function getUserRole(identifier?: string, customAdmins?: (string | AdminConfig)[]): string; /** * Check if user has specific permissions */ declare function hasPermission(user: User | null, requiredPermissions: string | string[], customAdmins?: (string | AdminConfig)[]): boolean; /** * Check if user can access admin features */ declare function canAccessAdmin(user: User | null, customAdmins?: (string | AdminConfig)[]): PermissionCheckResult; /** * Get role-aware error message */ declare function getRoleAwareErrorMessage(requiredRole: string, userRole: string, action?: string): string; /** * Get user display information with role indicators */ declare function getUserDisplay(user: User | null, customAdmins?: (string | AdminConfig)[]): { displayName: string; role: string; badge: string | null; isAdmin: boolean; }; /** * Permission checker class for easier usage */ declare class PermissionChecker { private customAdmins; constructor(customAdmins?: (string | AdminConfig)[]); isAdmin(identifier: string): boolean; getRole(identifier?: string): string; hasPermission(user: User | null, permissions: string | string[]): boolean; canAccessAdmin(user: User | null): PermissionCheckResult; getErrorMessage(requiredRole: string, userRole: string, action?: string): string; getUserDisplay(user: User | null): { displayName: string; role: string; badge: string | null; isAdmin: boolean; }; requiresAuth(user: User | null): boolean; requiresAdmin(user: User | null): boolean; addCustomAdmin(admin: string | AdminConfig): void; removeCustomAdmin(admin: string | AdminConfig): void; } /** * Create a permission checker instance */ declare function createPermissionChecker(customAdmins?: (string | AdminConfig)[]): PermissionChecker; declare const defaultPermissionChecker: PermissionChecker; type ApiKeyType = 'publishable' | 'secret'; interface SPAPSConfig { apiUrl?: string; /** @deprecated Use publishableKey or secretKey instead */ apiKey?: string; /** Browser-safe key for client-side usage (spaps_pub_xxx) */ publishableKey?: string; /** Server-only key for full access (spaps_sec_xxx) */ secretKey?: string; autoDetect?: boolean; timeout?: number; } interface CheckoutLineItemPriceData { currency: string; unit_amount: number; product_data: { name: string; description?: string; metadata?: Record<string, string>; }; } interface CheckoutLineItem { price_id?: string; product_id?: string; quantity: number; price_data?: CheckoutLineItemPriceData; } interface CreateCheckoutSessionPayload { mode: 'payment' | 'subscription'; line_items: CheckoutLineItem[]; success_url: string; cancel_url: string; metadata?: Record<string, string>; customer_email?: string; client_reference_id?: string; payment_intent_data?: { metadata?: Record<string, string>; }; subscription_data?: { metadata?: Record<string, string>; trial_period_days?: number; }; allow_promotion_codes?: boolean; locale?: string; require_legal_consent?: boolean; legal_consent_text?: string; } declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Record<string, any>> { private client; private apiKey?; private accessToken?; private refreshToken?; private _isLocalMode; private unwrapApiResponse; private isAxiosResponse; private isResponseLikeWithData; private isApiResponse; admin: { createProduct: (productData: CreateProductRequest) => Promise<{ data: Product; }>; updateProduct: (productId: string, updates: UpdateProductRequest) => Promise<{ data: Product; }>; deleteProduct: (productId: string) => Promise<{ data: { id: string; active: boolean; archived: boolean; }; }>; createPrice: (priceData: CreatePriceRequest) => Promise<{ data: Price; }>; syncProducts: () => Promise<{ data: ProductSyncResult; }>; getProducts: () => Promise<{ data: { products: Product[]; total: number; adminMetadata?: any; }; }>; triggerCryptoReconcile: (opts?: CryptoReconcileRequest) => Promise<{ job_id: string; scheduled_at: string; cursor?: Record<string, unknown>; }>; }; secureMessages: { create: (payload: CreateSecureMessageRequest<SecureMessageMetadata>) => Promise<SecureMessage<SecureMessageMetadata>>; list: () => Promise<SecureMessage<SecureMessageMetadata>[]>; }; constructor(config?: SPAPSConfig); /** Raw API request helper that returns an ApiResponse-like shape */ request<T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH', url: string, data?: any, requiresAuth?: boolean): Promise<{ success: boolean; data?: T; error?: { code: string; message: string; details?: any; }; }>; /** Health check helper that returns boolean */ healthCheck(): Promise<boolean>; login(email: string, password: string): Promise<{ data: AuthResponse; }>; register(email: string, password: string): Promise<{ data: AuthResponse; }>; walletSignIn(walletAddress: string, signature: string, message: string, chainType?: 'solana' | 'ethereum'): Promise<{ data: AuthResponse; }>; refresh(refreshToken?: string): Promise<{ data: AuthResponse; }>; logout(): Promise<void>; getUser(): Promise<{ data: User$1; }>; auth: { /** * Verify magic link token without mutating token state. * Returns a simple success object from the API. */ getNonce: (walletAddress: string) => Promise<any>; signInWithWallet: (req: { wallet_address: string; signature: string; message: string; chain_type?: "solana" | "ethereum" | "bitcoin" | "base"; username?: string; }) => Promise<any>; authenticateWallet: (walletAddress: string, signFn: (message: string) => Promise<string> | string, chainType?: "solana" | "ethereum" | "bitcoin" | "base", username?: string) => Promise<any>; signInWithPassword: (payload: { email: string; password: string; }) => Promise<any>; requestMagicLink: (payload: { email: string; redirect_url?: string; }) => Promise<void>; requestPasswordReset: (payload: { email: string; redirect_url?: string; }) => Promise<void>; confirmPasswordReset: (payload: { token: string; new_password: string; }) => Promise<void>; register: (payload: { email: string; password: string; }) => Promise<any>; /** * Verify a magic link token and authenticate the user. * Returns full auth response with tokens and auto-stores them. */ verifyMagicLink: (payload: { token: string; type?: string; }) => Promise<AuthResponse>; solana: { linkWallet: (payload: { wallet_address: string; signature: string; message: string; }) => Promise<any>; verifySignature: (payload: { wallet_address: string; signature: string; message: string; }) => Promise<any>; generateMessage: (wallet_address: string) => Promise<any>; getWallets: () => Promise<any>; networkInfo: () => Promise<any>; }; ethereum: { linkWallet: (payload: { wallet_address: string; signature: string; message: string; }) => Promise<any>; verifySignature: (payload: { wallet_address: string; signature: string; message: string; }) => Promise<any>; verifyTypedData: (payload: { wallet_address: string; signature: string; typed_data: any; }) => Promise<any>; generateMessage: (wallet_address: string) => Promise<any>; generateTypedData: (wallet_address: string) => Promise<any>; getWallets: () => Promise<any>; networkInfo: () => Promise<any>; balance: (wallet_address: string) => Promise<any>; contractCheck: (wallet_address: string, contract_address: string) => Promise<any>; }; refreshToken: (refreshToken: string) => Promise<any>; /** * Logout and clear tokens. Network errors are intentionally swallowed * to avoid trapping users in a bad state during sign‑out flows. */ logout: () => Promise<void>; getCurrentUser: () => Promise<User$1>; isAuthenticated: () => boolean; }; payments: { crypto: { createInvoice: (payload: CreateCryptoInvoiceRequest) => Promise<spaps_types.CryptoInvoice>; getInvoice: (invoiceId: string) => Promise<spaps_types.CryptoInvoice>; getInvoiceStatus: (invoiceId: string) => Promise<CryptoInvoiceStatusSnapshot>; reconcile: (options?: CryptoReconcileRequest) => Promise<{ job_id: string; scheduled_at: string; cursor?: Record<string, unknown>; }>; }; createCheckoutSession: (payload: CreateCheckoutSessionPayload) => Promise<CheckoutSession>; createPaymentCheckout: (params: { price_id: string; quantity?: number; success_url: string; cancel_url: string; require_legal_consent?: boolean; legal_consent_text?: string; }) => Promise<CheckoutSession>; createSubscriptionCheckout: (params: { price_id: string; success_url: string; cancel_url: string; trial_period_days?: number; require_legal_consent?: boolean; legal_consent_text?: string; }) => Promise<CheckoutSession>; getCheckoutSession: (sessionId: string) => Promise<CheckoutSession>; listCheckoutSessions: (query?: { limit?: number; starting_after?: string; }) => Promise<{ sessions: any[]; has_more: boolean; next_cursor?: string; }>; expireCheckoutSession: (sessionId: string) => Promise<{ id: string; status: string; expired: boolean; }>; listProducts: (query?: { category?: string; active?: boolean; limit?: number; starting_after?: string; }) => Promise<{ products: Product[]; total: number; adminMetadata?: any; }>; getProduct: (productId: string) => Promise<Product>; createCustomerPortalSession: (payload: { return_url: string; }) => Promise<{ id: string; url: string; }>; createGuestCheckoutSession: (payload: any) => Promise<any>; getGuestCheckoutSession: (sessionId: string) => Promise<any>; listGuestCheckoutSessions: (query?: { limit?: number; starting_after?: string; }) => Promise<any>; convertGuestCheckoutSession: (payload: { session_id: string; }) => Promise<any>; convertGuestCheckout: (payload: { session_id: string; }) => Promise<any>; listAllProductsSuperAdmin: () => Promise<any>; updateProductSuperAdmin: (productId: string, updates: any) => Promise<any>; deleteProductSuperAdmin: (productId: string) => Promise<any>; createProductWithPrice: (payload: any) => Promise<any>; createProductWithPriceSuperAdmin: (productId: string, payload: any) => Promise<any>; setDefaultPrice: (productId: string, payload: { price_id: string; }) => Promise<any>; setDefaultPriceSuperAdmin: (productId: string, payload: { price_id: string; }) => Promise<any>; createDefaultNewPrice: (productId: string, payload: any) => Promise<any>; superAdminListAllProducts: () => Promise<any>; superAdminUpdateProduct: (productId: string, updates: any) => Promise<any>; superAdminArchiveProduct: (productId: string) => Promise<any>; superAdminCreateProductWithPrice: (applicationId: string, payload: any) => Promise<any>; superAdminCreatePriceAndSetDefault: (productId: string, payload: any) => Promise<any>; superAdminSetDefaultPrice: (productId: string, payload: { price_id: string; }) => Promise<any>; }; sessions: { getCurrent: () => Promise<any>; list: (params?: { limit?: number; starting_after?: string; }) => Promise<any>; validate: () => Promise<any>; revoke: (sessionId: string) => Promise<any>; revokeAll: () => Promise<any>; touch: () => Promise<any>; }; /** * CFO (QuickBooks) namespace for managing multi-account connections */ cfo: { /** * Get all QuickBooks connections for the current user */ getConnections: () => Promise<{ connections: Array<{ id: string; status: string; companyName?: string; realmId?: string; connectedAt?: string; }>; }>; /** * Get connection status (returns primary connection for backward compatibility) */ getStatus: () => Promise<{ connection: { id: string; status: string; companyName?: string; realmId?: string; } | null; totalConnections: number; }>; /** * Start adding a new QuickBooks connection * Returns auth URL to redirect user to QuickBooks OAuth */ addConnection: () => Promise<{ authUrl: string; connectionId: string; existingConnections: number; }>; /** * Disconnect a specific QuickBooks account */ disconnect: (connectionId: string) => Promise<{ success: boolean; message: string; }>; }; /** * DayRate (Dynamic Scheduling) namespace * For booking half-day sessions with dynamic pricing */ dayrate: { /** * Get available slots with current pricing * Public endpoint - no authentication required */ getAvailability: () => Promise<DayrateAvailabilityResponse>; /** * Create a single-slot booking and get Stripe checkout URL * Returns a 10-minute reservation with checkout link */ createBooking: (payload: DayrateBookingRequest) => Promise<DayrateBookingResponse>; /** * Create a multi-slot booking and get Stripe checkout URL * Reserves multiple slots with a single checkout session */ createMultiBooking: (payload: DayrateMultiBookingRequest) => Promise<DayrateMultiBookingResponse>; }; createCheckoutSession(priceId: string, successUrl: string, cancelUrl?: string): Promise<{ data: CheckoutSession; }>; getSubscription(): Promise<{ data: Subscription; }>; cancelSubscription(): Promise<void>; getUsageBalance(): Promise<{ data: UsageBalance; }>; recordUsage(feature: string, amount: number): Promise<void>; private createSecureMessage; private listSecureMessages; /** * Create a new Stripe product (Admin required) */ createProduct(productData: CreateProductRequest): Promise<{ data: Product; }>; /** * Update an existing Stripe product (Admin required) */ updateProduct(productId: string, updates: UpdateProductRequest): Promise<{ data: Product; }>; /** * Archive (soft delete) a Stripe product (Admin required) */ deleteProduct(productId: string): Promise<{ data: { id: string; active: boolean; archived: boolean; }; }>; /** * Create a new price for a product (Admin required) */ createPrice(priceData: CreatePriceRequest): Promise<{ data: Price; }>; /** * Sync all products from Stripe to local database (Super Admin required) */ syncProducts(): Promise<{ data: ProductSyncResult; }>; /** * Get products with admin metadata (if user is admin) */ getProducts(): Promise<{ data: { products: Product[]; total: number; adminMetadata?: any; }; }>; isAuthenticated(): boolean; getAccessToken(): string | undefined; setAccessToken(token: string): void; setRefreshToken(token: string): void; /** * Restore tokens (for SSO/OAuth flows) * Sets both access and refresh tokens */ restoreTokens(accessToken: string, refreshToken?: string): void; isLocalMode(): boolean; /** * Check if current user has admin privileges * Note: This requires the user object from authentication */ isAdmin(user?: User$1): boolean; health(): Promise<{ data: any; }>; } declare function verifyCryptoWebhookSignature(options: VerifyCryptoWebhookSignatureOptions): boolean; declare class TokenManager { private static readonly ACCESS_TOKEN_KEY; private static readonly REFRESH_TOKEN_KEY; private static readonly USER_KEY; /** * Cross-platform base64 decode (works in both Node.js and browser) */ private static base64Decode; private static getStorage; static storeTokens(tokens: AuthResponse): void; static getAccessToken(): string | null; static getRefreshToken(): string | null; static getStoredUser(): User$1 | null; static clearTokens(): void; static isTokenExpired(token: string): boolean; static autoRefreshToken(sdk: SPAPSClient): Promise<boolean>; /** * Parse auth tokens from URL fragment (for OAuth callbacks) * URL format: https://app.com/callback#access_token=xxx&refresh_token=xxx */ static parseTokensFromUrlFragment(url?: string): { access_token?: string; refresh_token?: string; user_id?: string; } | null; /** * Handle OAuth callback - parse tokens from URL and store them * Returns user info decoded from JWT, or null if no tokens found */ static handleOAuthCallback(sdk: SPAPSClient): { id: string; email?: string; role?: string; } | null; } declare class WalletUtils { static detectChainType(address: string): 'solana' | 'ethereum' | 'bitcoin' | null; static isValidAddress(address: string, chainType?: 'solana' | 'ethereum' | 'bitcoin' | 'base'): boolean; } /** * Create a SPAPS client for browser/client-side usage * Uses publishable key which is safe to expose in client bundles * * @example * ```typescript * // In your frontend code * const spaps = createBrowserClient('spaps_pub_xxx'); * * // Use for authentication and checkout * const { user } = await spaps.auth.signInWithPassword({ email, password }); * const checkout = await spaps.payments.createCheckoutSession({...}); * ``` */ declare function createBrowserClient(publishableKey: string, options?: Omit<SPAPSConfig, 'publishableKey' | 'secretKey' | 'apiKey'>): SPAPSClient; /** * Create a SPAPS client for server-side usage * Uses secret key which provides full access to all endpoints * * @example * ```typescript * // In your API routes (Next.js, Express, etc.) * const spaps = createServerClient('spaps_sec_xxx'); * * // Full access to admin operations * await spaps.admin.createProduct({...}); * await spaps.payments.crypto.reconcile(); * ``` */ declare function createServerClient(secretKey: string, options?: Omit<SPAPSConfig, 'publishableKey' | 'secretKey' | 'apiKey'>): SPAPSClient; /** * Detect key type from key prefix */ declare function detectKeyType(key: string): ApiKeyType | null; export { type AdminConfig, type ApiKeyType, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, verifyCryptoWebhookSignature };