@zendfi/sdk 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,13 +30,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- AuthenticationError: () => AuthenticationError,
33
+ ApiError: () => ApiError,
34
+ AuthenticationError: () => AuthenticationError2,
34
35
  ConfigLoader: () => ConfigLoader,
35
- NetworkError: () => NetworkError,
36
- RateLimitError: () => RateLimitError,
37
- ValidationError: () => ValidationError,
36
+ ERROR_CODES: () => ERROR_CODES,
37
+ InterceptorManager: () => InterceptorManager,
38
+ NetworkError: () => NetworkError2,
39
+ PaymentError: () => PaymentError,
40
+ RateLimitError: () => RateLimitError2,
41
+ ValidationError: () => ValidationError2,
42
+ WebhookError: () => WebhookError,
38
43
  ZendFiClient: () => ZendFiClient,
39
- ZendFiError: () => ZendFiError,
44
+ ZendFiError: () => ZendFiError2,
45
+ createZendFiError: () => createZendFiError,
46
+ isZendFiError: () => isZendFiError,
40
47
  processWebhook: () => processWebhook,
41
48
  verifyExpressWebhook: () => verifyExpressWebhook,
42
49
  verifyNextWebhook: () => verifyNextWebhook,
@@ -49,41 +56,6 @@ module.exports = __toCommonJS(index_exports);
49
56
  var import_cross_fetch = __toESM(require("cross-fetch"));
50
57
  var import_crypto = require("crypto");
51
58
 
52
- // src/types.ts
53
- var ZendFiError = class extends Error {
54
- constructor(message, statusCode, code, details) {
55
- super(message);
56
- this.statusCode = statusCode;
57
- this.code = code;
58
- this.details = details;
59
- this.name = "ZendFiError";
60
- }
61
- };
62
- var AuthenticationError = class extends ZendFiError {
63
- constructor(message = "Authentication failed") {
64
- super(message, 401, "AUTHENTICATION_ERROR");
65
- this.name = "AuthenticationError";
66
- }
67
- };
68
- var ValidationError = class extends ZendFiError {
69
- constructor(message, details) {
70
- super(message, 400, "VALIDATION_ERROR", details);
71
- this.name = "ValidationError";
72
- }
73
- };
74
- var NetworkError = class extends ZendFiError {
75
- constructor(message) {
76
- super(message, 0, "NETWORK_ERROR");
77
- this.name = "NetworkError";
78
- }
79
- };
80
- var RateLimitError = class extends ZendFiError {
81
- constructor(message = "Rate limit exceeded") {
82
- super(message, 429, "RATE_LIMIT_ERROR");
83
- this.name = "RateLimitError";
84
- }
85
- };
86
-
87
59
  // src/utils.ts
