@inkress/admin-sdk 1.0.0 → 1.1.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +213 -0
  2. package/README.md +1174 -87
  3. package/dist/client.d.ts +3 -3
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/data-mappings.d.ts +177 -0
  6. package/dist/data-mappings.d.ts.map +1 -0
  7. package/dist/index.d.ts +34 -4
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.esm.js +4166 -154
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +4203 -153
  12. package/dist/index.js.map +1 -1
  13. package/dist/resources/addresses.d.ts +58 -0
  14. package/dist/resources/addresses.d.ts.map +1 -0
  15. package/dist/resources/billing-plans.d.ts +32 -15
  16. package/dist/resources/billing-plans.d.ts.map +1 -1
  17. package/dist/resources/categories.d.ts +30 -20
  18. package/dist/resources/categories.d.ts.map +1 -1
  19. package/dist/resources/currencies.d.ts +41 -0
  20. package/dist/resources/currencies.d.ts.map +1 -0
  21. package/dist/resources/exchange-rates.d.ts +50 -0
  22. package/dist/resources/exchange-rates.d.ts.map +1 -0
  23. package/dist/resources/fees.d.ts +58 -0
  24. package/dist/resources/fees.d.ts.map +1 -0
  25. package/dist/resources/financial-accounts.d.ts +47 -0
  26. package/dist/resources/financial-accounts.d.ts.map +1 -0
  27. package/dist/resources/financial-requests.d.ts +51 -0
  28. package/dist/resources/financial-requests.d.ts.map +1 -0
  29. package/dist/resources/generics.d.ts +57 -0
  30. package/dist/resources/generics.d.ts.map +1 -0
  31. package/dist/resources/kyc.d.ts +118 -0
  32. package/dist/resources/kyc.d.ts.map +1 -0
  33. package/dist/resources/merchants.d.ts +52 -15
  34. package/dist/resources/merchants.d.ts.map +1 -1
  35. package/dist/resources/orders.d.ts +74 -2
  36. package/dist/resources/orders.d.ts.map +1 -1
  37. package/dist/resources/payment-links.d.ts +65 -0
  38. package/dist/resources/payment-links.d.ts.map +1 -0
  39. package/dist/resources/payment-methods.d.ts +48 -0
  40. package/dist/resources/payment-methods.d.ts.map +1 -0
  41. package/dist/resources/products.d.ts +62 -16
  42. package/dist/resources/products.d.ts.map +1 -1
  43. package/dist/resources/public.d.ts +27 -7
  44. package/dist/resources/public.d.ts.map +1 -1
  45. package/dist/resources/subscriptions.d.ts +86 -20
  46. package/dist/resources/subscriptions.d.ts.map +1 -1
  47. package/dist/resources/tokens.d.ts +62 -0
  48. package/dist/resources/tokens.d.ts.map +1 -0
  49. package/dist/resources/transaction-entries.d.ts +48 -0
  50. package/dist/resources/transaction-entries.d.ts.map +1 -0
  51. package/dist/resources/users.d.ts +43 -21
  52. package/dist/resources/users.d.ts.map +1 -1
  53. package/dist/resources/webhook-urls.d.ts +49 -0
  54. package/dist/resources/webhook-urls.d.ts.map +1 -0
  55. package/dist/types/resources.d.ts +1294 -0
  56. package/dist/types/resources.d.ts.map +1 -0
  57. package/dist/types.d.ts +1069 -197
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/utils/query-builders.d.ts +518 -0
  60. package/dist/utils/query-builders.d.ts.map +1 -0
  61. package/dist/utils/query-transformer.d.ts +123 -0
  62. package/dist/utils/query-transformer.d.ts.map +1 -0
  63. package/dist/utils/translators.d.ts +126 -0
  64. package/dist/utils/translators.d.ts.map +1 -0
  65. package/dist/utils/webhooks.d.ts +19 -4
  66. package/dist/utils/webhooks.d.ts.map +1 -1
  67. package/package.json +14 -4
package/README.md CHANGED
@@ -11,7 +11,10 @@ Official Inkress Commerce API SDK for JavaScript/TypeScript applications.
11
11
  - 🚀 **Modern TypeScript SDK** - Built with TypeScript for excellent developer experience
12
12
  - 🔒 **Secure Authentication** - JWT-based authentication with automatic token management
13
13
  - 🌐 **Public Endpoints** - Access public merchant information without authentication
14
- - 📦 **Comprehensive API Coverage** - Full coverage of Inkress Commerce API endpoints
14
+ - 📦 **Comprehensive API Coverage** - 23+ resources with full CRUD operations
15
+ - 🎯 **100% Type-Safe** - Every method fully typed with specific interfaces
16
+ - 🔄 **Advanced Query System** - Fluent query builder with intelligent transformations
17
+ - 🌍 **Contextual Translations** - Human-readable strings automatically converted to API integers
15
18
  - 🛠️ **Easy Integration** - Simple setup and intuitive API design
16
19
  - 🔄 **Automatic Retries** - Built-in retry logic for resilient applications
17
20
  - 📱 **Cross-Platform** - Works in Node.js, browsers, and React Native
@@ -38,12 +41,77 @@ pnpm add @inkress/admin-sdk
38
41
  import { InkressSDK } from '@inkress/admin-sdk';
39
42
 
40
43
  const inkress = new InkressSDK({
41
- bearerToken: 'your-jwt-token', // Can be an empty string for public endpoints
42
- clientId: 'm-merchant-username', // Required for merchant-specific endpoints
43
- endpoint: 'https://api.inkress.com', // Optional, defaults to production
44
+ accessToken: 'your-jwt-token',
45
+ username: 'merchant-username', // Optional - automatically prepended with 'm-'
46
+ mode: 'live', // Optional - 'live' (default) or 'sandbox'
44
47
  });
45
48
  ```
46
49
 
50
+ ### Configuration Options
51
+
52
+ ```typescript
53
+ const inkress = new InkressSDK({
54
+ // Required
55
+ accessToken: 'your-jwt-token',
56
+
57
+ // Optional
58
+ username: 'merchant-username', // Prepended with 'm-' for Client-Id header
59
+ mode: 'live', // 'live' = api.inkress.com, 'sandbox' = api-dev.inkress.com
60
+ apiVersion: 'v1', // API version (default: 'v1')
61
+ timeout: 30000, // Request timeout in ms (default: 30000)
62
+ retries: 3, // Number of retry attempts (default: 0)
63
+ headers: { // Custom headers for all requests
64
+ 'X-Custom-Header': 'value'
65
+ }
66
+ });
67
+ ```
68
+
69
+ ### Why This SDK?
70
+
71
+ **🎯 100% Type-Safe** - Every single method across all 23 resources is fully typed:
72
+ ```typescript
73
+ // Every parameter and return value has explicit types
74
+ const balances: ApiResponse<MerchantBalance> = await inkress.merchants.balances();
75
+ // balances.data: { available: number, pending: number, currency: string }
76
+ ```
77
+
78
+ **🌍 Human-Readable API** - Use contextual strings instead of cryptic integers:
79
+ ```typescript
80
+ // Clear, self-documenting code with contextual strings
81
+ await inkress.orders.update(123, {
82
+ status: 'confirmed',
83
+ kind: 'online'
84
+ });
85
+ // SDK automatically converts to integers for the API
86
+ ```
87
+
88
+ **🔍 Powerful Queries** - Intuitive query syntax or fluent builders:
89
+ ```typescript
90
+ // Direct query syntax
91
+ await inkress.orders.query({
92
+ status: ['confirmed', 'shipped'],
93
+ total: { min: 100, max: 1000 },
94
+ reference_id: { contains: 'VIP' }
95
+ });
96
+
97
+ // Fluent query builder
98
+ await inkress.orders.createQueryBuilder()
99
+ .whereStatus(['confirmed', 'shipped'])
100
+ .whereTotalRange(100, 1000)
101
+ .whereReferenceContains('VIP')
102
+ .execute();
103
+ ```
104
+
105
+ **📦 Complete Coverage** - 22 resources with 125+ fully-typed methods:
106
+ - Core: Merchants, Products, Categories, Orders, Users
107
+ - Billing: Plans, Subscriptions, Payment Links, Payment Methods
108
+ - Financial: Accounts, Requests, Fees, Currencies, Exchange Rates
109
+ - Identity: Addresses, Tokens, Webhooks
110
+ - Content: Public Data
111
+ - And more...
112
+
113
+ ---
114
+
47
115
  ### Public Endpoints (No Authentication Required)
48
116
 
49
117
  ```typescript
