@tapsilat/tapsilat-js 1.0.3 → 1.0.5

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/dist/index.js CHANGED
@@ -1,8 +1,25 @@
1
1
  'use strict';
2
2
 
3
+ var crypto = require('crypto');
4
+
5
+ /**
6
+ * @category Errors
7
+ * @summary Base error class for Tapsilat SDK
8
+ * @description Foundation error class for all Tapsilat-specific errors with error code and details support
9
+ * @class TapsilatError
10
+ * @extends Error
11
+ */
3
12
  class TapsilatError extends Error {
4
13
  code;
5
14
  details;
15
+ /**
16
+ * @summary Creates a new TapsilatError instance
17
+ * @description Initializes base error with message, code and optional details
18
+ *
19
+ * @param message - Human-readable error message
20
+ * @param code - Error code for programmatic identification
21
+ * @param details - Optional additional error context
22
+ */
6
23
  constructor(message, code, details) {
7
24
  super(message);
8
25
  this.name = 'TapsilatError';
@@ -13,32 +30,96 @@ class TapsilatError extends Error {
13
30
  Error.captureStackTrace(this, TapsilatError);
14
31
  }
15
32
  }
33
+ /**
34
+ * @summary Creates a TapsilatError from an API error response
35
+ * @description Factory method to convert API error format to a TapsilatError instance
36
+ *
37
+ * @param apiError - API error object from response
38
+ * @returns New TapsilatError instance with properties from API error
39
+ */
16
40
  static fromAPIError(apiError) {
17
41
  return new TapsilatError(apiError.message, apiError.code, apiError.details);
18
42
  }
19
43
  }
44
+ /**
45
+ * @category Errors
46
+ * @summary Network-related error class for HTTP issues
47
+ * @description Error class for network and HTTP-specific problems including status codes
48
+ * @class TapsilatNetworkError
49
+ * @extends TapsilatError
50
+ */
20
51
  class TapsilatNetworkError extends TapsilatError {
21
52
  statusCode;
53
+ /**
54
+ * @summary Creates a new network error instance
55
+ * @description Initializes network error with message, code, status code and optional details
56
+ *
57
+ * @param message - Human-readable error message
58
+ * @param code - Error code for programmatic identification
59
+ * @param statusCode - HTTP status code if applicable
60
+ * @param details - Optional additional error context
61
+ */
22
62
  constructor(message, code, statusCode, details) {
23
63
  super(message, code, details);
24
64
  this.name = 'TapsilatNetworkError';
25
65
  this.statusCode = statusCode;
26
66
  }
27
67
  }
68
+ /**
69
+ * @category Errors
70
+ * @summary Error class for validation failures
71
+ * @description Error class for data validation issues with field-specific error details
72
+ * @class TapsilatValidationError
73
+ * @extends TapsilatError
74
+ */
28
75
  class TapsilatValidationError extends TapsilatError {
76
+ /**
77
+ * @summary Creates a new validation error instance
78
+ * @description Initializes validation error with message and optional field-specific details
79
+ *
80
+ * @param message - Human-readable error message
81
+ * @param details - Optional field-specific validation errors
82
+ */
29
83
  constructor(message, details) {
30
84
  super(message, 'VALIDATION_ERROR', details);
31
85
  this.name = 'TapsilatValidationError';
32
86
  }
33
87
  }
88
+ /**
89
+ * @category Errors
90
+ * @summary Error class for authentication issues
91
+ * @description Error class for problems related to API keys, authentication tokens, or permissions
92
+ * @class TapsilatAuthenticationError
93
+ * @extends TapsilatError
94
+ */
34
95
  class TapsilatAuthenticationError extends TapsilatError {
96
+ /**
97
+ * @summary Creates a new authentication error instance
98
+ * @description Initializes authentication error with an optional custom message
99
+ *
100
+ * @param message - Human-readable error message (defaults to 'Authentication failed')
101
+ */
35
102
  constructor(message = 'Authentication failed') {
36
103
  super(message, 'AUTHENTICATION_ERROR');
37
104
  this.name = 'TapsilatAuthenticationError';
38
105
  }
39
106
  }