88
60
  var ConfigLoader = class {
89
61
  /**
@@ -101,7 +73,8 @@ var ConfigLoader = class {
101
73
  mode,
102
74
  timeout: options?.timeout ?? 3e4,
103
75
  retries: options?.retries ?? 3,
104
- idempotencyEnabled: options?.idempotencyEnabled ?? true
76
+ idempotencyEnabled: options?.idempotencyEnabled ?? true,
77
+ debug: options?.debug ?? false
105
78
  };
106
79
  }
107
80
  /**
@@ -213,28 +186,6 @@ var ConfigLoader = class {
213
186
  }
214
187
  }
215
188
  };
216
- function parseError(response, body) {
217
- const statusCode = response.status;
218
- const errorMessage = body?.error || body?.message || response.statusText || "Unknown error";
219
- const errorCode = body?.code;
220
- const details = body?.details;
221
- switch (statusCode) {
222
- case 401:
223
- case 403:
224
- return new AuthenticationError(errorMessage);
225
- case 400:
226
- return new ValidationError(errorMessage, details);
227
- case 429:
228
- return new RateLimitError(errorMessage);
229
- case 500:
230
- case 502:
231
- case 503:
232
- case 504:
233
- return new NetworkError(errorMessage);
234
- default:
235
- return new ZendFiError(errorMessage, statusCode, errorCode, details);
236
- }
237
- }
238
189
  function generateIdempotencyKey() {
239
190
  const timestamp = Date.now();
240
191
  const random = Math.random().toString(36).substring(2, 15);
@@ -244,16 +195,261 @@ function sleep(ms) {
244
195
  return new Promise((resolve) => setTimeout(resolve, ms));
245
196
  }
246
197
 
198
+ // src/errors.ts
199
+ var ZendFiError2 = class _ZendFiError extends Error {
200
+ code;
201
+ type;
202
+ suggestion;
203
+ docs_url;
204
+ statusCode;
205
+ response;
206
+ constructor(data) {
207
+ super(data.message);
208
+ this.name = "ZendFiError";
209
+ this.code = data.code;
210
+ this.type = data.type;
211
+ this.suggestion = data.suggestion;
212
+ this.statusCode = data.statusCode;
213
+ this.response = data.response;
214
+ this.docs_url = `https://docs.zendfi.com/errors/${data.code}`;
215
+ if (Error.captureStackTrace) {
216
+ Error.captureStackTrace(this, _ZendFiError);
217
+ }
218
+ }
219
+ /**
220
+ * Format error for display
221
+ */
222
+ toString() {
223
+ let message = `[${this.code}] ${this.message}`;
224
+ if (this.suggestion) {
225
+ message += `
226
+ \u{1F4A1} Suggestion: ${this.suggestion}`;
227
+ }
228
+ message += `
229
+ \u{1F4DA} Docs: ${this.docs_url}`;
230
+ return message;
231
+ }
232
+ /**
233
+ * Convert error to JSON
234
+ */
235
+ toJSON() {
236
+ return {
237
+ name: this.name,
238
+ code: this.code,
239
+ type: this.type,
240
+ message: this.message,
241
+ suggestion: this.suggestion,
242
+ docs_url: this.docs_url,
243
+ statusCode: this.statusCode
244
+ };
245
+ }
246
+ };
247
+ var AuthenticationError2 = class extends ZendFiError2 {
248
+ constructor(message, code = "authentication_failed", suggestion) {
249
+ super({
250
+ code,
251
+ message,
252
+ type: "authentication_error",
253
+ suggestion: suggestion || "Check your API key in the dashboard at https://app.zendfi.com/settings/api-keys",
254
+ statusCode: 401
255
+ });
256
+ this.name = "AuthenticationError";
257
+ }
258
+ };
259
+ var PaymentError = class extends ZendFiError2 {
260
+ constructor(message, code = "payment_failed", suggestion) {
261
+ super({
262
+ code,
263
+ message,
264
+ type: "payment_error",
265
+ suggestion,
266
+ statusCode: 400
267
+ });
268
+ this.name = "PaymentError";
269
+ }
270
+ };
271
+ var ValidationError2 = class extends ZendFiError2 {
272
+ constructor(message, code = "validation_failed", suggestion) {
273
+ super({
274
+ code,
275
+ message,
276
+ type: "validation_error",
277
+ suggestion,
278
+ statusCode: 400
279
+ });
280
+ this.name = "ValidationError";
281
+ }
282
+ };
283
+ var NetworkError2 = class extends ZendFiError2 {
284
+ constructor(message, code = "network_error", suggestion) {
285
+ super({
286
+ code,
287
+ message,
288
+ type: "network_error",
289
+ suggestion: suggestion || "Check your internet connection and try again",
290
+ statusCode: 0
291
+ });
292
+ this.name = "NetworkError";
293
+ }
294
+ };
295
+ var RateLimitError2 = class extends ZendFiError2 {
296
+ constructor(message, retryAfter) {
297
+ super({
298
+ code: "rate_limit_exceeded",
299
+ message,
300
+ type: "rate_limit_error",
301
+ suggestion: retryAfter ? `Wait ${retryAfter} seconds before retrying` : "You are making too many requests. Please slow down.",
302
+ statusCode: 429
303
+ });
304
+ this.name = "RateLimitError";
305
+ }
306
+ };
307
+ var ApiError = class extends ZendFiError2 {
308
+ constructor(message, code, statusCode, response) {
309
+ super({
310
+ code,
311
+ message,
312
+ type: "api_error",
313
+ statusCode,
314
+ response
315
+ });
316
+ this.name = "ApiError";
317
+ }
318
+ };
319
+ var WebhookError = class extends ZendFiError2 {
320
+ constructor(message, code = "webhook_verification_failed", suggestion) {
321
+ super({
322
+ code,
323
+ message,
324
+ type: "webhook_error",
325
+ suggestion: suggestion || "Check your webhook secret matches the one in your dashboard",
326
+ statusCode: 400
327
+ });
328
+ this.name = "WebhookError";
329
+ }
330
+ };
331
+ function createZendFiError(statusCode, responseBody, message) {
332
+ const errorMessage = message || responseBody?.error?.message || responseBody?.message || "An error occurred";
333
+ const errorCode = responseBody?.error?.code || responseBody?.code || "unknown_error";
334
+ if (statusCode === 401) {
335
+ return new AuthenticationError2(
336
+ errorMessage,
337
+ errorCode,
338
+ "Verify your API key is correct and not expired"
339
+ );
340
+ }
341
+ if (statusCode === 429) {
342
+ const retryAfter = responseBody?.retry_after;
343
+ return new RateLimitError2(errorMessage, retryAfter);
344
+ }
345
+ if (statusCode === 400 || statusCode === 422) {
346
+ return new ValidationError2(errorMessage, errorCode);
347
+ }
348
+ if (statusCode === 402) {
349
+ return new PaymentError(errorMessage, errorCode);
350
+ }
351
+ if (statusCode === 0 || statusCode >= 500) {
352
+ return new NetworkError2(
353
+ errorMessage,
354
+ errorCode,
355
+ statusCode >= 500 ? "The ZendFi API is experiencing issues. Please try again later." : void 0
356
+ );
357
+ }
358
+ return new ApiError(errorMessage, errorCode, statusCode, responseBody);
359
+ }
360
+ var ERROR_CODES = {
361
+ // Authentication
362
+ INVALID_API_KEY: "invalid_api_key",
363
+ API_KEY_EXPIRED: "api_key_expired",
364
+ API_KEY_REVOKED: "api_key_revoked",
365
+ // Payment
366
+ INSUFFICIENT_BALANCE: "insufficient_balance",
367
+ PAYMENT_DECLINED: "payment_declined",
368
+ PAYMENT_EXPIRED: "payment_expired",
369
+ INVALID_AMOUNT: "invalid_amount",
370
+ INVALID_CURRENCY: "invalid_currency",
371
+ // Validation
372
+ MISSING_REQUIRED_FIELD: "missing_required_field",
373
+ INVALID_PARAMETER: "invalid_parameter",
374
+ // Network
375
+ NETWORK_ERROR: "network_error",
376
+ TIMEOUT: "timeout",
377
+ // Rate limiting
378
+ RATE_LIMIT_EXCEEDED: "rate_limit_exceeded",
379
+ // Webhook
380
+ WEBHOOK_SIGNATURE_INVALID: "webhook_signature_invalid",
381
+ WEBHOOK_TIMESTAMP_TOO_OLD: "webhook_timestamp_too_old"
382
+ };
383
+ function isZendFiError(error) {
384
+ return error instanceof ZendFiError2;
385
+ }
386
+
387
+ // src/interceptors.ts
388
+ var InterceptorManager = class {
389
+ handlers = [];
390
+ /**
391
+ * Add an interceptor
392
+ */
393
+ use(handler) {
394
+ this.handlers.push(handler);
395
+ return this.handlers.length - 1;
396
+ }
397
+ /**
398
+ * Remove an interceptor
399
+ */
400
+ eject(id) {
401
+ if (this.handlers[id]) {
402
+ this.handlers[id] = null;
403
+ }
404
+ }
405
+ /**
406
+ * Execute all interceptors in sequence
407
+ */
408
+ async execute(initialValue) {
409
+ let result = initialValue;
410
+ for (const handler of this.handlers) {
411
+ if (handler !== null) {
412
+ result = await handler(result);
413
+ }
414
+ }
415
+ return result;
416
+ }
417
+ /**
418
+ * Check if any interceptors are registered
419
+ */
420
+ has() {
421
+ return this.handlers.some((h) => h !== null);
422
+ }
423
+ /**
424
+ * Clear all interceptors
425
+ */
426
+ clear() {
427
+ this.handlers = [];
428
+ }
429
+ };
430
+ function createInterceptors() {
431
+ return {
432
+ request: new InterceptorManager(),
433
+ response: new InterceptorManager(),
434
+ error: new InterceptorManager()
435
+ };
436
+ }
437
+
247
438
  // src/client.ts