@@ -60,41 +128,73 @@ const products = await inkress.public.getMerchantProducts('merchant-username', {
60
128
 
61
129
  // Get merchant fees
62
130
  const fees = await inkress.public.getMerchantFees('merchant-username', {
63
- currency: 'JMD',
64
- total: 1000
131
+ currency: 'JMD',
132
+ total: 1000
65
133
  });
66
134
  ```
67
135
 
68
- ### Authenticated Operations
69
-
70
- ```typescript
71
- // List categories
72
- const categories = await inkress.categories.list({ kind: 1 });
136
+ ### Authenticated Operations with Contextual Values
73
137
 
74
- // Create a new category
75
- const category = await inkress.categories.create({
76
- name: 'Electronics',
77
- description: 'Electronic devices and accessories',
78
- kind: 1
79
- });
138
+ The SDK automatically translates human-readable strings to API integers:
80
139
 
81
- // List products
82
- const products = await inkress.products.list({
83
- limit: 50,
84
- category_id: 1
85
- });
86
-
87
- // Create an order
140
+ ```typescript
141
+ // Create order with contextual strings
88
142
  const order = await inkress.orders.create({
143
+ total: 99.99,
89
144
  currency_code: 'USD',
145
+ status: 'pending', // SDK converts 'pending' → integer
146
+ kind: 'online', // SDK converts 'online' → integer
90
147
  customer: {
91
148
  email: 'customer@example.com',
92
149
  first_name: 'John',
93
150
  last_name: 'Doe'
94
151
  },
95
- total: 29.99,
96
152
  reference_id: 'order-123'
97
153
  });
154
+
155
+ // Update merchant with contextual values
156
+ const merchant = await inkress.merchants.update(123, {
157
+ status: 'approved', // SDK converts to integer
158
+ platform_fee_structure: 'customer_pay', // SDK converts to integer
159
+ provider_fee_structure: 'merchant_absorb'
160
+ });
161
+
162
+ // Create user with contextual status and kind
163
+ const user = await inkress.users.create({
164
+ email: 'user@example.com',
165
+ first_name: 'John',
166
+ last_name: 'Doe',
167
+ password: 'secure-password',
168
+ status: 'pending', // SDK converts to account_pending integer
169
+ kind: 'organisation' // SDK converts to user_organisation integer
170
+ });
171
+ ```
172
+
173
+ ### Advanced Query System
174
+
175
+ Use the intuitive query system for powerful filtering:
176
+
177
+ ```typescript
178
+ // Query with multiple conditions
179
+ const orders = await inkress.orders.query({
180
+ status: ['confirmed', 'shipped'], // Array → IN query
181
+ total: { min: 100, max: 1000 }, // Range query
182
+ reference_id: { contains: 'VIP' }, // String search
183
+ inserted_at: { after: '2024-01-01' },
184
+ page: 1,
185
+ page_size: 20
186
+ });
187
+
188
+ // Or use fluent query builder
189
+ const products = await inkress.products
190
+ .createQueryBuilder()
191
+ .whereStatus('published')
192
+ .wherePriceRange(50, 500)
193
+ .whereTitleContains('laptop')
194
+ .whereCategory(5)
195
+ .paginate(1, 20)
196
+ .orderBy('price', 'desc')
197
+ .execute();
98
198
  ```
99
199
 
100
200
  ## Configuration Options
@@ -111,179 +211,925 @@ const order = await inkress.orders.create({
111
211
 
112
212
  ## API Resources
113
213
 
114
- ### Public Resource (No Authentication)
214
+ The SDK provides access to 23+ fully-typed resources:
215
+
216
+ ### Core Resources
217
+ - **Merchants** - Merchant management and account operations
218
+ - **Products** - Product catalog with full CRUD
219
+ - **Categories** - Product categorization
220
+ - **Orders** - Order processing and tracking
221
+ - **Users** - User and account management
222
+
223
+ ### Billing & Subscriptions
224
+ - **Billing Plans** - Subscription plan management
225
+ - **Subscriptions** - Recurring billing and subscriptions
226
+ - **Payment Links** - Payment link generation
227
+ - **Payment Methods** - Payment method configuration
228
+
229
+ ### Financial
230
+ - **Financial Accounts** - Account management
231
+ - **Financial Requests** - Payout and withdrawal requests
232
+ - **Transaction Entries** - Transaction tracking
233
+ - **Fees** - Fee management and configuration
234
+ - **Exchange Rates** - Currency exchange rates
235
+ - **Currencies** - Multi-currency support
236
+
237
+ ### Identity & Access
238
+ - **Addresses** - Address management
239
+ - **Tokens** - API token management
240
+ - **Webhook URLs** - Webhook configuration
241
+
242
+ ### Content & Other
243
+ - **Generics** - Dynamic endpoint access
244
+ - **KYC** - Know Your Customer operations
245
+ - **Payout** - Payout processing
246
+ - **Public** - Public-facing merchant data
115
247
 
116
- Access public merchant information without authentication:
248
+ ---
117
249
 
118
- ```typescript
119
- // Get merchant by username or domain
120
- await inkress.public.getMerchant({ username: 'merchant-name' });
121
- await inkress.public.getMerchant({ 'domain.cname': 'store.example.com' });
250
+ ## Core Resource Examples
122
251
 
123
- // Get merchant products with filtering
124
- await inkress.public.getMerchantProducts('merchant-name', {
125
- search: 'laptop',
126
- category: 'electronics',
127
- limit: 20
128
- });
129
-
130
- // Get merchant fees
131
- await inkress.public.getMerchantFees('merchant-name');
132
- ```
252
+ ### Merchants Resource
133
253
 
134
254
  ### Merchants Resource
135
255
 
256
+ Full merchant management with contextual translations and account methods:
257
+
136
258
  ```typescript
137
- // List merchants
138
- await inkress.merchants.list();
259
+ // List merchants with contextual filtering
260
+ const merchants = await inkress.merchants.list({
261
+ status: 'approved', // Contextual: converts to integer
262
+ platform_fee_structure: 'customer_pay',
263
+ organisation_id: 123,
264
+ q: 'coffee shop'
265
+ });
266
+
267
+ // Query merchants with advanced filtering
268
+ const merchants = await inkress.merchants.query({
269
+ status: ['approved', 'active'],
270
+ platform_fee_structure: 'customer_pay',
271
+ inserted_at: { after: '2024-01-01' }
272
+ });
139
273
 
140
274
  // Get merchant details
141
- await inkress.merchants.get(merchantId);
275
+ const merchant = await inkress.merchants.get(merchantId);
276
+
277
+ // Create merchant with contextual values
278
+ const newMerchant = await inkress.merchants.create({
279
+ name: 'My Store',
280
+ email: 'store@example.com',
281
+ username: 'mystore',
282
+ status: 'pending', // Contextual
283
+ platform_fee_structure: 'customer_pay',
284
+ provider_fee_structure: 'merchant_absorb'
285
+ });
142
286
 
143
287
  // Update merchant
144
- await inkress.merchants.update(merchantId, { name: 'New Name' });
288
+ await inkress.merchants.update(merchantId, {
289
+ name: 'Updated Store Name',
290
+ status: 'approved'
291
+ });
292
+
293
+ // Merchant account methods (properly typed)
294
+ const balances = await inkress.merchants.balances();
295
+ // Returns: { available: number, pending: number, currency: string }
296
+
297
+ const limits = await inkress.merchants.limits();
298
+ // Returns: { transaction_limit: number, daily_limit: number, monthly_limit: number, currency: string }
299
+
300
+ const subscription = await inkress.merchants.subscription();
301
+ // Returns: { plan_name: string, status: string, billing_cycle: string, price: number, ... }
302
+
303
+ const invoices = await inkress.merchants.invoices();
304
+ // Returns: MerchantInvoice[]
305
+
306
+ const invoice = await inkress.merchants.invoice('invoice-123');
307
+ // Returns: MerchantInvoice
308
+
309
+ // Query builder
310
+ const merchants = await inkress.merchants
311
+ .createQueryBuilder()
312
+ .whereStatus('approved')
313
+ .wherePlatformFeeStructure('customer_pay')
314
+ .whereOrganisation(123)
315
+ .search('electronics')
316
+ .execute();
145
317
  ```
146
318
 
147
319
  ### Products Resource
148
320
 
321
+ Complete product management with status translations and advanced querying:
322
+
149
323
  ```typescript
150
- // List products with pagination and filtering
324
+ // List products with contextual filtering
151
325
  await inkress.products.list({
152
- page: 1,
153
- per_page: 50,
326
+ status: 'published', // Contextual: 'published' instead of integer
154
327
  category_id: 1,
155
- search: 'laptop'
328
+ price: { min: 50, max: 500 },
329
+ q: 'laptop'
330
+ });
331
+
332
+ // Query products with advanced filters
333
+ const products = await inkress.products.query({
334
+ status: ['published', 'featured'], // Array query
335
+ category_id: [1, 2, 3],
336
+ price: { min: 100 },
337
+ title: { contains: 'gaming' },
338
+ inserted_at: { after: '2024-01-01' }
156
339
  });
157
340
 
158
341
  // Get product details
159
342
  await inkress.products.get(productId);
160
343
 
161
- // Create a new product
344
+ // Create product with contextual status
162
345
  await inkress.products.create({
163
346
  name: 'Gaming Laptop',
164
347
  description: 'High-performance gaming laptop',
165
348
  price: 1299.99,
166
- category_id: 1
349
+ category_id: 1,
350
+ status: 'draft', // Contextual
351
+ kind: 'published' // Contextual
167
352
  });
168
353
 
169
354
  // Update product
170
- await inkress.products.update(productId, { price: 1199.99 });
355
+ await inkress.products.update(productId, {
356
+ price: 1199.99,
357
+ status: 'published' // Contextual translation
358
+ });
171
359
 
172
360
  // Delete product
173
361
  await inkress.products.delete(productId);
362
+
363
+ // Query builder
364
+ const products = await inkress.products
365
+ .createQueryBuilder()
366
+ .whereStatus('published')
367
+ .wherePriceRange(50, 500)
368
+ .whereCategory(5)
369
+ .whereTitleContains('laptop')
370
+ .paginate(1, 20)
371
+ .orderBy('price', 'desc')
372
+ .search('gaming')
373
+ .execute();
174
374
  ```
175
375
 
176
376
  ### Categories Resource
177
377
 
378
+ Category management with kind translations:
379
+
178
380
  ```typescript
179
- // List categories
180
- await inkress.categories.list({ kind: 1 });
381
+ // List categories with contextual filtering
382
+ await inkress.categories.list({
383
+ kind: 'published', // Contextual translation
384
+ q: 'electronics'
385
+ });
386
+
387
+ // Query categories
388
+ const categories = await inkress.categories.query({
389
+ kind: ['published', 'featured'],
390
+ parent_id: 1
391
+ });
181
392
 
182
393
  // Get category details
183
394
  await inkress.categories.get(categoryId);
184
395
 
185
- // Create category
396
+ // Create category with contextual kind
186
397
  await inkress.categories.create({
187
398
  name: 'Electronics',
188
399
  description: 'Electronic devices',
189
- kind: 1
400
+ kind: 'published', // Contextual
401
+ parent_id: null
190
402
  });
191
403
 
192
404
  // Update category
193
- await inkress.categories.update(categoryId, { name: 'Updated Name' });
405
+ await inkress.categories.update(categoryId, {
406
+ name: 'Updated Name',
407
+ kind: 'featured' // Contextual
408
+ });
194
409
 
195
- // Delete category
196
- await inkress.categories.delete(categoryId);
410
+ // Query builder
411
+ const categories = await inkress.categories
412
+ .createQueryBuilder()
413
+ .whereKind('published')
414
+ .whereParent(1)
415
+ .search('electronics')
416
+ .execute();
197
417
  ```
198
418
 
199
419
  ### Orders Resource
200
420
 
421
+ Order processing with full status and kind translations:
422
+
201
423
  ```typescript
202
- // Create an order
424
+ // Create order with contextual strings
203
425
  await inkress.orders.create({
426
+ total: 99.99,
204
427
  currency_code: 'USD',
205
428
  customer: {
206
429
  email: 'customer@example.com',
207
430
  first_name: 'John',
208
431
  last_name: 'Doe'
209
432
  },
210
- total: 99.99,
211
433
  reference_id: 'order-123',
212
- kind: 'online'
434
+ kind: 'online', // Contextual: converts to integer
435
+ status: 'pending' // Contextual: converts to integer
213
436
  });
214
437
 
215
438
  // Get order details
216
439
  await inkress.orders.get(orderId);
217
440
 
218
- // Update order status
219
- await inkress.orders.update(orderId, { status: 2 });
441
+ // Update order status with contextual string
442
+ await inkress.orders.update(orderId, {
443
+ status: 'confirmed' // Contextual translation
444
+ });
445
+
446
+ // List orders with contextual filtering
447
+ await inkress.orders.list({
448
+ status: 'shipped', // Contextual
449
+ kind: 'online', // Contextual
450
+ customer_id: 123,
451
+ q: 'electronics'
452
+ });
453
+
454
+ // Query orders with advanced filters
455
+ const orders = await inkress.orders.query({
456
+ status: ['confirmed', 'shipped'],
457
+ kind: ['online', 'subscription'],
458
+ total: { min: 100, max: 1000 },
459
+ reference_id: { contains: 'VIP' },
460
+ inserted_at: { after: '2024-01-01' }
461
+ });
462
+
463
+ // Delete order
464
+ await inkress.orders.delete(orderId);
220
465
 
221
466
  // Get order status (public endpoint)
222
467
  await inkress.orders.getStatus(orderId);
223
468
 
224
- // List orders
225
- await inkress.orders.list();
469
+ // Query builder
470
+ const orders = await inkress.orders
471
+ .createQueryBuilder()
472
+ .whereStatus(['confirmed', 'shipped'])
473
+ .whereKind('online')
474
+ .whereTotalRange(100, 1000)
475
+ .whereReferenceContains('PREMIUM')
476
+ .whereCustomer(123)
477
+ .paginate(1, 20)
478
+ .execute();
226
479
  ```
227
480
 
228
481
  ### Users Resource
229
482
 
483
+ User management with status and kind translations:
484
+
230
485
  ```typescript
231
- // List users
232
- await inkress.users.list();
486
+ // List users with contextual filtering
487
+ await inkress.users.list({
488
+ status: 'approved', // Contextual: account_approved
489
+ kind: 'organisation', // Contextual: user_organisation
490
+ organisation_id: 123,
491
+ q: 'admin'
492
+ });
493
+
494
+ // Query users
495
+ const users = await inkress.users.query({
496
+ status: ['approved', 'active'],
497
+ kind: ['organisation', 'merchant'],
498
+ inserted_at: { after: '2024-01-01' }
499
+ });
233
500
 
234
501
  // Get user details
235
502
  await inkress.users.get(userId);
236
503
 
237
- // Create user
504
+ // Create user with contextual values
238
505
  await inkress.users.create({
239
506
  email: 'user@example.com',
240
507
  first_name: 'John',
241
508
  last_name: 'Doe',
242
- role: 'customer'
509
+ password: 'secure-password',
510
+ status: 'pending', // Contextual
511
+ kind: 'organisation' // Contextual
243
512
  });
244
513
 
245
- // Update user
246
- await inkress.users.update(userId, { first_name: 'Jane' });
514
+ // Update user with contextual status
515
+ await inkress.users.update(userId, {
516
+ first_name: 'Jane',
517
+ status: 'approved' // Contextual
518
+ });
247
519
 
248
520
  // Delete user
249
521
  await inkress.users.delete(userId);
522
+
523
+ // Query builder
524
+ const users = await inkress.users
525
+ .createQueryBuilder()
526
+ .whereStatus('approved')
527
+ .whereKind('organisation')
528
+ .whereOrganisation(123)
529
+ .search('john')
530
+ .execute();
250
531
  ```
251
532
 
252
533
  ### Billing Plans Resource
253
534
 
535
+ Billing plan management with kind translations:
536
+
254
537
  ```typescript
255
538
  // List billing plans
256
- await inkress.billingPlans.list();
539
+ await inkress.billingPlans.list({
540
+ kind: 'subscription', // Contextual
541
+ status: 'active'
542
+ });
543
+
544
+ // Query plans
545
+ const plans = await inkress.billingPlans.query({
546
+ kind: 'subscription',
547
+ public: true,
548
+ amount: { min: 10, max: 100 }
549
+ });
257
550
 
258
551
  // Get plan details
259
552
  await inkress.billingPlans.get(planId);
260
553
 
261
- // Create billing plan
554
+ // Create billing plan with contextual kind
262
555
  await inkress.billingPlans.create({
263
556
  name: 'Premium Plan',
264
557
  amount: 29.99,
265
- currency: 'USD'
558
+ currency: 'USD',
559
+ kind: 'subscription', // Contextual
560
+ status: 'active'
561
+ });
562
+
563
+ // Update plan
564
+ await inkress.billingPlans.update(planId, {
565
+ amount: 24.99,
566
+ status: 'active'
266
567
  });
568
+
569
+ // Delete plan
570
+ await inkress.billingPlans.delete(planId);
571
+
572
+ // Query builder
573
+ const plans = await inkress.billingPlans
574
+ .createQueryBuilder()
575
+ .whereKind('subscription')
576
+ .wherePublic(true)
577
+ .whereAmountRange(10, 50)
578
+ .execute();
267
579
  ```
268
580
 
269
581
  ### Subscriptions Resource
270
582
 
583
+ Subscription management with proper typing for all methods:
584
+
271
585
  ```typescript
272
586
  // List subscriptions
273
- await inkress.subscriptions.list();
587
+ await inkress.subscriptions.list({
588
+ status: 'active', // Contextual
589
+ billing_plan_id: 1,
590
+ customer_id: 123
591
+ });
592
+
593
+ // Query subscriptions
594
+ const subscriptions = await inkress.subscriptions.query({
595
+ status: ['active', 'trialing'],
596
+ billing_plan_id: [1, 2, 3],
597
+ inserted_at: { after: '2024-01-01' }
598
+ });
274
599
 
275
600
  // Get subscription details
276
601
  await inkress.subscriptions.get(subscriptionId);
277
602
 
278
- // Create subscription
603
+ // Create subscription with contextual values
279
604
  await inkress.subscriptions.create({
280
- plan_id: 1,
605
+ billing_plan_id: 1,
606
+ record: 'customer',
607
+ record_id: 123,
608
+ start_date: '2024-01-01',
609
+ status: 'active', // Contextual
610
+ kind: 'recurring' // Contextual
611
+ });
612
+
613
+ // Delete subscription
614
+ await inkress.subscriptions.delete(subscriptionId);
615
+
616
+ // Create subscription link (fully typed)
617
+ const link = await inkress.subscriptions.createLink({
618
+ reference_id: 'sub-123',
619
+ title: 'Premium Subscription',
620
+ plan_uid: 'plan-abc',
281
621
  customer: {
282
- email: 'customer@example.com',
283
622
  first_name: 'John',
284
- last_name: 'Doe'
623
+ last_name: 'Doe',
624
+ email: 'john@example.com'
285
625
  }
286
626
  });
627
+ // Returns: CreateSubscriptionLinkResponse
628
+
629
+ // Charge subscription (fully typed)
630
+ const charge = await inkress.subscriptions.charge('sub-uid', {
631
+ reference_id: 'charge-123',
632
+ total: 29.99,
633
+ title: 'Monthly charge'
634
+ });
635
+ // Returns: ChargeSubscriptionResponse with typed transaction
636
+
637
+ // Record usage (fully typed)
638
+ const usage = await inkress.subscriptions.usage('sub-uid', {
639
+ reference_id: 'usage-123',
640
+ total: 5.00,
641
+ title: 'API calls'
642
+ });
643
+ // Returns: SubscriptionUsageResponse
644
+
645
+ // Cancel subscription (fully typed)
646
+ const cancelled = await inkress.subscriptions.cancel(123, 'reason-code');
647
+ // Returns: SubscriptionCancelResponse
648
+
649
+ // Get subscription periods
650
+ const periods = await inkress.subscriptions.getPeriods('sub-uid', {
651
+ status: 'paid',
652
+ limit: 10
653
+ });
654
+
655
+ // Query builder
656
+ const subscriptions = await inkress.subscriptions
657
+ .createQueryBuilder()
658
+ .whereStatus('active')
659
+ .whereBillingPlan(1)
660
+ .whereCustomer(123)
661
+ .execute();
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Additional Resources
667
+
668
+ ### Payment Links
669
+
670
+ ```typescript
671
+ // List payment links
672
+ await inkress.paymentLinks.list({ status: 'active' });
673
+
674
+ // Create payment link
675
+ await inkress.paymentLinks.create({
676
+ title: 'Product Payment',
677
+ amount: 99.99,
678
+ currency: 'USD',
679
+ status: 'active'
680
+ });
681
+
682
+ // Update payment link
683
+ await inkress.paymentLinks.update(linkId, { amount: 89.99 });
684
+
685
+ // Delete payment link
686
+ await inkress.paymentLinks.delete(linkId);
687
+ ```
688
+
689
+ ### Financial Accounts
690
+
691
+ ```typescript
692
+ // List financial accounts
693
+ await inkress.financialAccounts.list();
694
+
695
+ // Create account
696
+ await inkress.financialAccounts.create({
697
+ name: 'Main Account',
698
+ type: 'checking',
699
+ currency: 'USD'
700
+ });
701
+
702
+ // Update account
703
+ await inkress.financialAccounts.update(accountId, { name: 'Updated Name' });
704
+ ```
705
+
706
+ ### Tokens
707
+
708
+ ```typescript
709
+ // List tokens
710
+ await inkress.tokens.list({ kind: 'api', enabled: true });
711
+
712
+ // Create token
713
+ await inkress.tokens.create({
714
+ title: 'Production API Key',
715
+ provider: 'stripe',
716
+ kind: 'api'
717
+ });
718
+
719
+ // Delete token
720
+ await inkress.tokens.delete(tokenId);
721
+ ```
722
+
723
+ ### Addresses
724
+
725
+ ```typescript
726
+ // List addresses
727
+ await inkress.addresses.list({ country: 'US' });
728
+
729
+ // Create address
730
+ await inkress.addresses.create({
731
+ line1: '123 Main St',
732
+ city: 'New York',
733
+ state: 'NY',
734
+ country: 'US',
735
+ postal_code: '10001'
736
+ });
737
+
738
+ // Update address
739
+ await inkress.addresses.update(addressId, { line1: '456 Broadway' });
740
+
741
+ // Delete address
742
+ await inkress.addresses.delete(addressId);
743
+ ```
744
+
745
+ ### Fees
746
+
747
+ ```typescript
748
+ // List fees
749
+ await inkress.fees.list({ active: true });
750
+
751
+ // Create fee
752
+ await inkress.fees.create({
753
+ name: 'Processing Fee',
754
+ amount: 2.50,
755
+ percentage: 2.9,
756
+ kind: 'transaction'
757
+ });
758
+
759
+ // Update fee
760
+ await inkress.fees.update(feeId, { amount: 2.75 });
761
+
762
+ // Delete fee
763
+ await inkress.fees.delete(feeId);
764
+ ```
765
+
766
+ ### Exchange Rates & Currencies
767
+
768
+ ```typescript
769
+ // List currencies
770
+ await inkress.currencies.list();
771
+
772
+ // Create currency
773
+ await inkress.currencies.create({
774
+ code: 'USD',
775
+ name: 'US Dollar',
776
+ symbol: '$'
777
+ });
778
+
779
+ // List exchange rates
780
+ await inkress.exchangeRates.list({ from_currency: 'USD' });
781
+
782
+ // Create exchange rate
783
+ await inkress.exchangeRates.create({
784
+ from_currency: 'USD',
785
+ to_currency: 'EUR',
786
+ rate: 0.85
787
+ });
788
+
789
+ // Update exchange rate
790
+ await inkress.exchangeRates.update(rateId, { rate: 0.86 });
791
+ ```
792
+
793
+ ### Public Resource
794
+
795
+ Access public merchant information without authentication:
796
+
797
+ ```typescript
798
+ // Get merchant by username or domain
799
+ await inkress.public.getMerchant({ username: 'merchant-name' });
800
+ await inkress.public.getMerchant({ 'domain.cname': 'store.example.com' });
801
+
802
+ // Get merchant products with filtering
803
+ await inkress.public.getMerchantProducts('merchant-name', {
804
+ search: 'laptop',
805
+ category: 'electronics',
806
+ limit: 20
807
+ });
808
+
809
+ // Get merchant fees (fully typed)
810
+ const fees = await inkress.public.getMerchantFees('merchant-name', {
811
+ currency: 'USD',
812
+ total: 100
813
+ });
814
+ // Returns: PublicMerchantFees
815
+ ```
816
+
817
+ ## Advanced Query System
818
+
819
+ The SDK provides a powerful type-safe query system with two interfaces:
820
+
821
+ ### 1. Direct Query Method
822
+
823
+ Use the `query()` method with an intuitive object-based syntax:
824
+
825
+ ```typescript
826
+ // Simple equality
827
+ await inkress.orders.query({
828
+ status: 'confirmed',
829
+ customer_id: 123
830
+ });
831
+
832
+ // Array queries (IN operations)
833
+ await inkress.orders.query({
834
+ status: ['confirmed', 'shipped', 'delivered'],
835
+ id: [1, 2, 3, 4, 5]
836
+ });
837
+
838
+ // Range queries
839
+ await inkress.products.query({
840
+ price: { min: 100, max: 1000 },
841
+ rating: { min: 4 }
842
+ });
843
+
844
+ // String contains
845
+ await inkress.products.query({
846
+ title: { contains: 'laptop' },
847
+ description: { contains: 'gaming' }
848
+ });
849
+
850
+ // Date ranges
851
+ await inkress.orders.query({
852
+ inserted_at: { after: '2024-01-01', before: '2024-12-31' },
853
+ updated_at: { on: '2024-06-15' }
854
+ });
855
+
856
+ // Complex combined queries
857
+ await inkress.orders.query({
858
+ status: ['confirmed', 'shipped'],
859
+ kind: 'online',
860
+ total: { min: 50, max: 500 },
861
+ reference_id: { contains: 'VIP' },
862
+ customer_id: [100, 101, 102],
863
+ inserted_at: { after: '2024-01-01' },
864
+ page: 1,
865
+ page_size: 20,
866
+ q: 'electronics'
867
+ });
868
+ ```
869
+
870
+ ### 2. Fluent Query Builder
871
+
872
+ Use the query builder for a fluent, chainable interface:
873
+
874
+ ```typescript
875
+ // Products query builder
876
+ const products = await inkress.products
877
+ .createQueryBuilder()
878
+ .whereStatus('published')
879
+ .whereStatusIn(['published', 'featured'])
880
+ .whereCategory(5)
881
+ .whereCategoryIn([1, 2, 3])
882
+ .wherePriceRange(50, 500)
883
+ .wherePriceMin(50)
884
+ .wherePriceMax(500)
885
+ .whereTitleContains('gaming')
886
+ .whereDescriptionContains('RGB')
887
+ .whereCreatedAfter('2024-01-01')
888
+ .whereCreatedBefore('2024-12-31')
889
+ .whereCreatedBetween('2024-01-01', '2024-12-31')
890
+ .paginate(1, 20)
891
+ .orderBy('price', 'desc')
892
+ .search('laptop')
893
+ .execute();
894
+
895
+ // Orders query builder
896
+ const orders = await inkress.orders
897
+ .createQueryBuilder()
898
+ .whereStatus('confirmed')
899
+ .whereStatusIn(['confirmed', 'shipped'])
900
+ .whereKind('online')
901
+ .whereKindIn(['online', 'subscription'])
902
+ .whereTotalRange(100, 1000)
903
+ .whereReferenceContains('VIP')
904
+ .whereCustomer(123)
905
+ .whereCustomerIn([100, 101, 102])
906
+ .whereCreatedBetween('2024-01-01', '2024-12-31')
907
+ .paginate(1, 20)
908
+ .search('electronics')
909
+ .execute();
910
+
911
+ // Merchants query builder
912
+ const merchants = await inkress.merchants
913
+ .createQueryBuilder()
914
+ .whereStatus('approved')
915
+ .wherePlatformFeeStructure('customer_pay')
916
+ .whereProviderFeeStructure('merchant_absorb')
917
+ .whereOrganisation(123)
918
+ .whereNameContains('coffee')
919
+ .paginate(1, 50)
920
+ .execute();
921
+
922
+ // Users query builder
923
+ const users = await inkress.users
924
+ .createQueryBuilder()
925
+ .whereStatus('approved')
926
+ .whereKind('organisation')
927
+ .whereOrganisation(123)
928
+ .whereEmailContains('@company.com')
929
+ .paginate(1, 50)
930
+ .search('admin')
931
+ .execute();
932
+ ```
933
+
934
+ ### Query Transformation
935
+
936
+ The SDK automatically transforms your clean queries into API-compatible format:
937
+
938
+ ```typescript
939
+ // You write:
940
+ {
941
+ status: ['confirmed', 'shipped'],
942
+ total: { min: 100, max: 1000 },
943
+ reference_id: { contains: 'VIP' }
944
+ }
945
+
946
+ // SDK transforms to:
947
+ {
948
+ status_in: [4, 7], // Contextual strings → integers with _in suffix
949
+ total_min: 100, // min/max → _min/_max suffixes
950
+ total_max: 1000,
951
+ "contains.reference_id": "VIP" // contains → prefix format
952
+ }
953
+ ```
954
+
955
+ ### Available Query Operations
956
+
957
+ | Operation | Input Syntax | API Output | Description |
958
+ |-----------|-------------|------------|-------------|
959
+ | **Equality** | `field: value` | `field: value` | Direct match |
960
+ | **Array (IN)** | `field: [1,2,3]` | `field_in: [1,2,3]` | Value in array |
961
+ | **Range Min** | `field: {min: 10}` | `field_min: 10` | Minimum value |
962
+ | **Range Max** | `field: {max: 100}` | `field_max: 100` | Maximum value |
963
+ | **Contains** | `field: {contains: 'text'}` | `"contains.field": "text"` | String contains |
964
+ | **Date After** | `field: {after: 'date'}` | `"after.field": "date"` | After date |
965
+ | **Date Before** | `field: {before: 'date'}` | `"before.field": "date"` | Before date |
966
+ | **Date On** | `field: {on: 'date'}` | `"on.field": "date"` | Exact date |
967
+
968
+ ### Query Builder Methods
969
+
970
+ All resources with query support provide these methods:
971
+
972
+ **Equality Methods:**
973
+ - `.whereField(value)` - Direct equality
974
+ - `.whereFieldIn([values])` - Array IN query
975
+
976
+ **Range Methods:**
977
+ - `.whereFieldRange(min, max)` - Min and max
978
+ - `.whereFieldMin(value)` - Minimum value
979
+ - `.whereFieldMax(value)` - Maximum value
980
+
981
+ **String Methods:**
982
+ - `.whereFieldContains(text)` - String contains
983
+
984
+ **Date Methods:**
985
+ - `.whereCreatedAfter(date)` - After date
986
+ - `.whereCreatedBefore(date)` - Before date
987
+ - `.whereCreatedBetween(start, end)` - Date range
988
+ - `.whereUpdatedAfter(date)` - After date
989
+ - `.whereUpdatedBefore(date)` - Before date
990
+
991
+ **Utility Methods:**
992
+ - `.paginate(page, pageSize)` - Pagination
993
+ - `.orderBy(field, direction)` - Sorting
994
+ - `.search(query)` - General search (q parameter)
995
+ - `.execute()` - Execute the query
996
+
997
+ ### Field Type Validation
998
+
999
+ The SDK validates field types at runtime to prevent API errors:
1000
+
1001
+ ```typescript
1002
+ // Runtime validation ensures correct types
1003
+ await inkress.products.query({
1004
+ price: 99.99, // ✅ Number field
1005
+ unlimited: true, // ✅ Boolean field
1006
+ category_id: 5, // ✅ Integer field
1007
+ inserted_at: '2024-01-01' // ✅ String field
1008
+ });
1009
+
1010
+ // Type mismatches are caught early
1011
+ await inkress.products.query({
1012
+ price: { contains: 'text' } // ❌ Error: price is numeric, not string
1013
+ });
1014
+ ```
1015
+
1016
+ ## Search and Filtering
1017
+
1018
+ All list operations support comprehensive search and filtering capabilities:
1019
+
1020
+ ### General Search with `q`
1021
+
1022
+ Use the `q` parameter for intelligent searching across multiple relevant fields:
1023
+
1024
+ ```typescript
1025
+ // Search merchants - searches name, email, username, etc.
1026
+ await inkress.merchants.list({ q: 'john smith' });
1027
+
1028
+ // Search products - searches title, description, etc.
1029
+ await inkress.products.list({ q: 'gaming laptop' });
1030
+
1031
+ // Search orders - searches reference ID, customer details, etc.
1032
+ await inkress.orders.list({ q: 'ORDER-12345' });
1033
+ ```
1034
+
1035
+ ### String-Based Status and Kind Values
1036
+
1037
+ The SDK supports human-readable string values for better code clarity:
1038
+
1039
+ ```typescript
1040
+ // Use descriptive strings for merchants
1041
+ await inkress.merchants.list({
1042
+ status: 'account_approved',
1043
+ platform_fee_structure: 'customer_pay',
1044
+ q: 'electronics'
1045
+ });
1046
+
1047
+ // Filter orders with readable values
1048
+ await inkress.orders.list({
1049
+ status: 'order_confirmed',
1050
+ kind: 'order_online',
1051
+ q: 'laptop'
1052
+ });
1053
+
1054
+ // Filter products by status
1055
+ await inkress.products.list({
1056
+ status: 'product_published',
1057
+ q: 'smartphone'
1058
+ });
1059
+
1060
+ // Integers also work for backward compatibility
1061
+ await inkress.merchants.list({
1062
+ status: 2,
1063
+ platform_fee_structure: 1
1064
+ });
1065
+ ```
1066
+
1067
+ ### Available String Values
1068
+
1069
+ **Status Values:**
1070
+ - Orders: `order_pending`, `order_confirmed`, `order_shipped`, `order_delivered`, `order_cancelled`, etc.
1071
+ - Accounts: `account_pending`, `account_approved`, `account_suspended`, etc.
1072
+ - Products: `product_draft`, `product_published`, `product_archived`
1073
+ - Transactions: `transaction_pending`, `transaction_authorized`, `transaction_captured`, etc.
1074
+
1075
+ **Kind Values:**
1076
+ - Orders: `order_online`, `order_offline`, `order_subscription`, `order_invoice`
1077
+ - Products: `product_draft`, `product_published`, `product_archived`
1078
+ - Users: `user_address`, `role_organisation`, `role_store`
1079
+ - Billing: `billing_plan_subscription`, `billing_plan_payout`
1080
+
1081
+ **Fee Structure Values:**
1082
+ - `customer_pay` - Customer pays the fees
1083
+ - `merchant_absorb` - Merchant absorbs the fees
1084
+
1085
+ ### Database Field Filtering
1086
+
1087
+ Filter by any database field for precise results:
1088
+
1089
+ ```typescript
1090
+ // Filter products by specific criteria
1091
+ await inkress.products.list({
1092
+ status: 'product_published', // Published only (string format)
1093
+ category_id: 5, // Specific category
1094
+ price: 1000, // Exact price
1095
+ unlimited: true, // Unlimited quantity
1096
+ inserted_at: '2024-01-01' // Created after date
1097
+ });
1098
+
1099
+ // Filter merchants by organization
1100
+ await inkress.merchants.list({
1101
+ organisation_id: 123,
1102
+ status: 'account_approved',
1103
+ platform_fee_structure: 'customer_pay'
1104
+ });
1105
+ ```
1106
+
1107
+ ### Combining Search and Filters
1108
+
1109
+ Mix general search with specific filters for powerful queries:
1110
+
1111
+ ```typescript
1112
+ await inkress.products.list({
1113
+ q: 'phone', // General search
1114
+ status: 'product_published', // Published only
1115
+ category_id: 5, // Electronics category
1116
+ page: 1, // Pagination
1117
+ per_page: 20, // Results per page
1118
+ sort: 'price', // Sort by price
1119
+ order: 'desc' // Descending order
1120
+ });
1121
+ ```
1122
+
1123
+ ### Legacy Search Field
1124
+
1125
+ Many resources still support the legacy `search` field for backward compatibility:
1126
+
1127
+ ```typescript
1128
+ // Legacy approach (still works)
1129
+ await inkress.products.list({ search: 'laptop' });
1130
+
1131
+ // Recommended approach
1132
+ await inkress.products.list({ q: 'laptop' });
287
1133
  ```
288
1134
 
289
1135
  ## Error Handling
@@ -297,7 +1143,7 @@ try {
297
1143
  if (error.response?.status === 404) {
298
1144
  console.log('Product not found');
299
1145
  } else if (error.response?.status === 422) {
300
- console.log('Validation errors:', error.response.data);
1146
+ console.log('Validation errors:', error.response.result);
301
1147
  } else {
302
1148
  console.log('Unexpected error:', error.message);
303
1149
  }
@@ -306,22 +1152,192 @@ try {
306
1152
 
307
1153
  ## TypeScript Support
308
1154
 
309
- The SDK is built with TypeScript and provides comprehensive type definitions:
1155
+ The SDK is built with TypeScript and provides **100% type safety** across all 128+ methods:
1156
+
1157
+ ### Complete Type Coverage
1158
+
1159
+ Every method has:
1160
+ - ✅ Fully typed input parameters (no `any` types)
1161
+ - ✅ Fully typed return values with specific interfaces
1162
+ - ✅ IDE autocomplete for all fields and methods
1163
+ - ✅ Compile-time type checking
310
1164
 
311
1165
  ```typescript
312
1166
  import {
313
1167
  InkressSDK,
1168
+ // Core types
314
1169
  Product,
315
1170
  Category,
316
1171
  Order,
317
1172
  Merchant,
1173
+ User,
1174
+ BillingPlan,
1175
+ Subscription,
1176
+
1177
+ // Create/Update types
318
1178
  CreateProductData,
319
- ApiResponse
1179
+ UpdateProductData,
1180
+ CreateOrderData,
1181
+ UpdateMerchantData,
1182
+ CreateUserData,
1183
+
1184
+ // Response types
1185
+ ApiResponse,
1186
+ ProductListResponse,
1187
+ OrderListResponse,
1188
+ MerchantBalance,
1189
+ MerchantLimits,
1190
+ MerchantSubscription,
1191
+ MerchantInvoice,
1192
+
1193
+ // Query types
1194
+ ProductQueryParams,
1195
+ OrderQueryParams,
1196
+ RangeQuery,
1197
+ StringQuery,
1198
+
1199
+ // Enum types
1200
+ OrderStatus,
1201
+ OrderKind,
1202
+ AccountStatus,
1203
+ UserKind,
1204
+ FeeStructureKey,
1205
+ StatusKey,
1206
+ KindKey
320
1207
  } from '@inkress/admin-sdk';
321
1208
 
322
- // All API responses are properly typed
323
- const response: ApiResponse<Product[]> = await inkress.products.list();
324
- const products: Product[] = response.result || response.data || [];
1209
+ // All responses are properly typed
1210
+ const response: ApiResponse<ProductListResponse> = await inkress.products.list();
1211
+ const products: Product[] = response.result?.entries || [];
1212
+
1213
+ // Create operations with full typing
1214
+ const createData: CreateProductData = {
1215
+ name: 'Gaming Laptop',
1216
+ price: 1299.99,
1217
+ category_id: 1,
1218
+ status: 'published',
1219
+ kind: 'published'
1220
+ };
1221
+ const product: ApiResponse<Product> = await inkress.products.create(createData);
1222
+
1223
+ // Update operations with full typing
1224
+ const updateData: UpdateMerchantData = {
1225
+ name: 'Updated Store',
1226
+ status: 'approved',
1227
+ platform_fee_structure: 'customer_pay'
1228
+ };
1229
+ await inkress.merchants.update(123, updateData);
1230
+
1231
+ // Merchant account methods with specific types
1232
+ const balances: ApiResponse<MerchantBalance> = await inkress.merchants.balances();
1233
+ // balances.data: { available: number, pending: number, currency: string }
1234
+
1235
+ const limits: ApiResponse<MerchantLimits> = await inkress.merchants.limits();
1236
+ // limits.data: { transaction_limit: number, daily_limit: number, monthly_limit: number, currency: string }
1237
+
1238
+ const subscription: ApiResponse<MerchantSubscription> = await inkress.merchants.subscription();
1239
+ // subscription.data: { plan_name: string, status: string, billing_cycle: string, price: number, ... }
1240
+
1241
+ const invoices: ApiResponse<MerchantInvoice[]> = await inkress.merchants.invoices();
1242
+ // invoices.data: Array of typed MerchantInvoice objects
1243
+
1244
+ // Query with full type safety
1245
+ const orderQuery: OrderQueryParams = {
1246
+ status: ['confirmed', 'shipped'],
1247
+ total: { min: 100, max: 1000 },
1248
+ reference_id: { contains: 'VIP' },
1249
+ page: 1,
1250
+ page_size: 20
1251
+ };
1252
+ const orders: ApiResponse<OrderListResponse> = await inkress.orders.query(orderQuery);
1253
+
1254
+ // Contextual enums for type safety
1255
+ const status: OrderStatus = 'confirmed'; // Only valid order statuses allowed
1256
+ const kind: OrderKind = 'online'; // Only valid order kinds allowed
1257
+ const feeStructure: FeeStructureKey = 'customer_pay'; // Only valid fee structures allowed
1258
+ ```
1259
+
1260
+ ### Type-Safe Query Building
1261
+
1262
+ Query builders provide complete type safety:
1263
+
1264
+ ```typescript
1265
+ // Typed query builder - only valid methods for each field type
1266
+ const products = await inkress.products
1267
+ .createQueryBuilder()
1268
+ .whereStatus('published') // String field - contextual value
1269
+ .whereCategory(5) // Number field - direct value
1270
+ .wherePriceRange(50, 500) // Number field - range
1271
+ .whereTitleContains('gaming') // String field - contains
1272
+ .whereUnlimited(true) // Boolean field - direct value
1273
+ .whereCreatedAfter('2024-01-01') // Date field - after
1274
+ .paginate(1, 20)
1275
+ .execute();
1276
+
1277
+ // TypeScript ensures you can't use wrong query types
1278
+ products
1279
+ .wherePrice({ contains: 'text' }) // ❌ Compile error: price is numeric
1280
+ .whereTitle(123); // ❌ Compile error: title is string
1281
+ ```
1282
+
1283
+ ### Response Type Handling
1284
+
1285
+ Handle responses with full type awareness:
1286
+
1287
+ ```typescript
1288
+ async function getProduct(id: number): Promise<Product | null> {
1289
+ try {
1290
+ const response: ApiResponse<Product> = await inkress.products.get(id);
1291
+
1292
+ // TypeScript knows the exact structure
1293
+ if (response.result) {
1294
+ return response.result; // Product type
1295
+ }
1296
+ return null;
1297
+ } catch (error) {
1298
+ console.error('Failed to fetch product:', error);
1299
+ return null;
1300
+ }
1301
+ }
1302
+
1303
+ async function listProducts(): Promise<Product[]> {
1304
+ const response: ApiResponse<ProductListResponse> = await inkress.products.list();
1305
+
1306
+ // TypeScript knows ProductListResponse structure
1307
+ const entries = response.result?.entries || [];
1308
+
1309
+ // entries is Product[]
1310
+ return entries.map(product => ({
1311
+ ...product,
1312
+ discounted_price: product.price * 0.9 // TypeScript knows price is a number
1313
+ }));
1314
+ }
1315
+ ```
1316
+
1317
+ ### Discriminated Unions
1318
+
1319
+ Use TypeScript's discriminated unions for status/kind handling:
1320
+
1321
+ ```typescript
1322
+ type OrderStatusAction =
1323
+ | { status: 'pending', action: 'await_payment' }
1324
+ | { status: 'confirmed', action: 'process_order' }
1325
+ | { status: 'shipped', action: 'track_shipment' }
1326
+ | { status: 'delivered', action: 'request_feedback' };
1327
+
1328
+ async function handleOrder(orderId: number, statusAction: OrderStatusAction) {
1329
+ await inkress.orders.update(orderId, { status: statusAction.status });
1330
+
1331
+ // TypeScript narrows the type based on status
1332
+ switch (statusAction.status) {
1333
+ case 'pending':
1334
+ // statusAction.action is 'await_payment'
1335
+ break;
1336
+ case 'confirmed':
1337
+ // statusAction.action is 'process_order'
1338
+ break;
1339
+ }
1340
+ }
325
1341
  ```
326
1342
 
327
1343
  ## Environment Configuration
@@ -401,7 +1417,7 @@ Always implement proper error handling:
401
1417
  async function fetchProducts() {
402
1418
  try {
403
1419
  const response = await inkress.products.list();
404
- return response.result || response.data || [];
1420
+ return response.result || [];
405
1421
  } catch (error) {
406
1422
  console.error('Failed to fetch products:', error);
407
1423
  return [];
@@ -431,10 +1447,81 @@ async function getCachedMerchant(username: string) {
431
1447
  }
432
1448
  ```
433
1449
 
1450
+ ### 4. Webhook Verification
1451
+
1452
+ Verify incoming webhook requests from Inkress using HMAC SHA256:
1453
+
1454
+ ```typescript
1455
+ import { WebhookUtils } from '@inkress/admin-sdk';
1456
+
1457
+ // Method 1: Verify with signature, body, and secret
1458
+ app.post('/webhooks/inkress', (req, res) => {
1459
+ const signature = req.headers['x-inkress-webhook-signature'];
1460
+ const body = JSON.stringify(req.body);
1461
+ const secret = process.env.INKRESS_WEBHOOK_SECRET;
1462
+
1463
+ const isValid = WebhookUtils.verifySignature(body, signature, secret);
1464
+
1465
+ if (!isValid) {
1466
+ return res.status(401).json({ error: 'Invalid signature' });
1467
+ }
1468
+
1469
+ // Process webhook...
1470
+ res.status(200).json({ received: true });
1471
+ });
1472
+
1473
+ // Method 2: Let the SDK extract everything from the request
1474
+ app.post('/webhooks/inkress', (req, res) => {
1475
+ const secret = process.env.INKRESS_WEBHOOK_SECRET;
1476
+
1477
+ try {
1478
+ const { isValid, body } = WebhookUtils.verifyRequest(req, secret);
1479
+
1480
+ if (!isValid) {
1481
+ return res.status(401).json({ error: 'Invalid signature' });
1482
+ }
1483
+
1484
+ // Parse the verified body
1485
+ const webhookPayload = WebhookUtils.parsePayload(body);
1486
+
1487
+ // Process webhook...
1488
+ res.status(200).json({ received: true });
1489
+ } catch (error) {
1490
+ res.status(400).json({ error: error.message });
1491
+ }
1492
+ });
1493
+
1494
+ // Method 3: Use Express middleware for automatic verification
1495
+ import { createWebhookMiddleware } from '@inkress/admin-sdk';
1496
+
1497
+ app.use('/webhooks/inkress', createWebhookMiddleware(process.env.INKRESS_WEBHOOK_SECRET));
1498
+
1499
+ app.post('/webhooks/inkress', (req, res) => {
1500
+ // Request is already verified, payload attached to req.webhookPayload
1501
+ const { webhookPayload } = req;
1502
+
1503
+ console.log(`Received event: ${webhookPayload.event.action}`);
1504
+
1505
+ res.status(200).json({ received: true });
1506
+ });
1507
+ ```
1508
+
1509
+ **Webhook Signature Format:**
1510
+ - Header: `X-Inkress-Webhook-Signature`
1511
+ - Algorithm: HMAC SHA256
1512
+ - Encoding: Base64
1513
+ - Equivalent to: `crypto.mac(:hmac, :sha256, secret, body) |> Base.encode64()`
1514
+
1515
+ See [examples/webhook-server.ts](examples/webhook-server.ts) for a complete implementation.
1516
+
434
1517
  ## Contributing
435
1518
 
436
1519
  We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
437
1520
 
1521
+ ## Migrating from Older Versions
1522
+
1523
+ See [MIGRATION.md](MIGRATION.md) for upgrade instructions and breaking changes between versions.
1524
+
438
1525
  ## Support
439
1526
 
440
1527
  - 📚 [API Documentation](https://docs.inkress.com)