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..2f09740f 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 { /** @@ -14,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 { @@ -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?: Invoice.Minimum; + 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;