248
439
  var ZendFiClient = class {
249
440
  config;
441
+ interceptors;
250
442
  constructor(options) {
251
443
  this.config = ConfigLoader.load(options);
252
444
  ConfigLoader.validateApiKey(this.config.apiKey);
253
- if (this.config.environment === "development") {
445
+ this.interceptors = createInterceptors();
446
+ if (this.config.environment === "development" || this.config.debug) {
254
447
  console.log(
255
448
  `\u2713 ZendFi SDK initialized in ${this.config.mode} mode (${this.config.mode === "test" ? "devnet" : "mainnet"})`
256
449
  );
450
+ if (this.config.debug) {
451
+ console.log("[ZendFi] Debug mode enabled");
452
+ }
257
453
  }
258
454
  }
259
455
  /**
@@ -573,11 +769,12 @@ var ZendFiClient = class {
573
769
  return result === 0;
574
770
  }
575
771
  /**
576
- * Make an HTTP request with retry logic
772
+ * Make an HTTP request with retry logic, interceptors, and debug logging
577
773
  */
578
774
  async request(method, endpoint, data, options = {}) {
579
775
  const attempt = options.attempt || 1;
580
776
  const idempotencyKey = options.idempotencyKey || (this.config.idempotencyEnabled && method !== "GET" ? generateIdempotencyKey() : void 0);
777
+ const startTime = Date.now();
581
778
  try {
582
779
  const url = `${this.config.baseURL}${endpoint}`;
583
780
  const headers = {
@@ -587,12 +784,27 @@ var ZendFiClient = class {
587
784
  if (idempotencyKey) {
588
785
  headers["Idempotency-Key"] = idempotencyKey;
589
786
  }
590
- const controller = new AbortController();
591
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
592
- const response = await (0, import_cross_fetch.default)(url, {
787
+ let requestConfig = {
593
788
  method,
789
+ url,
594
790
  headers,
595
- body: data ? JSON.stringify(data) : void 0,
791
+ body: data
792
+ };
793
+ if (this.interceptors.request.has()) {
794
+ requestConfig = await this.interceptors.request.execute(requestConfig);
795
+ }
796
+ if (this.config.debug) {
797
+ console.log(`[ZendFi] ${method} ${endpoint}`);
798
+ if (data) {
799
+ console.log("[ZendFi] Request:", JSON.stringify(data, null, 2));
800
+ }
801
+ }
802
+ const controller = new AbortController();
803
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
804
+ const response = await (0, import_cross_fetch.default)(requestConfig.url, {
805
+ method: requestConfig.method,
806
+ headers: requestConfig.headers,
807
+ body: requestConfig.body ? JSON.stringify(requestConfig.body) : void 0,
596
808
  signal: controller.signal
597
809
  });
598
810
  clearTimeout(timeoutId);
@@ -602,32 +814,78 @@ var ZendFiClient = class {
602
814
  } catch {
603
815
  body = null;
604
816
  }
817
+ const duration = Date.now() - startTime;
605
818
  if (!response.ok) {
606
- const error = parseError(response, body);
819
+ const error = createZendFiError(response.status, body);
820
+ if (this.config.debug) {
821
+ console.error(`[ZendFi] \u274C ${response.status} ${response.statusText} (${duration}ms)`);
822
+ console.error(`[ZendFi] Error:`, error.toString());
823
+ }
607
824
  if (response.status >= 500 && attempt < this.config.retries) {
608
825
  const delay = Math.pow(2, attempt) * 1e3;
826
+ if (this.config.debug) {
827
+ console.log(`[ZendFi] Retrying in ${delay}ms... (attempt ${attempt + 1}/${this.config.retries})`);
828
+ }
609
829
  await sleep(delay);
610
830
  return this.request(method, endpoint, data, {
611
831
  idempotencyKey,
612
832
  attempt: attempt + 1
613
833
  });
614
834
  }
835
+ if (this.interceptors.error.has()) {
836
+ const interceptedError = await this.interceptors.error.execute(error);
837
+ throw interceptedError;
838
+ }
615
839
  throw error;
616
840
  }
617
- return body;
841
+ if (this.config.debug) {
842
+ console.log(`[ZendFi] \u2713 ${response.status} ${response.statusText} (${duration}ms)`);
843
+ if (body) {
844
+ console.log("[ZendFi] Response:", JSON.stringify(body, null, 2));
845
+ }
846
+ }
847
+ const headersObj = {};
848
+ response.headers.forEach((value, key) => {
849
+ headersObj[key] = value;
850
+ });
851
+ let responseData = {
852
+ status: response.status,
853
+ statusText: response.statusText,
854
+ headers: headersObj,
855
+ data: body,
856
+ config: requestConfig
857
+ };
858
+ if (this.interceptors.response.has()) {
859
+ responseData = await this.interceptors.response.execute(responseData);
860
+ }
861
+ return responseData.data;
618
862
  } catch (error) {
619
863
  if (error.name === "AbortError") {
620
- throw new Error(`Request timeout after ${this.config.timeout}ms`);
864
+ const timeoutError = createZendFiError(0, {}, `Request timeout after ${this.config.timeout}ms`);
865
+ if (this.config.debug) {
866
+ console.error(`[ZendFi] \u274C Timeout (${this.config.timeout}ms)`);
867
+ }
868
+ throw timeoutError;
621
869
  }
622
- if (attempt < this.config.retries && error.message?.includes("fetch")) {
870
+ if (attempt < this.config.retries && (error.message?.includes("fetch") || error.message?.includes("network"))) {
623
871
  const delay = Math.pow(2, attempt) * 1e3;
872
+ if (this.config.debug) {
873
+ console.log(`[ZendFi] Network error, retrying in ${delay}ms... (attempt ${attempt + 1}/${this.config.retries})`);
874
+ }
624
875
  await sleep(delay);
625
876
  return this.request(method, endpoint, data, {
626
877
  idempotencyKey,
627
878
  attempt: attempt + 1
628
879
  });
629
880
  }
630
- throw error;
881
+ if (isZendFiError(error)) {
882
+ throw error;
883
+ }
884
+ const wrappedError = createZendFiError(0, {}, error.message || "An unknown error occurred");
885
+ if (this.config.debug) {
886
+ console.error(`[ZendFi] \u274C Unexpected error:`, error);
887
+ }
888
+ throw wrappedError;
631
889
  }
632
890
  }
633
891
  };
@@ -847,13 +1105,20 @@ async function processWebhook(a, b, c) {
847
1105
  }
848
1106
  // Annotate the CommonJS export names for ESM import in node:
849
1107
  0 && (module.exports = {
1108
+ ApiError,
850
1109
  AuthenticationError,
851
1110
  ConfigLoader,
1111
+ ERROR_CODES,
1112
+ InterceptorManager,
852
1113
  NetworkError,
1114
+ PaymentError,
853
1115
  RateLimitError,
854
1116
  ValidationError,
1117
+ WebhookError,
855
1118
  ZendFiClient,
856
1119
  ZendFiError,
1120
+ createZendFiError,
1121
+ isZendFiError,
857
1122
  processWebhook,
858
1123
  verifyExpressWebhook,
859
1124
  verifyNextWebhook,