From 503d6a17a89a3836996e9077e36ac9529afdb0e8 Mon Sep 17 00:00:00 2001 From: dadams Date: Tue, 7 Jan 2025 18:25:22 -0800 Subject: [PATCH 1/4] dadams-factory: Add a comprehensive WebhookEvent type encompassing all the events listed at https://docs.withorb.com/guides/integrations-and-exports/webhooks --- src/index.ts | 3 + src/resources/webhooks.ts | 288 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) diff --git a/src/index.ts b/src/index.ts index 1b5ba1f5..dfe7b000 100644 --- a/src/index.ts +++ b/src/index.ts @@ -145,6 +145,7 @@ import { Prices, PricesPage, } from './resources/prices/prices'; +import { WebhookEvent } from './resources/webhooks'; export interface ClientOptions { /** @@ -519,6 +520,8 @@ export declare namespace Orb { type DimensionalPriceGroupListParams as DimensionalPriceGroupListParams, }; + export { type WebhookEvent as WebhookEvent }; + export type AmountDiscount = API.AmountDiscount; export type BillingCycleRelativeDate = API.BillingCycleRelativeDate; export type Discount = API.Discount; diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts index f04f6fef..c81a6453 100644 --- a/src/resources/webhooks.ts +++ b/src/resources/webhooks.ts @@ -3,6 +3,11 @@ import { APIResource } from 'orb-billing/resource'; import { createHmac } from 'node:crypto'; import { debug, getRequiredHeader, HeadersLike } from 'orb-billing/core'; +import { Customer } from 'orb-billing/resources/customers'; +import { Subscription } from 'orb-billing/resources/subscriptions'; +import { Invoice } from 'orb-billing/resources/invoices'; +import { Discount } from 'orb-billing/resources/shared'; +import { CreditNote } from 'orb-billing/resources/credit-notes'; export class Webhooks extends APIResource { /** @@ -140,3 +145,286 @@ export class Webhooks extends APIResource { throw new Error('None of the given webhook signatures match the expected signature'); } } + +// Base webhook event interface +interface BaseWebhookEvent { + id: string; + created_at: string; + type: string; +} + +// Customer events +interface CustomerCreatedEvent extends BaseWebhookEvent { + type: 'customer.created'; + customer: Customer; + properties: Record; +} + +interface CustomerEditedEvent extends BaseWebhookEvent { + type: 'customer.edited'; + customer: Customer; + properties: { + previous_attributes: Partial; + }; +} + +// Subscription events +interface SubscriptionEvent extends BaseWebhookEvent { + subscription: Subscription; +} + +interface SubscriptionCreatedEvent extends SubscriptionEvent { + type: 'subscription.created'; + properties: Record; +} + +interface SubscriptionStartedEvent extends SubscriptionEvent { + type: 'subscription.started'; + properties: Record; +} + +interface SubscriptionFixedFeeQuantityUpdatedEvent extends SubscriptionEvent { + type: 'subscription.fixed_fee_quantity_updated'; + properties: { + old_quantity: number; + new_quantity: number; + effective_date: string; + price_id: string; + }; +} + +interface SubscriptionEditedEvent extends SubscriptionEvent { + type: 'subscription.edited'; + properties: { + previous_attributes: Partial; + }; +} + +interface SubscriptionEndedEvent extends SubscriptionEvent { + type: 'subscription.ended'; + properties: Record; +} + +interface SubscriptionPlanChangedEvent extends SubscriptionEvent { + type: 'subscription.plan_changed'; + properties: { + previous_plan_id: string; + }; +} + +interface SubscriptionPlanVersionChangeScheduledEvent extends SubscriptionEvent { + type: 'subscription.plan_version_change_scheduled'; + properties: { + effective_date: string; + previous_plan_version_number: number; + new_plan_version_number: number; + }; +} + +interface SubscriptionPlanVersionChangedEvent extends SubscriptionEvent { + type: 'subscription.plan_version_changed'; + properties: { + effective_date: string; + previous_plan_version_number: number; + new_plan_version_number: number; + }; +} + +// Invoice events +interface InvoiceEvent extends BaseWebhookEvent { + invoice: Invoice; +} + +interface InvoiceDateElapsedEvent extends InvoiceEvent { + type: 'invoice.invoice_date_elapsed'; + properties: { + invoice_date: string; + }; +} + +interface InvoiceIssuedEvent extends InvoiceEvent { + type: 'invoice.issued'; + properties: { + automatically_marked_as_paid: boolean; + }; +} + +interface InvoiceIssueFailedEvent extends InvoiceEvent { + type: 'invoice.issue_failed'; + properties: { + reason: string; + }; +} + +interface InvoicePaymentFailedEvent extends InvoiceEvent { + type: 'invoice.payment_failed'; + properties: { + payment_provider: 'stripe'; + payment_provider_id: string; + payment_provider_transaction_id: string | null; + }; +} + +interface InvoicePaymentProcessingEvent extends InvoiceEvent { + type: 'invoice.payment_processing'; + properties: { + payment_provider: 'stripe'; + payment_provider_id: string; + }; +} + +interface InvoicePaymentSucceededEvent extends InvoiceEvent { + type: 'invoice.payment_succeeded'; + properties: { + payment_provider: 'stripe'; + payment_provider_id: string; + payment_provider_transaction_id: string; + }; +} + +interface InvoiceEditedEvent extends InvoiceEvent { + type: 'invoice.edited'; + properties: { + previous_attributes: { + amount_due?: string; + subtotal?: string; + total?: string; + discounts?: Array; + minimum?: any; + line_items?: Array; + }; + }; +} + +interface InvoiceManuallyMarkedAsVoidEvent extends InvoiceEvent { + type: 'invoice.manually_marked_as_void'; + properties: Record; +} + +interface InvoiceManuallyMarkedAsPaidEvent extends InvoiceEvent { + type: 'invoice.manually_marked_as_paid'; + properties: { + payment_received_date: string; + external_id: string; + notes: string; + }; +} + +interface InvoiceUndoMarkAsPaidEvent extends InvoiceEvent { + type: 'invoice.undo_mark_as_paid'; + properties: Record; +} + +interface InvoiceSyncSucceededEvent extends InvoiceEvent { + type: 'invoice.sync_succeeded'; + properties: { + payment_provider: string; + payment_provider_id: string; + }; +} + +interface InvoiceSyncFailedEvent extends InvoiceEvent { + type: 'invoice.sync_failed'; + properties: { + payment_provider: string; + payment_provider_id: string; + }; +} + +// Credit note events +interface CreditNoteEvent extends BaseWebhookEvent { + credit_note: CreditNote; +} + +interface CreditNoteIssuedEvent extends CreditNoteEvent { + type: 'credit_note.issued'; + properties: Record; +} + +interface CreditNoteMarkedAsVoidEvent extends CreditNoteEvent { + type: 'credit_note.marked_as_void'; + properties: Record; +} + +// Usage and balance events +interface SubscriptionUsageExceededEvent extends SubscriptionEvent { + type: 'subscription.usage_exceeded'; + properties: { + billable_metric_id: string; + timeframe_start: string; + timeframe_end: string; + quantity_threshold: number; + }; +} + +interface SubscriptionCostExceededEvent extends SubscriptionEvent { + type: 'subscription.cost_exceeded'; + properties: { + timeframe_start: string; + timeframe_end: string; + amount_threshold: number; + }; +} + +interface CustomerCreditBalanceEvent extends BaseWebhookEvent { + type: 'customer.credit_balance_depleted' | 'customer.credit_balance_recovered'; + customer: Customer; + properties: { + pricing_unit: { + name: string; + symbol: string; + display_name: string; + }; + }; +} + +interface CustomerCreditBalanceDroppedEvent extends BaseWebhookEvent { + type: 'customer.credit_balance_dropped'; + customer: Customer; + properties: { + balance_threshold: string; + pricing_unit: { + name: string; + symbol: string; + display_name: string; + }; + }; +} + +// Test event +interface ResourceEventTest extends BaseWebhookEvent { + type: 'resource_event.test'; + message: string; +} + +// Union type of all possible webhook events +export type WebhookEvent = + | CustomerCreatedEvent + | CustomerEditedEvent + | SubscriptionCreatedEvent + | SubscriptionStartedEvent + | SubscriptionFixedFeeQuantityUpdatedEvent + | SubscriptionEditedEvent + | SubscriptionEndedEvent + | SubscriptionPlanChangedEvent + | SubscriptionPlanVersionChangeScheduledEvent + | SubscriptionPlanVersionChangedEvent + | InvoiceDateElapsedEvent + | InvoiceIssuedEvent + | InvoiceIssueFailedEvent + | InvoicePaymentFailedEvent + | InvoicePaymentProcessingEvent + | InvoicePaymentSucceededEvent + | InvoiceEditedEvent + | InvoiceManuallyMarkedAsVoidEvent + | InvoiceManuallyMarkedAsPaidEvent + | InvoiceUndoMarkAsPaidEvent + | InvoiceSyncSucceededEvent + | InvoiceSyncFailedEvent + | CreditNoteIssuedEvent + | CreditNoteMarkedAsVoidEvent + | SubscriptionUsageExceededEvent + | SubscriptionCostExceededEvent + | CustomerCreditBalanceEvent + | CustomerCreditBalanceDroppedEvent + | ResourceEventTest; From 6ca9f0ba6bcada77a27ca461c489ccd2cb25421c Mon Sep 17 00:00:00 2001 From: dadams Date: Wed, 8 Jan 2025 14:36:29 -0800 Subject: [PATCH 2/4] update webhook unwrap function return type --- src/resources/webhooks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts index c81a6453..ed3fe9a5 100644 --- a/src/resources/webhooks.ts +++ b/src/resources/webhooks.ts @@ -19,9 +19,9 @@ export class Webhooks extends APIResource { payload: string, headers: HeadersLike, secret: string | undefined | null = this._client.webhookSecret, - ): Object { + ): WebhookEvent { this.verifySignature(payload, headers, secret); - return JSON.parse(payload); + return JSON.parse(payload) as WebhookEvent; } private parseSecret(secret: string | null | undefined): Uint8Array { From 7e91037886a54179b0ecc1d98b1d5fe3f6fbb925 Mon Sep 17 00:00:00 2001 From: dadams Date: Wed, 8 Jan 2025 14:38:43 -0800 Subject: [PATCH 3/4] use Invoice.Minimum in place of any type --- src/resources/webhooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts index ed3fe9a5..563759a7 100644 --- a/src/resources/webhooks.ts +++ b/src/resources/webhooks.ts @@ -290,7 +290,7 @@ interface InvoiceEditedEvent extends InvoiceEvent { subtotal?: string; total?: string; discounts?: Array; - minimum?: any; + minimum?: Invoice.Minimum; line_items?: Array; }; }; From 31253fdb1160083869f1e5260c8bc0a9b0da2809 Mon Sep 17 00:00:00 2001 From: dadams Date: Wed, 8 Jan 2025 14:39:49 -0800 Subject: [PATCH 4/4] update invoice line item type --- src/resources/webhooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/webhooks.ts b/src/resources/webhooks.ts index 563759a7..2f09740f 100644 --- a/src/resources/webhooks.ts +++ b/src/resources/webhooks.ts @@ -291,7 +291,7 @@ interface InvoiceEditedEvent extends InvoiceEvent { total?: string; discounts?: Array; minimum?: Invoice.Minimum; - line_items?: Array; + line_items?: Array; }; }; }