@explorins/pers-sdk
v1.6.47
Published
Platform-agnostic SDK for PERS (Phygital Experience Rewards System) - Core business logic and API integration
Readme
@explorins/pers-sdk
Platform-agnostic TypeScript SDK for PERS (Phygital Experience Rewards System) - Modern Manager-Service architecture providing clean, consistent APIs for tourism loyalty applications.
Table of Contents
- Overview
- Dependencies
- Installation
- Quick Start
- Platform Integration
- Architecture
- Core Features
- Configuration
- Advanced Authentication
- Error Handling
- Bundle Size
- TypeScript Types
- Documentation
- Related Packages
- License
Overview
PERS (Phygital Experience Rewards System) is a comprehensive tourism loyalty platform bridging physical and digital experiences. It provides:
- Loyalty Programs: Token-based rewards, credit systems, and user tiers
- Campaign Management: Marketing campaigns with automated rewards distribution
- Business Network: Multi-tenant business discovery and partnerships
- Blockchain Integration: EVM-compatible chains (Ethereum, Polygon, Camino)
- Redemption Engine: Reward fulfillment with optional blockchain signing
- Analytics: Real-time transaction and user engagement metrics
Dependencies
Required Peer Dependencies
These packages MUST be installed alongside @explorins/pers-sdk:
npm install @explorins/pers-shared ethers@^6.15.0| Package | Version | Purpose |
|---------|---------|---------|
| @explorins/pers-shared | * | Shared types, interfaces, and DTOs used across all SDK domains |
| ethers | ^6.15.0 | Blockchain/Web3 operations (required for sdk.web3 manager) |
Optional Dependencies (Platform-Specific)
| Package | Required For | Notes |
|---------|--------------|-------|
| rxjs | AngularHttpClientAdapter only | Loaded dynamically at runtime; NOT bundled into SDK |
AI Agent Note: The SDK has ZERO runtime RxJS dependency unless you specifically use
AngularHttpClientAdapter. Browser, Node.js, and React Native integrations do NOT require RxJS.
Direct Dependencies
| Package | Purpose |
|---------|---------|
| @explorins/web3-ts | Internal Web3 chain utilities |
Installation
# Core SDK
npm install @explorins/pers-sdk
# Required peer dependencies
npm install @explorins/pers-shared ethers@^6.15.0
# Optional: For Angular applications only
npm install rxjsQuick Start
Minimum Viable Example
import { PersSDK } from '@explorins/pers-sdk';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
// 1. Initialize SDK (required: adapter + project key)
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
apiProjectKey: 'your-project-key' // Get from PERS dashboard
});
// 2. Authenticate with external JWT (Firebase, Auth0, Cognito, etc.)
const externalJWT = await yourAuthProvider.getIdToken(); // Your JWT
const authResult = await sdk.auth.loginWithToken(externalJWT, 'user');
console.log('Authenticated user:', authResult.user.id);
// 3. Use SDK managers
const campaigns = await sdk.campaigns.getActiveCampaigns();
const tokens = await sdk.tokens.getTokens();Complete Example
import { PersSDK } from '@explorins/pers-sdk';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
// Initialize SDK with browser fetch adapter
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
environment: 'production',
apiProjectKey: 'your-project-key'
});
// Authentication - login with external JWT (Firebase, Auth0, etc.)
const authResult = await sdk.auth.loginWithToken(firebaseJWT, 'user');
const user = await sdk.auth.getCurrentUser();
// Business operations
const businesses = await sdk.businesses.getActiveBusinesses();
const business = await sdk.businesses.getBusinessById('business-123');
// Campaign management
const campaigns = await sdk.campaigns.getActiveCampaigns();
const claim = await sdk.campaigns.claimCampaign({
campaignId: 'campaign-123',
businessId: 'business-456'
});
// Token operations
const tokens = await sdk.tokens.getTokens();
const creditToken = await sdk.tokens.getActiveCreditToken();
const rewardTokens = await sdk.tokens.getRewardTokens();
// User claims history
const userClaims = await sdk.campaigns.getUserClaims();Platform Integration
Browser / React / Vue
No additional dependencies required.
import { PersSDK } from '@explorins/pers-sdk';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
environment: 'production',
apiProjectKey: 'your-project-key'
});
// Ready to use
const campaigns = await sdk.campaigns.getActiveCampaigns();Angular
Requires rxjs peer dependency.
npm install rxjsimport { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { createPersSDK, PersSDK } from '@explorins/pers-sdk';
import { AngularHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
import { IndexedDBTokenStorage } from '@explorins/pers-sdk/core';
@Injectable({ providedIn: 'root' })
export class PersSDKService {
private readonly sdk: PersSDK;
constructor() {
const httpClient = inject(HttpClient);
// Use createPersSDK factory or new PersSDK()
this.sdk = createPersSDK(
new AngularHttpClientAdapter(httpClient),
{
environment: 'production',
apiProjectKey: 'your-project-key',
authStorage: new IndexedDBTokenStorage() // Recommended
}
);
}
// Expose managers directly for clean component access
get auth() { return this.sdk.auth; }
get users() { return this.sdk.users; }
get tokens() { return this.sdk.tokens; }
get businesses() { return this.sdk.businesses; }
get campaigns() { return this.sdk.campaigns; }
get redemptions() { return this.sdk.redemptions; }
get transactions() { return this.sdk.transactions; }
get purchases() { return this.sdk.purchases; }
get web3() { return this.sdk.web3; }
// ... other managers as needed
}Node.js
No additional dependencies required.
import { PersSDK } from '@explorins/pers-sdk';
import { NodeHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
const sdk = new PersSDK(new NodeHttpClientAdapter(), {
environment: 'production',
apiProjectKey: 'your-project-key'
});
// Server-side operations
const businesses = await sdk.businesses.getActiveBusinesses();React Native
Use the dedicated React Native SDK (includes passkeys, secure storage, DPoP):
npm install @explorins/pers-sdk-react-native @react-native-async-storage/async-storage// In your root layout (e.g., _layout.tsx for Expo Router)
import { PersSDKProvider } from '@explorins/pers-sdk-react-native';
export default function RootLayout() {
return (
<PersSDKProvider config={{ apiProjectKey: 'your-project-key' }}>
<YourApp />
</PersSDKProvider>
);
}
// In your components - use hooks
import { useAuth, useTokens, useCampaigns } from '@explorins/pers-sdk-react-native';
function MyComponent() {
const { login, isAuthenticated } = useAuth();
const { getTokens } = useTokens();
const { getActiveCampaigns, claimCampaign } = useCampaigns();
// ...
}See @explorins/pers-sdk-react-native for full setup including passkey configuration.
Architecture
The SDK uses a clean Manager-Service pattern with three access levels:
// ═══════════════════════════════════════════════════════════════════════════
// MANAGER LAYER (Recommended - High-level, intuitive APIs)
// ═══════════════════════════════════════════════════════════════════════════
sdk.auth // Authentication & sessions
sdk.users // User profiles & management
sdk.userStatus // User status/tier management
sdk.tokens // Token types & configuration
sdk.businesses // Business operations
sdk.campaigns // Marketing campaigns
sdk.redemptions // Reward redemptions
sdk.transactions // Transaction history
sdk.purchases // Purchase/payment processing
sdk.analytics // Reporting & insights
sdk.tenants // Multi-tenant configuration
sdk.donations // Charitable giving
sdk.files // File upload/download operations
sdk.web3 // Blockchain operations
sdk.apiKeys // API key management (Admin)
// ═══════════════════════════════════════════════════════════════════════════
// SERVICE LAYER (Advanced - Full domain access)
// ═══════════════════════════════════════════════════════════════════════════
sdk.campaigns.getCampaignService() // Full CampaignService access
sdk.tokens.getTokenService() // Full TokenService access
sdk.businesses.getBusinessService() // Full BusinessService access
sdk.purchases.getPurchaseService() // Full PaymentService access
// ... each manager exposes its underlying service
// ═══════════════════════════════════════════════════════════════════════════
// API LAYER (Expert - Direct REST API access)
// ═══════════════════════════════════════════════════════════════════════════
const apiClient = sdk.api();
const customData = await apiClient.get<CustomType>('/custom-endpoint');
await apiClient.post('/custom-endpoint', { data: 'value' });Available Managers Reference
| Manager | Accessor | Primary Use Cases |
|---------|----------|-------------------|
| AuthManager | sdk.auth | Login, logout, token management, authentication status |
| UserManager | sdk.users | User profiles, account management |
| UserStatusManager | sdk.userStatus | User tiers, status levels |
| TokenManager | sdk.tokens | Token types, credit tokens, reward tokens |
| BusinessManager | sdk.businesses | Business discovery, details, types |
| CampaignManager | sdk.campaigns | Campaign discovery, claiming, user history |
| RedemptionManager | sdk.redemptions | Redeem rewards, redemption history |
| TransactionManager | sdk.transactions | Transaction history, details |
| PurchaseManager | sdk.purchases | Payment intents, purchase tokens |
| AnalyticsManager | sdk.analytics | Reporting, transaction analytics |
| TenantManager | sdk.tenants | Tenant config, client settings |
| DonationManager | sdk.donations | Donation types, charitable giving |
| FileManager | sdk.files | Signed URLs, media optimization |
| Web3Manager | sdk.web3 | Blockchain operations, token metadata |
| ApiKeyManager | sdk.apiKeys | API key CRUD (Admin only) |
Core Features
Event System
The SDK provides a platform-agnostic event system for subscribing to SDK-wide events. All events have a userMessage field ready for UI display.
// Subscribe to ALL events - one handler
const unsubscribe = sdk.events.subscribe((event) => {
showNotification(event.userMessage, event.level);
});
// Filter by domain and level
sdk.events.subscribe((event) => {
logToSentry(event);
}, { level: 'error' });
// Only transaction successes
sdk.events.subscribe((event) => {
playSuccessSound();
confetti();
}, { domain: 'transaction', level: 'success' });
// One-time event (auto-unsubscribe)
sdk.events.once((event) => {
console.log('First event:', event.type);
});
// Cleanup
unsubscribe();Event Domains
| Domain | Manager | Events |
|--------|---------|--------|
| authentication | sdk.auth | LOGIN_SUCCESS |
| user | sdk.users | PROFILE_UPDATED |
| business | sdk.businesses | BUSINESS_CREATED, BUSINESS_UPDATED, MEMBERSHIP_UPDATED |
| campaign | sdk.campaigns | CAMPAIGN_CLAIMED |
| redemption | sdk.redemptions | REDEMPTION_COMPLETED |
| transaction | sdk.transactions | TRANSACTION_CREATED, TRANSACTION_SUBMITTED |
Event Types
import type {
PersEvent, // Base event type (discriminated union)
SuccessEvent, // Success events (business domains)
ErrorEvent, // Error events (all domains including technical)
EventFilter, // Filter for subscriptions
EventHandler // Handler type (sync or async)
} from '@explorins/pers-sdk/core';
// Event structure
interface PersEvent {
id: string; // Unique event ID
timestamp: number; // Unix timestamp (ms)
domain: string; // Event domain
type: string; // Event type within domain
level: 'success' | 'error';
userMessage: string; // Ready for UI display
action?: string; // Suggested action
code?: string; // Backend error code
details?: object; // Additional data for logging
}Async Handler Support
Event handlers can be synchronous or asynchronous. Async errors are caught automatically:
sdk.events.subscribe(async (event) => {
await saveToDatabase(event); // Async operations supported
await sendAnalytics(event);
});Authentication
The SDK uses external JWT tokens for authentication. You provide a JWT from your authentication provider (Firebase, Auth0, Cognito, etc.), and the SDK exchanges it for PERS access tokens.
// Step 1: Get JWT from your auth provider (Firebase example)
import { getAuth } from 'firebase/auth';
const firebaseUser = getAuth().currentUser;
const externalJWT = await firebaseUser?.getIdToken();
// Step 2: Exchange JWT for PERS tokens
const authResult = await sdk.auth.loginWithToken(externalJWT, 'user');
console.log('User authenticated:', authResult.user.name);
// Admin login
const adminResult = await sdk.auth.loginWithToken(adminJWT, 'admin');
console.log('Admin authenticated:', adminResult.admin.email);
// Check authentication status (async - verifies with server)
const isAuth = await sdk.auth.isAuthenticated();
// Quick check if tokens exist locally (faster, less reliable)
const hasTokens = await sdk.auth.hasValidAuth();
// Get current user
const user = await sdk.auth.getCurrentUser();
// Logout / clear auth
await sdk.auth.clearAuth();How it works:
- Your app authenticates users via Firebase/Auth0/etc.
- You call
sdk.auth.loginWithToken(jwt)with that JWT - PERS validates the JWT and returns PERS-specific access/refresh tokens
- SDK stores tokens and handles automatic refresh
- All subsequent SDK calls are automatically authenticated
Security Features:
- DPoP (Demonstrating Proof-of-Possession): Enabled by default - binds tokens to client
- Automatic token refresh and validation
- Flexible storage strategies (LocalStorage, IndexedDB, Memory)
- Support for user and admin authentication flows
Business Management
// Get all active businesses
const businesses = await sdk.businesses.getActiveBusinesses();
// Get business by ID
const business = await sdk.businesses.getBusinessById('business-123');
// Get business types
const types = await sdk.businesses.getBusinessTypes();Campaign System
// Get active campaigns available for claiming
const campaigns = await sdk.campaigns.getActiveCampaigns();
// Get campaign details
const campaign = await sdk.campaigns.getCampaignById('campaign-123');
// Claim campaign rewards
const claim = await sdk.campaigns.claimCampaign({
campaignId: 'campaign-123',
businessId: 'business-456' // Optional: associated business
});
// Get user's campaign claim history
const userClaims = await sdk.campaigns.getUserClaims();Token Management
// Get all token types
const tokens = await sdk.tokens.getTokens();
// Get active credit token (main loyalty currency)
const creditToken = await sdk.tokens.getActiveCreditToken();
// Get reward tokens
const rewards = await sdk.tokens.getRewardTokens();
// Get status tokens (tier/achievement tokens)
const statusTokens = await sdk.tokens.getStatusTokens();
// Get token by blockchain contract
const token = await sdk.tokens.getTokenByContract('0x123...', 'token-id');User Token Balances (On-Chain)
Important Architecture Note: The PERS backend stores token definitions (contract addresses, ABIs, metadata), but does NOT store user token balances. User balances are queried directly from the blockchain via RPC calls using
sdk.web3.*methods.
Data Flow:
sdk.tokens.*→ Get token definitions (contract address, ABI, chainId) from PERS backendsdk.auth.getCurrentUser()→ Get user's wallet addresssdk.web3.*→ Query blockchain directly for user's token balance
Points Balance (ERC-20)
// Step 1: Get the credit token definition from PERS backend
const creditToken = await sdk.tokens.getActiveCreditToken();
// Step 2: Get user's wallet address
const user = await sdk.auth.getCurrentUser();
const walletAddress = user.wallets?.[0]?.address;
// Step 3: Query blockchain directly for balance
const balance = await sdk.web3.getTokenBalance({
accountAddress: walletAddress,
contractAddress: creditToken.contractAddress,
abi: creditToken.abi, // Raw ABI from backend works directly
chainId: creditToken.chainId,
tokenId: '' // Empty for ERC-20
});
console.log('Points balance:', balance.balance);
console.log('Has balance:', balance.hasBalance);Stamps Collection (ERC-721 / ERC-1155)
// Step 1: Get status token definitions (stamps/achievements)
const statusTokens = await sdk.tokens.getStatusTokens();
// Step 2: Get user's wallet address
const user = await sdk.auth.getCurrentUser();
const walletAddress = user.wallets?.[0]?.address;
// Step 3: Query blockchain for user's stamp collection
for (const stampToken of statusTokens) {
const collection = await sdk.web3.getTokenCollection({
accountAddress: walletAddress,
contractAddress: stampToken.contractAddress,
abi: stampToken.abi, // Raw ABI from backend works directly
chainId: stampToken.chainId,
maxTokens: 50 // Optional: limit results
});
// Filter to tokens with balance > 0
const ownedStamps = collection.tokens.filter(t => t.hasBalance && t.balance > 0);
console.log(`Stamps from ${stampToken.symbol}:`, ownedStamps.length);
// Each stamp includes metadata (name, description, image, etc.)
for (const stamp of ownedStamps) {
console.log(`- Token #${stamp.tokenId}: ${stamp.metadata?.name}`);
console.log(` Image: ${stamp.metadata?.imageUrl}`);
}
}Complete Example: Load All User Balances
async function loadUserTokenBalances(sdk: PersSDK) {
// Get user wallet
const user = await sdk.auth.getCurrentUser();
const walletAddress = user.wallets?.[0]?.address;
if (!walletAddress) throw new Error('User has no wallet');
// Get all token definitions
const [creditToken, statusTokens, rewardTokens] = await Promise.all([
sdk.tokens.getActiveCreditToken(),
sdk.tokens.getStatusTokens(),
sdk.tokens.getRewardTokens()
]);
// Query ERC-20 balance (Points)
const pointsBalance = creditToken ? await sdk.web3.getTokenBalance({
accountAddress: walletAddress,
contractAddress: creditToken.contractAddress,
abi: creditToken.abi,
chainId: creditToken.chainId,
tokenId: ''
}) : null;
// Query ERC-721/ERC-1155 collections (Stamps, Rewards)
const allNftTokens = [...(statusTokens || []), ...(rewardTokens || [])];
const collections = await Promise.all(
allNftTokens.map(token =>
sdk.web3.getTokenCollection({
accountAddress: walletAddress,
contractAddress: token.contractAddress,
abi: token.abi,
chainId: token.chainId,
maxTokens: 100
}).catch(() => null) // Handle individual failures gracefully
)
);
return {
points: pointsBalance,
stamps: collections.filter(Boolean)
};
}Transaction Request Building
The SDK provides factory functions for building transaction request DTOs. These are tree-shakeable and provide type-safe transaction creation:
import {
buildMintRequest,
buildBurnRequest,
buildTransferRequest,
buildPOSTransferRequest,
buildSubmissionRequest
} from '@explorins/pers-sdk/transaction';
import { AccountOwnerType } from '@explorins/pers-shared';
// Mint tokens to a recipient
const mintRequest = buildMintRequest({
amount: 100,
contractAddress: '0x...',
chainId: 137,
recipientAccountId: 'user-123',
recipientAccountType: AccountOwnerType.USER
});
// Burn tokens
const burnRequest = buildBurnRequest({
amount: 50,
contractAddress: '0x...',
chainId: 137,
senderAccountId: 'user-123',
senderAccountType: AccountOwnerType.USER
});
// Transfer tokens between accounts
const transferRequest = buildTransferRequest({
amount: 100,
contractAddress: '0x...',
chainId: 137,
senderAccountId: 'user-123',
senderAccountType: AccountOwnerType.USER,
recipientAccountId: 'business-456',
recipientAccountType: AccountOwnerType.BUSINESS
});
// Submit the transaction
const result = await sdk.transactions.createTransaction(mintRequest);POS Transaction Flow
For Point-of-Sale scenarios where a business submits a transaction on behalf of a user, use buildPOSTransferRequest:
import { buildPOSTransferRequest } from '@explorins/pers-sdk/transaction';
// POS flow: User pays business, business authorized to submit
const posRequest = buildPOSTransferRequest({
amount: 100,
contractAddress: '0x...',
chainId: 137,
userAccountId: 'user-123', // User sending tokens
businessAccountId: 'business-456' // Business receiving & authorized to submit
});
// This automatically sets:
// - engagedBusinessId: 'business-456' (for reporting)
// - authorizedSubmitterId: 'business-456' (can submit the signed tx)
// - authorizedSubmitterType: AccountOwnerType.BUSINESS
const result = await sdk.transactions.createTransaction(posRequest);For custom authorization scenarios, use buildTransferRequest with manual POS fields:
import { buildTransferRequest, type POSAuthorizationOptions } from '@explorins/pers-sdk/transaction';
const customPOSRequest = buildTransferRequest({
amount: 100,
contractAddress: '0x...',
chainId: 137,
senderAccountId: 'user-123',
senderAccountType: AccountOwnerType.USER,
recipientAccountId: 'business-456',
recipientAccountType: AccountOwnerType.BUSINESS,
// POS authorization fields
engagedBusinessId: 'business-456',
authorizedSubmitterId: 'business-456',
authorizedSubmitterType: AccountOwnerType.BUSINESS
});| POS Field | Type | Description |
|-----------|------|-------------|
| engagedBusinessId | string | Business commercially involved (for stats/reporting) |
| authorizedSubmitterId | string | Entity authorized to submit the signed transaction |
| authorizedSubmitterType | AccountOwnerType | Type of authorized submitter (USER or BUSINESS) |
### Purchase/Payment Processing
```typescript
// Create payment intent
const intent = await sdk.purchases.createPaymentIntent(
100, // amount
'usd', // currency
'[email protected]', // email
'Token Purchase' // description
);
// Get user's purchase history
const purchases = await sdk.purchases.getAllUserPurchases();
// Get available purchase tokens
const purchaseTokens = await sdk.purchases.getActivePurchaseTokens();Configuration
API Project Key (Required)
The apiProjectKey is a tenant-specific identifier that associates your application with a PERS tenant (organization). You must obtain this from the PERS admin dashboard or from your PERS account manager.
// Example project key format (64-character hex string)
apiProjectKey: 'e3e16b5863f0a042b949650d236a37b0758bd51177463d627921112d2291fe01'Full Configuration Example
import { PersSDK } from '@explorins/pers-sdk';
import { IndexedDBTokenStorage } from '@explorins/pers-sdk/core';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
// Environment: 'development' | 'staging' | 'production'
environment: 'production', // Default: 'production'
// Project key for API authentication (required)
apiProjectKey: 'your-project-key',
// API version (currently only v2 supported)
apiVersion: 'v2', // Default: 'v2'
// Request timeout in milliseconds
timeout: 30000, // Default: 30000
// Retry attempts for failed requests
retries: 3, // Default: 3
// Token storage strategy (recommended: IndexedDB)
authStorage: new IndexedDBTokenStorage(), // Default: LocalStorage
// DPoP (Demonstrating Proof-of-Possession) configuration
dpop: {
enabled: true, // Default: true (recommended for security)
},
// Authentication type for auto-created provider
authType: 'user', // 'user' | 'admin', Default: 'user'
// Token refresh margin (seconds before expiry to refresh)
tokenRefreshMargin: 60, // Default: 60
// Background refresh threshold (seconds)
backgroundRefreshThreshold: 30 // Default: 30
});Configuration Options Reference
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| environment | 'development' \| 'staging' \| 'production' | 'production' | API environment target |
| apiProjectKey | string | - | Required. Project key for API authentication |
| apiVersion | 'v2' | 'v2' | API version |
| timeout | number | 30000 | Request timeout (ms) |
| retries | number | 3 | Retry attempts |
| authStorage | TokenStorage | LocalStorage | Token storage implementation |
| dpop.enabled | boolean | true | Enable DPoP security |
| authType | 'user' \| 'admin' | 'user' | Authentication type |
| authProvider | PersAuthProvider | auto-created | Custom auth provider (overrides authType) |
Advanced Authentication
Using IndexedDB Storage (Recommended)
For better security and performance, use IndexedDB instead of LocalStorage:
import { PersSDK } from '@explorins/pers-sdk';
import { IndexedDBTokenStorage } from '@explorins/pers-sdk/core';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
environment: 'production',
apiProjectKey: 'your-key',
authStorage: new IndexedDBTokenStorage() // Secure, async storage
});Custom Storage Implementation
Implement the TokenStorage interface for custom storage backends:
import type { TokenStorage } from '@explorins/pers-sdk/core';
import { AUTH_STORAGE_KEYS, DPOP_STORAGE_KEYS } from '@explorins/pers-sdk/core';
class CustomStorage implements TokenStorage {
// CRITICAL: Set to 'false' for string-only backends (LocalStorage-like)
// Set to 'true' for object-capable backends (IndexedDB-like)
readonly supportsObjects = false;
async get(key: string): Promise<unknown | null> {
// Use AUTH_STORAGE_KEYS.ACCESS_TOKEN, DPOP_STORAGE_KEYS.PRIVATE, etc.
return yourBackend.get(key);
}
async set(key: string, value: unknown): Promise<void> {
await yourBackend.set(key, value);
}
async remove(key: string): Promise<void> {
await yourBackend.remove(key);
}
async clear(): Promise<void> {
await yourBackend.clear();
}
}Error Handling
The SDK provides structured error types for consistent error handling:
import {
PersApiError,
AuthenticationError,
ErrorUtils
} from '@explorins/pers-sdk/core';
try {
const user = await sdk.auth.getCurrentUser();
} catch (error) {
// Check for specific error types
if (error instanceof AuthenticationError) {
// Handle authentication failure (401)
console.error('Auth failed:', error.message);
console.error('Endpoint:', error.endpoint);
console.error('User message:', error.userMessage);
// Redirect to login...
} else if (error instanceof PersApiError) {
// Handle general API errors
console.error('API Error:', error.message);
console.error('Status:', error.status);
console.error('Retryable:', error.retryable);
} else {
// Handle unexpected errors
console.error('Unexpected error:', error);
}
}
// Utility functions for error inspection
const status = ErrorUtils.getStatus(error); // Extract status code
const message = ErrorUtils.getMessage(error); // Extract error message
const retryable = ErrorUtils.isRetryable(error); // Check if retryableError Types Reference
| Error Class | Status | Use Case |
|-------------|--------|----------|
| PersApiError | Various | General API request failures |
| AuthenticationError | 401 | Authentication/authorization failures |
| NetworkError | - | Network connectivity issues |
| TokenRefreshNeeded | - | Internal: token refresh required |
| LogoutRequired | - | Internal: session invalidated |
Bundle Size
- Core SDK: ~85 KB (minified)
- Tree-shaking: Fully supported - import only what you need
- Zero runtime dependencies: No RxJS, no heavy libraries in core
- Native APIs: Uses browser-native
fetch,crypto,IndexedDB
TypeScript Types
All domain interfaces are exported from @explorins/pers-shared. Import types directly for strong typing:
import type {
// User & Auth
UserDTO,
SessionAuthContextResponseDTO,
// Business
BusinessDTO,
BusinessTypeDTO,
// Campaigns
CampaignDTO,
CampaignClaimDTO,
CampaignClaimRequestDTO,
// Tokens
TokenDTO,
TokenMetadataDTO,
// Transactions
TransactionDTO,
// Redemptions
RedemptionDTO,
RedemptionRedeemDTO,
// Payments
PaymentIntentDTO,
PurchaseDTO,
PurchaseTokenDTO,
// Tenant
TenantDTO,
TenantClientConfigDTO
} from '@explorins/pers-shared';Key Interfaces Reference
| Domain | Interface | Description |
|--------|-----------|-------------|
| Auth | UserDTO | Authenticated user profile |
| Auth | SessionAuthContextResponseDTO | Login response with tokens + user/admin |
| Business | BusinessDTO | Business entity with details |
| Business | BusinessTypeDTO | Business category/type definition |
| Campaign | CampaignDTO | Campaign with rewards and rules |
| Campaign | CampaignClaimDTO | User's claim record |
| Campaign | CampaignClaimRequestDTO | Request body for claiming |
| Token | TokenDTO | Token type definition (credit, reward, status) |
| Token | TokenMetadataDTO | On-chain token metadata |
| Transaction | TransactionDTO | Transaction record |
| Redemption | RedemptionDTO | Redemption offer |
| Redemption | RedemptionRedeemDTO | Redemption result |
| Payment | PaymentIntentDTO | Stripe payment intent |
| Payment | PurchaseDTO | Purchase record |
| Tenant | TenantDTO | Tenant configuration |
Usage Example
import { PersSDK } from '@explorins/pers-sdk';
import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
import type { CampaignDTO, CampaignClaimRequestDTO } from '@explorins/pers-shared';
const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
apiProjectKey: 'your-key'
});
// Type-safe campaign operations
const campaigns: CampaignDTO[] = await sdk.campaigns.getActiveCampaigns();
const claimRequest: CampaignClaimRequestDTO = {
campaignId: campaigns[0].id,
businessId: 'business-123'
};
const claim = await sdk.campaigns.claimCampaign(claimRequest);Documentation
- Getting Started Guide: https://docs.pers.ninja/1.intro
- API Reference: https://docs.pers.ninja/sdk
- TypeDoc API Docs: Generated with
npm run docs
Related Packages
| Package | Description | |---------|-------------| | @explorins/pers-sdk-react-native | React Native integration with passkey support | | @explorins/pers-shared | Shared types, interfaces, and DTOs |
License
MIT License
