@zendfi/sdk 0.1.1 → 0.1.2

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.d.mts CHANGED
@@ -155,7 +155,7 @@ interface WebhookPayload {
155
155
  data: Payment | Subscription;
156
156
  }
157
157
  interface VerifyWebhookRequest {
158
- payload: string;
158
+ payload: string | object;
159
159
  signature: string;
160
160
  secret: string;
161
161
  }
@@ -608,4 +608,64 @@ declare function verifyExpressWebhook(request: any, secret?: string): Promise<We
608
608
  */
609
609
  declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
610
610
 
611
- export { type ApproveEscrowRequest, AuthenticationError, ConfigLoader, type CreateEscrowRequest, type CreateInstallmentPlanRequest, type CreateInstallmentPlanResponse, type CreateInvoiceRequest, type CreatePaymentLinkRequest, type CreatePaymentRequest, type CreateSubscriptionPlanRequest, type CreateSubscriptionRequest, type Currency, type DisputeEscrowRequest, type Environment, type Escrow, type EscrowStatus, type InstallmentPlan, type InstallmentPlanStatus, type InstallmentScheduleItem, type Invoice, type InvoiceLineItem, type InvoiceStatus, type ListPaymentsRequest, NetworkError, type PaginatedResponse, type Payment, type PaymentLink, type PaymentStatus, type PaymentToken, RateLimitError, type RefundEscrowRequest, type ReleaseCondition, type SplitRecipient, type SplitStatus, type Subscription, type SubscriptionPlan, type SubscriptionStatus, ValidationError, type VerifyWebhookRequest, type WebhookEvent, type WebhookPayload, ZendFiClient, type ZendFiConfig, ZendFiError, verifyExpressWebhook, verifyNextWebhook, verifyWebhookSignature, zendfi };
611
+ /**
612
+ * ZendFi Webhook Handlers
613
+ * Type-safe webhook handlers with automatic verification and deduplication
614
+ */
615
+
616
+ /**
617
+ * Webhook handler configuration
618
+ */
619
+ interface WebhookHandlerConfig {
620
+ /** Your webhook secret from ZendFi dashboard */
621
+ secret: string;
622
+ /** Optional: Path to store processed webhook IDs (for deduplication) */
623
+ onProcessed?: (webhookId: string) => Promise<void>;
624
+ /** Optional: Check if webhook was already processed */
625
+ isProcessed?: (webhookId: string) => Promise<boolean>;
626
+ /** Optional: Custom error handler */
627
+ onError?: (error: Error, event?: WebhookEvent) => void | Promise<void>;
628
+ }
629
+ /**
630
+ * Event handler function type
631
+ */
632
+ type WebhookEventHandler<T = any> = (data: T, event: WebhookPayload) => void | Promise<void>;
633
+ /**
634
+ * Webhook handlers map - type-safe event handlers
635
+ */
636
+ type WebhookHandlers = Partial<{
637
+ 'payment.created': WebhookEventHandler;
638
+ 'payment.confirmed': WebhookEventHandler;
639
+ 'payment.failed': WebhookEventHandler;
640
+ 'payment.expired': WebhookEventHandler;
641
+ 'subscription.created': WebhookEventHandler;
642
+ 'subscription.activated': WebhookEventHandler;
643
+ 'subscription.canceled': WebhookEventHandler;
644
+ 'subscription.payment_failed': WebhookEventHandler;
645
+ 'split.completed': WebhookEventHandler;
646
+ 'split.failed': WebhookEventHandler;
647
+ 'installment.due': WebhookEventHandler;
648
+ 'installment.paid': WebhookEventHandler;
649
+ 'installment.late': WebhookEventHandler;
650
+ 'escrow.funded': WebhookEventHandler;
651
+ 'escrow.released': WebhookEventHandler;
652
+ 'escrow.refunded': WebhookEventHandler;
653
+ 'escrow.disputed': WebhookEventHandler;
654
+ 'invoice.sent': WebhookEventHandler;
655
+ 'invoice.paid': WebhookEventHandler;
656
+ }>;
657
+ /**
658
+ * Webhook processing result
659
+ */
660
+ interface WebhookResult {
661
+ success: boolean;
662
+ processed: boolean;
663
+ error?: string;
664
+ event?: WebhookEvent;
665
+ }
666
+ /**
667
+ * Process webhook with handlers
668
+ */
669
+ declare function processWebhook(payload: WebhookPayload, handlers: WebhookHandlers, config: WebhookHandlerConfig): Promise<WebhookResult>;
670
+
671
+ export { type ApproveEscrowRequest, AuthenticationError, ConfigLoader, type CreateEscrowRequest, type CreateInstallmentPlanRequest, type CreateInstallmentPlanResponse, type CreateInvoiceRequest, type CreatePaymentLinkRequest, type CreatePaymentRequest, type CreateSubscriptionPlanRequest, type CreateSubscriptionRequest, type Currency, type DisputeEscrowRequest, type Environment, type Escrow, type EscrowStatus, type InstallmentPlan, type InstallmentPlanStatus, type InstallmentScheduleItem, type Invoice, type InvoiceLineItem, type InvoiceStatus, type ListPaymentsRequest, NetworkError, type PaginatedResponse, type Payment, type PaymentLink, type PaymentStatus, type PaymentToken, RateLimitError, type RefundEscrowRequest, type ReleaseCondition, type SplitRecipient, type SplitStatus, type Subscription, type SubscriptionPlan, type SubscriptionStatus, ValidationError, type VerifyWebhookRequest, type WebhookEvent, type WebhookEventHandler, type WebhookHandlerConfig, type WebhookHandlers, type WebhookPayload, type WebhookResult, ZendFiClient, type ZendFiConfig, ZendFiError, processWebhook, verifyExpressWebhook, verifyNextWebhook, verifyWebhookSignature, zendfi };
package/dist/index.d.ts CHANGED
@@ -155,7 +155,7 @@ interface WebhookPayload {
155
155
  data: Payment | Subscription;
156
156
  }
