spaps-sdk
Version:
Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking and role-based access control
451 lines (445 loc) • 17.2 kB
TypeScript
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, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
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';
/**
* 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;
interface SPAPSConfig {
apiUrl?: string;
apiKey?: string;
autoDetect?: boolean;
timeout?: number;
}
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. Does not set access/refresh tokens.
* Consumers should redirect or prompt login based on the returned status.
*/
verifyMagicLink: (payload: {
token: string;
}) => Promise<{
success: boolean;
}>;
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: any) => Promise<CheckoutSession>;
createPaymentCheckout: (params: {
price_id: string;
quantity?: number;
success_url: string;
cancel_url: string;
}) => Promise<CheckoutSession>;
createSubscriptionCheckout: (params: {
price_id: string;
success_url: string;
cancel_url: string;
trial_period_days?: number;
}) => 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>;
};
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;
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;
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>;
}
declare class WalletUtils {
static detectChainType(address: string): 'solana' | 'ethereum' | 'bitcoin' | null;
static isValidAddress(address: string, chainType?: 'solana' | 'ethereum' | 'bitcoin' | 'base'): boolean;
}
export { type AdminConfig, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createPermissionChecker, SPAPSClient as default, defaultPermissionChecker, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, verifyCryptoWebhookSignature };