Type-safe client library for Dorisio payment infrastructure. Send tips, verify wallets, and manage creator payouts on Stellar.
β¨ Production-Ready
- Type-safe with full TypeScript support
- Domain-specific error handling (PaymentError, WalletVerificationError, AuthError)
- Idempotency support prevents double-charging on retries
- Webhook signature verification for async events
- Comprehensive examples (vanilla JS + React)
π Developer Experience
- Exported Zod schemas for consumer validation
- Full JSDoc documentation with examples
- TypeDoc API reference auto-generated
- React hooks for seamless integration
- 86 unit tests (100% passing)
π° Payment Features
- Create tips with optional messages
- Build and submit Stellar transactions
- Check transaction confirmation status
- Transaction history with pagination
- Creator earnings tracking
π Security
- Challenge-response wallet verification
- Wallet linking with Stellar integration
- Safe retry logic with exponential backoff
- Constant-time signature verification
npm install dorisio-sdkimport { DorisioClient, PaymentError, Schemas } from 'dorisio-sdk';
import { v4 as uuidv4 } from 'uuid';
const client = new DorisioClient({
baseURL: 'https://api.dorisio.com',
timeout: 30000,
});
// Validate input with exported schemas
const tipInput = Schemas.Payment.createTip.parse({
amount: 50,
currency: 'USD',
creatorId: '550e8400-e29b-41d4-a716-446655440000',
message: 'Great content!',
idempotencyKey: uuidv4(), // Safe to retry with same key
});
try {
const tip = await client.payments.createTip(tipInput);
console.log('Tip created:', tip.id);
} catch (error) {
if (error instanceof PaymentError) {
console.error('Payment failed:', error.message);
}
}import { DorisioProvider, useCreateTip, useWallet } from 'dorisio-sdk/react';
import { Schemas } from 'dorisio-sdk';
function TipButton() {
const { createTip, loading, error } = useCreateTip();
const handleTip = async () => {
const input = Schemas.Payment.createTip.parse({
amount: 50,
currency: 'USD',
creatorId: 'xxx',
idempotencyKey: uuidv4(),
});
await createTip(input);
};
return (
<button onClick={handleTip} disabled={loading}>
{loading ? 'Processing...' : 'Send Tip'}
</button>
);
}
export function App() {
return (
<DorisioProvider config={{ baseURL: 'https://api.dorisio.com' }}>
<TipButton />
</DorisioProvider>
);
}Test the SDK offline without hitting testnet or the Dorisio API. When mode: "sandbox" is set, HttpClient bypasses fetch and returns deterministic mock responses for every client method.
import { DorisioClient } from 'dorisio-sdk';
const client = new DorisioClient({
baseUrl: 'https://api.dorisio.com',
token: 'test-token',
mode: 'sandbox', // no network calls
sandboxSeed: 42, // optional β same seed => same mocks
});
const tip = await client.createTip({
creatorId: 'mock-creator-123',
amount: 50,
message: 'Test tip',
});
// Inspect what the sandbox handled
console.log(client.getSandboxHistory());
// Toggle to live without recreating the client
client.setMode('live');Or use the dedicated helper:
import { createSandboxClient } from 'dorisio-sdk';
const client = createSandboxClient({ seed: 42, latency: 0 });
await client.getCurrentUser();DorisioClient({ mode: 'sandbox' })works with zero network calls- Client methods return deterministic mocks (seeded)
client.getSandboxHistory()/clearSandboxHistory()for test assertionsclient.setMode('sandbox' | 'live')toggles without recreating the client
- Published API Docs - Auto-generated from JSDoc and deployed to GitHub Pages on every push to
main - Local TypeDoc build - Regenerate with
npm run docs - Examples - Runnable code samples
-
Vanilla JS - Auth, wallet, payments
-
React Components - CreateTip, WalletStatus
-
Interceptors - Logging, metrics, auth refresh, retry policy
-
Hook into the request/response lifecycle for cross-cutting concerns (logging, metrics, retry policy, header injection). See INTERCEPTORS.md for the full guide.
import { DorisioClient } from 'dorisio-sdk';
const client = new DorisioClient({ baseUrl: 'https://api.dorisio.com', token: 'token' });
const interceptors = client.getHttpClient().getInterceptors();
interceptors.addRequestInterceptor((options) => {
options.headers = { ...options.headers, 'X-Request-Id': crypto.randomUUID() };
return options;
});InterceptorManager, RequestInterceptor, ResponseInterceptor, ErrorInterceptor, and
RequestOptions are exported from the package root. Interceptors run sequentially in registration
order and may be async; error interceptors observe failures but do not swallow them.
Catch specific errors and handle them appropriately:
import { PaymentError, WalletVerificationError, AuthError } from 'dorisio-sdk';
try {
await client.payments.createTip({/* ... */});
} catch (error) {
if (error instanceof PaymentError) {
console.error('Payment failed:', error.message);
console.error('Transaction:', error.transactionHash);
} else if (error instanceof WalletVerificationError) {
console.error('Wallet error:', error.message);
} else if (error instanceof AuthError) {
console.error('Auth failed:', error.message);
}
}Dorisio SDK exports both Zod schemas for runtime validation and their corresponding inferred TypeScript types for compile-time safety:
- When to use TypeScript types: Use inferred types (
CreateTipInput,LoginInput,WalletInfo, etc.) across your application code, component props, and API boundaries for compile-time type checking without runtime overhead. - When to use Zod schemas / Normalizers: Use schemas (
PaymentSchemas,AuthSchemas,CreatorSchemas,WalletSchemas) or SDK normalizers (normalizeCreateTip,normalizeCreatorProfile) at I/O boundariesβsuch as processing user forms, untrusted API responses, webhooks, or query parametersβto validate data shapes and prevent invalid requests.
import {
PaymentSchemas,
type CreateTipInput,
normalizeCreateTip,
} from 'dorisio-sdk';
import { v4 as uuidv4 } from 'uuid';
// 1. Static typing with inferred types
const tipData: CreateTipInput = {
amount: 50,
currency: 'USD',
creatorId: '550e8400-e29b-41d4-a716-446655440000',
message: 'Great work!',
idempotencyKey: uuidv4(),
};
// 2. Runtime validation via Zod schemas
const validatedTip = PaymentSchemas.createTip.parse(tipData);
// Or safe parsing with error handling
const result = PaymentSchemas.createTip.safeParse(untrustedInput);
if (!result.success) {
console.error('Validation errors:', result.error.flatten());
}
// 3. Normalizer helper for runtime validation & transformation
const normalized = normalizeCreateTip(untrustedInput);Safe retries with unique keys:
const idempotencyKey = uuidv4(); // Generate once
try {
const tip = await client.payments.createTip({
creatorId: 'xxx',
amount: 50,
idempotencyKey, // Prevents double-charging on retry
});
} catch (error) {
// Safe to retry with same key - returns same tip
const tip = await client.payments.createTip({
creatorId: 'xxx',
amount: 50,
idempotencyKey, // Same key = same result
});
}Verify incoming webhooks are authentic:
import { verifyWebhookSignature, parseWebhookPayload } from 'dorisio-sdk';
// In your webhook handler
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
req.headers['x-dorisio-signature'],
process.env.DORISIO_WEBHOOK_SECRET!
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = parseWebhookPayload(req.body);
console.log(`Event: ${event.event}`, event.data);const { createTip, loading, error, data } = useCreateTip();
const tip = await createTip({
amount: 50,
currency: 'USD',
creatorId: 'xxx',
idempotencyKey: uuidv4(),
});const { wallet, loading, error, refetch } = useWallet();
console.log(wallet?.balance, wallet?.network);
refetch(); // Manual refreshconst { balance, loading, error } = useCreatorBalance(creatorId);
console.log('Total earnings:', balance?.totalEarnings);
console.log('Pending:', balance?.pendingBalance);const { transactions, total, page, goToPage, loading } = useTransactionHistory({
limit: 20,
});
transactions.forEach((tx) => {
console.log(`$${tx.amount} to ${tx.creatorId}`);
});const client = new DorisioClient({
baseURL: 'https://api.dorisio.com',
timeout: 30000,
retryAttempts: 3,
retryDelay: 1000,
});DORISIO_API_URL=https://api.dorisio.com
DORISIO_WEBHOOK_SECRET=your-secret-key# Install
npm install
# Run tests (86 tests, 100% passing)
npm run test
# Type check
npm run type-check
# Lint
npm run lint
# Generate docs (output: docs/, deployed to GitHub Pages by .github/workflows/docs.yml)
npm run docs
# Build
npm run build
# Watch mode
npm run devSee examples/ for complete working examples:
- Auth Flow - Register, login, session validation
- Wallet Linking - Challenge-response verification
- Payments - Tips with idempotency
- React Components - CreateTip form, WalletStatus display
- Interceptors - Logging, metrics, auth refresh, retry policy wrappers
// Create tip (with idempotency)
await client.payments.createTip({
creatorId: string;
amount: number;
message?: string;
idempotencyKey?: string;
});
// Get transaction
await client.payments.getTransaction(transactionId);
// Get history
await client.payments.getTransactionHistory({ limit: 20, offset: 0 });// Register
await client.auth.register({
email: string;
password: string;
name: string;
});
// Login
await client.auth.login({
email: string;
password: string;
});
// Validate session
await client.auth.validateSession();
// Logout
await client.auth.logout();// Request challenge
const challenge = await client.auth.requestWalletChallenge();
// Verify and link
await client.auth.verifyAndLinkWallet({
challenge: string;
signature: string;
publicKey: string;
});
// Get balance
await client.wallet.getBalance();The SDK provides domain-specific error classes:
- DorisioError - Base error class
- AuthError - Authentication failures
- PaymentError - Payment processing failures (includes transactionHash)
- WalletVerificationError - Wallet linking issues (includes challenge)
- ValidationError - Input validation errors (includes details)
- RateLimitError - Rate limiting (includes retryAfter)
- TimeoutError - Network timeouts
- Modern browsers with ES2020+ support
- Node.js >=20.0.0
- React >=18.0.0 (optional, for React hooks)
- Automatic retry with exponential backoff
- Request timeout: 30s (configurable)
- Concurrent request limit: 10
- Response compression enabled
- HTTPS only in production
- Bearer token authentication
- HMAC-SHA256 webhook verification
- Constant-time signature comparison
- No secrets in logs
- Input validation with Zod
See CHANGELOG.md for version history and breaking changes.
See CONTRIBUTING.md for development guidelines.
MIT - See LICENSE for details
- π Published API Documentation
- π Local API Documentation
- π¬ GitHub Issues
- π§ Support: support@dorisio.com