157
157
  interface VerifyWebhookRequest {
158
- payload: string;
158
+ payload: string | object;
159
159
  signature: string;
160
160
  secret: string;
161
161
  }
@@ -608,4 +608,64 @@ declare function verifyExpressWebhook(request: any, secret?: string): Promise<We
608
608
  */
609
609
  declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
610
610
 
611
- export { type ApproveEscrowRequest, AuthenticationError, ConfigLoader, type CreateEscrowRequest, type CreateInstallmentPlanRequest, type CreateInstallmentPlanResponse, type CreateInvoiceRequest, type CreatePaymentLinkRequest, type CreatePaymentRequest, type CreateSubscriptionPlanRequest, type CreateSubscriptionRequest, type Currency, type DisputeEscrowRequest, type Environment, type Escrow, type EscrowStatus, type InstallmentPlan, type InstallmentPlanStatus, type InstallmentScheduleItem, type Invoice, type InvoiceLineItem, type InvoiceStatus, type ListPaymentsRequest, NetworkError, type PaginatedResponse, type Payment, type PaymentLink, type PaymentStatus, type PaymentToken, RateLimitError, type RefundEscrowRequest, type ReleaseCondition, type SplitRecipient, type SplitStatus, type Subscription, type SubscriptionPlan, type SubscriptionStatus, ValidationError, type VerifyWebhookRequest, type WebhookEvent, type WebhookPayload, ZendFiClient, type ZendFiConfig, ZendFiError, verifyExpressWebhook, verifyNextWebhook, verifyWebhookSignature, zendfi };
611
+ /**
612
+ * ZendFi Webhook Handlers
613
+ * Type-safe webhook handlers with automatic verification and deduplication
614
+ */
615
+
616
+ /**
617
+ * Webhook handler configuration
618
+ */
619
+ interface WebhookHandlerConfig {
620
+ /** Your webhook secret from ZendFi dashboard */
621
+ secret: string;
622
+ /** Optional: Path to store processed webhook IDs (for deduplication) */
623
+ onProcessed?: (webhookId: string) => Promise<void>;
624
+ /** Optional: Check if webhook was already processed */
625
+ isProcessed?: (webhookId: string) => Promise<boolean>;
626
+ /** Optional: Custom error handler */
627
+ onError?: (error: Error, event?: WebhookEvent) => void | Promise<void>;
628
+ }
629
+ /**
630
+ * Event handler function type
631
+ */
632
+ type WebhookEventHandler<T = any> = (data: T, event: WebhookPayload) => void | Promise<void>;
633
+ /**
634
+ * Webhook handlers map - type-safe event handlers
635
+ */
636
+ type WebhookHandlers = Partial<{
637
+ 'payment.created': WebhookEventHandler;
638
+ 'payment.confirmed': WebhookEventHandler;
639
+ 'payment.failed': WebhookEventHandler;
640
+ 'payment.expired': WebhookEventHandler;
641
+ 'subscription.created': WebhookEventHandler;
642
+ 'subscription.activated': WebhookEventHandler;
643
+ 'subscription.canceled': WebhookEventHandler;
644
+ 'subscription.payment_failed': WebhookEventHandler;
645
+ 'split.completed': WebhookEventHandler;
646
+ 'split.failed': WebhookEventHandler;
647
+ 'installment.due': WebhookEventHandler;
648
+ 'installment.paid': WebhookEventHandler;
649
+ 'installment.late': WebhookEventHandler;
650
+ 'escrow.funded': WebhookEventHandler;
651
+ 'escrow.released': WebhookEventHandler;
652
+ 'escrow.refunded': WebhookEventHandler;
653
+ 'escrow.disputed': WebhookEventHandler;
654
+ 'invoice.sent': WebhookEventHandler;
655
+ 'invoice.paid': WebhookEventHandler;
656
+ }>;
657
+ /**
658
+ * Webhook processing result
659
+ */
660
+ interface WebhookResult {
661
+ success: boolean;
662
+ processed: boolean;
663
+ error?: string;
664
+ event?: WebhookEvent;
665
+ }
666
+ /**
667
+ * Process webhook with handlers
668
+ */
669
+ declare function processWebhook(payload: WebhookPayload, handlers: WebhookHandlers, config: WebhookHandlerConfig): Promise<WebhookResult>;
670
+
671
+ export { type ApproveEscrowRequest, AuthenticationError, ConfigLoader, type CreateEscrowRequest, type CreateInstallmentPlanRequest, type CreateInstallmentPlanResponse, type CreateInvoiceRequest, type CreatePaymentLinkRequest, type CreatePaymentRequest, type CreateSubscriptionPlanRequest, type CreateSubscriptionRequest, type Currency, type DisputeEscrowRequest, type Environment, type Escrow, type EscrowStatus, type InstallmentPlan, type InstallmentPlanStatus, type InstallmentScheduleItem, type Invoice, type InvoiceLineItem, type InvoiceStatus, type ListPaymentsRequest, NetworkError, type PaginatedResponse, type Payment, type PaymentLink, type PaymentStatus, type PaymentToken, RateLimitError, type RefundEscrowRequest, type ReleaseCondition, type SplitRecipient, type SplitStatus, type Subscription, type SubscriptionPlan, type SubscriptionStatus, ValidationError, type VerifyWebhookRequest, type WebhookEvent, type WebhookEventHandler, type WebhookHandlerConfig, type WebhookHandlers, type WebhookPayload, type WebhookResult, ZendFiClient, type ZendFiConfig, ZendFiError, processWebhook, verifyExpressWebhook, verifyNextWebhook, verifyWebhookSignature, zendfi };
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ __export(index_exports, {
37
37
  ValidationError: () => ValidationError,
38
38
  ZendFiClient: () => ZendFiClient,
39
39
  ZendFiError: () => ZendFiError,
40
+ processWebhook: () => processWebhook,
40
41
  verifyExpressWebhook: () => verifyExpressWebhook,
41
42
  verifyNextWebhook: () => verifyNextWebhook,
42
43
  verifyWebhookSignature: () => verifyWebhookSignature,
@@ -327,9 +328,6 @@ var ZendFiClient = class {
327
328
  async listPaymentLinks() {
328
329
  return [];
329
330
  }
330
- // ===================================================================
331
- // INSTALLMENT PLANS - Pay over time
332
- // ===================================================================
333
331
  /**
334
332
  * Create an installment plan
335
333
  * Split a purchase into multiple scheduled payments
@@ -380,9 +378,6 @@ var ZendFiClient = class {
380
378
  `/api/v1/installment-plans/${planId}/cancel`
381
379
  );
382
380
  }
383
- // ===================================================================
384
- // ESCROW - Secure fund holding
385
- // ===================================================================
386
381
  /**
387
382
  * Create an escrow transaction
388
383
  * Hold funds until conditions are met
@@ -432,9 +427,6 @@ var ZendFiClient = class {
432
427
  async disputeEscrow(escrowId, request) {
433
428
  return this.request("POST", `/api/v1/escrows/${escrowId}/dispute`, request);
434
429
  }
435
- // ===================================================================
436
- // INVOICES - Professional billing
437
- // ===================================================================
438
430
  /**
439
431
  * Create an invoice
440
432
  */
@@ -486,15 +478,34 @@ var ZendFiClient = class {
486
478
  if (!request.payload || !request.signature || !request.secret) {
487
479
  return false;
488
480
  }
489
- const parsedPayload = JSON.parse(request.payload);
490
- if (!parsedPayload.event || !parsedPayload.merchant_id || !parsedPayload.timestamp) {
481
+ let payloadString;
482
+ let parsedPayload = null;
483
+ if (typeof request.payload === "string") {
484
+ payloadString = request.payload;
485
+ try {
486
+ parsedPayload = JSON.parse(payloadString);
487
+ } catch (e) {
488
+ return false;
489
+ }
490
+ } else if (typeof request.payload === "object") {
491
+ parsedPayload = request.payload;
492
+ try {
493
+ payloadString = JSON.stringify(request.payload);
494
+ } catch (e) {
495
+ return false;
496
+ }
497
+ } else {
498
+ return false;
499
+ }
500
+ if (!parsedPayload || !parsedPayload.event || !parsedPayload.merchant_id || !parsedPayload.timestamp) {
491
501
  return false;
492
502
  }
493
- const computedSignature = this.computeHmacSignature(request.payload, request.secret);
503
+ const computedSignature = this.computeHmacSignature(payloadString, request.secret);
494
504
  return this.timingSafeEqual(request.signature, computedSignature);
495
- } catch (error) {
505
+ } catch (err) {
506
+ const error = err;
496
507
  if (this.config.environment === "development") {
497
- console.error("Webhook verification error:", error);
508
+ console.error("Webhook verification error:", error?.message || String(error));
498
509
  }
499
510
  return false;
500
511
  }
@@ -664,6 +675,65 @@ function verifyWebhookSignature(payload, signature, secret) {
664
675
  secret
665
676
  });
666
677
  }
678
+
679
+ // src/webhook-handler.ts
680
+ var processedWebhooks = /* @__PURE__ */ new Set();
681
+ var defaultIsProcessed = async (webhookId) => {
682
+ return processedWebhooks.has(webhookId);
683
+ };
684
+ var defaultOnProcessed = async (webhookId) => {
685
+ processedWebhooks.add(webhookId);
686
+ if (processedWebhooks.size > 1e4) {
687
+ const iterator = processedWebhooks.values();
688
+ for (let i = 0; i < 1e3; i++) {
689
+ const { value } = iterator.next();
690
+ if (value) processedWebhooks.delete(value);
691
+ }
692
+ }
693
+ };
694
+ function generateWebhookId(payload) {
695
+ return `${payload.merchant_id}:${payload.event}:${payload.timestamp}`;
696
+ }
697
+ async function processWebhook(payload, handlers, config) {
698
+ try {
699
+ const webhookId = generateWebhookId(payload);
700
+ const isProcessed = config.isProcessed || defaultIsProcessed;
701
+ const onProcessed = config.onProcessed || defaultOnProcessed;
702
+ if (await isProcessed(webhookId)) {
703
+ return {
704
+ success: true,
705
+ processed: false,
706
+ event: payload.event
707
+ };
708
+ }
709
+ const handler = handlers[payload.event];
710
+ if (!handler) {
711
+ return {
712
+ success: true,
713
+ processed: false,
714
+ event: payload.event
715
+ };
716
+ }
717
+ await handler(payload.data, payload);
718
+ await onProcessed(webhookId);
719
+ return {
720
+ success: true,
721
+ processed: true,
722
+ event: payload.event
723
+ };
724
+ } catch (error) {
725
+ const err = error;
726
+ if (config.onError) {
727
+ await config.onError(err, payload?.event);
728
+ }
729
+ return {
730
+ success: false,
731
+ processed: false,
732
+ error: err.message,
733
+ event: payload?.event
734
+ };
735
+ }
736
+ }
667
737
  // Annotate the CommonJS export names for ESM import in node:
668
738
  0 && (module.exports = {
669
739
  AuthenticationError,
@@ -673,6 +743,7 @@ function verifyWebhookSignature(payload, signature, secret) {
673
743
  ValidationError,
674
744
  ZendFiClient,
675
745
  ZendFiError,
746
+ processWebhook,
676
747
  verifyExpressWebhook,
677
748
  verifyNextWebhook,
678
749
  verifyWebhookSignature,
package/dist/index.mjs CHANGED
@@ -288,9 +288,6 @@ var ZendFiClient = class {
288
288
  async listPaymentLinks() {
289
289
  return [];
290
290
  }
291
- // ===================================================================
292
- // INSTALLMENT PLANS - Pay over time
293
- // ===================================================================
294
291
  /**
295
292
  * Create an installment plan
296
293
  * Split a purchase into multiple scheduled payments
@@ -341,9 +338,6 @@ var ZendFiClient = class {
341
338
  `/api/v1/installment-plans/${planId}/cancel`
342
339
  );
343
340
  }
344
- // ===================================================================
345
- // ESCROW - Secure fund holding
346
- // ===================================================================
347
341
  /**
348
342
  * Create an escrow transaction
349
343
  * Hold funds until conditions are met
@@ -393,9 +387,6 @@ var ZendFiClient = class {
393
387
  async disputeEscrow(escrowId, request) {
394
388
  return this.request("POST", `/api/v1/escrows/${escrowId}/dispute`, request);
395
389
  }
396
- // ===================================================================
397
- // INVOICES - Professional billing
398
- // ===================================================================
399
390
  /**
400
391
  * Create an invoice
401
392
  */
@@ -447,15 +438,34 @@ var ZendFiClient = class {
447
438
  if (!request.payload || !request.signature || !request.secret) {
448
439
  return false;
449
440
  }
450
- const parsedPayload = JSON.parse(request.payload);
451
- if (!parsedPayload.event || !parsedPayload.merchant_id || !parsedPayload.timestamp) {
441
+ let payloadString;
442
+ let parsedPayload = null;
443
+ if (typeof request.payload === "string") {
444
+ payloadString = request.payload;
445
+ try {
446
+ parsedPayload = JSON.parse(payloadString);
447
+ } catch (e) {
448
+ return false;
449
+ }
450
+ } else if (typeof request.payload === "object") {
451
+ parsedPayload = request.payload;
452
+ try {
453
+ payloadString = JSON.stringify(request.payload);
454
+ } catch (e) {
455
+ return false;
456
+ }
457
+ } else {
458
+ return false;
459
+ }
460
+ if (!parsedPayload || !parsedPayload.event || !parsedPayload.merchant_id || !parsedPayload.timestamp) {
452
461
  return false;
453
462
  }
454
- const computedSignature = this.computeHmacSignature(request.payload, request.secret);
463
+ const computedSignature = this.computeHmacSignature(payloadString, request.secret);
455
464
  return this.timingSafeEqual(request.signature, computedSignature);
456
- } catch (error) {
465
+ } catch (err) {
466
+ const error = err;
457
467
  if (this.config.environment === "development") {
458
- console.error("Webhook verification error:", error);
468
+ console.error("Webhook verification error:", error?.message || String(error));
459
469
  }
460
470
  return false;
461
471
  }
@@ -625,6 +635,65 @@ function verifyWebhookSignature(payload, signature, secret) {
625
635
  secret
626
636
  });
627
637
  }
638
+
639
+ // src/webhook-handler.ts
640
+ var processedWebhooks = /* @__PURE__ */ new Set();
641
+ var defaultIsProcessed = async (webhookId) => {
642
+ return processedWebhooks.has(webhookId);
643
+ };
644
+ var defaultOnProcessed = async (webhookId) => {
645
+ processedWebhooks.add(webhookId);
646
+ if (processedWebhooks.size > 1e4) {
647
+ const iterator = processedWebhooks.values();
648
+ for (let i = 0; i < 1e3; i++) {
649
+ const { value } = iterator.next();
650
+ if (value) processedWebhooks.delete(value);
651
+ }
652
+ }
653
+ };
654
+ function generateWebhookId(payload) {
655
+ return `${payload.merchant_id}:${payload.event}:${payload.timestamp}`;
656
+ }
657
+ async function processWebhook(payload, handlers, config) {
658
+ try {
659
+ const webhookId = generateWebhookId(payload);
660
+ const isProcessed = config.isProcessed || defaultIsProcessed;
661
+ const onProcessed = config.onProcessed || defaultOnProcessed;
662
+ if (await isProcessed(webhookId)) {
663
+ return {
664
+ success: true,
665
+ processed: false,
666
+ event: payload.event
667
+ };
668
+ }
669
+ const handler = handlers[payload.event];
670
+ if (!handler) {
671
+ return {
672
+ success: true,
673
+ processed: false,
674
+ event: payload.event
675
+ };
676
+ }
677
+ await handler(payload.data, payload);
678
+ await onProcessed(webhookId);
679
+ return {
680
+ success: true,
681
+ processed: true,
682
+ event: payload.event
683
+ };
684
+ } catch (error) {
685
+ const err = error;
686
+ if (config.onError) {
687
+ await config.onError(err, payload?.event);
688
+ }
689
+ return {
690
+ success: false,
691
+ processed: false,
692
+ error: err.message,
693
+ event: payload?.event
694
+ };
695
+ }
696
+ }
628
697
  export {
629
698
  AuthenticationError,
630
699
  ConfigLoader,
@@ -633,6 +702,7 @@ export {
633
702
  ValidationError,
634
703
  ZendFiClient,
635
704
  ZendFiError,
705
+ processWebhook,
636
706
  verifyExpressWebhook,
637
707
  verifyNextWebhook,
638
708
  verifyWebhookSignature,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zendfi/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Zero-config TypeScript SDK for ZendFi crypto payments",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -10,14 +10,24 @@
10
10
  "import": "./dist/index.mjs",
11
11
  "require": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts"
13
+ },
14
+ "./nextjs": {
15
+ "import": "./dist/nextjs.mjs",
16
+ "require": "./dist/nextjs.js",
17
+ "types": "./dist/nextjs.d.ts"
18
+ },
19
+ "./express": {
20
+ "import": "./dist/express.mjs",
21
+ "require": "./dist/express.js",
22
+ "types": "./dist/express.d.ts"
13
23
  }
14
24
  },
15
25
  "files": [
16
26
  "dist"
17
27
  ],
18
28
  "scripts": {
19
- "build": "tsup src/index.ts --format esm,cjs --dts --clean",
20
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
29
+ "build": "tsup src/index.ts src/nextjs.ts src/express.ts --format esm,cjs --dts --clean",
30
+ "dev": "tsup src/index.ts src/nextjs.ts src/express.ts --format esm,cjs --dts --watch",
21
31
  "test": "vitest run",
22
32
  "test:watch": "vitest",
23
33
  "lint": "tsc --noEmit",