A high-performance, type-safe TypeScript SDK for the Swapped Commerce API. Built with functional programming principles, Bun-native optimizations, and comprehensive type coverage.
- Overview
- Features
- Installation
- Quick Start
- Configuration
- API Reference
- Webhooks
- Error Handling
- TypeScript Support
- Examples
- Testing
- Performance
- Requirements
- Documentation
- License
The Swapped Commerce SDK provides a robust, type-safe interface for integrating cryptocurrency payment processing into your applications. Designed with performance and developer experience in mind, it leverages modern TypeScript features and Bun's native capabilities to deliver a seamless integration experience.
Key Design Principles:
- Type Safety: Complete TypeScript coverage with zero tolerance for
anytypes - Functional Architecture: Pure functions with immutable data structures
- Performance First: Minimal allocations and efficient data handling
- Bun-Native: Leverages Bun's built-in APIs for optimal performance
- Developer Experience: Intuitive API design with comprehensive error handling
- Complete Type Coverage: Every API response and parameter is fully typed
- Functional Programming: Pure functions with no hidden side effects
- Automatic Retry Logic: Exponential backoff for transient failures
- Webhook Verification: Secure signature validation using Web Crypto API
- Comprehensive Error Handling: Typed error classes for all failure scenarios
- Zero Runtime Dependencies: Uses only Bun's built-in capabilities
- Immutable Data Structures: All types are readonly for safety
bun add swapped-commerce-sdknpm install swapped-commerce-sdkyarn add swapped-commerce-sdkpnpm add swapped-commerce-sdkimport { createClient } from 'swapped-commerce-sdk'
// Initialize the client
const client = createClient({
apiKey: process.env.SWAPPED_API_KEY!,
environment: 'sandbox',
})
// Create a payment link
const response = await client.paymentLinks.create({
purchase: {
name: 'Premium Subscription',
price: '99.99',
currency: 'USD',
},
metadata: {
customerEmail: 'customer@example.com',
},
})
if (response.success) {
console.log('Payment link:', response.data.paymentLink)
console.log('Order ID:', response.data.orderId)
}The SDK accepts a configuration object with the following options:
import { createClient, type SwappedConfig } from 'swapped-commerce-sdk'
const config: SwappedConfig = {
apiKey: 'your-api-key-here', // Required: Your Swapped API key
environment: 'sandbox', // Optional: 'sandbox' | 'production' (default: 'production')
timeout: 30000, // Optional: Request timeout in milliseconds (default: 30000)
retries: 3, // Optional: Number of retry attempts (default: 3)
}
const client = createClient(config)For production applications, it's recommended to use environment variables:
const client = createClient({
apiKey: process.env.SWAPPED_API_KEY!,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
timeout: parseInt(process.env.SWAPPED_TIMEOUT ?? '30000', 10),
retries: parseInt(process.env.SWAPPED_RETRIES ?? '3', 10),
})The Orders API allows you to manage customer orders, track payment status, and process refunds.
Retrieve a paginated list of orders with optional filtering:
const response = await client.orders.list({
page: 1,
limit: 10,
type: 'PAYMENT_ROUTE',
searchId: 'order_123',
startDate: Date.now() - 7 * 24 * 60 * 60 * 1000, // Last 7 days
endDate: Date.now(),
})
if (response.success) {
console.log(`Found ${response.data.pagination.totalItems} orders`)
for (const order of response.data.orders) {
console.log(`Order ${order.id}: ${order.status}`)
console.log(`Amount: ${order.quote.toAmount.amount} ${order.quote.toAmount.currency.symbol}`)
}
}Retrieve detailed information about a specific order:
const response = await client.orders.get('order_123')
if (response.success) {
const order = response.data
console.log('Order Details:')
console.log(` ID: ${order.id}`)
console.log(` Status: ${order.status}`)
console.log(` Created: ${order.createdAt}`)
console.log(` Expires: ${order.expiresAt}`)
console.log(` Payment Address: ${order.depositAddress.address}`)
if (order.payments.length > 0) {
console.log('Payments:')
for (const payment of order.payments) {
console.log(` ${payment.receivedAmount} ${payment.receivedCurrency.symbol} - ${payment.status}`)
}
}
}Process a refund for a completed order:
const response = await client.orders.refund('order_123', {
amount: '50.00', // Optional: Partial refund amount
reason: 'Customer requested refund', // Optional: Refund reason
})
if (response.success) {
console.log(`Refund initiated: ${response.data.refundId}`)
}Note: Refunds require sufficient balance in the order's currency.
Generate shareable payment links for customers to complete cryptocurrency payments.
const response = await client.paymentLinks.create({
purchase: {
name: 'Premium Subscription',
description: 'Monthly subscription to premium features',
notes: 'Includes access to all premium features',
price: '99.99',
currency: 'USD',
imageUrl: 'https://example.com/product-image.jpg', // Optional
},
metadata: {
externalId: 'internal_order_456', // Optional: Your internal order ID
customerId: 'customer_789', // Optional: Customer identifier
customerEmail: 'customer@example.com', // Optional: Customer email
customerName: 'John Doe', // Optional: Customer name
customerCountry: 'US', // Optional: Customer country
customerLang: 'en', // Optional: Customer language
redirectUrl: 'https://example.com/success', // Optional: Redirect after payment
},
testMode: false, // Optional: Enable test mode
preferredPayCurrency: { // Optional: Preferred cryptocurrency
symbol: 'BTC',
blockchain: 'bitcoin',
},
})
if (response.success) {
console.log('Payment link created:')
console.log(` Link: ${response.data.paymentLink}`)
console.log(` Order ID: ${response.data.orderId}`)
// Share the payment link with your customer
sendEmailToCustomer(response.data.paymentLink)
}Create direct payment routes for programmatic integration with deposit addresses.
const response = await client.paymentRoutes.create({
purchaseAmount: '99.99',
purchaseCurrency: 'USD',
preferredPayCurrency: 'BTC', // Optional: Preferred cryptocurrency
externalId: 'order_123', // Optional: Your internal order ID
customerId: 'customer_456', // Optional: Customer identifier
metadata: { // Optional: Additional metadata
invoiceNumber: 'INV-2024-001',
productId: 'prod_123',
},
})
if (response.success) {
console.log('Payment route created:')
console.log(` Order ID: ${response.data.orderId}`)
console.log(` Deposit Address: ${response.data.depositAddress.address}`)
console.log(` Supported Currencies: ${response.data.depositAddress.supportedCurrencies.map(c => c.symbol).join(', ')}`)
console.log(` Quote: ${response.data.quote.toAmount.amount} ${response.data.quote.toAmount.currency.symbol}`)
// Display deposit address to customer
displayPaymentInstructions(response.data.depositAddress, response.data.quote)
}Retrieve payment information and transaction details.
const response = await client.payments.get('payment_123')
if (response.success) {
const payment = response.data
console.log('Payment Details:')
console.log(` ID: ${payment.id}`)
console.log(` Order ID: ${payment.orderId}`)
console.log(` Amount: ${payment.receivedAmount} ${payment.receivedCurrency.symbol}`)
console.log(` Status: ${payment.status}`)
console.log(` Transaction Hash: ${payment.txHash}`)
console.log(` Confirmed At: ${payment.confirmedAt}`)
console.log(` Deposit Address: ${payment.depositAddress.address}`)
}Monitor your merchant cryptocurrency balances for settlements and refunds.
Retrieve all available balances:
const response = await client.balances.list({
currency: 'BTC', // Optional: Filter by currency
blockchain: 'bitcoin', // Optional: Filter by blockchain
})
if (response.success) {
console.log('Account Balances:')
for (const balance of response.data.balances) {
console.log(`${balance.currency.symbol} (${balance.currency.name}):`)
console.log(` Available: ${balance.available}`)
console.log(` Pending: ${balance.pending}`)
console.log(` Total: ${balance.total}`)
console.log(` Last Updated: ${balance.lastUpdated}`)
}
}Retrieve balance for a specific currency:
const response = await client.balances.get('BTC')
if (response.success) {
const balance = response.data
console.log(`BTC Balance:`)
console.log(` Available: ${balance.available} BTC`)
console.log(` Pending: ${balance.pending} BTC`)
console.log(` Total: ${balance.total} BTC`)
}Obtain real-time conversion rates for cryptocurrency and fiat currency pairs.
Retrieve a quote for currency conversion:
const response = await client.quotes.get({
fromCurrency: 'BTC',
toCurrency: 'USD',
amount: '1.0',
amountType: 'FROM', // 'FROM' | 'TO' - specifies which currency the amount refers to
})
if (response.success) {
const quote = response.data.quote
console.log('Conversion Quote:')
console.log(` From: ${quote.fromAmount.amount} ${quote.fromAmount.currency.symbol}`)
console.log(` To: ${quote.toAmount.amount} ${quote.toAmount.currency.symbol}`)
console.log(` Exchange Rate ID: ${quote.exchangeRateSnapshotId}`)
console.log(` Expires: ${response.data.expiresAt}`)
if (quote.fees.length > 0) {
console.log(' Fees:')
for (const fee of quote.fees) {
console.log(` ${fee.label}: ${fee.amount} ${fee.currency.symbol}`)
}
}
}Initiate withdrawals to bank accounts or cryptocurrency wallets.
Bank Account Payout:
const response = await client.payouts.create({
amount: '1000.00',
currency: 'USD',
destinationType: 'BANK',
destination: {
accountNumber: '1234567890',
routingNumber: '987654321', // Required for US accounts
accountHolderName: 'John Doe',
// For international accounts, use:
// iban: 'GB82WEST12345698765432',
// swift: 'NWBKGB2L',
},
reference: 'Monthly payout - January 2024', // Optional: Internal reference
})
if (response.success) {
console.log('Bank payout initiated:')
console.log(` Payout ID: ${response.data.payoutId}`)
console.log(` Status: ${response.data.status}`)
console.log(` Estimated Arrival: ${response.data.estimatedArrival}`)
}Cryptocurrency Payout:
const response = await client.payouts.create({
amount: '0.5',
currency: 'BTC',
destinationType: 'CRYPTO',
destination: {
address: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
blockchain: 'bitcoin',
memo: 'Payment for invoice #123', // Optional: Memo for supported blockchains
},
reference: 'Customer withdrawal',
})
if (response.success) {
console.log('Crypto payout initiated:')
console.log(` Payout ID: ${response.data.payoutId}`)
console.log(` Status: ${response.data.status}`)
}Retrieve a paginated list of payouts:
const response = await client.payouts.list({
page: 1,
limit: 20,
})
if (response.success) {
console.log(`Total Payouts: ${response.data.pagination.totalItems}`)
for (const payout of response.data.payouts) {
console.log(`Payout ${payout.id}:`)
console.log(` Amount: ${payout.amount} ${payout.currency.symbol}`)
console.log(` Status: ${payout.status}`)
console.log(` Created: ${payout.createdAt}`)
if (payout.completedAt) {
console.log(` Completed: ${payout.completedAt}`)
}
if (payout.failureReason) {
console.log(` Failure: ${payout.failureReason}`)
}
}
}Retrieve detailed information about a specific payout:
const response = await client.payouts.get('payout_123')
if (response.success) {
const payout = response.data
console.log('Payout Details:')
console.log(` ID: ${payout.id}`)
console.log(` Amount: ${payout.amount} ${payout.currency.symbol}`)
console.log(` Status: ${payout.status}`)
console.log(` Destination:`, payout.destination)
console.log(` Created: ${payout.createdAt}`)
if (payout.status === 'COMPLETED' && payout.completedAt) {
console.log(` Completed: ${payout.completedAt}`)
} else if (payout.status === 'FAILED' && payout.failureReason) {
console.log(` Failure Reason: ${payout.failureReason}`)
}
}Manage customer Know Your Customer (KYC) verification status and submissions.
Check the verification status for a customer:
const response = await client.kyc.getStatus('customer_123')
if (response.success) {
const status = response.data
console.log(`KYC Status for ${status.customerId}:`)
console.log(` Status: ${status.status}`)
if (status.submittedAt) {
console.log(` Submitted: ${status.submittedAt}`)
}
if (status.reviewedAt) {
console.log(` Reviewed: ${status.reviewedAt}`)
}
if (status.rejectionReason) {
console.log(` Rejection Reason: ${status.rejectionReason}`)
}
}Submit KYC information for customer verification:
const response = await client.kyc.submit({
customerId: 'customer_123',
firstName: 'John',
lastName: 'Doe',
dateOfBirth: '1990-01-01',
nationality: 'US',
address: {
street: '123 Main Street',
city: 'New York',
state: 'NY',
postalCode: '10001',
country: 'US',
},
documents: [
{
type: 'PASSPORT',
frontImage: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...', // Base64 encoded image
},
{
type: 'PROOF_OF_ADDRESS',
frontImage: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
},
],
})
if (response.success) {
console.log(`KYC submission created: ${response.data.submissionId}`)
console.log(`Status: ${response.data.status}`)
}Webhooks provide real-time notifications for order and payment events. The SDK includes utilities for verifying webhook signatures and parsing event payloads.
The following event types are supported:
- ORDER_CREATED: A new order has been created
- PAYMENT_RECEIVED: Payment has been received for an order
- ORDER_COMPLETED: Order has been completed successfully
- SETTLEMENT_CREATED: A settlement has been created
- PAYMENT_CONVERSION_SETTLED: Payment conversion has been settled
Always verify webhook signatures to ensure requests are authentic:
import { verifyWebhookSignature, parseWebhookEvent } from 'swapped-commerce-sdk'
async function handleWebhook(request: Request): Promise<Response> {
// Extract signature from headers
const signature = request.headers.get('X-Signature') ?? ''
const payload = await request.text()
const webhookSecret = process.env.WEBHOOK_SECRET!
// Verify signature
const isValid = await verifyWebhookSignature(payload, signature, webhookSecret)
if (!isValid) {
return new Response('Invalid signature', { status: 401 })
}
// Parse and handle event
const event = parseWebhookEvent(payload)
switch (event.event_type) {
case 'ORDER_CREATED':
await handleOrderCreated(event)
break
case 'PAYMENT_RECEIVED':
await handlePaymentReceived(event)
break
case 'ORDER_COMPLETED':
await handleOrderCompleted(event)
break
case 'SETTLEMENT_CREATED':
await handleSettlementCreated(event)
break
case 'PAYMENT_CONVERSION_SETTLED':
await handleConversionSettled(event)
break
}
return new Response('OK', { status: 200 })
}
async function handleOrderCreated(event: WebhookEvent) {
console.log(`New order created: ${event.order_id}`)
console.log(`Purchase amount: ${event.order_purchase_amount} ${event.order_purchase_currency}`)
// Update your database, send notifications, etc.
}
async function handlePaymentReceived(event: WebhookEvent) {
console.log(`Payment received for order: ${event.order_id}`)
console.log(`Crypto amount: ${event.order_crypto_amount} ${event.order_crypto}`)
console.log(`Network: ${event.network}`)
// Process payment, update order status, etc.
}
async function handleOrderCompleted(event: WebhookEvent) {
console.log(`Order completed: ${event.order_id}`)
// Fulfill order, send confirmation email, etc.
}Complete example using Bun's built-in server:
import { createClient, verifyWebhookSignature, parseWebhookEvent } from 'swapped-commerce-sdk'
Bun.serve({
port: 3000,
async fetch(request) {
if (request.method === 'POST' && new URL(request.url).pathname === '/webhook') {
return await handleWebhook(request)
}
return new Response('Not Found', { status: 404 })
},
})
console.log('Webhook server listening on http://localhost:3000/webhook')The SDK provides typed error classes for different error scenarios, enabling precise error handling in your application.
import {
SwappedError,
SwappedAuthenticationError,
SwappedValidationError,
SwappedRateLimitError,
SwappedNotFoundError,
} from 'swapped-commerce-sdk'try {
const response = await client.orders.get('invalid_order')
} catch (error) {
if (error instanceof SwappedNotFoundError) {
console.error('Order not found')
// Handle not found case
} else if (error instanceof SwappedAuthenticationError) {
console.error('Authentication failed - check your API key')
// Handle authentication error
} else if (error instanceof SwappedValidationError) {
console.error('Validation error:', error.details)
// Handle validation error
} else if (error instanceof SwappedRateLimitError) {
console.error('Rate limit exceeded - please retry later')
// Implement retry logic with backoff
} else if (error instanceof SwappedError) {
console.error(`API error (${error.statusCode}): ${error.message}`)
if (error.details) {
console.error('Details:', error.details)
}
// Handle generic API error
} else {
console.error('Unknown error:', error)
// Handle unexpected errors
}
}All error classes extend SwappedError and include:
message: Human-readable error messagestatusCode: HTTP status code (if applicable)code: Error code stringdetails: Additional error details (if available)
The SDK is fully typed with comprehensive TypeScript definitions. All types are exported and can be imported for use in your application.
import type {
// Core types
SwappedConfig,
SwappedClient,
ApiResponse,
// Order types
Order,
OrderStatus,
OrdersResponse,
// Payment types
Payment,
PaymentLinkResponse,
PaymentRouteResponse,
// Balance types
Balance,
BalancesResponse,
// Quote types
Quote,
QuoteResponse,
// Payout types
Payout,
CreatePayoutResponse,
PayoutsResponse,
// KYC types
KYCStatusResponse,
SubmitKYCResponse,
// Webhook types
WebhookEvent,
WebhookEventType,
} from 'swapped-commerce-sdk'import type { Order, OrderStatus } from 'swapped-commerce-sdk'
function processOrder(order: Order) {
// TypeScript knows all properties of Order
console.log(order.id)
console.log(order.status)
console.log(order.quote.toAmount.amount)
// Type-safe status checking
const isCompleted = (status: OrderStatus): boolean => {
return status === 'COMPLETED'
}
if (isCompleted(order.status)) {
fulfillOrder(order)
}
}Complete working examples are available in the examples/ directory:
- Payment Links:
create-payment-link.ts- Creating and sharing payment links - Webhook Handling:
handle-webhooks.ts- Receiving and processing webhook events - Payout Processing:
process-payout.ts- Managing payouts and settlements
# Set your API key
export SWAPPED_API_KEY=your-api-key-here
# Run an example
bun run examples/create-payment-link.tsThe SDK includes comprehensive test coverage with 27 passing unit tests covering all core functionality.
✅ 27 unit tests passing
✅ 0 unit tests failing
✅ 69 assertions
✅ 100% core functionality covered
✅ 6 test files
✅ 24 source files
Test Coverage:
- Error handling and factory functions (6 tests)
- Webhook signature verification (6 tests)
- HTTP client utilities (6 tests)
- Retry logic with exponential backoff (6 tests)
- Resource endpoint validation (3 tests)
- Performance benchmarks (4 tests)
Code Statistics:
- 24 TypeScript source files
- 6 comprehensive test suites
- Zero runtime dependencies
- 100% type coverage (zero
anytypes)
Run tests with:
bun testIntegration tests are available for testing against the Swapped sandbox environment:
SWAPPED_API_KEY=your-key bun test tests/integrationThe SDK is designed with performance as a core consideration and includes comprehensive benchmarks.
Performance benchmarks demonstrate the SDK's efficiency. Run benchmarks with bun run benchmark to see live results:
| Operation | Performance | Description |
|---|---|---|
| URL Building | ~209,000 ops/sec | Efficient query parameter handling |
| Request Config | ~4,800,000 ops/sec | Fast request object creation |
| Webhook Verification | ~15,600 ops/sec | Cryptographic signature validation |
| Type Safety Overhead | ~23,500,000 ops/sec | Minimal compile-time cost |
Latest Benchmark Run:
┌──────────────────────────────────────┬──────────────┬─────────────┐
│ Operation │ Ops/Second │ Duration │
├──────────────────────────────────────┼──────────────┼─────────────┤
│ URL Building │ 209,058 │ 47.83ms │
│ Request Config Creation │ 4,788,412 │ 2.09ms │
│ Webhook Signature Verification │ 15,620 │ 6.40ms │
│ Type Safety Overhead │ 23,559,440 │ 4.24ms │
└──────────────────────────────────────┴──────────────┴─────────────┘
Summary:
• Average performance: 7,143,133 ops/sec
• Fastest operation: Type Safety Overhead
• Slowest operation: Webhook Signature Verification
The SDK is optimized for minimal bundle size:
- Bundled Size: 9.97 KB (gzipped: 2.6 KB)
- Zero Runtime Dependencies: No external packages required
- Tree-Shakeable: Only import what you need
- Type Definitions: Included inline, no separate @types package needed
Build the bundle with:
bun run build- Pure Functions: No unnecessary object creation or mutations
- Immutable Types: All data structures are readonly, enabling safe sharing
- Efficient HTTP: Uses Bun's native fetch with connection pooling
- Minimal Dependencies: Zero runtime dependencies, uses only Bun built-ins
- Optimized Retry Logic: Exponential backoff prevents unnecessary requests
- Type Safety: Compile-time checks eliminate runtime type errors
- Reuse Client Instances: Create the client once and reuse it throughout your application
- Batch Operations: Use list endpoints with pagination instead of multiple individual requests
- Error Handling: Implement proper retry logic for transient failures
- Webhook Processing: Process webhooks asynchronously to avoid blocking
Formatted Output (Recommended):
bun run benchmarkThis displays a formatted table with detailed performance metrics.
Test Format:
bun run benchmark:test
# or
bun test benchmarks/performance.test.tsThis runs benchmarks as part of the test suite with assertions.
- Bun: >= 1.0.0 (recommended runtime)
- TypeScript: >= 5.0.0
The SDK is optimized for Bun but can be used with Node.js 18+ and other JavaScript runtimes that support modern ES modules.
For complete API documentation and integration guides, visit:
Swapped Commerce API Documentation: https://docs.swapped.com/swapped-commerce/commerce-integration
MIT
See LICENSE file for details.