107
+ /**
108
+ * @category Errors
109
+ * @summary Error class for rate limit exceeded issues
110
+ * @description Error class for API rate limiting with information about when to retry
111
+ * @class TapsilatRateLimitError
112
+ * @extends TapsilatError
113
+ */
40
114
  class TapsilatRateLimitError extends TapsilatError {
41
115
  retryAfter;
116
+ /**
117
+ * @summary Creates a new rate limit error instance
118
+ * @description Initializes rate limit error with message and optional retry-after seconds
119
+ *
120
+ * @param message - Human-readable error message (defaults to 'Rate limit exceeded')
121
+ * @param retryAfter - Optional seconds to wait before retrying
122
+ */
42
123
  constructor(message = 'Rate limit exceeded', retryAfter) {
43
124
  super(message, 'RATE_LIMIT_ERROR');
44
125
  this.name = 'TapsilatRateLimitError';
@@ -46,20 +127,52 @@ class TapsilatRateLimitError extends TapsilatError {
46
127
  }
47
128
  }
48
129
 
130
+ /**
131
+ * @category HTTP
132
+ * @summary Central registry and execution engine for all HTTP interceptors
133
+ * @description Maintains lists of request, response and error interceptors and provides methods to execute them in sequence
134
+ * @class InterceptorManager
135
+ */
49
136
  class InterceptorManager {
50
137
  requestInterceptors = [];
51
138
  responseInterceptors = [];
52
139
  errorInterceptors = [];
53
- // Request interceptors
140
+ /**
141
+ * Adds a request interceptor to the chain
142
+ *
143
+ * @summary Register a new request interceptor function
144
+ * @description Adds a new interceptor to process requests before they are sent
145
+ *
146
+ * @param interceptor - The request interceptor function to add
147
+ * @returns The index of the added interceptor (can be used to remove it later)
148
+ */
54
149
  addRequestInterceptor(interceptor) {
55
150
  this.requestInterceptors.push(interceptor);
56
151
  return this.requestInterceptors.length - 1;
57
152
  }
153
+ /**
154
+ * Removes a previously added request interceptor
155
+ *
156
+ * @summary Unregister a request interceptor by its ID
157
+ * @description Removes the request interceptor at the specified position
158
+ *
159
+ * @param id - The index of the interceptor to remove
160
+ */
58
161
  removeRequestInterceptor(id) {
59
162
  if (this.requestInterceptors[id]) {
60
163
  delete this.requestInterceptors[id];
61
164
  }
62
165
  }
166
+ /**
167
+ * Runs all registered request interceptors in sequence
168
+ *
169
+ * @summary Process a request through all interceptors
170
+ * @description Executes each registered request interceptor in order, passing modified url and options to each
171
+ *
172
+ * @param url - The initial request URL
173
+ * @param options - The initial request options
174
+ * @returns Modified URL and options after all interceptors have processed them
175
+ */
63
176
  async executeRequestInterceptors(url, options) {
64
177
  let currentUrl = url;
65
178
  let currentOptions = options;
@@ -72,16 +185,36 @@ class InterceptorManager {
72
185
  }
73
186
  return { url: currentUrl, options: currentOptions };
74
187
  }
75
- // Response interceptors
188
+ /**
189
+ * @summary Register a new response interceptor function
190
+ * @description Adds a new interceptor to process responses before they are returned to the caller
191
+ *
192
+ * @param interceptor - The response interceptor function to add
193
+ * @returns The index of the added interceptor (can be used to remove it later)
194
+ */
76
195
  addResponseInterceptor(interceptor) {
77
196
  this.responseInterceptors.push(interceptor);
78
197
  return this.responseInterceptors.length - 1;
79
198
  }
199
+ /**
200
+ * @summary Unregister a response interceptor by its ID
201
+ * @description Removes the response interceptor at the specified position
202
+ *
203
+ * @param id - The index of the interceptor to remove
204
+ */
80
205
  removeResponseInterceptor(id) {
81
206
  if (this.responseInterceptors[id]) {
82
207
  delete this.responseInterceptors[id];
83
208
  }
84
209
  }
210
+ /**
211
+ * @summary Process a response through all interceptors
212
+ * @description Executes each registered response interceptor in order
213
+ *
214
+ * @param response - The API response to process
215
+ * @param request - The original request information
216
+ * @returns Modified response after all interceptors have processed it
217
+ */
85
218
  async executeResponseInterceptors(response, request) {
86
219
  let currentResponse = response;
87
220
  for (const interceptor of this.responseInterceptors) {
@@ -91,16 +224,36 @@ class InterceptorManager {
91
224
  }
92
225
  return currentResponse;
93
226
  }
94
- // Error interceptors
227
+ /**
228
+ * @summary Register a new error interceptor function
229
+ * @description Adds a new interceptor to process errors before they are thrown
230
+ *
231
+ * @param interceptor - The error interceptor function to add
232
+ * @returns The index of the added interceptor (can be used to remove it later)
233
+ */
95
234
  addErrorInterceptor(interceptor) {
96
235
  this.errorInterceptors.push(interceptor);
97
236
  return this.errorInterceptors.length - 1;
98
237
  }
238
+ /**
239
+ * @summary Unregister an error interceptor by its ID
240
+ * @description Removes the error interceptor at the specified position
241
+ *
242
+ * @param id - The index of the interceptor to remove
243
+ */
99
244
  removeErrorInterceptor(id) {
100
245
  if (this.errorInterceptors[id]) {
101
246
  delete this.errorInterceptors[id];
102
247
  }
103
248
  }
249
+ /**
250
+ * @summary Process an error through all interceptors
251
+ * @description Executes each registered error interceptor in order until one handles the error
252
+ *
253
+ * @param error - The error that occurred during the request
254
+ * @param request - The original request information
255
+ * @returns Either a recovery response or throws the error if no interceptors handle it
256
+ */
104
257
  async executeErrorInterceptors(error, request) {
105
258
  for (const interceptor of this.errorInterceptors) {
106
259
  if (interceptor) {
@@ -116,7 +269,10 @@ class InterceptorManager {
116
269
  // If no interceptor handled the error, re-throw
117
270
  throw error;
118
271
  }
119
- // Clear all interceptors
272
+ /**
273
+ * @summary Remove all registered interceptors
274
+ * @description Clears all request, response, and error interceptors at once
275
+ */
120
276
  clear() {
121
277
  this.requestInterceptors = [];
122
278
  this.responseInterceptors = [];
@@ -125,8 +281,10 @@ class InterceptorManager {
125
281
  }
126
282
 
127
283
  /**
128
- * HTTP Client for making API requests with comprehensive error handling,
129
- * retry logic, and request/response interceptors
284
+ * @category HTTP
285
+ * @summary HTTP Client for making API requests to Tapsilat services
286
+ * @description Provides a robust client with comprehensive error handling,
287
+ * retry logic, and request/response interceptors for interacting with the Tapsilat API
130
288
  *
131
289
  * @example
132
290
  * ```typescript
@@ -137,10 +295,17 @@ class InterceptorManager {
137
295
  *
138
296
  * const data = await client.get<UserResponse>('/users/123');
139
297
  * ```
298
+ * @class HttpClient
140
299
  */
141
300
  class HttpClient {
142
301
  config;
143
302
  interceptors = new InterceptorManager();
303
+ /**
304
+ * @summary Creates a new HTTP client instance
305
+ * @description Initializes the HTTP client with the provided configuration
306
+ *
307
+ * @param config - The configuration for the Tapsilat API client
308
+ */
144
309
  constructor(config) {
145
310
  this.config = config;
146
311
  }
@@ -160,7 +325,9 @@ class HttpClient {
160
325
  return this.request("DELETE", url, undefined, config);
161
326
  }
162
327
  /**
163
- * Core request method with comprehensive error handling and retry logic
328
+ * @category HTTP Client
329
+ * @summary Core request method with comprehensive error handling and retry logic
330
+ * @description Handles all HTTP requests with error handling, retries, and interceptors
164
331
  *
165
332
  * @param method - HTTP method to use
166
333
  * @param url - Request URL (relative to baseURL)
@@ -211,7 +378,14 @@ class HttpClient {
211
378
  }
212
379
  }
213
380
  /**
214
- * Makes the actual HTTP request with timeout handling
381
+ * @category HTTP Client Internals
382
+ * @summary Makes the actual HTTP request with timeout handling
383
+ * @description Performs the fetch request with timeout handling and abort controller
384
+ *
385
+ * @param url - Full URL to request
386
+ * @param options - Fetch request options
387
+ * @param timeout - Optional timeout in milliseconds
388
+ * @returns Promise resolving to API response
215
389
  */
216
390
  async makeRequest(url, options, timeout) {
217
391
  const controller = new AbortController();
@@ -243,7 +417,12 @@ class HttpClient {
243
417
  }
244
418
  }
245
419
  /**
246
- * Processes the fetch response and converts to APIResponse format
420
+ * @category HTTP Client Internals
421
+ * @summary Processes the fetch response and converts to APIResponse format
422
+ * @description Parses the response and formats it according to the APIResponse interface
423
+ *
424
+ * @param response - The raw fetch Response object
425
+ * @returns Processed API response
247
426
  */
248
427
  async processResponse(response) {
249
428
  const contentType = response.headers.get("content-type");
@@ -279,7 +458,13 @@ class HttpClient {
279
458
  };
280
459
  }
281
460
  /**
282
- * Creates appropriate error based on response status and data
461
+ * @category HTTP Client Internals
462
+ * @summary Creates appropriate error based on response status and data
463
+ * @description Maps HTTP status codes to specific Tapsilat error types
464
+ *
465
+ * @param response - The raw fetch Response object
466
+ * @param data - The parsed response data
467
+ * @returns Appropriate TapsilatError subclass instance
283
468
  */
284
469
  createErrorFromResponse(response, data) {
285
470
  const status = response.status;
@@ -311,7 +496,12 @@ class HttpClient {
311
496
  }
312
497
  }
313
498
  /**
314
- * Extracts error message from response data
499
+ * @category HTTP Client Internals
500
+ * @summary Extracts error message from response data
501
+ * @description Attempts to find an error message in various locations within the response
502
+ *
503
+ * @param data - The parsed response data
504
+ * @returns Error message if found
315
505
  */
316
506
  extractErrorMessage(data) {
317
507
  if (typeof data === "string") {
@@ -324,7 +514,12 @@ class HttpClient {
324
514
  return undefined;
325
515
  }
326
516
  /**
327
- * Extracts validation details from error response
517
+ * @category HTTP Client Internals
518
+ * @summary Extracts validation details from error response
519
+ * @description Parses field-specific validation errors from the API response
520
+ *
521
+ * @param data - The parsed response data
522
+ * @returns Object mapping field names to error messages
328
523
  */
329
524
  extractValidationDetails(data) {
330
525
  if (data && typeof data === "object") {
@@ -334,7 +529,12 @@ class HttpClient {
334
529
  return undefined;
335
530
  }
336
531
  /**
337
- * Extracts rate limit information from response headers
532
+ * @category HTTP Client Internals
533
+ * @summary Extracts rate limit information from response headers
534
+ * @description Reads and parses rate limiting headers from the API response
535
+ *
536
+ * @param response - The raw fetch Response object
537
+ * @returns Rate limit information including limits and reset time
338
538
  */
339
539
  extractRateLimitInfo(response) {
340
540
  const limit = response.headers.get("x-ratelimit-limit");
@@ -347,7 +547,14 @@ class HttpClient {
347
547
  };
348
548
  }
349
549
  /**
350
- * Builds the complete URL for the request
550
+ * @category HTTP Client Internals
551
+ * @summary Builds the complete URL for the request
552
+ * @description Constructs the full URL with base URL and query parameters
553
+ *
554
+ * @param url - The endpoint path or full URL
555
+ * @param customBaseURL - Optional override for the base URL
556
+ * @param params - Optional query parameters
557
+ * @returns Complete request URL
351
558
  */
352
559
  buildUrl(url, customBaseURL, params) {
353
560
  const baseURL = customBaseURL ||
@@ -375,7 +582,14 @@ class HttpClient {
375
582
  return fullUrl;
376
583
  }
377
584
  /**
378
- * Builds RequestInit options for the fetch request
585
+ * @category HTTP Client Internals
586
+ * @summary Builds RequestInit options for the fetch request
587
+ * @description Prepares headers and request configuration for fetch API
588
+ *
589
+ * @param method - The HTTP method to use
590
+ * @param body - Optional request body
591
+ * @param config - Additional request configuration
592
+ * @returns Configured RequestInit object for fetch
379
593
  */
380
594
  async buildRequestOptions(method, body, config) {
381
595
  const headers = new Headers({
@@ -410,7 +624,12 @@ class HttpClient {
410
624
  return options;
411
625
  }
412
626
  /**
413
- * Determines if an error should prevent retrying
627
+ * @category HTTP Client Internals
628
+ * @summary Determines if an error should prevent retrying
629
+ * @description Checks error types to decide if retry attempts should be aborted
630
+ *
631
+ * @param error - The error that occurred
632
+ * @returns Whether retry should be prevented for this error
414
633
  */
415
634
  shouldNotRetry(error) {
416
635
  // Don't retry authentication, validation, or client errors
@@ -419,7 +638,12 @@ class HttpClient {
419
638
  (error instanceof TapsilatError && error.code === "NOT_FOUND"));
420
639
  }
421
640
  /**
422
- * Calculates retry delay with exponential backoff
641
+ * @category HTTP Client Internals
642
+ * @summary Calculates retry delay with exponential backoff
643
+ * @description Implements exponential backoff with jitter for retry attempts
644
+ *
645
+ * @param attempt - The current retry attempt number (0-indexed)
646
+ * @returns Delay in milliseconds before next retry
423
647
  */
424
648
  calculateRetryDelay(attempt) {
425
649
  const baseDelay = 1000; // 1 second
@@ -429,14 +653,21 @@ class HttpClient {
429
653
  return delay + Math.random() * 1000;
430
654
  }
431
655
  /**
432
- * Utility method to create delay
656
+ * @category HTTP Client Internals
657
+ * @summary Utility method to create delay
658
+ * @description Creates a promise that resolves after the specified time
659
+ *
660
+ * @param ms - Delay time in milliseconds
661
+ * @returns Promise that resolves after the delay
433
662
  */
434
663
  delay(ms) {
435
664
  return new Promise((resolve) => setTimeout(resolve, ms));
436
665
  }
437
666
  // Interceptor management methods
438
667
  /**
439
- * Gets the interceptor manager for advanced request/response handling
668
+ * @category Interceptors
669
+ * @summary Gets the interceptor manager for advanced request/response handling
670
+ * @description Provides access to the internal interceptor manager for custom handling
440
671
  *
441
672
  * @returns InterceptorManager instance
442
673
  */
@@ -444,7 +675,9 @@ class HttpClient {
444
675
  return this.interceptors;
445
676
  }
446
677
  /**
447
- * Adds a request interceptor
678
+ * @category Interceptors
679
+ * @summary Adds a request interceptor
680
+ * @description Registers a function to intercept and modify requests before they are sent
448
681
  *
449
682
  * @param interceptor - Request interceptor function
450
683
  * @returns Interceptor ID for removal
@@ -453,7 +686,9 @@ class HttpClient {
453
686
  return this.interceptors.addRequestInterceptor(interceptor);
454
687
  }
455
688
  /**
456
- * Adds a response interceptor
689
+ * @category Interceptors
690
+ * @summary Adds a response interceptor
691
+ * @description Registers a function to intercept and modify responses before they are returned
457
692
  *
458
693
  * @param interceptor - Response interceptor function
459
694
  * @returns Interceptor ID for removal
@@ -462,7 +697,9 @@ class HttpClient {
462
697
  return this.interceptors.addResponseInterceptor(interceptor);
463
698
  }
464
699
  /**
465
- * Adds an error interceptor
700
+ * @category Interceptors
701
+ * @summary Adds an error interceptor
702
+ * @description Registers a function to intercept and potentially recover from errors
466
703
  *
467
704
  * @param interceptor - Error interceptor function
468
705
  * @returns Interceptor ID for removal
@@ -472,43 +709,108 @@ class HttpClient {
472
709
  }
473
710
  }
474
711
 
475
- // Type Guards
712
+ // TYPE GUARDS
713
+ // Summary: Type guards for payment method validation
714
+ // Description: Checks if a value matches the allowed payment method types
476
715
  /**
477
716
  * Type guard to check if a value is a valid PaymentMethod
717
+ *
718
+ * @summary Validates a value against supported payment methods
719
+ * @description Checks if provided value is a string and matches one of the supported payment methods
720
+ *
721
+ * @param value - The value to check
722
+ * @returns True if the value is a valid PaymentMethod, narrowing the type
478
723
  */
479
724
  function isPaymentMethod(value) {
480
725
  return (typeof value === "string" &&
481
726
  ["credit_card", "debit_card", "bank_transfer", "digital_wallet"].includes(value));
482
727
  }
728
+ // CURRENCY VALIDATION
729
+ // Summary: Type guard for currency validation
730
+ // Description: Checks if a value matches the allowed currency types
483
731
  /**
484
732
  * Type guard to check if a value is a valid Currency
733
+ *
734
+ * @summary Validates a value against supported currencies
735
+ * @description Checks if provided value is a string and matches one of the supported currencies (TRY, USD, EUR, GBP)
736
+ *
737
+ * @param value - The value to check
738
+ * @returns True if the value is a valid Currency, narrowing the type
485
739
  */
486
740
  function isCurrency(value) {
487
741
  return (typeof value === "string" && ["TRY", "USD", "EUR", "GBP"].includes(value));
488
742
  }
743
+ // STRING VALIDATION
744
+ // Summary: Type guard for non-empty string validation
745
+ // Description: Checks if a value is a string and contains non-whitespace characters
489
746
  /**
490
747
  * Type guard to check if a value is a non-empty string
748
+ *
749
+ * @summary Validates a value is a non-empty string
750
+ * @description Checks if provided value is of string type and contains at least one non-whitespace character
751
+ *
752
+ * @param value - The value to check
753
+ * @returns True if the value is a non-empty string, narrowing the type
491
754
  */
492
755
  function isNonEmptyString(value) {
493
756
  return typeof value === "string" && value.trim().length > 0;
494
757
  }
758
+ // NUMBER VALIDATION
759
+ // Summary: Type guard for positive number validation
760
+ // Description: Checks if a value is a number greater than zero
495
761
  /**
496
762
  * Type guard to check if a value is a positive number
763
+ *
764
+ * @summary Validates a value is a positive number
765
+ * @description Checks if provided value is of number type, is greater than zero, and is not NaN or infinity
766
+ *
767
+ * @param value - The value to check
768
+ * @returns True if the value is a positive number, narrowing the type
497
769
  */
498
770
  function isPositiveNumber(value) {
499
771
  return (typeof value === "number" && value > 0 && !isNaN(value) && isFinite(value));
500
772
  }
773
+ // INTEGER VALIDATION
774
+ // Summary: Type guard for integer validation
775
+ // Description: Checks if a value is an integer number
776
+ /**
777
+ * Type guard to check if a value is an integer
778
+ *
779
+ * @summary Validates a value is an integer
780
+ * @description Checks if provided value is of number type and is an integer (no decimal places)
781
+ *
782
+ * @param value - The value to check
783
+ * @returns True if the value is an integer, narrowing the type
784
+ */
785
+ function isInteger(value) {
786
+ return typeof value === "number" && Number.isInteger(value);
787
+ }
788
+ // DECIMAL PLACES VALIDATION
789
+ // Summary: Validates that a number has maximum 2 decimal places
790
+ // Description: Ensures monetary values have appropriate precision for currency operations
501
791
  /**
502
792
  * Type guard to check if amount has max 2 decimal places
793
+ *
794
+ * @summary Validates that a number has maximum 2 decimal places
795
+ * @description Ensures monetary values have appropriate precision for currency operations
796
+ *
797
+ * @param amount - The numeric amount to validate
798
+ * @returns True if the amount has 2 or fewer decimal places
503
799
  */
504
800
  function hasValidDecimalPlaces(amount) {
505
801
  const decimalPlaces = (amount.toString().split(".")[1] || "").length;
506
802
  return decimalPlaces <= 2;
507
803
  }
508
- // Validation Functions
804
+ // VALIDATION FUNCTIONS
805
+ // BEARER TOKEN VALIDATION
806
+ // Summary: Validates authentication token format and security
807
+ // Description: Checks token for required length, character set, and format constraints
509
808
  /**
510
809
  * Validates Bearer token format and strength
511
810
  *
811
+ * @summary Validates authentication token format and security
812
+ * @description Checks token for required length, character set, and format constraints
813
+ *
512
814
  * @param bearerToken - Bearer token to validate
513
815
  * @throws {TapsilatValidationError} When Bearer token is invalid
514
816
  */
@@ -519,14 +821,19 @@ function validateBearerToken(bearerToken) {
519
821
  if (bearerToken.length < 10) {
520
822
  throw new TapsilatValidationError("Bearer token must be at least 10 characters long");
521
823
  }
522
- // Karakter kontrolü (opsiyonel, isterseniz ek kurallar koyabilirsiniz)
523
824
  if (!/^[a-zA-Z0-9._-]+$/.test(bearerToken)) {
524
825
  throw new TapsilatValidationError("Bearer token contains invalid characters");
525
826
  }
526
827
  }
828
+ // PAYMENT REQUEST VALIDATION
829
+ // Summary: Comprehensive validation of payment request data
830
+ // Description: Validates all required and optional fields in a payment request for integrity and correctness
527
831
  /**
528
832
  * Validates payment request data comprehensively
529
833
  *
834
+ * @summary Comprehensive validation of payment request data
835
+ * @description Validates all required and optional fields in a payment request for integrity and correctness
836
+ *
530
837
  * @param request - Payment request to validate
531
838
  * @throws {TapsilatValidationError} When any field is invalid
532
839
  */
@@ -570,9 +877,15 @@ function validatePaymentRequest(request) {
570
877
  throw new TapsilatValidationError("Metadata must be an object when provided");
571
878
  }
572
879
  }
880
+ // EMAIL FORMAT VALIDATION
881
+ // Summary: Validates email address format with comprehensive regex
882
+ // Description: Ensures email addresses conform to RFC standards with thorough pattern matching
573
883
  /**
574
884
  * Validates email format using robust regex
575
885
  *
886
+ * @summary Validates email address format with comprehensive regex
887
+ * @description Ensures email addresses conform to RFC standards with thorough pattern matching
888
+ *
576
889
  * @param email - Email address to validate
577
890
  * @returns True if email format is valid
578
891
  */
@@ -584,21 +897,45 @@ function validateEmail(email) {
584
897
  const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
585
898
  return emailRegex.test(email);
586
899
  }
900
+ // DEPRECATED CURRENCY VALIDATION
901
+ // Summary: Legacy currency validation function
902
+ // Description: Maintained for backwards compatibility, redirects to the new type guard
587
903
  /**
588
904
  * @deprecated Use isCurrency type guard instead
905
+ *
906
+ * @summary Legacy currency validation function
907
+ * @description Maintained for backwards compatibility, redirects to the new type guard
908
+ *
909
+ * @param currency - Currency code to validate
910
+ * @returns True if currency is supported
589
911
  */
590
912
  function isValidCurrency(currency) {
591
913
  return isCurrency(currency);
592
914
  }
915
+ // DEPRECATED PAYMENT METHOD VALIDATION
916
+ // Summary: Legacy payment method validation function
917
+ // Description: Maintained for backwards compatibility, redirects to the new type guard
593
918
  /**
594
919
  * @deprecated Use isPaymentMethod type guard instead
920
+ *
921
+ * @summary Legacy payment method validation function
922
+ * @description Maintained for backwards compatibility, redirects to the new type guard
923
+ *
924
+ * @param paymentMethod - Payment method to validate
925
+ * @returns True if payment method is supported
595
926
  */
596
927
  function isValidPaymentMethod(paymentMethod) {
597
928
  return isPaymentMethod(paymentMethod);
598
929
  }
930
+ // URL FORMAT VALIDATION
931
+ // Summary: Validates URL format and ensures proper protocol
932
+ // Description: Checks that URLs are well-formed and use HTTP or HTTPS protocols for security
599
933
  /**
600
934
  * Validates URL format and protocol
601
935
  *
936
+ * @summary Validates URL format and ensures proper protocol
937
+ * @description Checks that URLs are well-formed and use HTTP or HTTPS protocols for security
938
+ *
602
939
  * @param url - URL to validate
603
940
  * @returns True if URL is valid HTTP/HTTPS
604
941
  */
@@ -614,9 +951,15 @@ function isValidUrl(url) {
614
951
  return false;
615
952
  }
616
953
  }
954
+ // METADATA SANITIZATION
955
+ // Summary: Removes sensitive data from metadata before processing
956
+ // Description: Filters out security-sensitive keys and normalizes values for safe transmission
617
957
  /**
618
958
  * Sanitizes metadata by removing sensitive keys and cleaning values
619
959
  *
960
+ * @summary Removes sensitive data from metadata before processing
961
+ * @description Filters out security-sensitive keys and normalizes values for safe transmission
962
+ *
620
963
  * @param metadata - Raw metadata object
621
964
  * @returns Sanitized metadata object
622
965
  */
@@ -661,13 +1004,122 @@ function sanitizeMetadata(metadata) {
661
1004
  return sanitized;
662
1005
  }
663
1006
 
1007
+ /**
1008
+ * @category Configuration
1009
+ * @summary Configuration management for Tapsilat SDK
1010
+ * @description Handles SDK configuration updates, validation, and secure access to configuration data
1011
+ * @class ConfigManager
1012
+ */
1013
+ class ConfigManager {
1014
+ config;
1015
+ /**
1016
+ * @summary Creates a new configuration manager
1017
+ * @description Initializes the configuration manager with the provided configuration options
1018
+ *
1019
+ * @param config - Initial SDK configuration
1020
+ */
1021
+ constructor(config) {
1022
+ this.config = { ...config }; // Create a copy to avoid external mutations
1023
+ }
1024
+ /**
1025
+ * @category Configuration Access
1026
+ * @summary Get current SDK configuration without sensitive information
1027
+ * @description Returns a copy of the current configuration with sensitive data (like bearer token) excluded
1028
+ *
1029
+ * @returns Copy of current configuration without bearer token
1030
+ */
1031
+ getConfig() {
1032
+ const { bearerToken, ...safeConfig } = this.config;
1033
+ return {
1034
+ ...safeConfig,
1035
+ hasBearerToken: Boolean(bearerToken),
1036
+ };
1037
+ }
1038
+ /**
1039
+ * @category Configuration Management
1040
+ * @summary Update SDK configuration with new values
1041
+ * @description Updates the SDK configuration with provided values, validating bearer token if changed
1042
+ *
1043
+ * @param newConfig - Partial configuration to update
1044
+ * @throws {TapsilatValidationError} When Bearer token is invalid
1045
+ */
1046
+ updateConfig(newConfig) {
1047
+ if (newConfig.bearerToken) {
1048
+ validateBearerToken(newConfig.bearerToken);
1049
+ }
1050
+ Object.assign(this.config, newConfig);
1051
+ }
1052
+ /**
1053
+ * @category Internal
1054
+ * @summary Gets the internal configuration for SDK use
1055
+ * @description Returns a full copy of the configuration for internal SDK operations
1056
+ *
1057
+ * @internal
1058
+ * @returns Full configuration including sensitive data
1059
+ */
1060
+ getInternalConfig() {
1061
+ return { ...this.config }; // Return a copy to prevent external mutations
1062
+ }
1063
+ /**
1064
+ * @category Authentication
1065
+ * @summary Gets the bearer token for authentication
1066
+ * @description Retrieves the configured bearer token for API authentication
1067
+ *
1068
+ * @internal
1069
+ * @returns Bearer token
1070
+ */
1071
+ getBearerToken() {
1072
+ return this.config.bearerToken;
1073
+ }
1074
+ /**
1075
+ * @category Endpoints
1076
+ * @summary Gets the base URL for API requests
1077
+ * @description Retrieves the configured base URL for API endpoints
1078
+ *
1079
+ * @internal
1080
+ * @returns API base URL
1081
+ */
1082
+ getBaseUrl() {
1083
+ return this.config.baseURL;
1084
+ }
1085
+ /**
1086
+ * @category Networking
1087
+ * @summary Gets the request timeout value
1088
+ * @description Retrieves the configured timeout value for API requests
1089
+ *
1090
+ * @internal
1091
+ * @returns Request timeout in milliseconds
1092
+ */
1093
+ getTimeout() {
1094
+ return this.config.timeout;
1095
+ }
1096
+ }
1097
+
1098
+ // EMAIL VALIDATION
1099
+ // Summary: Validates email format using basic regex pattern
1100
+ // Description: Ensures email addresses conform to standard format with username, @ symbol, and domain
1101
+ /**
1102
+ * Validates email format using basic regex pattern
1103
+ *
1104
+ * @summary Validates email format using basic regex pattern
1105
+ * @description Ensures email addresses conform to standard format with username, @ symbol, and domain
1106
+ *
1107
+ * @param email - Email address to validate
1108
+ * @returns True if email matches required format, false otherwise
1109
+ */
664
1110
  const isValidEmail = (email) => {
665
1111
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
666
1112
  return emailRegex.test(email);
667
1113
  };
1114
+ // API RESPONSE HANDLING
1115
+ // Summary: Process API responses consistently across the SDK
1116
+ // Description: Validates response structure, extracts data or converts API errors to domain errors
668
1117
  /**
669
1118
  * Generic response handler - consistent way to handle API responses
670
1119
  *
1120
+ * @summary Process API responses consistently across the SDK
1121
+ * @description Validates response structure, extracts data or converts API errors to domain errors
1122
+ *
671
1123
  * @param response - The API response to process
672
1124
  * @param errorContext - Descriptive context for error messages
673
1125
  * @returns The data from a successful response
@@ -683,9 +1135,15 @@ const handleResponse = (response, errorContext) => {
683
1135
  }
684
1136
  return response.data;
685
1137
  };
1138
+ // ERROR HANDLING
1139
+ // Summary: Centralized error handling for consistent error reporting
1140
+ // Description: Processes caught errors and ensures they are properly converted to TapsilatError types
686
1141
  /**
687
1142
  * Generic error handler - consistent way to handle errors
688
1143
  *
1144
+ * @summary Centralized error handling for consistent error reporting
1145
+ * @description Processes caught errors and ensures they are properly converted to TapsilatError types
1146
+ *
689
1147
  * @param error - The caught error to process
690
1148
  * @param errorContext - Descriptive context for error messages
691
1149
  * @throws {TapsilatError} Always throws an appropriate error type
@@ -700,6 +1158,27 @@ const handleError = (error, errorContext) => {
700
1158
  ? error.message
701
1159
  : `Unknown error during ${errorContext}`, "NETWORK_ERROR");
702
1160
  };
1161
+ // WEBHOOK SIGNATURE VERIFICATION
1162
+ // Summary: Security verification of webhook payloads using HMAC-SHA256
1163
+ // Description: Computes expected signature from payload and secret, compares with provided signature
1164
+ /**
1165
+ * Verifies HMAC-SHA256 signature for webhook security
1166
+ *
1167
+ * @summary Security verification of webhook payloads using HMAC-SHA256
1168
+ * @description Computes expected signature from payload and secret, compares with provided signature
1169
+ *
1170
+ * @param payload - Raw webhook payload string
1171
+ * @param signature - Webhook signature from headers (should include "sha256=" prefix)
1172
+ * @param secret - Your webhook secret key
1173
+ * @returns true if signature is valid, false otherwise
1174
+ */
1175
+ const verifyHmacSignature = (payload, signature, secret) => {
1176
+ const expectedSignature = crypto
1177
+ .createHmac("sha256", secret)
1178
+ .update(payload)
1179
+ .digest("hex");
1180
+ return `sha256=${expectedSignature}` === signature;
1181
+ };
703
1182
 
704
1183
  /**
705
1184
  * Main SDK class for Tapsilat payment operations
@@ -720,7 +1199,10 @@ const handleError = (error, errorContext) => {
720
1199
  */
721
1200
  class TapsilatSDK {
722
1201
  httpClient;
723
- config;
1202
+ configManager;
1203
+ // SDK INITIALIZATION
1204
+ // Summary: Initializes the Tapsilat SDK with configuration options
1205
+ // Description: Creates and configures a new instance of the TapsilatSDK with the provided configuration
724
1206
  /**
725
1207
  * Creates a new TapsilatSDK instance
726
1208
  *
@@ -747,8 +1229,8 @@ class TapsilatSDK {
747
1229
  */
748
1230
  constructor(config) {
749
1231
  validateBearerToken(config.bearerToken);
750
- this.config = config;
751
- this.httpClient = new HttpClient(config);
1232
+ this.configManager = new ConfigManager(config);
1233
+ this.httpClient = new HttpClient(this.configManager.getInternalConfig());
752
1234
  }
753
1235
  // ORDER CREATION
754
1236
  // Summary: Create new payment order and get checkout URL
@@ -849,9 +1331,9 @@ class TapsilatSDK {
849
1331
  }
850
1332
  try {
851
1333
  // Make the API request
852
- const response = await this.httpClient.post("/order/create", orderRequest);
1334
+ const createOrderResponse = await this.httpClient.post("/order/create", orderRequest);
853
1335
  // Use our generic response handler
854
- return handleResponse(response, "Order creation");
1336
+ return handleResponse(createOrderResponse, "Order creation");
855
1337
  }
856
1338
  catch (error) {
857
1339
  // Use our generic error handler
@@ -880,9 +1362,9 @@ class TapsilatSDK {
880
1362
  }
881
1363
  try {
882
1364
  // Make the API request
883
- const response = await this.httpClient.get(`/order/${referenceId}`);
1365
+ const getOrderResponse = await this.httpClient.get(`/order/${referenceId}`);
884
1366
  // Use our generic response handler
885
- return handleResponse(response, "Order retrieval");
1367
+ return handleResponse(getOrderResponse, "Order retrieval");
886
1368
  }
887
1369
  catch (error) {
888
1370
  // Use our generic error handler
@@ -906,18 +1388,34 @@ class TapsilatSDK {
906
1388
  async getOrders(params = {}) {
907
1389
  try {
908
1390
  // Validate pagination parameters if provided
909
- if (params.page !== undefined &&
910
- (!Number.isInteger(params.page) || params.page < 1)) {
911
- throw new TapsilatValidationError("Page number must be a positive integer", { provided: params.page });
1391
+ // Check if page is defined
1392
+ if (params.page !== undefined) {
1393
+ // Check if page is integer
1394
+ if (!isInteger(params.page)) {
1395
+ throw new TapsilatValidationError("Page number must be an integer", {
1396
+ provided: params.page,
1397
+ });
1398
+ }
1399
+ // Check if page is positive
1400
+ if (params.page < 1) {
1401
+ throw new TapsilatValidationError("Page number must be greater than 0", { provided: params.page });
1402
+ }
912
1403
  }
913
- if (params.per_page !== undefined &&
914
- (!Number.isInteger(params.per_page) || params.per_page < 1)) {
915
- throw new TapsilatValidationError("Items per page must be a positive integer", { provided: params.per_page });
1404
+ // Check if per_page is defined
1405
+ if (params.per_page !== undefined) {
1406
+ // Check if per_page is integer
1407
+ if (!isInteger(params.per_page)) {
1408
+ throw new TapsilatValidationError("Items per page must be an integer", { provided: params.per_page });
1409
+ }
1410
+ // Check if per_page is positive
1411
+ if (params.per_page < 1) {
1412
+ throw new TapsilatValidationError("Items per page must be greater than 0", { provided: params.per_page });
1413
+ }
916
1414
  }
917
1415
  // Make the API request
918
- const response = await this.httpClient.get("/order/list", { params: params });
1416
+ const getOrdersResponse = await this.httpClient.get("/order/list", { params: params });
919
1417
  // Use our generic response handler
920
- return handleResponse(response, "Order listing");
1418
+ return handleResponse(getOrdersResponse, "Order listing");
921
1419
  }
922
1420
  catch (error) {
923
1421
  // Use our generic error handler
@@ -932,19 +1430,34 @@ class TapsilatSDK {
932
1430
  *
933
1431
  * @summary Cancel a pending order before payment completion
934
1432
  * @description Cancels unpaid order and prevents further payment processing.
1433
+ *
1434
+ * @param referenceId - The unique reference ID of the order to cancel
1435
+ * @returns Promise resolving to the canceled order details
1436
+ * @throws {TapsilatValidationError} When referenceId is invalid
1437
+ * @throws {TapsilatError} When API returns an error response or order cannot be canceled
935
1438
  */
936
1439
  async cancelOrder(referenceId) {
937
- if (!referenceId) {
938
- throw new Error("Order referenceId is required for cancellation");
1440
+ // Validate referenceId
1441
+ if (!isNonEmptyString(referenceId)) {
1442
+ throw new TapsilatValidationError("Order referenceId is required and must be a non-empty string", { provided: referenceId });
939
1443
  }
940
- const response = await this.httpClient.post("/order/cancel", {
1444
+ const cancelOrderResponse = await this.httpClient.post("/order/cancel", {
941
1445
  reference_id: referenceId,
942
1446
  });
943
- if (!response.success || !response.data) {
944
- throw new Error(response.error?.message || "Order cancellation failed");
1447
+ // Check if API call was successful
1448
+ if (!cancelOrderResponse.success) {
1449
+ throw new TapsilatError(cancelOrderResponse.error?.message ||
1450
+ "Order cancellation API call failed", cancelOrderResponse.error?.code || "CANCELLATION_API_FAILED");
1451
+ }
1452
+ // Check if response data exists
1453
+ if (!cancelOrderResponse.data) {
1454
+ throw new TapsilatError("Order cancellation response data is missing", "CANCELLATION_DATA_MISSING");
945
1455
  }
946
- return response.data;
1456
+ return cancelOrderResponse.data;
947
1457
  }
1458
+ // ORDER STATUS RETRIEVAL
1459
+ // Summary: Retrieves real-time payment status and tracking information for a specific order
1460
+ // Description: Queries the payment platform to retrieve current status of an order using its reference ID
948
1461
  /**
949
1462
  * Gets the current status of an order by reference ID
950
1463
  *
@@ -988,9 +1501,9 @@ class TapsilatSDK {
988
1501
  }
989
1502
  try {
990
1503
  // Make the API request
991
- const response = await this.httpClient.get(`/order/${referenceId}/status`);
1504
+ const getOrderStatusResponse = await this.httpClient.get(`/order/${referenceId}/status`);
992
1505
  // Use our generic response handler
993
- return handleResponse(response, "Order status retrieval");
1506
+ return handleResponse(getOrderStatusResponse, "Order status retrieval");
994
1507
  }
995
1508
  catch (error) {
996
1509
  // Use our generic error handler
@@ -1009,14 +1522,19 @@ class TapsilatSDK {
1009
1522
  *
1010
1523
  * @param refundData - The refund details, including referenceId and amount.
1011
1524
  * @returns Promise resolving to the refund transaction details.
1525
+ * @throws {TapsilatError} When API returns an error response or refund fails
1012
1526
  */
1013
1527
  async refundOrder(refundData) {
1014
- const response = await this.httpClient.post("/order/refund", refundData);
1015
- if (!response.success || !response.data) {
1016
- throw new Error(response.error?.message || "Order refund failed");
1528
+ const refundOrderResponse = await this.httpClient.post("/order/refund", refundData);
1529
+ if (!refundOrderResponse.success) {
1530
+ throw new TapsilatError(refundOrderResponse.error?.message || "Order refund failed", refundOrderResponse.error?.code || "REFUND_FAILED");
1017
1531
  }
1018
- return response.data;
1532
+ if (!refundOrderResponse.data) {
1533
+ throw new TapsilatError("Order refund response data is missing", "REFUND_DATA_MISSING");
1534
+ }
1535
+ return refundOrderResponse.data;
1019
1536
  }
1537
+ // ORDER FULL REFUND OPERATIONS
1020
1538
  // Summary: Process full refund for a completed order
1021
1539
  // Description: Refunds entire order amount and returns transaction details
1022
1540
  /**
@@ -1028,13 +1546,19 @@ class TapsilatSDK {
1028
1546
  *
1029
1547
  * @param referenceId - The unique identifier of the order to fully refund.
1030
1548
  * @returns Promise resolving to the refund transaction details.
1549
+ * @throws {TapsilatError} When API returns an error response or full refund fails
1031
1550
  */
1032
1551
  async refundAllOrder(referenceId) {
1033
- const response = await this.httpClient.post("/order/refund-all", { reference_id: referenceId });
1034
- if (!response.success || !response.data) {
1035
- throw new Error(response.error?.message || "Full order refund failed");
1552
+ const refundAllOrderResponse = await this.httpClient.post("/order/refund-all", {
1553
+ reference_id: referenceId,
1554
+ });
1555
+ if (!refundAllOrderResponse.success) {
1556
+ throw new TapsilatError(refundAllOrderResponse.error?.message || "Full order refund failed", refundAllOrderResponse.error?.code || "FULL_REFUND_FAILED");
1036
1557
  }
1037
- return response.data;
1558
+ if (!refundAllOrderResponse.data) {
1559
+ throw new TapsilatError("Full order refund response data is missing", "FULL_REFUND_DATA_MISSING");
1560
+ }
1561
+ return refundAllOrderResponse.data;
1038
1562
  }
1039
1563
  // ORDER PAYMENT DETAILS
1040
1564
  // Summary: Retrieve detailed payment transaction information for an order
@@ -1049,24 +1573,24 @@ class TapsilatSDK {
1049
1573
  * @param referenceId - The unique identifier of the order.
1050
1574
  * @param conversationId - Optional conversation ID for more specific querying.
1051
1575
  * @returns Promise resolving to a list of payment details.
1576
+ * @throws {TapsilatValidationError} When referenceId is invalid
1577
+ * @throws {TapsilatError} When API returns an error response or payment details are missing
1052
1578
  */
1053
1579
  async getOrderPaymentDetails(referenceId, conversationId) {
1054
- if (conversationId) {
1055
- // If conversation_id is provided, use POST to /order/payment-details
1056
- const response = await this.httpClient.post("/order/payment-details", { conversation_id: conversationId, reference_id: referenceId });
1057
- if (!response.success || !response.data) {
1058
- throw new Error(response.error?.message || "Failed to get payment details");
1059
- }
1060
- return response.data;
1061
- }
1062
- else {
1063
- // If no conversation_id, use GET to /order/{reference_id}/payment-details
1064
- const response = await this.httpClient.get(`/order/${referenceId}/payment-details`);
1065
- if (!response.success || !response.data) {
1066
- throw new Error(response.error?.message || "Failed to get payment details");
1067
- }
1068
- return response.data;
1069
- }
1580
+ if (!isNonEmptyString(referenceId))
1581
+ throw new TapsilatValidationError("Reference ID is required and must be a non-empty string");
1582
+ const getOrderPaymentDetailsResponse = conversationId
1583
+ ? await this.httpClient.post("/order/payment-details", {
1584
+ conversation_id: conversationId,
1585
+ })
1586
+ : await this.httpClient.get(`/order/${referenceId}/payment-details`);
1587
+ if (!getOrderPaymentDetailsResponse.success)
1588
+ throw new TapsilatError(getOrderPaymentDetailsResponse.error?.message ||
1589
+ "Payment details API call failed", getOrderPaymentDetailsResponse.error?.code ||
1590
+ "PAYMENT_DETAILS_API_FAILED");
1591
+ if (!getOrderPaymentDetailsResponse.data)
1592
+ throw new TapsilatError("Payment details response data is missing", "PAYMENT_DETAILS_DATA_MISSING");
1593
+ return getOrderPaymentDetailsResponse.data;
1070
1594
  }
1071
1595
  // ORDER LOOKUP BY CONVERSATION ID
1072
1596
  // Summary: Find order using conversation ID instead of reference ID
@@ -1077,65 +1601,104 @@ class TapsilatSDK {
1077
1601
  *
1078
1602
  * @summary Find order using conversation ID instead of reference ID
1079
1603
  * @description Alternative lookup method for orders using custom conversation identifier.
1604
+ *
1605
+ * @param conversationId - The custom conversation identifier used when creating the order
1606
+ * @returns Promise resolving to the complete order details
1607
+ * @throws {TapsilatValidationError} When conversationId is invalid
1608
+ * @throws {TapsilatError} When API returns an error response
1080
1609
  */
1081
1610
  async getOrderByConversationId(conversationId) {
1082
- if (!conversationId) {
1083
- throw new Error("Order conversationId is required");
1611
+ // Validate conversationId
1612
+ if (!isNonEmptyString(conversationId)) {
1613
+ throw new TapsilatValidationError("Order conversationId is required and must be a non-empty string", { provided: conversationId });
1084
1614
  }
1085
- const response = await this.httpClient.get(`/order/conversation/${conversationId}`);
1086
- if (!response.success || !response.data) {
1087
- throw new Error(response.error?.message || "Order retrieval by conversation ID failed");
1088
- }
1089
- return response.data;
1090
- }
1615
+ const getOrderByConversationIdResponse = await this.httpClient.get(`/order/conversation/${conversationId}`);
1616
+ if (!getOrderByConversationIdResponse.success)
1617
+ throw new TapsilatError(getOrderByConversationIdResponse.error?.message ||
1618
+ "Order retrieval by conversation ID failed", getOrderByConversationIdResponse.error?.code || "ORDER_RETRIEVAL_FAILED");
1619
+ if (!getOrderByConversationIdResponse.data)
1620
+ throw new TapsilatError("Order response data is missing", "ORDER_DATA_MISSING");
1621
+ return getOrderByConversationIdResponse.data;
1622
+ }
1623
+ // ORDER TRANSACTION HISTORY
1624
+ // Summary: Retrieve transaction history for an order
1625
+ // Description: Gets detailed transaction records and payment attempts for a specific order
1091
1626
  /**
1092
1627
  * Gets order transactions by reference_id.
1093
1628
  * Based on `get_order_transactions` from the Python SDK.
1094
1629
  *
1095
1630
  * @summary Retrieve transaction history for an order
1096
1631
  * @description Gets detailed transaction records and payment attempts for a specific order.
1632
+ *
1633
+ * @param referenceId - The unique reference ID of the order
1634
+ * @returns Promise resolving to array of transaction records
1635
+ * @throws {TapsilatValidationError} When referenceId is invalid
1636
+ * @throws {TapsilatError} When API returns an error response
1097
1637
  */
1098
1638
  async getOrderTransactions(referenceId) {
1099
- if (!referenceId) {
1100
- throw new Error("Order referenceId is required");
1101
- }
1102
- const response = await this.httpClient.get(`/order/${referenceId}/transactions`);
1103
- if (!response.success || !response.data) {
1104
- throw new Error(response.error?.message || "Order transactions retrieval failed");
1639
+ // Validate referenceId
1640
+ if (!isNonEmptyString(referenceId)) {
1641
+ throw new TapsilatValidationError("Order referenceId is required and must be a non-empty string", { provided: referenceId });
1105
1642
  }
1106
- return response.data;
1107
- }
1643
+ const getOrderTransactionsResponse = await this.httpClient.get(`/order/${referenceId}/transactions`);
1644
+ if (!getOrderTransactionsResponse.success)
1645
+ throw new TapsilatError(getOrderTransactionsResponse.error?.message ||
1646
+ "Order transactions retrieval failed", getOrderTransactionsResponse.error?.code ||
1647
+ "TRANSACTIONS_RETRIEVAL_FAILED");
1648
+ if (!getOrderTransactionsResponse.data)
1649
+ throw new TapsilatError("Order transactions response data is missing", "TRANSACTIONS_DATA_MISSING");
1650
+ return getOrderTransactionsResponse.data;
1651
+ }
1652
+ // ORDER SUBMERCHANT LISTING
1653
+ // Summary: Retrieve paginated list of submerchants for orders
1654
+ // Description: Gets submerchant information with pagination support for order management
1108
1655
  /**
1109
1656
  * Gets order submerchants with pagination.
1110
1657
  * Based on `get_order_submerchants` from the Python SDK.
1111
1658
  *
1112
1659
  * @summary Retrieve paginated list of submerchants for orders
1113
1660
  * @description Gets submerchant information with pagination support for order management.
1661
+ *
1662
+ * @param params - Optional pagination parameters (page number and items per page)
1663
+ * @returns Promise resolving to paginated list of submerchants
1664
+ * @throws {TapsilatError} When API returns an error response
1114
1665
  */
1115
1666
  async getOrderSubmerchants(params = {}) {
1116
- const response = await this.httpClient.get("/order/submerchants", {
1667
+ const getOrderSubmerchantsResponse = await this.httpClient.get("/order/submerchants", {
1117
1668
  params: params,
1118
1669
  });
1119
- if (!response.success || !response.data) {
1120
- throw new Error(response.error?.message || "Order submerchants retrieval failed");
1121
- }
1122
- return response.data;
1123
- }
1670
+ if (!getOrderSubmerchantsResponse.success)
1671
+ throw new TapsilatError(getOrderSubmerchantsResponse.error?.message ||
1672
+ "Order submerchants retrieval failed", getOrderSubmerchantsResponse.error?.code ||
1673
+ "SUBMERCHANTS_RETRIEVAL_FAILED");
1674
+ if (!getOrderSubmerchantsResponse.data)
1675
+ throw new TapsilatError("Order submerchants response data is missing", "SUBMERCHANTS_DATA_MISSING");
1676
+ return getOrderSubmerchantsResponse.data;
1677
+ }
1678
+ // ORDER CHECKOUT URL RETRIEVAL
1679
+ // Summary: Retrieve checkout URL for existing order
1680
+ // Description: Gets the payment checkout URL for an existing order using reference ID
1124
1681
  /**
1125
1682
  * Gets checkout URL for an order.
1126
1683
  * Based on `get_checkout_url` from the Python SDK.
1127
1684
  *
1128
1685
  * @summary Retrieve checkout URL for existing order
1129
1686
  * @description Gets the payment checkout URL for an existing order using reference ID.
1687
+ *
1688
+ * @param referenceId - The unique reference ID of the order
1689
+ * @returns Promise resolving to the checkout URL string
1690
+ * @throws {TapsilatError} When checkout URL is not found in order response
1130
1691
  */
1131
1692
  async getCheckoutUrl(referenceId) {
1132
1693
  const order = await this.getOrder(referenceId);
1133
1694
  if (order && order.checkout_url) {
1134
1695
  return order.checkout_url;
1135
1696
  }
1136
- throw new Error("Checkout URL not found in order response");
1697
+ throw new TapsilatError("Checkout URL not found in order response", "CHECKOUT_URL_NOT_FOUND");
1137
1698
  }
1138
- // Utility Methods
1699
+ // WEBHOOK SIGNATURE VERIFICATION
1700
+ // Summary: Validate webhook signature for security verification
1701
+ // Description: Verifies the authenticity of webhook payloads using HMAC signature validation
1139
1702
  /**
1140
1703
  * Verifies webhook signature for security
1141
1704
  *
@@ -1146,65 +1709,68 @@ class TapsilatSDK {
1146
1709
  * @param signature - Webhook signature from headers
1147
1710
  * @param secret - Your webhook secret key
1148
1711
  * @returns Promise resolving to true if signature is valid
1712
+ * @throws {TapsilatValidationError} When input validation fails for payload, signature or secret
1149
1713
  */
1150
1714
  async verifyWebhook(payload, signature, secret) {
1151
- // Implementation would depend on your webhook signature verification method
1152
- // This is a placeholder implementation
1153
- const crypto = await import('crypto');
1154
- const expectedSignature = crypto
1155
- .createHmac("sha256", secret)
1156
- .update(payload)
1157
- .digest("hex");
1158
- return `sha256=${expectedSignature}` === signature;
1159
- }
1715
+ if (!isNonEmptyString(payload))
1716
+ throw new TapsilatValidationError("Webhook payload is required and must be a non-empty string");
1717
+ if (!isNonEmptyString(signature))
1718
+ throw new TapsilatValidationError("Webhook signature is required and must be a non-empty string");
1719
+ if (!isNonEmptyString(secret))
1720
+ throw new TapsilatValidationError("Webhook secret is required and must be a non-empty string");
1721
+ return verifyHmacSignature(payload, signature, secret);
1722
+ }
1723
+ // API HEALTH STATUS CHECK
1724
+ // Summary: Check API service health and availability
1725
+ // Description: Verifies that the Tapsilat API service is operational and accessible
1160
1726
  /**
1161
1727
  * Checks API service health and availability
1162
1728
  *
1163
1729
  * @summary Check API service health and availability
1164
1730
  * @description Verifies that the Tapsilat API service is operational and accessible.
1165
1731
  *
1166
- * @returns Promise resolving to service status
1732
+ * @returns Promise resolving to service status with status string and timestamp
1733
+ * @throws {TapsilatError} When API health check fails or returns invalid data
1167
1734
  */
1168
1735
  async healthCheck() {
1169
- const response = await this.httpClient.get("/health");
1170
- if (!response.success || !response.data) {
1171
- throw new Error(response.error?.message || "Health check failed");
1172
- }
1173
- return response.data;
1174
- }
1175
- // Configuration methods
1736
+ const healthCheckResponse = await this.httpClient.get("/health");
1737
+ if (!healthCheckResponse.success)
1738
+ throw new TapsilatError(healthCheckResponse.error?.message || "Health check failed", healthCheckResponse.error?.code || "HEALTH_CHECK_FAILED");
1739
+ if (!healthCheckResponse.data)
1740
+ throw new TapsilatError("Health check response data is missing", "HEALTH_CHECK_DATA_MISSING");
1741
+ return healthCheckResponse.data;
1742
+ }
1743
+ // CONFIGURATION MANAGEMENT
1744
+ // Summary: Access to the configuration manager for advanced configuration management
1745
+ // Description: Provides direct access to the ConfigManager instance for configuration operations
1176
1746
  /**
1177
- * Gets current SDK configuration (without sensitive data)
1747
+ * Gets the configuration manager instance
1178
1748
  *
1179
- * @summary Get current SDK configuration without sensitive information
1180
- * @description Returns a copy of the current configuration with sensitive data (like bearer token) excluded.
1749
+ * @summary Access to the configuration manager for advanced configuration management
1750
+ * @description
1751
+ * Provides direct access to the ConfigManager instance for configuration operations.
1752
+ * Use this to get, update, or manage SDK configuration settings.
1181
1753
  *
1182
- * @returns Copy of current configuration
1183
- */
1184
- getConfig() {
1185
- const { bearerToken, ...safeConfig } = this.config;
1186
- return {
1187
- ...safeConfig,
1188
- hasBearerToken: Boolean(bearerToken),
1189
- };
1190
- }
1191
- /**
1192
- * Updates SDK configuration
1754
+ * @example
1755
+ * ```typescript
1756
+ * const sdk = new TapsilatSDK(config);
1757
+ * const configManager = sdk.getConfigManager();
1193
1758
  *
1194
- * @summary Update SDK configuration with new values
1195
- * @description Updates the SDK configuration with provided values, validating bearer token if changed.
1759
+ * // Get config (without sensitive data)
1760
+ * console.log(configManager.getConfig());
1196
1761
  *
1197
- * @param newConfig - Partial configuration to update
1198
- * @throws {TapsilatValidationError} When Bearer token is invalid
1762
+ * // Update config
1763
+ * configManager.updateConfig({ timeout: 60000 });
1764
+ * ```
1765
+ *
1766
+ * @returns ConfigManager instance for configuration operations
1199
1767
  */
1200
- updateConfig(newConfig) {
1201
- if (newConfig.bearerToken) {
1202
- validateBearerToken(newConfig.bearerToken);
1203
- }
1204
- Object.assign(this.config, newConfig);
1768
+ getConfigManager() {
1769
+ return this.configManager;
1205
1770
  }
1206
1771
  }
1207
1772
 
1773
+ exports.ConfigManager = ConfigManager;
1208
1774
  exports.TapsilatAuthenticationError = TapsilatAuthenticationError;
1209
1775
  exports.TapsilatError = TapsilatError;
1210
1776
  exports.TapsilatNetworkError = TapsilatNetworkError;