import Stripe from 'stripe'; import pg, { PoolConfig, QueryResult } from 'pg'; import { ConnectionOptions } from 'node:tls'; type PostgresConfig = { schema: string; poolConfig: PoolConfig; }; declare class PostgresClient { private config; pool: pg.Pool; constructor(config: PostgresConfig); delete(table: string, id: string): Promise; query(text: string, params?: any[]): Promise; upsertMany(entries: T[], table: string): Promise; upsertManyWithTimestampProtection(entries: T[], table: string, accountId: string, syncTimestamp?: string): Promise; private cleanseArrayField; findMissingEntries(table: string, ids: string[]): Promise; getSyncCursor(resource: string, accountId: string): Promise; updateSyncCursor(resource: string, accountId: string, cursor: number): Promise; markSyncRunning(resource: string, accountId: string): Promise; markSyncComplete(resource: string, accountId: string): Promise; markSyncError(resource: string, accountId: string, errorMessage: string): Promise; upsertAccount(accountData: { id: string; raw_data: any; }, apiKeyHash: string): Promise; getAllAccounts(): Promise; /** * Looks up an account ID by API key hash * Uses the GIN index on api_key_hashes for fast lookups * @param apiKeyHash - SHA-256 hash of the Stripe API key * @returns Account ID if found, null otherwise */ getAccountIdByApiKeyHash(apiKeyHash: string): Promise; getAccountRecordCounts(accountId: string): Promise<{ [tableName: string]: number; }>; deleteAccountWithCascade(accountId: string, useTransaction: boolean): Promise<{ [tableName: string]: number; }>; /** * Hash a string to a 32-bit integer for use with PostgreSQL advisory locks. * Uses a simple hash algorithm that produces consistent results. */ private hashToInt32; /** * Acquire a PostgreSQL advisory lock for the given key. * This lock is automatically released when the connection is closed or explicitly released. * Advisory locks are session-level and will block until the lock is available. * * @param key - A string key to lock on (will be hashed to an integer) */ acquireAdvisoryLock(key: string): Promise; /** * Release a PostgreSQL advisory lock for the given key. * * @param key - The same string key used to acquire the lock */ releaseAdvisoryLock(key: string): Promise; /** * Execute a function while holding an advisory lock. * The lock is automatically released after the function completes (success or error). * * IMPORTANT: This acquires a dedicated connection from the pool and holds it for the * duration of the function execution. PostgreSQL advisory locks are session-level, * so we must use the same connection for lock acquisition, operations, and release. * * @param key - A string key to lock on (will be hashed to an integer) * @param fn - The function to execute while holding the lock * @returns The result of the function */ withAdvisoryLock(key: string, fn: () => Promise): Promise; } /** * Simple logger interface compatible with both pino and console */ interface Logger { info(message?: unknown, ...optionalParams: unknown[]): void; warn(message?: unknown, ...optionalParams: unknown[]): void; error(message?: unknown, ...optionalParams: unknown[]): void; } type RevalidateEntity = 'charge' | 'credit_note' | 'customer' | 'dispute' | 'invoice' | 'payment_intent' | 'payment_method' | 'plan' | 'price' | 'product' | 'refund' | 'review' | 'radar.early_fraud_warning' | 'setup_intent' | 'subscription' | 'subscription_schedule' | 'tax_id' | 'entitlements'; type StripeSyncConfig = { /** @deprecated Use `poolConfig` with a connection string instead. */ databaseUrl?: string; /** Stripe secret key used to authenticate requests to the Stripe API. Defaults to empty string */ stripeSecretKey: string; /** Stripe account ID. If not provided, will be retrieved from Stripe API. Used as fallback option. */ stripeAccountId?: string; /** Stripe webhook signing secret for validating webhook signatures. Required if not using managed webhooks. */ stripeWebhookSecret?: string; /** Stripe API version for the webhooks, defaults to 2020-08-27 */ stripeApiVersion?: string; /** * Stripe limits related lists like invoice items in an invoice to 10 by default. * By enabling this, sync-engine automatically fetches the remaining elements before saving * */ autoExpandLists?: boolean; /** * If true, the sync engine will backfill related entities, i.e. when a invoice webhook comes in, it ensures that the customer is present and synced. * This ensures foreign key integrity, but comes at the cost of additional queries to the database (and added latency for Stripe calls if the entity is actually missing). */ backfillRelatedEntities?: boolean; /** * If true, the webhook data is not used and instead the webhook is just a trigger to fetch the entity from Stripe again. This ensures that a race condition with failed webhooks can never accidentally overwrite the data with an older state. * * Default: false */ revalidateObjectsViaStripeApi?: Array; /** @deprecated Use `poolConfig` instead. */ maxPostgresConnections?: number; poolConfig: PoolConfig; logger?: Logger; /** * Maximum number of retry attempts for 429 rate limit errors. * Default: 5 */ maxRetries?: number; /** * Initial delay in milliseconds before first retry attempt. * Delay increases exponentially: 1s, 2s, 4s, 8s, 16s, etc. * Default: 1000 (1 second) */ initialRetryDelayMs?: number; /** * Maximum delay in milliseconds between retry attempts. * Default: 60000 (60 seconds) */ maxRetryDelayMs?: number; /** * Random jitter in milliseconds added to retry delays to prevent thundering herd. * Default: 500 */ retryJitterMs?: number; }; type SyncObject = 'all' | 'customer' | 'customer_with_entitlements' | 'invoice' | 'price' | 'product' | 'subscription' | 'subscription_schedules' | 'setup_intent' | 'payment_method' | 'dispute' | 'charge' | 'payment_intent' | 'plan' | 'tax_id' | 'credit_note' | 'early_fraud_warning' | 'refund' | 'checkout_sessions'; interface Sync { synced: number; } interface SyncBackfill { products?: Sync; prices?: Sync; plans?: Sync; customers?: Sync; subscriptions?: Sync; subscriptionSchedules?: Sync; invoices?: Sync; setupIntents?: Sync; paymentIntents?: Sync; paymentMethods?: Sync; disputes?: Sync; charges?: Sync; taxIds?: Sync; creditNotes?: Sync; earlyFraudWarnings?: Sync; refunds?: Sync; checkoutSessions?: Sync; } interface SyncBackfillParams { created?: { /** * Minimum value to filter by (exclusive) */ gt?: number; /** * Minimum value to filter by (inclusive) */ gte?: number; /** * Maximum value to filter by (exclusive) */ lt?: number; /** * Maximum value to filter by (inclusive) */ lte?: number; }; object?: SyncObject; backfillRelatedEntities?: boolean; } interface SyncEntitlementsParams { object: 'entitlements'; customerId: string; pagination?: Pick; } interface SyncFeaturesParams { object: 'features'; pagination?: Pick; } declare class StripeSync { private config; stripe: Stripe; postgresClient: PostgresClient; private cachedAccount; constructor(config: StripeSyncConfig); /** * Get the Stripe account ID. Uses database lookup by API key hash for fast lookups, * with fallback to Stripe API if not found (first-time setup or new API key). */ getAccountId(objectAccountId?: string): Promise; /** * Upsert Stripe account information to the database * @param account - Stripe account object * @param apiKeyHash - SHA-256 hash of API key to store for fast lookups */ private upsertAccount; /** * Get the current account being synced */ getCurrentAccount(): Promise; /** * Get all accounts that have been synced to the database */ getAllSyncedAccounts(): Promise; /** * DANGEROUS: Delete an account and all associated data from the database * This operation cannot be undone! * * @param accountId - The Stripe account ID to delete * @param options - Options for deletion behavior * @param options.dryRun - If true, only count records without deleting (default: false) * @param options.useTransaction - If true, use transaction for atomic deletion (default: true) * @returns Deletion summary with counts and warnings */ dangerouslyDeleteSyncedAccountData(accountId: string, options?: { dryRun?: boolean; useTransaction?: boolean; }): Promise<{ deletedAccountId: string; deletedRecordCounts: { [tableName: string]: number; }; warnings: string[]; }>; processWebhook(payload: Buffer | string, signature: string | undefined): Promise; private readonly eventHandlers; processEvent(event: Stripe.Event): Promise; /** * Returns an array of all webhook event types that this sync engine can handle. * Useful for configuring webhook endpoints with specific event subscriptions. */ getSupportedEventTypes(): Stripe.WebhookEndpointCreateParams.EnabledEvent[]; private handleChargeEvent; private handleCustomerDeletedEvent; private handleCustomerEvent; private handleCheckoutSessionEvent; private handleSubscriptionEvent; private handleTaxIdEvent; private handleTaxIdDeletedEvent; private handleInvoiceEvent; private handleProductEvent; private handleProductDeletedEvent; private handlePriceEvent; private handlePriceDeletedEvent; private handlePlanEvent; private handlePlanDeletedEvent; private handleSetupIntentEvent; private handleSubscriptionScheduleEvent; private handlePaymentMethodEvent; private handleDisputeEvent; private handlePaymentIntentEvent; private handleCreditNoteEvent; private handleEarlyFraudWarningEvent; private handleRefundEvent; private handleReviewEvent; private handleEntitlementSummaryEvent; private getSyncTimestamp; private shouldRefetchEntity; private fetchOrUseWebhookData; syncSingleEntity(stripeId: string): Promise; syncBackfill(params?: SyncBackfillParams): Promise; syncProducts(syncParams?: SyncBackfillParams): Promise; syncPrices(syncParams?: SyncBackfillParams): Promise; syncPlans(syncParams?: SyncBackfillParams): Promise; syncCustomers(syncParams?: SyncBackfillParams): Promise; syncSubscriptions(syncParams?: SyncBackfillParams): Promise; syncSubscriptionSchedules(syncParams?: SyncBackfillParams): Promise; syncInvoices(syncParams?: SyncBackfillParams): Promise; syncCharges(syncParams?: SyncBackfillParams): Promise; syncSetupIntents(syncParams?: SyncBackfillParams): Promise; syncPaymentIntents(syncParams?: SyncBackfillParams): Promise; syncTaxIds(syncParams?: SyncBackfillParams): Promise; syncPaymentMethods(syncParams?: SyncBackfillParams): Promise; syncDisputes(syncParams?: SyncBackfillParams): Promise; syncEarlyFraudWarnings(syncParams?: SyncBackfillParams): Promise; syncRefunds(syncParams?: SyncBackfillParams): Promise; syncCreditNotes(syncParams?: SyncBackfillParams): Promise; syncFeatures(syncParams?: SyncFeaturesParams): Promise; syncEntitlements(customerId: string, syncParams?: SyncEntitlementsParams): Promise; syncCheckoutSessions(syncParams?: SyncBackfillParams): Promise; private fetchAndUpsert; private upsertCharges; private backfillCharges; private backfillPaymentIntents; private upsertCreditNotes; upsertCheckoutSessions(checkoutSessions: Stripe.Checkout.Session[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertEarlyFraudWarning(earlyFraudWarnings: Stripe.Radar.EarlyFraudWarning[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertRefunds(refunds: Stripe.Refund[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertReviews(reviews: Stripe.Review[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertCustomers(customers: (Stripe.Customer | Stripe.DeletedCustomer)[], accountId: string, syncTimestamp?: string): Promise<(Stripe.Customer | Stripe.DeletedCustomer)[]>; backfillCustomers(customerIds: string[], accountId: string): Promise; upsertDisputes(disputes: Stripe.Dispute[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertInvoices(invoices: Stripe.Invoice[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; backfillInvoices: (invoiceIds: string[], accountId: string) => Promise; backfillPrices: (priceIds: string[], accountId: string) => Promise; upsertPlans(plans: Stripe.Plan[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; deletePlan(id: string): Promise; upsertPrices(prices: Stripe.Price[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; deletePrice(id: string): Promise; upsertProducts(products: Stripe.Product[], accountId: string, syncTimestamp?: string): Promise; deleteProduct(id: string): Promise; backfillProducts(productIds: string[], accountId: string): Promise; upsertPaymentIntents(paymentIntents: Stripe.PaymentIntent[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertPaymentMethods(paymentMethods: Stripe.PaymentMethod[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertSetupIntents(setupIntents: Stripe.SetupIntent[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertTaxIds(taxIds: Stripe.TaxId[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; deleteTaxId(id: string): Promise; upsertSubscriptionItems(subscriptionItems: Stripe.SubscriptionItem[], accountId: string, syncTimestamp?: string): Promise; fillCheckoutSessionsLineItems(checkoutSessionIds: string[], accountId: string, syncTimestamp?: string): Promise; upsertCheckoutSessionLineItems(lineItems: Stripe.LineItem[], checkoutSessionId: string, accountId: string, syncTimestamp?: string): Promise; markDeletedSubscriptionItems(subscriptionId: string, currentSubItemIds: string[]): Promise<{ rowCount: number; }>; upsertSubscriptionSchedules(subscriptionSchedules: Stripe.SubscriptionSchedule[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; upsertSubscriptions(subscriptions: Stripe.Subscription[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise; deleteRemovedActiveEntitlements(customerId: string, currentActiveEntitlementIds: string[]): Promise<{ rowCount: number; }>; upsertFeatures(features: Stripe.Entitlements.Feature[], accountId: string, syncTimestamp?: string): Promise; backfillFeatures(featureIds: string[], accountId: string): Promise; upsertActiveEntitlements(customerId: string, activeEntitlements: Stripe.Entitlements.ActiveEntitlement[], accountId: string, backfillRelatedEntities?: boolean, syncTimestamp?: string): Promise<{ id: string; object: "entitlements.active_entitlement"; feature: string; customer: string; livemode: boolean; lookup_key: string; }[]>; findOrCreateManagedWebhook(url: string, params?: Omit): Promise; getManagedWebhook(id: string): Promise; /** * Get a managed webhook by URL and account ID. * Used for race condition recovery: when createManagedWebhook hits a unique constraint * violation (another instance created the webhook), we need to fetch the existing webhook * by URL since we only know the URL, not the ID of the webhook that won the race. */ getManagedWebhookByUrl(url: string): Promise; listManagedWebhooks(): Promise>; updateManagedWebhook(id: string, params: Stripe.WebhookEndpointUpdateParams): Promise; deleteManagedWebhook(id: string): Promise; upsertManagedWebhooks(webhooks: Array, accountId: string, syncTimestamp?: string): Promise>; backfillSubscriptions(subscriptionIds: string[], accountId: string): Promise; backfillSubscriptionSchedules: (subscriptionIds: string[], accountId: string) => Promise; /** * Stripe only sends the first 10 entries by default, the option will actively fetch all entries. */ private expandEntity; private fetchMissingEntities; } type MigrationConfig = { databaseUrl: string; ssl?: ConnectionOptions; logger?: Logger; }; declare function runMigrations(config: MigrationConfig): Promise; /** * Hashes a Stripe API key using SHA-256 * Used to store API key hashes in the database for fast account lookups * without storing the actual API key or making Stripe API calls * * @param apiKey - The Stripe API key (e.g., sk_test_... or sk_live_...) * @returns SHA-256 hash of the API key as a hex string */ declare function hashApiKey(apiKey: string): string; export { type Logger, PostgresClient, type RevalidateEntity, StripeSync, type StripeSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncEntitlementsParams, type SyncFeaturesParams, type SyncObject, hashApiKey, runMigrations };