@happyvertical/accounting 0.74.8

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DO-cM79R.js","sources":["../../src/providers/stripe/index.ts"],"sourcesContent":["/**\n * Stripe Provider\n *\n * Implements Stripe AR, payment, webhook, and billing operations using the\n * Stripe HTTP API directly. The SDK keeps Stripe as an optional runtime\n * dependency by relying on the platform fetch implementation.\n */\n\nimport { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';\nimport type {\n AccountingProviderType,\n AuditMatch,\n AuditOperations,\n AuditReport,\n BillInput,\n BillOperations,\n CustomerInput,\n CustomerOperations,\n DateRange,\n ExternalBill,\n ExternalCustomer,\n ExternalInvoice,\n ExternalPayment,\n ExternalRecord,\n ExternalVendor,\n FieldDiff,\n InvoiceInput,\n InvoiceOperations,\n ListOptions,\n PaymentInput,\n PaymentOperations,\n StripeAccountingProvider,\n StripeBillingOperations,\n StripeCheckoutLineItem,\n StripeCheckoutSession,\n StripeCheckoutSessionInput,\n StripeCustomerPortalSession,\n StripeCustomerPortalSessionInput,\n StripeOptions,\n StripeSubscriptionStatus,\n StripeSubscriptionStatusResult,\n SyncResult,\n VendorInput,\n VendorOperations,\n WebhookEvent,\n WebhookOperations,\n} from '../../types.js';\n\ntype StripePrimitive = string | number | boolean | Date | null | undefined;\ntype StripeFormValue =\n | StripePrimitive\n | StripeFormValue[]\n | { [key: string]: StripeFormValue };\n\ninterface StripeListResponse<T> {\n object: 'list';\n data: T[];\n has_more?: boolean;\n}\n\ninterface StripeCustomer {\n id: string;\n name?: string | null;\n email?: string | null;\n phone?: string | null;\n address?: StripeAddress | null;\n balance?: number | null;\n currency?: string | null;\n}\n\ninterface StripeAddress {\n line1?: string | null;\n line2?: string | null;\n city?: string | null;\n state?: string | null;\n postal_code?: string | null;\n country?: string | null;\n}\n\ninterface StripeInvoice {\n id: string;\n number?: string | null;\n customer?: string | StripeCustomer | null;\n created?: number | null;\n due_date?: number | null;\n subtotal?: number | null;\n tax?: number | null;\n total?: number | null;\n amount_paid?: number | null;\n amount_remaining?: number | null;\n status?: string | null;\n currency?: string | null;\n}\n\ninterface StripePaymentIntent {\n id: string;\n amount?: number | null;\n currency?: string | null;\n created?: number | null;\n status?: string | null;\n invoice?: string | null;\n payment_method_types?: string[] | null;\n latest_charge?: string | null;\n}\n\ninterface StripeCheckoutSessionResponse {\n id: string;\n url?: string | null;\n customer?: string | null;\n subscription?: string | null;\n payment_intent?: string | null;\n}\n\ninterface StripePortalSessionResponse {\n id: string;\n url: string;\n}\n\ninterface StripeSubscription {\n id: string;\n status: StripeSubscriptionStatus;\n customer: string | StripeCustomer;\n current_period_start?: number | null;\n current_period_end?: number | null;\n cancel_at_period_end?: boolean | null;\n canceled_at?: number | null;\n trial_end?: number | null;\n}\n\ninterface StripeWebhookPayload {\n type?: string | null;\n created?: number | null;\n data?: {\n object?: {\n id?: string | null;\n object?: string | null;\n } | null;\n } | null;\n}\n\n/**\n * Stripe accounting provider.\n */\nexport class StripeProvider implements StripeAccountingProvider {\n readonly type = 'stripe' as const;\n\n private readonly options: StripeOptions;\n private readonly fetchImpl: typeof fetch;\n\n readonly customers: CustomerOperations;\n readonly invoices: InvoiceOperations;\n readonly vendors: VendorOperations;\n readonly bills: BillOperations;\n readonly payments: PaymentOperations;\n readonly audit: AuditOperations;\n readonly webhooks: WebhookOperations;\n readonly billing: StripeBillingOperations;\n\n constructor(options: StripeOptions) {\n this.options = {\n timeout: 30000,\n maxRetries: 3,\n apiBaseUrl: 'https://api.stripe.com',\n webhookTolerance: 300,\n ...options,\n };\n this.fetchImpl = options.fetch || fetch;\n\n this.customers = new StripeCustomerOperations(this);\n this.invoices = new StripeInvoiceOperations(this);\n this.vendors = new UnsupportedVendorOperations();\n this.bills = new UnsupportedBillOperations();\n this.payments = new StripePaymentOperations(this);\n this.audit = new StripeAuditOperations(this);\n this.webhooks = new StripeWebhookOperations(this.options);\n this.billing = new StripeBillingOperationsImpl(this);\n }\n\n async request<T>(\n method: 'GET' | 'POST' | 'DELETE',\n endpoint: string,\n params?: Record<string, StripeFormValue>,\n ): Promise<T> {\n const timeout = this.options.timeout || 30000;\n const maxRetries = this.options.maxRetries || 3;\n const idempotencyKey = method === 'POST' ? `sdk-${randomUUID()}` : null;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const url = this.buildUrl(endpoint, method === 'GET' ? params : {});\n const body =\n method === 'POST' && params\n ? encodeStripeForm(params).toString()\n : undefined;\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.options.secretKey}`,\n Accept: 'application/json',\n };\n if (method === 'POST') {\n headers['Content-Type'] = 'application/x-www-form-urlencoded';\n if (idempotencyKey) {\n headers['Idempotency-Key'] = idempotencyKey;\n }\n }\n if (this.options.apiVersion) {\n headers['Stripe-Version'] = this.options.apiVersion;\n }\n\n const response = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n const error = new Error(\n `Stripe API error (${response.status}): ${errorText}`,\n );\n\n if (response.status < 500 && response.status !== 429) {\n throw error;\n }\n\n lastError = error;\n } else {\n const text = await response.text();\n return (text ? JSON.parse(text) : {}) as T;\n }\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n const statusMatch = lastError.message.match(\n /Stripe API error \\((\\d+)\\)/,\n );\n if (statusMatch) {\n const status = Number.parseInt(statusMatch[1], 10);\n if (status >= 400 && status < 500 && status !== 429) {\n throw lastError;\n }\n }\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (attempt < maxRetries) {\n await sleep(2 ** attempt * 500);\n }\n }\n\n throw lastError || new Error('Stripe request failed after retries');\n }\n\n private buildUrl(\n endpoint: string,\n params?: Record<string, StripeFormValue>,\n ): string {\n const baseUrl = this.options.apiBaseUrl || 'https://api.stripe.com';\n const url = new URL(endpoint, baseUrl);\n\n if (params && Object.keys(params).length > 0) {\n for (const [key, value] of encodeStripeForm(params).entries()) {\n url.searchParams.append(key, value);\n }\n }\n\n return url.toString();\n }\n}\n\nclass StripeCustomerOperations implements CustomerOperations {\n constructor(private readonly provider: StripeProvider) {}\n\n async push(customer: CustomerInput): Promise<SyncResult> {\n const response = await this.provider.request<StripeCustomer>(\n 'POST',\n '/v1/customers',\n mapCustomerToStripe(customer),\n );\n\n return {\n action: 'created',\n externalId: response.id,\n syncedAt: new Date(),\n };\n }\n\n async pull(externalId: string): Promise<ExternalCustomer> {\n const customer = await this.provider.request<StripeCustomer>(\n 'GET',\n `/v1/customers/${encodeURIComponent(externalId)}`,\n );\n return mapStripeCustomer(customer);\n }\n\n async list(options: ListOptions = {}): Promise<ExternalCustomer[]> {\n const response = await this.provider.request<\n StripeListResponse<StripeCustomer>\n >('GET', '/v1/customers', mapListOptions(options));\n return response.data.map(mapStripeCustomer);\n }\n\n async sync(customer: CustomerInput): Promise<SyncResult> {\n if (!customer.externalId) {\n return this.push(customer);\n }\n\n await this.provider.request<StripeCustomer>(\n 'POST',\n `/v1/customers/${encodeURIComponent(customer.externalId)}`,\n mapCustomerToStripe(customer),\n );\n\n return {\n action: 'updated',\n externalId: customer.externalId,\n syncedAt: new Date(),\n };\n }\n}\n\nclass StripeInvoiceOperations implements InvoiceOperations {\n constructor(private readonly provider: StripeProvider) {}\n\n async push(invoice: InvoiceInput): Promise<SyncResult> {\n if (!invoice.customerExternalId) {\n throw new Error('Stripe invoices require customerExternalId');\n }\n\n for (const lineItem of invoice.lineItems) {\n await this.provider.request('POST', '/v1/invoiceitems', {\n customer: invoice.customerExternalId,\n currency: invoice.currency || 'usd',\n description: lineItem.description,\n quantity: lineItem.quantity,\n unit_amount_decimal: String(Math.round(lineItem.unitPrice * 100)),\n metadata: normalizeMetadata({\n local_sku: lineItem.sku,\n local_discount: lineItem.discount,\n local_tax_rate: lineItem.taxRate,\n }),\n });\n }\n\n const response = await this.provider.request<StripeInvoice>(\n 'POST',\n '/v1/invoices',\n mapInvoiceToStripe(invoice),\n );\n\n return {\n action: 'created',\n externalId: response.id,\n syncedAt: new Date(),\n };\n }\n\n async pull(externalId: string): Promise<ExternalInvoice> {\n const invoice = await this.provider.request<StripeInvoice>(\n 'GET',\n `/v1/invoices/${encodeURIComponent(externalId)}`,\n );\n return mapStripeInvoice(invoice);\n }\n\n async list(options: ListOptions = {}): Promise<ExternalInvoice[]> {\n const response = await this.provider.request<\n StripeListResponse<StripeInvoice>\n >('GET', '/v1/invoices', mapListOptions(options));\n return response.data.map(mapStripeInvoice);\n }\n\n async sync(invoice: InvoiceInput): Promise<SyncResult> {\n if (!invoice.externalId) {\n return this.push(invoice);\n }\n\n await this.provider.request<StripeInvoice>(\n 'POST',\n `/v1/invoices/${encodeURIComponent(invoice.externalId)}`,\n mapInvoiceUpdateToStripe(invoice),\n );\n\n return {\n action: 'updated',\n externalId: invoice.externalId,\n syncedAt: new Date(),\n };\n }\n\n async send(externalId: string): Promise<void> {\n await this.provider.request(\n 'POST',\n `/v1/invoices/${encodeURIComponent(externalId)}/send`,\n );\n }\n\n async void(externalId: string): Promise<void> {\n await this.provider.request(\n 'POST',\n `/v1/invoices/${encodeURIComponent(externalId)}/void`,\n );\n }\n}\n\nclass StripePaymentOperations implements PaymentOperations {\n constructor(private readonly provider: StripeProvider) {}\n\n async pull(externalId: string): Promise<ExternalPayment> {\n const paymentIntent = await this.provider.request<StripePaymentIntent>(\n 'GET',\n `/v1/payment_intents/${encodeURIComponent(externalId)}`,\n );\n return mapStripePaymentIntent(paymentIntent);\n }\n\n async list(options: ListOptions = {}): Promise<ExternalPayment[]> {\n const response = await this.provider.request<\n StripeListResponse<StripePaymentIntent>\n >('GET', '/v1/payment_intents', mapListOptions(options));\n return response.data.map(mapStripePaymentIntent);\n }\n}\n\nclass StripeBillingOperationsImpl implements StripeBillingOperations {\n constructor(private readonly provider: StripeProvider) {}\n\n async createCheckoutSession(\n input: StripeCheckoutSessionInput,\n ): Promise<StripeCheckoutSession> {\n const response = await this.provider.request<StripeCheckoutSessionResponse>(\n 'POST',\n '/v1/checkout/sessions',\n mapCheckoutSessionToStripe(input),\n );\n\n return {\n externalId: response.id,\n url: response.url || null,\n customerExternalId: response.customer || undefined,\n subscriptionExternalId: response.subscription || undefined,\n paymentIntentExternalId: response.payment_intent || undefined,\n raw: response,\n };\n }\n\n async createCustomerPortalSession(\n input: StripeCustomerPortalSessionInput,\n ): Promise<StripeCustomerPortalSession> {\n const response = await this.provider.request<StripePortalSessionResponse>(\n 'POST',\n '/v1/billing_portal/sessions',\n {\n customer: input.customerExternalId,\n return_url: input.returnUrl,\n configuration: input.configurationExternalId,\n },\n );\n\n return {\n externalId: response.id,\n url: response.url,\n raw: response,\n };\n }\n\n async retrieveSubscriptionStatus(\n subscriptionExternalId: string,\n ): Promise<StripeSubscriptionStatusResult> {\n const subscription = await this.provider.request<StripeSubscription>(\n 'GET',\n `/v1/subscriptions/${encodeURIComponent(subscriptionExternalId)}`,\n );\n\n return mapStripeSubscription(subscription);\n }\n\n async listCustomerSubscriptions(\n customerExternalId: string,\n ): Promise<StripeSubscriptionStatusResult[]> {\n const response = await this.provider.request<\n StripeListResponse<StripeSubscription>\n >('GET', '/v1/subscriptions', {\n customer: customerExternalId,\n status: 'all',\n limit: 100,\n });\n\n return response.data.map(mapStripeSubscription);\n }\n}\n\nclass UnsupportedVendorOperations implements VendorOperations {\n async push(_vendor: VendorInput): Promise<SyncResult> {\n throw new Error('Stripe does not support vendor operations');\n }\n\n async pull(_externalId: string): Promise<ExternalVendor> {\n throw new Error('Stripe does not support vendor operations');\n }\n\n async list(_options?: ListOptions): Promise<ExternalVendor[]> {\n throw new Error('Stripe does not support vendor operations');\n }\n\n async sync(_vendor: VendorInput): Promise<SyncResult> {\n throw new Error('Stripe does not support vendor operations');\n }\n}\n\nclass UnsupportedBillOperations implements BillOperations {\n async push(_bill: BillInput): Promise<SyncResult> {\n throw new Error('Stripe does not support bill operations');\n }\n\n async pull(_externalId: string): Promise<ExternalBill> {\n throw new Error('Stripe does not support bill operations');\n }\n\n async list(_options?: ListOptions): Promise<ExternalBill[]> {\n throw new Error('Stripe does not support bill operations');\n }\n\n async sync(_bill: BillInput): Promise<SyncResult> {\n throw new Error('Stripe does not support bill operations');\n }\n}\n\nclass StripeAuditOperations implements AuditOperations {\n constructor(private readonly provider: StripeProvider) {}\n\n async reconcileCustomers(\n locals: CustomerInput[],\n ): Promise<AuditReport<CustomerInput>> {\n const externals = await this.provider.customers.list({ limit: 100 });\n return reconcileRecords('stripe', locals, externals, compareCustomer);\n }\n\n async reconcileInvoices(\n locals: InvoiceInput[],\n dateRange?: DateRange,\n ): Promise<AuditReport<InvoiceInput>> {\n const externals = await this.provider.invoices.list({\n limit: 100,\n startDate: dateRange?.start,\n endDate: dateRange?.end,\n });\n return reconcileRecords('stripe', locals, externals, compareInvoice);\n }\n\n async reconcileVendors(\n _locals: VendorInput[],\n ): Promise<AuditReport<VendorInput>> {\n throw new Error('Stripe does not support vendor operations');\n }\n\n async reconcileBills(_locals: BillInput[]): Promise<AuditReport<BillInput>> {\n throw new Error('Stripe does not support bill operations');\n }\n\n async reconcilePayments(\n locals: PaymentInput[],\n dateRange?: DateRange,\n ): Promise<AuditReport<PaymentInput>> {\n const externals = await this.provider.payments.list({\n limit: 100,\n startDate: dateRange?.start,\n endDate: dateRange?.end,\n });\n return reconcileRecords('stripe', locals, externals, comparePayment);\n }\n}\n\nclass StripeWebhookOperations implements WebhookOperations {\n constructor(private readonly options: StripeOptions) {}\n\n verify(payload: string, signature: string, secret: string): boolean {\n const endpointSecret = secret || this.options.webhookSecret;\n if (!endpointSecret) {\n throw new Error(\n 'Stripe webhook secret not provided. Pass it as the secret parameter or configure webhookSecret in options.',\n );\n }\n\n const parsed = parseStripeSignatureHeader(signature);\n if (!parsed.timestamp || parsed.signatures.length === 0) {\n return false;\n }\n\n const tolerance = this.options.webhookTolerance ?? 300;\n const timestamp = Number.parseInt(parsed.timestamp, 10);\n if (\n !Number.isFinite(timestamp) ||\n Math.abs(Date.now() / 1000 - timestamp) > tolerance\n ) {\n return false;\n }\n\n const signedPayload = `${parsed.timestamp}.${payload}`;\n const expected = createHmac('sha256', endpointSecret)\n .update(signedPayload)\n .digest('hex');\n\n return parsed.signatures.some((value) =>\n constantTimeEqualHex(value, expected),\n );\n }\n\n parse(payload: string): WebhookEvent {\n let event: StripeWebhookPayload;\n try {\n event = JSON.parse(payload) as StripeWebhookPayload;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Unknown JSON parse error';\n throw new Error(`Invalid Stripe webhook payload: ${message}`);\n }\n\n const resource = event?.data?.object;\n return {\n type: event?.type || 'unknown',\n provider: 'stripe',\n timestamp: unixToDate(event?.created) || new Date(),\n payload: event,\n resourceType: mapStripeResourceType(\n resource?.object || undefined,\n event?.type || undefined,\n ),\n resourceId: typeof resource?.id === 'string' ? resource.id : undefined,\n };\n }\n}\n\nfunction encodeStripeForm(params: Record<string, StripeFormValue>) {\n const form = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n appendStripeFormValue(form, key, value);\n }\n\n return form;\n}\n\nfunction appendStripeFormValue(\n form: URLSearchParams,\n key: string,\n value: StripeFormValue,\n): void {\n if (value === undefined || value === null) {\n return;\n }\n\n if (value instanceof Date) {\n form.append(key, String(toUnixSeconds(value)));\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n appendStripeFormValue(form, `${key}[${index}]`, item);\n });\n return;\n }\n\n if (typeof value === 'object') {\n for (const [childKey, childValue] of Object.entries(value)) {\n appendStripeFormValue(form, `${key}[${childKey}]`, childValue);\n }\n return;\n }\n\n form.append(key, String(value));\n}\n\nfunction mapCustomerToStripe(\n customer: CustomerInput,\n): Record<string, StripeFormValue> {\n const stripeCustomer: Record<string, StripeFormValue> = {\n name: customer.name,\n email: customer.email,\n phone: customer.phone,\n address: customer.billingAddress\n ? {\n line1: customer.billingAddress.street1,\n line2: customer.billingAddress.street2,\n city: customer.billingAddress.city,\n state: customer.billingAddress.state,\n postal_code: customer.billingAddress.postalCode,\n country: customer.billingAddress.country,\n }\n : undefined,\n metadata: normalizeMetadata({\n local_id: customer.id,\n payment_terms: customer.paymentTerms,\n ...customer.metadata,\n }),\n };\n\n if (customer.taxExempt !== undefined) {\n stripeCustomer.tax_exempt = customer.taxExempt ? 'exempt' : 'none';\n }\n\n return stripeCustomer;\n}\n\nfunction mapInvoiceToStripe(\n invoice: InvoiceInput,\n): Record<string, StripeFormValue> {\n return {\n customer: invoice.customerExternalId,\n collection_method: 'send_invoice',\n due_date: invoice.dueDate,\n auto_advance: false,\n pending_invoice_items_behavior: 'include',\n metadata: normalizeMetadata({\n local_id: invoice.id,\n invoice_number: invoice.invoiceNumber,\n reference: invoice.reference,\n memo: invoice.memo,\n tax_amount: invoice.taxAmount,\n subtotal: invoice.subtotal,\n total_amount: invoice.totalAmount,\n ...invoice.metadata,\n }),\n };\n}\n\nfunction mapInvoiceUpdateToStripe(\n invoice: InvoiceInput,\n): Record<string, StripeFormValue> {\n return {\n due_date: invoice.dueDate,\n metadata: normalizeMetadata({\n local_id: invoice.id,\n invoice_number: invoice.invoiceNumber,\n reference: invoice.reference,\n memo: invoice.memo,\n ...invoice.metadata,\n }),\n };\n}\n\nfunction mapCheckoutSessionToStripe(\n input: StripeCheckoutSessionInput,\n): Record<string, StripeFormValue> {\n return {\n mode: input.mode || 'subscription',\n success_url: input.successUrl,\n cancel_url: input.cancelUrl,\n customer: input.customerExternalId,\n customer_email: input.customerExternalId ? undefined : input.customerEmail,\n client_reference_id: input.clientReferenceId,\n allow_promotion_codes: input.allowPromotionCodes,\n line_items: input.lineItems.map(mapCheckoutLineItem),\n metadata: normalizeMetadata(input.metadata || {}),\n };\n}\n\nfunction mapCheckoutLineItem(\n lineItem: StripeCheckoutLineItem,\n): Record<string, StripeFormValue> {\n if (lineItem.price) {\n return {\n price: lineItem.price,\n quantity: lineItem.quantity || 1,\n };\n }\n\n if (!lineItem.priceData) {\n throw new Error('Stripe checkout line item requires price or priceData');\n }\n\n const productData = lineItem.priceData.product\n ? undefined\n : { name: lineItem.priceData.productName || 'Subscription' };\n\n return {\n quantity: lineItem.quantity || 1,\n price_data: {\n currency: lineItem.priceData.currency,\n unit_amount: lineItem.priceData.unitAmount,\n product: lineItem.priceData.product,\n product_data: productData,\n recurring: lineItem.priceData.recurring\n ? {\n interval: lineItem.priceData.recurring.interval,\n interval_count: lineItem.priceData.recurring.intervalCount,\n }\n : undefined,\n },\n };\n}\n\nfunction mapListOptions(options: ListOptions): Record<string, StripeFormValue> {\n return {\n limit: options.limit,\n starting_after:\n typeof options.offset === 'string' ? options.offset : undefined,\n created: {\n gte: options.startDate,\n lte: options.endDate,\n },\n status: options.status,\n };\n}\n\nfunction mapStripeCustomer(customer: StripeCustomer): ExternalCustomer {\n return {\n externalId: customer.id,\n provider: 'stripe',\n syncedAt: new Date(),\n name: customer.name || '',\n email: customer.email || undefined,\n phone: customer.phone || undefined,\n billingAddress: customer.address\n ? {\n street1: customer.address.line1 || undefined,\n street2: customer.address.line2 || undefined,\n city: customer.address.city || undefined,\n state: customer.address.state || undefined,\n postalCode: customer.address.postal_code || undefined,\n country: customer.address.country || undefined,\n }\n : undefined,\n balance:\n customer.balance === null || customer.balance === undefined\n ? undefined\n : centsToMoney(customer.balance),\n currency: customer.currency || undefined,\n raw: customer,\n };\n}\n\nfunction mapStripeInvoice(invoice: StripeInvoice): ExternalInvoice {\n return {\n externalId: invoice.id,\n provider: 'stripe',\n syncedAt: new Date(),\n invoiceNumber: invoice.number || invoice.id,\n customerExternalId:\n typeof invoice.customer === 'string'\n ? invoice.customer\n : invoice.customer?.id || '',\n issueDate: unixToDate(invoice.created) || new Date(),\n dueDate:\n unixToDate(invoice.due_date) || unixToDate(invoice.created) || new Date(),\n subtotal: centsToMoney(invoice.subtotal || 0),\n taxAmount: centsToMoney(invoice.tax || 0),\n totalAmount: centsToMoney(invoice.total || 0),\n amountPaid: centsToMoney(invoice.amount_paid || 0),\n balance: centsToMoney(invoice.amount_remaining || 0),\n status: mapStripeInvoiceStatus(invoice.status, invoice.due_date),\n currency: invoice.currency || 'usd',\n raw: invoice,\n };\n}\n\nfunction mapStripePaymentIntent(\n paymentIntent: StripePaymentIntent,\n): ExternalPayment {\n return {\n externalId: paymentIntent.id,\n provider: 'stripe',\n syncedAt: new Date(),\n amount: centsToMoney(paymentIntent.amount || 0),\n currency: paymentIntent.currency || 'usd',\n paidAt: unixToDate(paymentIntent.created) || new Date(),\n method: paymentIntent.payment_method_types?.[0],\n transactionId: paymentIntent.latest_charge || paymentIntent.id,\n invoiceExternalIds: paymentIntent.invoice\n ? [paymentIntent.invoice]\n : undefined,\n status: mapStripePaymentStatus(paymentIntent.status),\n raw: paymentIntent,\n };\n}\n\nfunction mapStripeSubscription(\n subscription: StripeSubscription,\n): StripeSubscriptionStatusResult {\n return {\n externalId: subscription.id,\n status: subscription.status,\n customerExternalId:\n typeof subscription.customer === 'string'\n ? subscription.customer\n : subscription.customer.id,\n currentPeriodStart: unixToDate(subscription.current_period_start),\n currentPeriodEnd: unixToDate(subscription.current_period_end),\n cancelAtPeriodEnd: Boolean(subscription.cancel_at_period_end),\n canceledAt: unixToDate(subscription.canceled_at),\n trialEnd: unixToDate(subscription.trial_end),\n raw: subscription,\n };\n}\n\nfunction mapStripeInvoiceStatus(\n status?: string | null,\n dueDate?: number | null,\n): ExternalInvoice['status'] {\n switch (status) {\n case 'draft':\n return 'draft';\n case 'paid':\n return 'paid';\n case 'void':\n return 'voided';\n case 'open':\n if (dueDate && dueDate * 1000 < Date.now()) {\n return 'overdue';\n }\n return 'sent';\n default:\n return 'sent';\n }\n}\n\nfunction mapStripePaymentStatus(\n status?: string | null,\n): ExternalPayment['status'] {\n switch (status) {\n case 'succeeded':\n return 'completed';\n case 'processing':\n case 'requires_action':\n case 'requires_capture':\n case 'requires_confirmation':\n return 'pending';\n case 'canceled':\n case 'requires_payment_method':\n return 'failed';\n default:\n return 'pending';\n }\n}\n\nfunction mapStripeResourceType(\n object?: string,\n eventType?: string,\n): WebhookEvent['resourceType'] | undefined {\n if (object === 'customer' || eventType?.startsWith('customer.')) {\n return 'customer';\n }\n if (object === 'invoice' || eventType?.startsWith('invoice.')) {\n return 'invoice';\n }\n if (\n object === 'payment_intent' ||\n object === 'charge' ||\n eventType?.startsWith('payment_intent.') ||\n eventType?.startsWith('charge.')\n ) {\n return 'payment';\n }\n return undefined;\n}\n\nfunction normalizeMetadata(\n metadata: Record<string, unknown>,\n): Record<string, string> {\n const normalized: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(metadata)) {\n if (value !== undefined && value !== null) {\n normalized[key] = String(value);\n }\n }\n\n return normalized;\n}\n\nfunction parseStripeSignatureHeader(signature: string): {\n timestamp?: string;\n signatures: string[];\n} {\n const parts = signature.split(',').map((part) => part.trim());\n const parsed: { timestamp?: string; signatures: string[] } = {\n signatures: [],\n };\n\n for (const part of parts) {\n const separatorIndex = part.indexOf('=');\n if (separatorIndex === -1) {\n continue;\n }\n\n const key = part.slice(0, separatorIndex);\n const value = part.slice(separatorIndex + 1);\n if (key === 't') {\n parsed.timestamp = value;\n }\n if (key === 'v1' && value) {\n parsed.signatures.push(value);\n }\n }\n\n return parsed;\n}\n\nfunction constantTimeEqualHex(left: string, right: string): boolean {\n try {\n const leftBuffer = Buffer.from(left, 'hex');\n const rightBuffer = Buffer.from(right, 'hex');\n return (\n leftBuffer.length === rightBuffer.length &&\n timingSafeEqual(leftBuffer, rightBuffer)\n );\n } catch {\n return false;\n }\n}\n\nfunction reconcileRecords<TLocal extends { externalId?: string }>(\n provider: AccountingProviderType,\n locals: TLocal[],\n externals: ExternalRecord[],\n compare: (local: TLocal, external: ExternalRecord) => FieldDiff[],\n): AuditReport<TLocal> {\n const externalById = new Map(\n externals.map((external) => [external.externalId, external]),\n );\n const matched: AuditMatch<TLocal>[] = [];\n const discrepancies: AuditReport<TLocal>['discrepancies'] = [];\n const localOnly: TLocal[] = [];\n const seenExternalIds = new Set<string>();\n\n for (const local of locals) {\n const external = local.externalId\n ? externalById.get(local.externalId)\n : undefined;\n\n if (!external) {\n localOnly.push(local);\n continue;\n }\n\n seenExternalIds.add(external.externalId);\n const differences = compare(local, external);\n if (differences.length === 0) {\n matched.push({ local, external, status: 'identical' });\n } else {\n discrepancies.push({ local, external, differences });\n }\n }\n\n const externalOnly = externals.filter(\n (external) => !seenExternalIds.has(external.externalId),\n );\n\n return {\n provider,\n auditedAt: new Date(),\n matched,\n localOnly,\n externalOnly,\n discrepancies,\n summary: {\n total: locals.length + externalOnly.length,\n matched: matched.length,\n localOnly: localOnly.length,\n externalOnly: externalOnly.length,\n discrepancies: discrepancies.length,\n },\n };\n}\n\nfunction compareCustomer(\n local: CustomerInput,\n external: ExternalRecord,\n): FieldDiff[] {\n const customer = external as ExternalCustomer;\n return [\n compareField('name', local.name, customer.name),\n compareField('email', local.email, customer.email),\n ].filter((diff): diff is FieldDiff => Boolean(diff));\n}\n\nfunction compareInvoice(\n local: InvoiceInput,\n external: ExternalRecord,\n): FieldDiff[] {\n const invoice = external as ExternalInvoice;\n return [\n compareField('totalAmount', local.totalAmount, invoice.totalAmount),\n compareField('currency', local.currency || 'usd', invoice.currency),\n ].filter((diff): diff is FieldDiff => Boolean(diff));\n}\n\nfunction comparePayment(\n local: PaymentInput,\n external: ExternalRecord,\n): FieldDiff[] {\n const payment = external as ExternalPayment;\n return [\n compareField('amount', local.amount, payment.amount),\n compareField('currency', local.currency || 'usd', payment.currency),\n ].filter((diff): diff is FieldDiff => Boolean(diff));\n}\n\nfunction compareField(\n field: string,\n localValue: unknown,\n externalValue: unknown,\n): FieldDiff | null {\n if (valuesEqual(localValue, externalValue)) {\n return null;\n }\n return { field, localValue, externalValue };\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n if (left === right) {\n return true;\n }\n if (typeof left === 'number' && typeof right === 'number') {\n return Math.abs(left - right) < 0.01;\n }\n return false;\n}\n\nfunction centsToMoney(cents: number): number {\n return cents / 100;\n}\n\nfunction toUnixSeconds(date: Date): number {\n return Math.floor(date.getTime() / 1000);\n}\n\nfunction unixToDate(value?: number | null): Date | undefined {\n return value === null || value === undefined\n ? undefined\n : new Date(value * 1000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=claude-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-context.d.ts","sourceRoot":"","sources":["../../src/cli/claude-context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, copyFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const Dirname = dirname(fileURLToPath(import.meta.url));
6
+ const pkgRoot = join(Dirname, "../..");
7
+ const targetDir = join(process.cwd(), ".claude");
8
+ if (!existsSync(targetDir)) {
9
+ mkdirSync(targetDir, { recursive: true });
10
+ }
11
+ const pkgName = "accounting";
12
+ const agentMdSrc = existsSync(join(pkgRoot, "AGENT.md")) ? join(pkgRoot, "AGENT.md") : join(pkgRoot, "CLAUDE.md");
13
+ const metaSrc = existsSync(join(pkgRoot, "metadata.json")) ? join(pkgRoot, "metadata.json") : join(pkgRoot, ".claude-meta.json");
14
+ if (existsSync(agentMdSrc)) {
15
+ copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));
16
+ }
17
+ if (existsSync(metaSrc)) {
18
+ copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));
19
+ }
20
+ console.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);
21
+ //# sourceMappingURL=claude-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-context.js","sources":["../../src/cli/claude-context.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI script to install agent context for @happyvertical/accounting\n * Run the published context installer binary for this package.\n */\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst Dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = join(Dirname, '../..');\nconst targetDir = join(process.cwd(), '.claude');\n\nif (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n}\n\nconst pkgName = 'accounting';\nconst agentMdSrc = existsSync(join(pkgRoot, 'AGENT.md'))\n ? join(pkgRoot, 'AGENT.md')\n : join(pkgRoot, 'CLAUDE.md');\nconst metaSrc = existsSync(join(pkgRoot, 'metadata.json'))\n ? join(pkgRoot, 'metadata.json')\n : join(pkgRoot, '.claude-meta.json');\n\nif (existsSync(agentMdSrc)) {\n copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));\n}\n\nif (existsSync(metaSrc)) {\n copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));\n}\n\nconsole.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);\n"],"names":[],"mappings":";;;;AASA,MAAM,UAAU,QAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,MAAM,UAAU,KAAK,SAAS,OAAO;AACrC,MAAM,YAAY,KAAK,QAAQ,IAAA,GAAO,SAAS;AAE/C,IAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAU,WAAW,EAAE,WAAW,KAAA,CAAM;AAC1C;AAEA,MAAM,UAAU;AAChB,MAAM,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,IACnD,KAAK,SAAS,UAAU,IACxB,KAAK,SAAS,WAAW;AAC7B,MAAM,UAAU,WAAW,KAAK,SAAS,eAAe,CAAC,IACrD,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,mBAAmB;AAErC,IAAI,WAAW,UAAU,GAAG;AAC1B,eAAa,YAAY,KAAK,WAAW,QAAQ,OAAO,KAAK,CAAC;AAChE;AAEA,IAAI,WAAW,OAAO,GAAG;AACvB,eAAa,SAAS,KAAK,WAAW,QAAQ,OAAO,YAAY,CAAC;AACpE;AAEA,QAAQ,IAAI,8BAA8B,OAAO,sBAAsB;"}
@@ -0,0 +1,53 @@
1
+ import { AccountingOptions, AccountingProvider, QuickBooksOptions, StripeOptions } from './types.js';
2
+ export * from './types.js';
3
+ /**
4
+ * Type guard for QuickBooks options
5
+ */
6
+ export declare function isQuickBooksOptions(options: AccountingOptions): options is QuickBooksOptions;
7
+ /**
8
+ * Type guard for Stripe options
9
+ */
10
+ export declare function isStripeOptions(options: AccountingOptions): options is StripeOptions;
11
+ /**
12
+ * Create an accounting provider instance
13
+ *
14
+ * Factory function that creates a provider-specific implementation
15
+ * of the AccountingProvider interface. Uses dynamic imports to
16
+ * load only the provider code you need.
17
+ *
18
+ * @param userOptions - Provider configuration options
19
+ * @returns AccountingProvider instance
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // QuickBooks Online
24
+ * const qbo = await getAccountingProvider({
25
+ * type: 'quickbooks',
26
+ * clientId: process.env.QBO_CLIENT_ID!,
27
+ * clientSecret: process.env.QBO_CLIENT_SECRET!,
28
+ * realmId: process.env.QBO_REALM_ID!,
29
+ * refreshToken: process.env.QBO_REFRESH_TOKEN!,
30
+ * environment: 'sandbox',
31
+ * onTokenRefresh: async (tokens) => {
32
+ * // Persist refreshed tokens
33
+ * await saveTokens(tokens);
34
+ * }
35
+ * });
36
+ *
37
+ * // Stripe
38
+ * const stripe = await getAccountingProvider({
39
+ * type: 'stripe',
40
+ * secretKey: process.env.STRIPE_SECRET_KEY!
41
+ * });
42
+ *
43
+ * // Use env vars for configuration
44
+ * const provider = await getAccountingProvider({ type: 'quickbooks' });
45
+ * // Reads HAVE_ACCOUNTING_CLIENT_ID, HAVE_ACCOUNTING_REALM_ID, etc.
46
+ * ```
47
+ */
48
+ export declare function getAccountingProvider(userOptions: Partial<AccountingOptions> & {
49
+ type: AccountingOptions['type'];
50
+ }): Promise<AccountingProvider>;
51
+ /** @internal */
52
+ export declare const PACKAGE_VERSION_INITIALIZED = true;
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,cAAc,YAAY,CAAC;AAmB3B;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,IAAI,iBAAiB,CAE9B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,iBAAiB,GACzB,OAAO,IAAI,aAAa,CAE1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG;IAAE,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAA;CAAE,GAC5E,OAAO,CAAC,kBAAkB,CAAC,CAmC7B;AAED,gBAAgB;AAChB,eAAO,MAAM,2BAA2B,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ import { loadEnvConfig } from "@happyvertical/utils";
2
+ const accountingOptionsSchema = {
3
+ type: "string",
4
+ clientId: "string",
5
+ clientSecret: "string",
6
+ realmId: "string",
7
+ refreshToken: "string",
8
+ environment: "string",
9
+ secretKey: "string",
10
+ apiKey: "string",
11
+ timeout: "number",
12
+ maxRetries: "number"
13
+ };
14
+ function isQuickBooksOptions(options) {
15
+ return options.type === "quickbooks";
16
+ }
17
+ function isStripeOptions(options) {
18
+ return options.type === "stripe";
19
+ }
20
+ async function getAccountingProvider(userOptions) {
21
+ const options = loadEnvConfig(userOptions, {
22
+ packageName: "accounting",
23
+ schema: accountingOptionsSchema
24
+ });
25
+ switch (options.type) {
26
+ case "quickbooks": {
27
+ if (!isQuickBooksOptions(options)) {
28
+ throw new Error("Invalid QuickBooks options");
29
+ }
30
+ const { QuickBooksProvider } = await import("./chunks/index-D0bqSiCo.js");
31
+ return new QuickBooksProvider(options);
32
+ }
33
+ case "stripe": {
34
+ if (!isStripeOptions(options)) {
35
+ throw new Error("Invalid Stripe options");
36
+ }
37
+ const { StripeProvider } = await import("./chunks/index-DO-cM79R.js");
38
+ return new StripeProvider(options);
39
+ }
40
+ case "paypal":
41
+ throw new Error("PayPal provider not yet implemented");
42
+ case "coinbase":
43
+ throw new Error("Coinbase provider not yet implemented");
44
+ default:
45
+ throw new Error(`Unknown provider type: ${options.type}`);
46
+ }
47
+ }
48
+ const PACKAGE_VERSION_INITIALIZED = true;
49
+ export {
50
+ PACKAGE_VERSION_INITIALIZED,
51
+ getAccountingProvider,
52
+ isQuickBooksOptions,
53
+ isStripeOptions
54
+ };
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @happyvertical/accounting - Multi-provider accounting integration\n *\n * This package provides a unified interface for syncing and auditing\n * accounting data (AR/AP) with external providers like QuickBooks Online,\n * Stripe, PayPal, and Coinbase Commerce.\n *\n * Key components:\n * - getAccountingProvider() - Factory function for creating provider instances\n * - AccountingProvider - Unified interface for all providers\n * - Audit operations for reconciling local vs external data\n *\n * @example\n * ```typescript\n * import { getAccountingProvider } from '@happyvertical/accounting';\n *\n * const provider = await getAccountingProvider({\n * type: 'quickbooks',\n * clientId: 'xxx',\n * clientSecret: 'xxx',\n * realmId: 'xxx',\n * refreshToken: 'xxx'\n * });\n *\n * // Sync a customer\n * const result = await provider.customers.sync(customer);\n *\n * // Audit invoices\n * const report = await provider.audit.reconcileInvoices(localInvoices);\n * ```\n */\n\nimport { loadEnvConfig } from '@happyvertical/utils';\nimport type {\n AccountingOptions,\n AccountingProvider,\n QuickBooksOptions,\n StripeOptions,\n} from './types.js';\n\n// Re-export all types\nexport * from './types.js';\n\n/**\n * Schema for environment variable type conversion\n */\nconst accountingOptionsSchema: Record<string, 'string' | 'number' | 'boolean'> =\n {\n type: 'string',\n clientId: 'string',\n clientSecret: 'string',\n realmId: 'string',\n refreshToken: 'string',\n environment: 'string',\n secretKey: 'string',\n apiKey: 'string',\n timeout: 'number',\n maxRetries: 'number',\n };\n\n/**\n * Type guard for QuickBooks options\n */\nexport function isQuickBooksOptions(\n options: AccountingOptions,\n): options is QuickBooksOptions {\n return options.type === 'quickbooks';\n}\n\n/**\n * Type guard for Stripe options\n */\nexport function isStripeOptions(\n options: AccountingOptions,\n): options is StripeOptions {\n return options.type === 'stripe';\n}\n\n/**\n * Create an accounting provider instance\n *\n * Factory function that creates a provider-specific implementation\n * of the AccountingProvider interface. Uses dynamic imports to\n * load only the provider code you need.\n *\n * @param userOptions - Provider configuration options\n * @returns AccountingProvider instance\n *\n * @example\n * ```typescript\n * // QuickBooks Online\n * const qbo = await getAccountingProvider({\n * type: 'quickbooks',\n * clientId: process.env.QBO_CLIENT_ID!,\n * clientSecret: process.env.QBO_CLIENT_SECRET!,\n * realmId: process.env.QBO_REALM_ID!,\n * refreshToken: process.env.QBO_REFRESH_TOKEN!,\n * environment: 'sandbox',\n * onTokenRefresh: async (tokens) => {\n * // Persist refreshed tokens\n * await saveTokens(tokens);\n * }\n * });\n *\n * // Stripe\n * const stripe = await getAccountingProvider({\n * type: 'stripe',\n * secretKey: process.env.STRIPE_SECRET_KEY!\n * });\n *\n * // Use env vars for configuration\n * const provider = await getAccountingProvider({ type: 'quickbooks' });\n * // Reads HAVE_ACCOUNTING_CLIENT_ID, HAVE_ACCOUNTING_REALM_ID, etc.\n * ```\n */\nexport async function getAccountingProvider(\n userOptions: Partial<AccountingOptions> & { type: AccountingOptions['type'] },\n): Promise<AccountingProvider> {\n // Load config from env vars, user options take precedence\n const options = loadEnvConfig<AccountingOptions>(userOptions, {\n packageName: 'accounting',\n schema: accountingOptionsSchema,\n });\n\n switch (options.type) {\n case 'quickbooks': {\n if (!isQuickBooksOptions(options)) {\n throw new Error('Invalid QuickBooks options');\n }\n const { QuickBooksProvider } = await import(\n './providers/quickbooks/index.js'\n );\n return new QuickBooksProvider(options);\n }\n\n case 'stripe': {\n if (!isStripeOptions(options)) {\n throw new Error('Invalid Stripe options');\n }\n const { StripeProvider } = await import('./providers/stripe/index.js');\n return new StripeProvider(options);\n }\n\n case 'paypal':\n throw new Error('PayPal provider not yet implemented');\n\n case 'coinbase':\n throw new Error('Coinbase provider not yet implemented');\n\n default:\n throw new Error(`Unknown provider type: ${(options as any).type}`);\n }\n}\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"names":[],"mappings":";AA8CA,MAAM,0BACJ;AAAA,EACE,MAAM;AAAA,EACN,UAAU;AAAA,EACV,cAAc;AAAA,EACd,SAAS;AAAA,EACT,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,YAAY;AACd;AAKK,SAAS,oBACd,SAC8B;AAC9B,SAAO,QAAQ,SAAS;AAC1B;AAKO,SAAS,gBACd,SAC0B;AAC1B,SAAO,QAAQ,SAAS;AAC1B;AAuCA,eAAsB,sBACpB,aAC6B;AAE7B,QAAM,UAAU,cAAiC,aAAa;AAAA,IAC5D,aAAa;AAAA,IACb,QAAQ;AAAA,EAAA,CACT;AAED,UAAQ,QAAQ,MAAA;AAAA,IACd,KAAK,cAAc;AACjB,UAAI,CAAC,oBAAoB,OAAO,GAAG;AACjC,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AACA,YAAM,EAAE,mBAAA,IAAuB,MAAM,OACnC,4BACF;AACA,aAAO,IAAI,mBAAmB,OAAO;AAAA,IACvC;AAAA,IAEA,KAAK,UAAU;AACb,UAAI,CAAC,gBAAgB,OAAO,GAAG;AAC7B,cAAM,IAAI,MAAM,wBAAwB;AAAA,MAC1C;AACA,YAAM,EAAE,eAAA,IAAmB,MAAM,OAAO,4BAA6B;AACrE,aAAO,IAAI,eAAe,OAAO;AAAA,IACnC;AAAA,IAEA,KAAK;AACH,YAAM,IAAI,MAAM,qCAAqC;AAAA,IAEvD,KAAK;AACH,YAAM,IAAI,MAAM,uCAAuC;AAAA,IAEzD;AACE,YAAM,IAAI,MAAM,0BAA2B,QAAgB,IAAI,EAAE;AAAA,EAAA;AAEvE;AAGO,MAAM,8BAA8B;"}
@@ -0,0 +1,43 @@
1
+ import { AccountingProvider, AuditOperations, BillOperations, CustomerOperations, InvoiceOperations, PaymentOperations, QuickBooksOptions, VendorOperations, WebhookOperations } from '../../types.js';
2
+ /**
3
+ * QuickBooks Online accounting provider
4
+ */
5
+ export declare class QuickBooksProvider implements AccountingProvider {
6
+ readonly type: "quickbooks";
7
+ private options;
8
+ private accessToken;
9
+ private tokenExpiresAt;
10
+ readonly customers: CustomerOperations;
11
+ readonly invoices: InvoiceOperations;
12
+ readonly vendors: VendorOperations;
13
+ readonly bills: BillOperations;
14
+ readonly payments: PaymentOperations;
15
+ readonly audit: AuditOperations;
16
+ readonly webhooks: WebhookOperations;
17
+ constructor(options: QuickBooksOptions);
18
+ /**
19
+ * Get the QBO API base URL
20
+ */
21
+ get baseUrl(): string;
22
+ /**
23
+ * Get the company/realm ID
24
+ */
25
+ get realmId(): string;
26
+ /**
27
+ * Ensure we have a valid access token, refreshing if needed
28
+ */
29
+ ensureAccessToken(): Promise<string>;
30
+ /**
31
+ * Refresh the OAuth access token
32
+ */
33
+ private refreshAccessToken;
34
+ /**
35
+ * Make an authenticated request to the QBO API with timeout and retry support
36
+ */
37
+ request<T>(method: 'GET' | 'POST' | 'DELETE', endpoint: string, body?: unknown): Promise<T>;
38
+ /**
39
+ * Execute a QBO query with pagination support
40
+ */
41
+ queryAll<T>(entity: string, whereClause?: string, maxResults?: number): Promise<T[]>;
42
+ }
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/quickbooks/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EACV,kBAAkB,EAIlB,eAAe,EAGf,cAAc,EAEd,kBAAkB,EAUlB,iBAAiB,EAGjB,iBAAiB,EACjB,iBAAiB,EAIjB,gBAAgB,EAEhB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AA2CxB;;GAEG;AACH,qBAAa,kBAAmB,YAAW,kBAAkB;IAC3D,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IAEtC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAqB;IAE3C,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;gBAEzB,OAAO,EAAE,iBAAiB;IAiBtC;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAIpB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyB1C;;OAEG;YACW,kBAAkB;IAgChC;;OAEG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,EACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC;IA0Eb;;OAEG;IACG,QAAQ,CAAC,CAAC,EACd,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,SAAO,GAChB,OAAO,CAAC,CAAC,EAAE,CAAC;CA6BhB"}
@@ -0,0 +1,26 @@
1
+ import { AuditOperations, BillOperations, CustomerOperations, InvoiceOperations, PaymentOperations, StripeAccountingProvider, StripeBillingOperations, StripeOptions, VendorOperations, WebhookOperations } from '../../types.js';
2
+ type StripePrimitive = string | number | boolean | Date | null | undefined;
3
+ type StripeFormValue = StripePrimitive | StripeFormValue[] | {
4
+ [key: string]: StripeFormValue;
5
+ };
6
+ /**
7
+ * Stripe accounting provider.
8
+ */
9
+ export declare class StripeProvider implements StripeAccountingProvider {
10
+ readonly type: "stripe";
11
+ private readonly options;
12
+ private readonly fetchImpl;
13
+ readonly customers: CustomerOperations;
14
+ readonly invoices: InvoiceOperations;
15
+ readonly vendors: VendorOperations;
16
+ readonly bills: BillOperations;
17
+ readonly payments: PaymentOperations;
18
+ readonly audit: AuditOperations;
19
+ readonly webhooks: WebhookOperations;
20
+ readonly billing: StripeBillingOperations;
21
+ constructor(options: StripeOptions);
22
+ request<T>(method: 'GET' | 'POST' | 'DELETE', endpoint: string, params?: Record<string, StripeFormValue>): Promise<T>;
23
+ private buildUrl;
24
+ }
25
+ export {};
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/stripe/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAGV,eAAe,EAGf,cAAc,EAEd,kBAAkB,EAUlB,iBAAiB,EAGjB,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EAMvB,aAAa,EAKb,gBAAgB,EAEhB,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB,KAAK,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AAC3E,KAAK,eAAe,GAChB,eAAe,GACf,eAAe,EAAE,GACjB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;CAAE,CAAC;AAwFvC;;GAEG;AACH,qBAAa,cAAe,YAAW,wBAAwB;IAC7D,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IAEzC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;gBAE9B,OAAO,EAAE,aAAa;IAoB5B,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,EACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GACvC,OAAO,CAAC,CAAC,CAAC;IA6Eb,OAAO,CAAC,QAAQ;CAejB"}