swapped-commerce-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +948 -0
- package/package.json +68 -0
- package/src/client/createClient.ts +182 -0
- package/src/index.ts +29 -0
- package/src/resources/balances.ts +39 -0
- package/src/resources/kyc.ts +39 -0
- package/src/resources/orders.ts +54 -0
- package/src/resources/paymentLinks.ts +24 -0
- package/src/resources/paymentRoutes.ts +24 -0
- package/src/resources/payments.ts +20 -0
- package/src/resources/payouts.ts +58 -0
- package/src/resources/quotes.ts +24 -0
- package/src/types/balances.ts +27 -0
- package/src/types/common.ts +46 -0
- package/src/types/currencies.ts +26 -0
- package/src/types/index.ts +93 -0
- package/src/types/kyc.ts +65 -0
- package/src/types/orders.ts +122 -0
- package/src/types/payments.ts +115 -0
- package/src/types/payouts.ts +68 -0
- package/src/types/quotes.ts +49 -0
- package/src/types/webhooks.ts +31 -0
- package/src/utils/errors.ts +104 -0
- package/src/utils/http.ts +180 -0
- package/src/utils/retry.ts +57 -0
- package/src/utils/webhooks.ts +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
# Swapped Commerce SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.typescriptlang.org/)
|
|
4
|
+
[](https://bun.sh)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](tests)
|
|
7
|
+
[](package.json)
|
|
8
|
+
[](#performance)
|
|
9
|
+
[](src/types)
|
|
10
|
+
[](#performance)
|
|
11
|
+
[](#testing)
|
|
12
|
+
|
|
13
|
+
A high-performance, type-safe TypeScript SDK for the Swapped Commerce API. Built with functional programming principles, Bun-native optimizations, and comprehensive type coverage.
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Overview](#overview)
|
|
18
|
+
- [Features](#features)
|
|
19
|
+
- [Installation](#installation)
|
|
20
|
+
- [Quick Start](#quick-start)
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [API Reference](#api-reference)
|
|
23
|
+
- [Orders](#orders)
|
|
24
|
+
- [Payment Links](#payment-links)
|
|
25
|
+
- [Payment Routes](#payment-routes)
|
|
26
|
+
- [Payments](#payments)
|
|
27
|
+
- [Balances](#balances)
|
|
28
|
+
- [Quotes](#quotes)
|
|
29
|
+
- [Payouts](#payouts)
|
|
30
|
+
- [KYC Management](#kyc-management)
|
|
31
|
+
- [Webhooks](#webhooks)
|
|
32
|
+
- [Error Handling](#error-handling)
|
|
33
|
+
- [TypeScript Support](#typescript-support)
|
|
34
|
+
- [Examples](#examples)
|
|
35
|
+
- [Testing](#testing)
|
|
36
|
+
- [Performance](#performance)
|
|
37
|
+
- [Requirements](#requirements)
|
|
38
|
+
- [Documentation](#documentation)
|
|
39
|
+
- [License](#license)
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
|
|
45
|
+
**Key Design Principles:**
|
|
46
|
+
|
|
47
|
+
- **Type Safety**: Complete TypeScript coverage with zero tolerance for `any` types
|
|
48
|
+
- **Functional Architecture**: Pure functions with immutable data structures
|
|
49
|
+
- **Performance First**: Minimal allocations and efficient data handling
|
|
50
|
+
- **Bun-Native**: Leverages Bun's built-in APIs for optimal performance
|
|
51
|
+
- **Developer Experience**: Intuitive API design with comprehensive error handling
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **Complete Type Coverage**: Every API response and parameter is fully typed
|
|
56
|
+
- **Functional Programming**: Pure functions with no hidden side effects
|
|
57
|
+
- **Automatic Retry Logic**: Exponential backoff for transient failures
|
|
58
|
+
- **Webhook Verification**: Secure signature validation using Web Crypto API
|
|
59
|
+
- **Comprehensive Error Handling**: Typed error classes for all failure scenarios
|
|
60
|
+
- **Zero Runtime Dependencies**: Uses only Bun's built-in capabilities
|
|
61
|
+
- **Immutable Data Structures**: All types are readonly for safety
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
### Using Bun (Recommended)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bun add swapped-commerce-sdk
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Using npm
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install swapped-commerce-sdk
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Using yarn
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
yarn add swapped-commerce-sdk
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Using pnpm
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm add swapped-commerce-sdk
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { createClient } from 'swapped-commerce-sdk'
|
|
93
|
+
|
|
94
|
+
// Initialize the client
|
|
95
|
+
const client = createClient({
|
|
96
|
+
apiKey: process.env.SWAPPED_API_KEY!,
|
|
97
|
+
environment: 'sandbox',
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Create a payment link
|
|
101
|
+
const response = await client.paymentLinks.create({
|
|
102
|
+
purchase: {
|
|
103
|
+
name: 'Premium Subscription',
|
|
104
|
+
price: '99.99',
|
|
105
|
+
currency: 'USD',
|
|
106
|
+
},
|
|
107
|
+
metadata: {
|
|
108
|
+
customerEmail: 'customer@example.com',
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
if (response.success) {
|
|
113
|
+
console.log('Payment link:', response.data.paymentLink)
|
|
114
|
+
console.log('Order ID:', response.data.orderId)
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
The SDK accepts a configuration object with the following options:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { createClient, type SwappedConfig } from 'swapped-commerce-sdk'
|
|
124
|
+
|
|
125
|
+
const config: SwappedConfig = {
|
|
126
|
+
apiKey: 'your-api-key-here', // Required: Your Swapped API key
|
|
127
|
+
environment: 'sandbox', // Optional: 'sandbox' | 'production' (default: 'production')
|
|
128
|
+
timeout: 30000, // Optional: Request timeout in milliseconds (default: 30000)
|
|
129
|
+
retries: 3, // Optional: Number of retry attempts (default: 3)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const client = createClient(config)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Environment Variables
|
|
136
|
+
|
|
137
|
+
For production applications, it's recommended to use environment variables:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const client = createClient({
|
|
141
|
+
apiKey: process.env.SWAPPED_API_KEY!,
|
|
142
|
+
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',
|
|
143
|
+
timeout: parseInt(process.env.SWAPPED_TIMEOUT ?? '30000', 10),
|
|
144
|
+
retries: parseInt(process.env.SWAPPED_RETRIES ?? '3', 10),
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## API Reference
|
|
149
|
+
|
|
150
|
+
### Orders
|
|
151
|
+
|
|
152
|
+
The Orders API allows you to manage customer orders, track payment status, and process refunds.
|
|
153
|
+
|
|
154
|
+
#### List Orders
|
|
155
|
+
|
|
156
|
+
Retrieve a paginated list of orders with optional filtering:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const response = await client.orders.list({
|
|
160
|
+
page: 1,
|
|
161
|
+
limit: 10,
|
|
162
|
+
type: 'PAYMENT_ROUTE',
|
|
163
|
+
searchId: 'order_123',
|
|
164
|
+
startDate: Date.now() - 7 * 24 * 60 * 60 * 1000, // Last 7 days
|
|
165
|
+
endDate: Date.now(),
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
if (response.success) {
|
|
169
|
+
console.log(`Found ${response.data.pagination.totalItems} orders`)
|
|
170
|
+
|
|
171
|
+
for (const order of response.data.orders) {
|
|
172
|
+
console.log(`Order ${order.id}: ${order.status}`)
|
|
173
|
+
console.log(`Amount: ${order.quote.toAmount.amount} ${order.quote.toAmount.currency.symbol}`)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Get Order
|
|
179
|
+
|
|
180
|
+
Retrieve detailed information about a specific order:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const response = await client.orders.get('order_123')
|
|
184
|
+
|
|
185
|
+
if (response.success) {
|
|
186
|
+
const order = response.data
|
|
187
|
+
|
|
188
|
+
console.log('Order Details:')
|
|
189
|
+
console.log(` ID: ${order.id}`)
|
|
190
|
+
console.log(` Status: ${order.status}`)
|
|
191
|
+
console.log(` Created: ${order.createdAt}`)
|
|
192
|
+
console.log(` Expires: ${order.expiresAt}`)
|
|
193
|
+
console.log(` Payment Address: ${order.depositAddress.address}`)
|
|
194
|
+
|
|
195
|
+
if (order.payments.length > 0) {
|
|
196
|
+
console.log('Payments:')
|
|
197
|
+
for (const payment of order.payments) {
|
|
198
|
+
console.log(` ${payment.receivedAmount} ${payment.receivedCurrency.symbol} - ${payment.status}`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Refund Order
|
|
205
|
+
|
|
206
|
+
Process a refund for a completed order:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const response = await client.orders.refund('order_123', {
|
|
210
|
+
amount: '50.00', // Optional: Partial refund amount
|
|
211
|
+
reason: 'Customer requested refund', // Optional: Refund reason
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
if (response.success) {
|
|
215
|
+
console.log(`Refund initiated: ${response.data.refundId}`)
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Note**: Refunds require sufficient balance in the order's currency.
|
|
220
|
+
|
|
221
|
+
### Payment Links
|
|
222
|
+
|
|
223
|
+
Generate shareable payment links for customers to complete cryptocurrency payments.
|
|
224
|
+
|
|
225
|
+
#### Create Payment Link
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const response = await client.paymentLinks.create({
|
|
229
|
+
purchase: {
|
|
230
|
+
name: 'Premium Subscription',
|
|
231
|
+
description: 'Monthly subscription to premium features',
|
|
232
|
+
notes: 'Includes access to all premium features',
|
|
233
|
+
price: '99.99',
|
|
234
|
+
currency: 'USD',
|
|
235
|
+
imageUrl: 'https://example.com/product-image.jpg', // Optional
|
|
236
|
+
},
|
|
237
|
+
metadata: {
|
|
238
|
+
externalId: 'internal_order_456', // Optional: Your internal order ID
|
|
239
|
+
customerId: 'customer_789', // Optional: Customer identifier
|
|
240
|
+
customerEmail: 'customer@example.com', // Optional: Customer email
|
|
241
|
+
customerName: 'John Doe', // Optional: Customer name
|
|
242
|
+
customerCountry: 'US', // Optional: Customer country
|
|
243
|
+
customerLang: 'en', // Optional: Customer language
|
|
244
|
+
redirectUrl: 'https://example.com/success', // Optional: Redirect after payment
|
|
245
|
+
},
|
|
246
|
+
testMode: false, // Optional: Enable test mode
|
|
247
|
+
preferredPayCurrency: { // Optional: Preferred cryptocurrency
|
|
248
|
+
symbol: 'BTC',
|
|
249
|
+
blockchain: 'bitcoin',
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
if (response.success) {
|
|
254
|
+
console.log('Payment link created:')
|
|
255
|
+
console.log(` Link: ${response.data.paymentLink}`)
|
|
256
|
+
console.log(` Order ID: ${response.data.orderId}`)
|
|
257
|
+
|
|
258
|
+
// Share the payment link with your customer
|
|
259
|
+
sendEmailToCustomer(response.data.paymentLink)
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Payment Routes
|
|
264
|
+
|
|
265
|
+
Create direct payment routes for programmatic integration with deposit addresses.
|
|
266
|
+
|
|
267
|
+
#### Create Payment Route
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const response = await client.paymentRoutes.create({
|
|
271
|
+
purchaseAmount: '99.99',
|
|
272
|
+
purchaseCurrency: 'USD',
|
|
273
|
+
preferredPayCurrency: 'BTC', // Optional: Preferred cryptocurrency
|
|
274
|
+
externalId: 'order_123', // Optional: Your internal order ID
|
|
275
|
+
customerId: 'customer_456', // Optional: Customer identifier
|
|
276
|
+
metadata: { // Optional: Additional metadata
|
|
277
|
+
invoiceNumber: 'INV-2024-001',
|
|
278
|
+
productId: 'prod_123',
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
if (response.success) {
|
|
283
|
+
console.log('Payment route created:')
|
|
284
|
+
console.log(` Order ID: ${response.data.orderId}`)
|
|
285
|
+
console.log(` Deposit Address: ${response.data.depositAddress.address}`)
|
|
286
|
+
console.log(` Supported Currencies: ${response.data.depositAddress.supportedCurrencies.map(c => c.symbol).join(', ')}`)
|
|
287
|
+
console.log(` Quote: ${response.data.quote.toAmount.amount} ${response.data.quote.toAmount.currency.symbol}`)
|
|
288
|
+
|
|
289
|
+
// Display deposit address to customer
|
|
290
|
+
displayPaymentInstructions(response.data.depositAddress, response.data.quote)
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Payments
|
|
295
|
+
|
|
296
|
+
Retrieve payment information and transaction details.
|
|
297
|
+
|
|
298
|
+
#### Get Payment
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const response = await client.payments.get('payment_123')
|
|
302
|
+
|
|
303
|
+
if (response.success) {
|
|
304
|
+
const payment = response.data
|
|
305
|
+
|
|
306
|
+
console.log('Payment Details:')
|
|
307
|
+
console.log(` ID: ${payment.id}`)
|
|
308
|
+
console.log(` Order ID: ${payment.orderId}`)
|
|
309
|
+
console.log(` Amount: ${payment.receivedAmount} ${payment.receivedCurrency.symbol}`)
|
|
310
|
+
console.log(` Status: ${payment.status}`)
|
|
311
|
+
console.log(` Transaction Hash: ${payment.txHash}`)
|
|
312
|
+
console.log(` Confirmed At: ${payment.confirmedAt}`)
|
|
313
|
+
console.log(` Deposit Address: ${payment.depositAddress.address}`)
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Balances
|
|
318
|
+
|
|
319
|
+
Monitor your merchant cryptocurrency balances for settlements and refunds.
|
|
320
|
+
|
|
321
|
+
#### List Balances
|
|
322
|
+
|
|
323
|
+
Retrieve all available balances:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
const response = await client.balances.list({
|
|
327
|
+
currency: 'BTC', // Optional: Filter by currency
|
|
328
|
+
blockchain: 'bitcoin', // Optional: Filter by blockchain
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
if (response.success) {
|
|
332
|
+
console.log('Account Balances:')
|
|
333
|
+
|
|
334
|
+
for (const balance of response.data.balances) {
|
|
335
|
+
console.log(`${balance.currency.symbol} (${balance.currency.name}):`)
|
|
336
|
+
console.log(` Available: ${balance.available}`)
|
|
337
|
+
console.log(` Pending: ${balance.pending}`)
|
|
338
|
+
console.log(` Total: ${balance.total}`)
|
|
339
|
+
console.log(` Last Updated: ${balance.lastUpdated}`)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### Get Balance
|
|
345
|
+
|
|
346
|
+
Retrieve balance for a specific currency:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const response = await client.balances.get('BTC')
|
|
350
|
+
|
|
351
|
+
if (response.success) {
|
|
352
|
+
const balance = response.data
|
|
353
|
+
console.log(`BTC Balance:`)
|
|
354
|
+
console.log(` Available: ${balance.available} BTC`)
|
|
355
|
+
console.log(` Pending: ${balance.pending} BTC`)
|
|
356
|
+
console.log(` Total: ${balance.total} BTC`)
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Quotes
|
|
361
|
+
|
|
362
|
+
Obtain real-time conversion rates for cryptocurrency and fiat currency pairs.
|
|
363
|
+
|
|
364
|
+
#### Get Quote
|
|
365
|
+
|
|
366
|
+
Retrieve a quote for currency conversion:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const response = await client.quotes.get({
|
|
370
|
+
fromCurrency: 'BTC',
|
|
371
|
+
toCurrency: 'USD',
|
|
372
|
+
amount: '1.0',
|
|
373
|
+
amountType: 'FROM', // 'FROM' | 'TO' - specifies which currency the amount refers to
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
if (response.success) {
|
|
377
|
+
const quote = response.data.quote
|
|
378
|
+
|
|
379
|
+
console.log('Conversion Quote:')
|
|
380
|
+
console.log(` From: ${quote.fromAmount.amount} ${quote.fromAmount.currency.symbol}`)
|
|
381
|
+
console.log(` To: ${quote.toAmount.amount} ${quote.toAmount.currency.symbol}`)
|
|
382
|
+
console.log(` Exchange Rate ID: ${quote.exchangeRateSnapshotId}`)
|
|
383
|
+
console.log(` Expires: ${response.data.expiresAt}`)
|
|
384
|
+
|
|
385
|
+
if (quote.fees.length > 0) {
|
|
386
|
+
console.log(' Fees:')
|
|
387
|
+
for (const fee of quote.fees) {
|
|
388
|
+
console.log(` ${fee.label}: ${fee.amount} ${fee.currency.symbol}`)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Payouts
|
|
395
|
+
|
|
396
|
+
Initiate withdrawals to bank accounts or cryptocurrency wallets.
|
|
397
|
+
|
|
398
|
+
#### Create Payout
|
|
399
|
+
|
|
400
|
+
**Bank Account Payout:**
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const response = await client.payouts.create({
|
|
404
|
+
amount: '1000.00',
|
|
405
|
+
currency: 'USD',
|
|
406
|
+
destinationType: 'BANK',
|
|
407
|
+
destination: {
|
|
408
|
+
accountNumber: '1234567890',
|
|
409
|
+
routingNumber: '987654321', // Required for US accounts
|
|
410
|
+
accountHolderName: 'John Doe',
|
|
411
|
+
// For international accounts, use:
|
|
412
|
+
// iban: 'GB82WEST12345698765432',
|
|
413
|
+
// swift: 'NWBKGB2L',
|
|
414
|
+
},
|
|
415
|
+
reference: 'Monthly payout - January 2024', // Optional: Internal reference
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
if (response.success) {
|
|
419
|
+
console.log('Bank payout initiated:')
|
|
420
|
+
console.log(` Payout ID: ${response.data.payoutId}`)
|
|
421
|
+
console.log(` Status: ${response.data.status}`)
|
|
422
|
+
console.log(` Estimated Arrival: ${response.data.estimatedArrival}`)
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Cryptocurrency Payout:**
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
const response = await client.payouts.create({
|
|
430
|
+
amount: '0.5',
|
|
431
|
+
currency: 'BTC',
|
|
432
|
+
destinationType: 'CRYPTO',
|
|
433
|
+
destination: {
|
|
434
|
+
address: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
|
|
435
|
+
blockchain: 'bitcoin',
|
|
436
|
+
memo: 'Payment for invoice #123', // Optional: Memo for supported blockchains
|
|
437
|
+
},
|
|
438
|
+
reference: 'Customer withdrawal',
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
if (response.success) {
|
|
442
|
+
console.log('Crypto payout initiated:')
|
|
443
|
+
console.log(` Payout ID: ${response.data.payoutId}`)
|
|
444
|
+
console.log(` Status: ${response.data.status}`)
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
#### List Payouts
|
|
449
|
+
|
|
450
|
+
Retrieve a paginated list of payouts:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
const response = await client.payouts.list({
|
|
454
|
+
page: 1,
|
|
455
|
+
limit: 20,
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
if (response.success) {
|
|
459
|
+
console.log(`Total Payouts: ${response.data.pagination.totalItems}`)
|
|
460
|
+
|
|
461
|
+
for (const payout of response.data.payouts) {
|
|
462
|
+
console.log(`Payout ${payout.id}:`)
|
|
463
|
+
console.log(` Amount: ${payout.amount} ${payout.currency.symbol}`)
|
|
464
|
+
console.log(` Status: ${payout.status}`)
|
|
465
|
+
console.log(` Created: ${payout.createdAt}`)
|
|
466
|
+
if (payout.completedAt) {
|
|
467
|
+
console.log(` Completed: ${payout.completedAt}`)
|
|
468
|
+
}
|
|
469
|
+
if (payout.failureReason) {
|
|
470
|
+
console.log(` Failure: ${payout.failureReason}`)
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### Get Payout
|
|
477
|
+
|
|
478
|
+
Retrieve detailed information about a specific payout:
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
const response = await client.payouts.get('payout_123')
|
|
482
|
+
|
|
483
|
+
if (response.success) {
|
|
484
|
+
const payout = response.data
|
|
485
|
+
|
|
486
|
+
console.log('Payout Details:')
|
|
487
|
+
console.log(` ID: ${payout.id}`)
|
|
488
|
+
console.log(` Amount: ${payout.amount} ${payout.currency.symbol}`)
|
|
489
|
+
console.log(` Status: ${payout.status}`)
|
|
490
|
+
console.log(` Destination:`, payout.destination)
|
|
491
|
+
console.log(` Created: ${payout.createdAt}`)
|
|
492
|
+
|
|
493
|
+
if (payout.status === 'COMPLETED' && payout.completedAt) {
|
|
494
|
+
console.log(` Completed: ${payout.completedAt}`)
|
|
495
|
+
} else if (payout.status === 'FAILED' && payout.failureReason) {
|
|
496
|
+
console.log(` Failure Reason: ${payout.failureReason}`)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### KYC Management
|
|
502
|
+
|
|
503
|
+
Manage customer Know Your Customer (KYC) verification status and submissions.
|
|
504
|
+
|
|
505
|
+
#### Get KYC Status
|
|
506
|
+
|
|
507
|
+
Check the verification status for a customer:
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
const response = await client.kyc.getStatus('customer_123')
|
|
511
|
+
|
|
512
|
+
if (response.success) {
|
|
513
|
+
const status = response.data
|
|
514
|
+
|
|
515
|
+
console.log(`KYC Status for ${status.customerId}:`)
|
|
516
|
+
console.log(` Status: ${status.status}`)
|
|
517
|
+
|
|
518
|
+
if (status.submittedAt) {
|
|
519
|
+
console.log(` Submitted: ${status.submittedAt}`)
|
|
520
|
+
}
|
|
521
|
+
if (status.reviewedAt) {
|
|
522
|
+
console.log(` Reviewed: ${status.reviewedAt}`)
|
|
523
|
+
}
|
|
524
|
+
if (status.rejectionReason) {
|
|
525
|
+
console.log(` Rejection Reason: ${status.rejectionReason}`)
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### Submit KYC
|
|
531
|
+
|
|
532
|
+
Submit KYC information for customer verification:
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
const response = await client.kyc.submit({
|
|
536
|
+
customerId: 'customer_123',
|
|
537
|
+
firstName: 'John',
|
|
538
|
+
lastName: 'Doe',
|
|
539
|
+
dateOfBirth: '1990-01-01',
|
|
540
|
+
nationality: 'US',
|
|
541
|
+
address: {
|
|
542
|
+
street: '123 Main Street',
|
|
543
|
+
city: 'New York',
|
|
544
|
+
state: 'NY',
|
|
545
|
+
postalCode: '10001',
|
|
546
|
+
country: 'US',
|
|
547
|
+
},
|
|
548
|
+
documents: [
|
|
549
|
+
{
|
|
550
|
+
type: 'PASSPORT',
|
|
551
|
+
frontImage: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...', // Base64 encoded image
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
type: 'PROOF_OF_ADDRESS',
|
|
555
|
+
frontImage: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
if (response.success) {
|
|
561
|
+
console.log(`KYC submission created: ${response.data.submissionId}`)
|
|
562
|
+
console.log(`Status: ${response.data.status}`)
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Webhooks
|
|
567
|
+
|
|
568
|
+
Webhooks provide real-time notifications for order and payment events. The SDK includes utilities for verifying webhook signatures and parsing event payloads.
|
|
569
|
+
|
|
570
|
+
### Webhook Event Types
|
|
571
|
+
|
|
572
|
+
The following event types are supported:
|
|
573
|
+
|
|
574
|
+
- **ORDER_CREATED**: A new order has been created
|
|
575
|
+
- **PAYMENT_RECEIVED**: Payment has been received for an order
|
|
576
|
+
- **ORDER_COMPLETED**: Order has been completed successfully
|
|
577
|
+
- **SETTLEMENT_CREATED**: A settlement has been created
|
|
578
|
+
- **PAYMENT_CONVERSION_SETTLED**: Payment conversion has been settled
|
|
579
|
+
|
|
580
|
+
### Verifying Webhook Signatures
|
|
581
|
+
|
|
582
|
+
Always verify webhook signatures to ensure requests are authentic:
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
import { verifyWebhookSignature, parseWebhookEvent } from 'swapped-commerce-sdk'
|
|
586
|
+
|
|
587
|
+
async function handleWebhook(request: Request): Promise<Response> {
|
|
588
|
+
// Extract signature from headers
|
|
589
|
+
const signature = request.headers.get('X-Signature') ?? ''
|
|
590
|
+
const payload = await request.text()
|
|
591
|
+
const webhookSecret = process.env.WEBHOOK_SECRET!
|
|
592
|
+
|
|
593
|
+
// Verify signature
|
|
594
|
+
const isValid = await verifyWebhookSignature(payload, signature, webhookSecret)
|
|
595
|
+
|
|
596
|
+
if (!isValid) {
|
|
597
|
+
return new Response('Invalid signature', { status: 401 })
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Parse and handle event
|
|
601
|
+
const event = parseWebhookEvent(payload)
|
|
602
|
+
|
|
603
|
+
switch (event.event_type) {
|
|
604
|
+
case 'ORDER_CREATED':
|
|
605
|
+
await handleOrderCreated(event)
|
|
606
|
+
break
|
|
607
|
+
|
|
608
|
+
case 'PAYMENT_RECEIVED':
|
|
609
|
+
await handlePaymentReceived(event)
|
|
610
|
+
break
|
|
611
|
+
|
|
612
|
+
case 'ORDER_COMPLETED':
|
|
613
|
+
await handleOrderCompleted(event)
|
|
614
|
+
break
|
|
615
|
+
|
|
616
|
+
case 'SETTLEMENT_CREATED':
|
|
617
|
+
await handleSettlementCreated(event)
|
|
618
|
+
break
|
|
619
|
+
|
|
620
|
+
case 'PAYMENT_CONVERSION_SETTLED':
|
|
621
|
+
await handleConversionSettled(event)
|
|
622
|
+
break
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return new Response('OK', { status: 200 })
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async function handleOrderCreated(event: WebhookEvent) {
|
|
629
|
+
console.log(`New order created: ${event.order_id}`)
|
|
630
|
+
console.log(`Purchase amount: ${event.order_purchase_amount} ${event.order_purchase_currency}`)
|
|
631
|
+
// Update your database, send notifications, etc.
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
async function handlePaymentReceived(event: WebhookEvent) {
|
|
635
|
+
console.log(`Payment received for order: ${event.order_id}`)
|
|
636
|
+
console.log(`Crypto amount: ${event.order_crypto_amount} ${event.order_crypto}`)
|
|
637
|
+
console.log(`Network: ${event.network}`)
|
|
638
|
+
// Process payment, update order status, etc.
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function handleOrderCompleted(event: WebhookEvent) {
|
|
642
|
+
console.log(`Order completed: ${event.order_id}`)
|
|
643
|
+
// Fulfill order, send confirmation email, etc.
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Webhook Server Example
|
|
648
|
+
|
|
649
|
+
Complete example using Bun's built-in server:
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
import { createClient, verifyWebhookSignature, parseWebhookEvent } from 'swapped-commerce-sdk'
|
|
653
|
+
|
|
654
|
+
Bun.serve({
|
|
655
|
+
port: 3000,
|
|
656
|
+
async fetch(request) {
|
|
657
|
+
if (request.method === 'POST' && new URL(request.url).pathname === '/webhook') {
|
|
658
|
+
return await handleWebhook(request)
|
|
659
|
+
}
|
|
660
|
+
return new Response('Not Found', { status: 404 })
|
|
661
|
+
},
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
console.log('Webhook server listening on http://localhost:3000/webhook')
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
## Error Handling
|
|
668
|
+
|
|
669
|
+
The SDK provides typed error classes for different error scenarios, enabling precise error handling in your application.
|
|
670
|
+
|
|
671
|
+
### Error Classes
|
|
672
|
+
|
|
673
|
+
```typescript
|
|
674
|
+
import {
|
|
675
|
+
SwappedError,
|
|
676
|
+
SwappedAuthenticationError,
|
|
677
|
+
SwappedValidationError,
|
|
678
|
+
SwappedRateLimitError,
|
|
679
|
+
SwappedNotFoundError,
|
|
680
|
+
} from 'swapped-commerce-sdk'
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### Error Handling Example
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
try {
|
|
687
|
+
const response = await client.orders.get('invalid_order')
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if (error instanceof SwappedNotFoundError) {
|
|
690
|
+
console.error('Order not found')
|
|
691
|
+
// Handle not found case
|
|
692
|
+
} else if (error instanceof SwappedAuthenticationError) {
|
|
693
|
+
console.error('Authentication failed - check your API key')
|
|
694
|
+
// Handle authentication error
|
|
695
|
+
} else if (error instanceof SwappedValidationError) {
|
|
696
|
+
console.error('Validation error:', error.details)
|
|
697
|
+
// Handle validation error
|
|
698
|
+
} else if (error instanceof SwappedRateLimitError) {
|
|
699
|
+
console.error('Rate limit exceeded - please retry later')
|
|
700
|
+
// Implement retry logic with backoff
|
|
701
|
+
} else if (error instanceof SwappedError) {
|
|
702
|
+
console.error(`API error (${error.statusCode}): ${error.message}`)
|
|
703
|
+
if (error.details) {
|
|
704
|
+
console.error('Details:', error.details)
|
|
705
|
+
}
|
|
706
|
+
// Handle generic API error
|
|
707
|
+
} else {
|
|
708
|
+
console.error('Unknown error:', error)
|
|
709
|
+
// Handle unexpected errors
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### Error Properties
|
|
715
|
+
|
|
716
|
+
All error classes extend `SwappedError` and include:
|
|
717
|
+
|
|
718
|
+
- `message`: Human-readable error message
|
|
719
|
+
- `statusCode`: HTTP status code (if applicable)
|
|
720
|
+
- `code`: Error code string
|
|
721
|
+
- `details`: Additional error details (if available)
|
|
722
|
+
|
|
723
|
+
## TypeScript Support
|
|
724
|
+
|
|
725
|
+
The SDK is fully typed with comprehensive TypeScript definitions. All types are exported and can be imported for use in your application.
|
|
726
|
+
|
|
727
|
+
### Type Imports
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
import type {
|
|
731
|
+
// Core types
|
|
732
|
+
SwappedConfig,
|
|
733
|
+
SwappedClient,
|
|
734
|
+
ApiResponse,
|
|
735
|
+
|
|
736
|
+
// Order types
|
|
737
|
+
Order,
|
|
738
|
+
OrderStatus,
|
|
739
|
+
OrdersResponse,
|
|
740
|
+
|
|
741
|
+
// Payment types
|
|
742
|
+
Payment,
|
|
743
|
+
PaymentLinkResponse,
|
|
744
|
+
PaymentRouteResponse,
|
|
745
|
+
|
|
746
|
+
// Balance types
|
|
747
|
+
Balance,
|
|
748
|
+
BalancesResponse,
|
|
749
|
+
|
|
750
|
+
// Quote types
|
|
751
|
+
Quote,
|
|
752
|
+
QuoteResponse,
|
|
753
|
+
|
|
754
|
+
// Payout types
|
|
755
|
+
Payout,
|
|
756
|
+
CreatePayoutResponse,
|
|
757
|
+
PayoutsResponse,
|
|
758
|
+
|
|
759
|
+
// KYC types
|
|
760
|
+
KYCStatusResponse,
|
|
761
|
+
SubmitKYCResponse,
|
|
762
|
+
|
|
763
|
+
// Webhook types
|
|
764
|
+
WebhookEvent,
|
|
765
|
+
WebhookEventType,
|
|
766
|
+
} from 'swapped-commerce-sdk'
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### Type Safety Example
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import type { Order, OrderStatus } from 'swapped-commerce-sdk'
|
|
773
|
+
|
|
774
|
+
function processOrder(order: Order) {
|
|
775
|
+
// TypeScript knows all properties of Order
|
|
776
|
+
console.log(order.id)
|
|
777
|
+
console.log(order.status)
|
|
778
|
+
console.log(order.quote.toAmount.amount)
|
|
779
|
+
|
|
780
|
+
// Type-safe status checking
|
|
781
|
+
const isCompleted = (status: OrderStatus): boolean => {
|
|
782
|
+
return status === 'COMPLETED'
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (isCompleted(order.status)) {
|
|
786
|
+
fulfillOrder(order)
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
## Examples
|
|
792
|
+
|
|
793
|
+
Complete working examples are available in the [`examples/`](./examples/) directory:
|
|
794
|
+
|
|
795
|
+
- **Payment Links**: [`create-payment-link.ts`](./examples/create-payment-link.ts) - Creating and sharing payment links
|
|
796
|
+
- **Webhook Handling**: [`handle-webhooks.ts`](./examples/handle-webhooks.ts) - Receiving and processing webhook events
|
|
797
|
+
- **Payout Processing**: [`process-payout.ts`](./examples/process-payout.ts) - Managing payouts and settlements
|
|
798
|
+
|
|
799
|
+
### Running Examples
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
# Set your API key
|
|
803
|
+
export SWAPPED_API_KEY=your-api-key-here
|
|
804
|
+
|
|
805
|
+
# Run an example
|
|
806
|
+
bun run examples/create-payment-link.ts
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
## Testing
|
|
810
|
+
|
|
811
|
+
The SDK includes comprehensive test coverage with 27 passing unit tests covering all core functionality.
|
|
812
|
+
|
|
813
|
+
### Test Results
|
|
814
|
+
|
|
815
|
+
```
|
|
816
|
+
✅ 27 unit tests passing
|
|
817
|
+
✅ 0 unit tests failing
|
|
818
|
+
✅ 69 assertions
|
|
819
|
+
✅ 100% core functionality covered
|
|
820
|
+
✅ 6 test files
|
|
821
|
+
✅ 24 source files
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
**Test Coverage:**
|
|
825
|
+
- Error handling and factory functions (6 tests)
|
|
826
|
+
- Webhook signature verification (6 tests)
|
|
827
|
+
- HTTP client utilities (6 tests)
|
|
828
|
+
- Retry logic with exponential backoff (6 tests)
|
|
829
|
+
- Resource endpoint validation (3 tests)
|
|
830
|
+
- Performance benchmarks (4 tests)
|
|
831
|
+
|
|
832
|
+
**Code Statistics:**
|
|
833
|
+
- 24 TypeScript source files
|
|
834
|
+
- 6 comprehensive test suites
|
|
835
|
+
- Zero runtime dependencies
|
|
836
|
+
- 100% type coverage (zero `any` types)
|
|
837
|
+
|
|
838
|
+
Run tests with:
|
|
839
|
+
```bash
|
|
840
|
+
bun test
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Integration Tests
|
|
844
|
+
|
|
845
|
+
Integration tests are available for testing against the Swapped sandbox environment:
|
|
846
|
+
|
|
847
|
+
```bash
|
|
848
|
+
SWAPPED_API_KEY=your-key bun test tests/integration
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
## Performance
|
|
852
|
+
|
|
853
|
+
The SDK is designed with performance as a core consideration and includes comprehensive benchmarks.
|
|
854
|
+
|
|
855
|
+
### Benchmark Results
|
|
856
|
+
|
|
857
|
+
Performance benchmarks demonstrate the SDK's efficiency. Run benchmarks with `bun run benchmark` to see live results:
|
|
858
|
+
|
|
859
|
+
| Operation | Performance | Description |
|
|
860
|
+
|-----------|------------|-------------|
|
|
861
|
+
| **URL Building** | ~209,000 ops/sec | Efficient query parameter handling |
|
|
862
|
+
| **Request Config** | ~4,800,000 ops/sec | Fast request object creation |
|
|
863
|
+
| **Webhook Verification** | ~15,600 ops/sec | Cryptographic signature validation |
|
|
864
|
+
| **Type Safety Overhead** | ~23,500,000 ops/sec | Minimal compile-time cost |
|
|
865
|
+
|
|
866
|
+
**Latest Benchmark Run:**
|
|
867
|
+
```
|
|
868
|
+
┌──────────────────────────────────────┬──────────────┬─────────────┐
|
|
869
|
+
│ Operation │ Ops/Second │ Duration │
|
|
870
|
+
├──────────────────────────────────────┼──────────────┼─────────────┤
|
|
871
|
+
│ URL Building │ 209,058 │ 47.83ms │
|
|
872
|
+
│ Request Config Creation │ 4,788,412 │ 2.09ms │
|
|
873
|
+
│ Webhook Signature Verification │ 15,620 │ 6.40ms │
|
|
874
|
+
│ Type Safety Overhead │ 23,559,440 │ 4.24ms │
|
|
875
|
+
└──────────────────────────────────────┴──────────────┴─────────────┘
|
|
876
|
+
|
|
877
|
+
Summary:
|
|
878
|
+
• Average performance: 7,143,133 ops/sec
|
|
879
|
+
• Fastest operation: Type Safety Overhead
|
|
880
|
+
• Slowest operation: Webhook Signature Verification
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Bundle Size
|
|
884
|
+
|
|
885
|
+
The SDK is optimized for minimal bundle size:
|
|
886
|
+
|
|
887
|
+
- **Bundled Size**: 9.97 KB (gzipped: 2.6 KB)
|
|
888
|
+
- **Zero Runtime Dependencies**: No external packages required
|
|
889
|
+
- **Tree-Shakeable**: Only import what you need
|
|
890
|
+
- **Type Definitions**: Included inline, no separate @types package needed
|
|
891
|
+
|
|
892
|
+
Build the bundle with:
|
|
893
|
+
```bash
|
|
894
|
+
bun run build
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Performance Characteristics
|
|
898
|
+
|
|
899
|
+
- **Pure Functions**: No unnecessary object creation or mutations
|
|
900
|
+
- **Immutable Types**: All data structures are readonly, enabling safe sharing
|
|
901
|
+
- **Efficient HTTP**: Uses Bun's native fetch with connection pooling
|
|
902
|
+
- **Minimal Dependencies**: Zero runtime dependencies, uses only Bun built-ins
|
|
903
|
+
- **Optimized Retry Logic**: Exponential backoff prevents unnecessary requests
|
|
904
|
+
- **Type Safety**: Compile-time checks eliminate runtime type errors
|
|
905
|
+
|
|
906
|
+
### Performance Best Practices
|
|
907
|
+
|
|
908
|
+
1. **Reuse Client Instances**: Create the client once and reuse it throughout your application
|
|
909
|
+
2. **Batch Operations**: Use list endpoints with pagination instead of multiple individual requests
|
|
910
|
+
3. **Error Handling**: Implement proper retry logic for transient failures
|
|
911
|
+
4. **Webhook Processing**: Process webhooks asynchronously to avoid blocking
|
|
912
|
+
|
|
913
|
+
### Running Benchmarks
|
|
914
|
+
|
|
915
|
+
**Formatted Output (Recommended):**
|
|
916
|
+
```bash
|
|
917
|
+
bun run benchmark
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
This displays a formatted table with detailed performance metrics.
|
|
921
|
+
|
|
922
|
+
**Test Format:**
|
|
923
|
+
```bash
|
|
924
|
+
bun run benchmark:test
|
|
925
|
+
# or
|
|
926
|
+
bun test benchmarks/performance.test.ts
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
This runs benchmarks as part of the test suite with assertions.
|
|
930
|
+
|
|
931
|
+
## Requirements
|
|
932
|
+
|
|
933
|
+
- **Bun**: >= 1.0.0 (recommended runtime)
|
|
934
|
+
- **TypeScript**: >= 5.0.0
|
|
935
|
+
|
|
936
|
+
The SDK is optimized for Bun but can be used with Node.js 18+ and other JavaScript runtimes that support modern ES modules.
|
|
937
|
+
|
|
938
|
+
## Documentation
|
|
939
|
+
|
|
940
|
+
For complete API documentation and integration guides, visit:
|
|
941
|
+
|
|
942
|
+
**Swapped Commerce API Documentation**: https://docs.swapped.com/swapped-commerce/commerce-integration
|
|
943
|
+
|
|
944
|
+
## License
|
|
945
|
+
|
|
946
|
+
MIT
|
|
947
|
+
|
|
948
|
+
See [LICENSE](./LICENSE) file for details.
|