@nehorai/payments-drizzle 0.1.0

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,269 @@
1
+ // src/schema/payment-transactions.ts
2
+ import { pgTable, uuid, text, numeric, timestamp, jsonb, index, unique } from "drizzle-orm/pg-core";
3
+ var paymentTransactions = pgTable(
4
+ "payment_transactions",
5
+ {
6
+ id: uuid("id").defaultRandom().primaryKey(),
7
+ // Idempotency - prevents duplicate charges
8
+ internal_payment_id: text("internal_payment_id").notNull(),
9
+ idempotency_key: text("idempotency_key"),
10
+ // User association (FK configured at application level)
11
+ user_id: uuid("user_id").notNull(),
12
+ // Transaction classification
13
+ transaction_type: text("transaction_type").$type().notNull(),
14
+ status: text("status").$type().notNull().default("created"),
15
+ // Amounts in smallest currency unit (cents/agorot)
16
+ amount_minor: numeric("amount_minor").notNull(),
17
+ currency: text("currency").notNull().default("USD"),
18
+ // Original amount if currency converted
19
+ original_amount_minor: numeric("original_amount_minor"),
20
+ original_currency: text("original_currency"),
21
+ currency_conversion_rate: numeric("currency_conversion_rate"),
22
+ // Provider information
23
+ provider: text("provider").notNull(),
24
+ // stripe, hyp, cardcom
25
+ provider_transaction_id: text("provider_transaction_id"),
26
+ provider_authorization_code: text("provider_authorization_code"),
27
+ provider_metadata: jsonb("provider_metadata").$type(),
28
+ // Two-phase commit tracking (J5)
29
+ authorized_at: timestamp("authorized_at", { withTimezone: true }),
30
+ captured_at: timestamp("captured_at", { withTimezone: true }),
31
+ voided_at: timestamp("voided_at", { withTimezone: true }),
32
+ capture_deadline: timestamp("capture_deadline", { withTimezone: true }),
33
+ // Refund tracking
34
+ refunded_amount_minor: numeric("refunded_amount_minor").default("0"),
35
+ last_refund_at: timestamp("last_refund_at", { withTimezone: true }),
36
+ // Tax invoice (Israeli requirement)
37
+ tax_invoice_status: text("tax_invoice_status").$type().default("pending"),
38
+ tax_invoice_number: text("tax_invoice_number"),
39
+ tax_invoice_url: text("tax_invoice_url"),
40
+ // Error tracking
41
+ failure_code: text("failure_code"),
42
+ failure_message: text("failure_message"),
43
+ failure_details: jsonb("failure_details"),
44
+ // Application metadata
45
+ description: text("description"),
46
+ metadata: jsonb("metadata").$type(),
47
+ // Timestamps
48
+ created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
49
+ updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
50
+ },
51
+ (table) => ({
52
+ // Performance indexes
53
+ userIdx: index("payment_transactions_user_idx").on(table.user_id),
54
+ statusIdx: index("payment_transactions_status_idx").on(table.status),
55
+ providerIdx: index("payment_transactions_provider_idx").on(table.provider),
56
+ providerTxIdx: index("payment_transactions_provider_tx_idx").on(table.provider_transaction_id),
57
+ createdAtIdx: index("payment_transactions_created_at_idx").on(table.created_at),
58
+ // Unique constraints for idempotency
59
+ internalIdUnique: unique("payment_transactions_internal_id_unique").on(
60
+ table.internal_payment_id
61
+ ),
62
+ idempotencyUnique: unique("payment_transactions_idempotency_unique").on(table.idempotency_key)
63
+ })
64
+ );
65
+
66
+ // src/schema/payment-methods.ts
67
+ import { pgTable as pgTable2, uuid as uuid2, text as text2, boolean, timestamp as timestamp2, jsonb as jsonb2, index as index2 } from "drizzle-orm/pg-core";
68
+ var paymentMethods = pgTable2(
69
+ "payment_methods",
70
+ {
71
+ id: uuid2("id").defaultRandom().primaryKey(),
72
+ // User association (FK configured at application level)
73
+ user_id: uuid2("user_id").notNull(),
74
+ // Method type
75
+ type: text2("type").$type().notNull(),
76
+ // Provider information
77
+ provider: text2("provider").notNull(),
78
+ // stripe, hyp, cardcom
79
+ provider_payment_method_id: text2("provider_payment_method_id").notNull(),
80
+ // Card details (tokenized, never full numbers)
81
+ card_brand: text2("card_brand").$type(),
82
+ card_last4: text2("card_last4"),
83
+ card_exp_month: text2("card_exp_month"),
84
+ card_exp_year: text2("card_exp_year"),
85
+ card_bin: text2("card_bin"),
86
+ // First 6-8 digits for routing
87
+ // State
88
+ is_default: boolean("is_default").default(false),
89
+ is_active: boolean("is_active").default(true),
90
+ // Provider-specific data
91
+ provider_metadata: jsonb2("provider_metadata").$type(),
92
+ // Timestamps
93
+ created_at: timestamp2("created_at", { withTimezone: true }).notNull().defaultNow(),
94
+ updated_at: timestamp2("updated_at", { withTimezone: true }).notNull().defaultNow(),
95
+ last_used_at: timestamp2("last_used_at", { withTimezone: true })
96
+ },
97
+ (table) => ({
98
+ // Performance indexes
99
+ userIdx: index2("payment_methods_user_idx").on(table.user_id),
100
+ userDefaultIdx: index2("payment_methods_user_default_idx").on(table.user_id, table.is_default),
101
+ providerIdx: index2("payment_methods_provider_idx").on(table.provider),
102
+ providerMethodIdx: index2("payment_methods_provider_method_idx").on(
103
+ table.provider_payment_method_id
104
+ ),
105
+ cardBinIdx: index2("payment_methods_card_bin_idx").on(table.card_bin)
106
+ })
107
+ );
108
+
109
+ // src/schema/webhook-events.ts
110
+ import {
111
+ pgTable as pgTable3,
112
+ uuid as uuid3,
113
+ text as text3,
114
+ timestamp as timestamp3,
115
+ jsonb as jsonb3,
116
+ index as index3,
117
+ unique as unique2
118
+ } from "drizzle-orm/pg-core";
119
+ var paymentWebhookEvents = pgTable3(
120
+ "payment_webhook_events",
121
+ {
122
+ id: uuid3("id").defaultRandom().primaryKey(),
123
+ // Event identification
124
+ provider: text3("provider").notNull(),
125
+ // stripe, hyp, cardcom
126
+ provider_event_id: text3("provider_event_id").notNull(),
127
+ event_type: text3("event_type").notNull(),
128
+ // Processing state
129
+ status: text3("status").$type().notNull().default("pending"),
130
+ attempts: text3("attempts").default("0"),
131
+ last_attempt_at: timestamp3("last_attempt_at", { withTimezone: true }),
132
+ // Linked transaction (if applicable)
133
+ transaction_id: uuid3("transaction_id"),
134
+ // Event payload (store for debugging and retry)
135
+ payload: jsonb3("payload").$type().notNull(),
136
+ signature: text3("signature"),
137
+ // Error tracking
138
+ error_message: text3("error_message"),
139
+ error_details: jsonb3("error_details"),
140
+ // Timestamps
141
+ received_at: timestamp3("received_at", { withTimezone: true }).notNull().defaultNow(),
142
+ processed_at: timestamp3("processed_at", { withTimezone: true }),
143
+ created_at: timestamp3("created_at", { withTimezone: true }).notNull().defaultNow()
144
+ },
145
+ (table) => ({
146
+ // Unique constraint for idempotency
147
+ providerEventUnique: unique2("webhook_events_provider_event_unique").on(
148
+ table.provider,
149
+ table.provider_event_id
150
+ ),
151
+ // Performance indexes
152
+ statusIdx: index3("webhook_events_status_idx").on(table.status),
153
+ transactionIdx: index3("webhook_events_transaction_idx").on(
154
+ table.transaction_id
155
+ ),
156
+ receivedAtIdx: index3("webhook_events_received_at_idx").on(table.received_at),
157
+ providerIdx: index3("webhook_events_provider_idx").on(table.provider),
158
+ eventTypeIdx: index3("webhook_events_event_type_idx").on(table.event_type)
159
+ })
160
+ );
161
+
162
+ // src/schema/payment-audit-log.ts
163
+ import {
164
+ pgTable as pgTable4,
165
+ uuid as uuid4,
166
+ text as text4,
167
+ timestamp as timestamp4,
168
+ jsonb as jsonb4,
169
+ index as index4
170
+ } from "drizzle-orm/pg-core";
171
+ var paymentAuditLog = pgTable4(
172
+ "payment_audit_log",
173
+ {
174
+ id: uuid4("id").defaultRandom().primaryKey(),
175
+ // What transaction was affected
176
+ transaction_id: uuid4("transaction_id").notNull(),
177
+ // What action occurred
178
+ action: text4("action").$type().notNull(),
179
+ // State before and after
180
+ previous_state: jsonb4("previous_state"),
181
+ new_state: jsonb4("new_state").notNull(),
182
+ // Who/what triggered this change
183
+ triggered_by: text4("triggered_by").$type().notNull(),
184
+ triggered_by_id: uuid4("triggered_by_id"),
185
+ // User ID if applicable
186
+ // Request context
187
+ ip_address: text4("ip_address"),
188
+ user_agent: text4("user_agent"),
189
+ // Correlation for distributed tracing
190
+ correlation_id: text4("correlation_id"),
191
+ // Additional context
192
+ metadata: jsonb4("metadata").$type(),
193
+ // Immutable timestamp (never updated)
194
+ created_at: timestamp4("created_at", { withTimezone: true }).notNull().defaultNow()
195
+ },
196
+ (table) => ({
197
+ // Performance indexes
198
+ transactionIdx: index4("payment_audit_log_transaction_idx").on(
199
+ table.transaction_id
200
+ ),
201
+ actionIdx: index4("payment_audit_log_action_idx").on(table.action),
202
+ correlationIdx: index4("payment_audit_log_correlation_idx").on(
203
+ table.correlation_id
204
+ ),
205
+ createdAtIdx: index4("payment_audit_log_created_at_idx").on(table.created_at),
206
+ triggeredByIdx: index4("payment_audit_log_triggered_by_idx").on(
207
+ table.triggered_by
208
+ )
209
+ })
210
+ );
211
+
212
+ // src/schema/provider-health.ts
213
+ import {
214
+ pgTable as pgTable5,
215
+ uuid as uuid5,
216
+ text as text5,
217
+ numeric as numeric2,
218
+ timestamp as timestamp5,
219
+ jsonb as jsonb5,
220
+ index as index5,
221
+ unique as unique3
222
+ } from "drizzle-orm/pg-core";
223
+ var providerHealth = pgTable5(
224
+ "payment_provider_health",
225
+ {
226
+ id: uuid5("id").defaultRandom().primaryKey(),
227
+ // Provider identification
228
+ provider: text5("provider").notNull(),
229
+ // stripe, hyp, cardcom
230
+ // Circuit breaker state
231
+ circuit_state: text5("circuit_state").$type().notNull().default("closed"),
232
+ // Failure tracking
233
+ failure_count: text5("failure_count").default("0"),
234
+ success_count: text5("success_count").default("0"),
235
+ last_failure_at: timestamp5("last_failure_at", { withTimezone: true }),
236
+ last_success_at: timestamp5("last_success_at", { withTimezone: true }),
237
+ // Circuit breaker timing
238
+ circuit_opened_at: timestamp5("circuit_opened_at", { withTimezone: true }),
239
+ next_retry_at: timestamp5("next_retry_at", { withTimezone: true }),
240
+ // Performance metrics
241
+ avg_latency_ms: numeric2("avg_latency_ms"),
242
+ error_rate: numeric2("error_rate"),
243
+ // 0-1
244
+ request_count_window: text5("request_count_window").default("0"),
245
+ // Health check results
246
+ last_health_check_at: timestamp5("last_health_check_at", { withTimezone: true }),
247
+ health_check_result: jsonb5("health_check_result").$type(),
248
+ // Timestamps
249
+ created_at: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow(),
250
+ updated_at: timestamp5("updated_at", { withTimezone: true }).notNull().defaultNow()
251
+ },
252
+ (table) => ({
253
+ // One record per provider
254
+ providerUnique: unique3("provider_health_provider_unique").on(table.provider),
255
+ // Performance indexes
256
+ circuitStateIdx: index5("provider_health_circuit_state_idx").on(
257
+ table.circuit_state
258
+ ),
259
+ nextRetryIdx: index5("provider_health_next_retry_idx").on(table.next_retry_at)
260
+ })
261
+ );
262
+ export {
263
+ paymentAuditLog,
264
+ paymentMethods,
265
+ paymentTransactions,
266
+ paymentWebhookEvents,
267
+ providerHealth
268
+ };
269
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/schema/payment-transactions.ts","../../src/schema/payment-methods.ts","../../src/schema/webhook-events.ts","../../src/schema/payment-audit-log.ts","../../src/schema/provider-health.ts"],"sourcesContent":["/**\r\n * PaymentOS - Payment Transactions Schema\r\n *\r\n * Core table for tracking all payment operations.\r\n * Implements Two-Phase Commit (J5) pattern with authorize/capture flow.\r\n *\r\n * Note: User FK is optional and configured via schema-config.ts\r\n */\r\n\r\nimport { pgTable, uuid, text, numeric, timestamp, jsonb, index, unique } from 'drizzle-orm/pg-core'\r\n\r\n// ============================================================================\r\n// Type Definitions\r\n// ============================================================================\r\n\r\n/**\r\n * Payment transaction states (strict state machine)\r\n * @see src/lib/payments/types/state-machine.ts for transition rules\r\n */\r\nexport type PaymentTransactionStatus =\r\n | 'created'\r\n | 'pending_authorization'\r\n | 'authorized'\r\n | 'capturing'\r\n | 'captured'\r\n | 'voided'\r\n | 'failed'\r\n | 'expired'\r\n | 'partially_refunded'\r\n | 'fully_refunded'\r\n\r\n/**\r\n * Payment transaction types\r\n */\r\nexport type PaymentTransactionType =\r\n | 'one_time_purchase'\r\n | 'subscription_initial'\r\n | 'subscription_renewal'\r\n | 'refund'\r\n\r\n/**\r\n * Tax invoice status (Israeli compliance)\r\n */\r\nexport type PaymentTaxInvoiceStatus = 'pending' | 'generated' | 'sent' | 'failed'\r\n\r\n// ============================================================================\r\n// Schema Definition\r\n// ============================================================================\r\n\r\n/**\r\n * Payment transactions table\r\n *\r\n * FK to user table is NOT defined here - it's application-specific.\r\n * The user_id column stores the UUID, but the FK constraint should be\r\n * added via migration if needed for your specific user table.\r\n *\r\n * To add FK constraint, create a migration:\r\n * ```sql\r\n * ALTER TABLE payment_transactions\r\n * ADD CONSTRAINT payment_transactions_user_id_fkey\r\n * FOREIGN KEY (user_id) REFERENCES your_users_table(id) ON DELETE CASCADE;\r\n * ```\r\n */\r\nexport const paymentTransactions = pgTable(\r\n 'payment_transactions',\r\n {\r\n id: uuid('id').defaultRandom().primaryKey(),\r\n\r\n // Idempotency - prevents duplicate charges\r\n internal_payment_id: text('internal_payment_id').notNull(),\r\n idempotency_key: text('idempotency_key'),\r\n\r\n // User association (FK configured at application level)\r\n user_id: uuid('user_id').notNull(),\r\n\r\n // Transaction classification\r\n transaction_type: text('transaction_type').$type<PaymentTransactionType>().notNull(),\r\n status: text('status').$type<PaymentTransactionStatus>().notNull().default('created'),\r\n\r\n // Amounts in smallest currency unit (cents/agorot)\r\n amount_minor: numeric('amount_minor').notNull(),\r\n currency: text('currency').notNull().default('USD'),\r\n\r\n // Original amount if currency converted\r\n original_amount_minor: numeric('original_amount_minor'),\r\n original_currency: text('original_currency'),\r\n currency_conversion_rate: numeric('currency_conversion_rate'),\r\n\r\n // Provider information\r\n provider: text('provider').notNull(), // stripe, hyp, cardcom\r\n provider_transaction_id: text('provider_transaction_id'),\r\n provider_authorization_code: text('provider_authorization_code'),\r\n provider_metadata: jsonb('provider_metadata').$type<Record<string, unknown>>(),\r\n\r\n // Two-phase commit tracking (J5)\r\n authorized_at: timestamp('authorized_at', { withTimezone: true }),\r\n captured_at: timestamp('captured_at', { withTimezone: true }),\r\n voided_at: timestamp('voided_at', { withTimezone: true }),\r\n capture_deadline: timestamp('capture_deadline', { withTimezone: true }),\r\n\r\n // Refund tracking\r\n refunded_amount_minor: numeric('refunded_amount_minor').default('0'),\r\n last_refund_at: timestamp('last_refund_at', { withTimezone: true }),\r\n\r\n // Tax invoice (Israeli requirement)\r\n tax_invoice_status: text('tax_invoice_status')\r\n .$type<PaymentTaxInvoiceStatus>()\r\n .default('pending'),\r\n tax_invoice_number: text('tax_invoice_number'),\r\n tax_invoice_url: text('tax_invoice_url'),\r\n\r\n // Error tracking\r\n failure_code: text('failure_code'),\r\n failure_message: text('failure_message'),\r\n failure_details: jsonb('failure_details'),\r\n\r\n // Application metadata\r\n description: text('description'),\r\n metadata: jsonb('metadata').$type<{\r\n credit_package_id?: string\r\n subscription_plan_id?: string\r\n credits_amount?: number\r\n [key: string]: unknown\r\n }>(),\r\n\r\n // Timestamps\r\n created_at: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\r\n updated_at: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),\r\n },\r\n (table) => ({\r\n // Performance indexes\r\n userIdx: index('payment_transactions_user_idx').on(table.user_id),\r\n statusIdx: index('payment_transactions_status_idx').on(table.status),\r\n providerIdx: index('payment_transactions_provider_idx').on(table.provider),\r\n providerTxIdx: index('payment_transactions_provider_tx_idx').on(table.provider_transaction_id),\r\n createdAtIdx: index('payment_transactions_created_at_idx').on(table.created_at),\r\n // Unique constraints for idempotency\r\n internalIdUnique: unique('payment_transactions_internal_id_unique').on(\r\n table.internal_payment_id\r\n ),\r\n idempotencyUnique: unique('payment_transactions_idempotency_unique').on(table.idempotency_key),\r\n })\r\n)\r\n","/**\r\n * PaymentOS - Payment Methods Schema\r\n *\r\n * Stores tokenized payment methods (PCI-compliant - no full card numbers).\r\n * Used for saved cards and recurring billing.\r\n *\r\n * Note: User FK is optional and configured via schema-config.ts\r\n */\r\n\r\nimport { pgTable, uuid, text, boolean, timestamp, jsonb, index } from 'drizzle-orm/pg-core'\r\n\r\n// ============================================================================\r\n// Type Definitions\r\n// ============================================================================\r\n\r\n/**\r\n * Payment method types\r\n */\r\nexport type PaymentMethodType = 'card' | 'bank_account' | 'paypal'\r\n\r\n/**\r\n * Card brands\r\n */\r\nexport type CardBrandType =\r\n | 'visa'\r\n | 'mastercard'\r\n | 'amex'\r\n | 'discover'\r\n | 'isracard'\r\n | 'diners'\r\n | 'unknown'\r\n\r\n// ============================================================================\r\n// Schema Definition\r\n// ============================================================================\r\n\r\n/**\r\n * Payment methods table\r\n *\r\n * FK to user table is NOT defined here - it's application-specific.\r\n * The user_id column stores the UUID, but the FK constraint should be\r\n * added via migration if needed for your specific user table.\r\n *\r\n * To add FK constraint, create a migration:\r\n * ```sql\r\n * ALTER TABLE payment_methods\r\n * ADD CONSTRAINT payment_methods_user_id_fkey\r\n * FOREIGN KEY (user_id) REFERENCES your_users_table(id) ON DELETE CASCADE;\r\n * ```\r\n */\r\nexport const paymentMethods = pgTable(\r\n 'payment_methods',\r\n {\r\n id: uuid('id').defaultRandom().primaryKey(),\r\n\r\n // User association (FK configured at application level)\r\n user_id: uuid('user_id').notNull(),\r\n\r\n // Method type\r\n type: text('type').$type<PaymentMethodType>().notNull(),\r\n\r\n // Provider information\r\n provider: text('provider').notNull(), // stripe, hyp, cardcom\r\n provider_payment_method_id: text('provider_payment_method_id').notNull(),\r\n\r\n // Card details (tokenized, never full numbers)\r\n card_brand: text('card_brand').$type<CardBrandType>(),\r\n card_last4: text('card_last4'),\r\n card_exp_month: text('card_exp_month'),\r\n card_exp_year: text('card_exp_year'),\r\n card_bin: text('card_bin'), // First 6-8 digits for routing\r\n\r\n // State\r\n is_default: boolean('is_default').default(false),\r\n is_active: boolean('is_active').default(true),\r\n\r\n // Provider-specific data\r\n provider_metadata: jsonb('provider_metadata').$type<Record<string, unknown>>(),\r\n\r\n // Timestamps\r\n created_at: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\r\n updated_at: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),\r\n last_used_at: timestamp('last_used_at', { withTimezone: true }),\r\n },\r\n (table) => ({\r\n // Performance indexes\r\n userIdx: index('payment_methods_user_idx').on(table.user_id),\r\n userDefaultIdx: index('payment_methods_user_default_idx').on(table.user_id, table.is_default),\r\n providerIdx: index('payment_methods_provider_idx').on(table.provider),\r\n providerMethodIdx: index('payment_methods_provider_method_idx').on(\r\n table.provider_payment_method_id\r\n ),\r\n cardBinIdx: index('payment_methods_card_bin_idx').on(table.card_bin),\r\n })\r\n)\r\n","import {\r\n pgTable,\r\n uuid,\r\n text,\r\n timestamp,\r\n jsonb,\r\n index,\r\n unique,\r\n} from 'drizzle-orm/pg-core'\r\n\r\n/**\r\n * Webhook processing status\r\n */\r\nexport type WebhookEventStatus =\r\n | 'pending'\r\n | 'processing'\r\n | 'processed'\r\n | 'failed'\r\n | 'ignored'\r\n\r\n/**\r\n * Payment webhook events table\r\n * Stores all incoming webhooks for idempotent processing.\r\n * Ensures each provider event is processed exactly once.\r\n */\r\nexport const paymentWebhookEvents = pgTable(\r\n 'payment_webhook_events',\r\n {\r\n id: uuid('id').defaultRandom().primaryKey(),\r\n\r\n // Event identification\r\n provider: text('provider').notNull(), // stripe, hyp, cardcom\r\n provider_event_id: text('provider_event_id').notNull(),\r\n event_type: text('event_type').notNull(),\r\n\r\n // Processing state\r\n status: text('status')\r\n .$type<WebhookEventStatus>()\r\n .notNull()\r\n .default('pending'),\r\n attempts: text('attempts').default('0'),\r\n last_attempt_at: timestamp('last_attempt_at', { withTimezone: true }),\r\n\r\n // Linked transaction (if applicable)\r\n transaction_id: uuid('transaction_id'),\r\n\r\n // Event payload (store for debugging and retry)\r\n payload: jsonb('payload').$type<Record<string, unknown>>().notNull(),\r\n signature: text('signature'),\r\n\r\n // Error tracking\r\n error_message: text('error_message'),\r\n error_details: jsonb('error_details'),\r\n\r\n // Timestamps\r\n received_at: timestamp('received_at', { withTimezone: true })\r\n .notNull()\r\n .defaultNow(),\r\n processed_at: timestamp('processed_at', { withTimezone: true }),\r\n created_at: timestamp('created_at', { withTimezone: true })\r\n .notNull()\r\n .defaultNow(),\r\n },\r\n (table) => ({\r\n // Unique constraint for idempotency\r\n providerEventUnique: unique('webhook_events_provider_event_unique').on(\r\n table.provider,\r\n table.provider_event_id\r\n ),\r\n // Performance indexes\r\n statusIdx: index('webhook_events_status_idx').on(table.status),\r\n transactionIdx: index('webhook_events_transaction_idx').on(\r\n table.transaction_id\r\n ),\r\n receivedAtIdx: index('webhook_events_received_at_idx').on(table.received_at),\r\n providerIdx: index('webhook_events_provider_idx').on(table.provider),\r\n eventTypeIdx: index('webhook_events_event_type_idx').on(table.event_type),\r\n })\r\n)\r\n","/**\n * @nehorai/payments-drizzle - Payment Audit Log Schema\n *\n * Immutable audit trail for all payment state changes.\n * Used for debugging, compliance, and dispute resolution.\n */\n\nimport {\n pgTable,\n uuid,\n text,\n timestamp,\n jsonb,\n index,\n} from 'drizzle-orm/pg-core'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/**\n * Audit log action types\n */\nexport type AuditLogAction =\n | 'created'\n | 'status_changed'\n | 'authorized'\n | 'captured'\n | 'voided'\n | 'refund_initiated'\n | 'refund_completed'\n | 'webhook_received'\n | 'webhook_processed'\n | 'error_occurred'\n | 'retry_attempted'\n | 'manual_intervention'\n\n/**\n * Who/what triggered the action\n */\nexport type AuditLogTrigger =\n | 'user'\n | 'webhook'\n | 'system'\n | 'admin'\n | 'cron'\n | 'api'\n\n// ============================================================================\n// Schema Definition\n// ============================================================================\n\n/**\n * Payment audit log table\n * Immutable audit trail for all payment state changes.\n * Used for debugging, compliance, and dispute resolution.\n */\nexport const paymentAuditLog = pgTable(\n 'payment_audit_log',\n {\n id: uuid('id').defaultRandom().primaryKey(),\n\n // What transaction was affected\n transaction_id: uuid('transaction_id').notNull(),\n\n // What action occurred\n action: text('action').$type<AuditLogAction>().notNull(),\n\n // State before and after\n previous_state: jsonb('previous_state'),\n new_state: jsonb('new_state').notNull(),\n\n // Who/what triggered this change\n triggered_by: text('triggered_by').$type<AuditLogTrigger>().notNull(),\n triggered_by_id: uuid('triggered_by_id'), // User ID if applicable\n\n // Request context\n ip_address: text('ip_address'),\n user_agent: text('user_agent'),\n\n // Correlation for distributed tracing\n correlation_id: text('correlation_id'),\n\n // Additional context\n metadata: jsonb('metadata').$type<Record<string, unknown>>(),\n\n // Immutable timestamp (never updated)\n created_at: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (table) => ({\n // Performance indexes\n transactionIdx: index('payment_audit_log_transaction_idx').on(\n table.transaction_id\n ),\n actionIdx: index('payment_audit_log_action_idx').on(table.action),\n correlationIdx: index('payment_audit_log_correlation_idx').on(\n table.correlation_id\n ),\n createdAtIdx: index('payment_audit_log_created_at_idx').on(table.created_at),\n triggeredByIdx: index('payment_audit_log_triggered_by_idx').on(\n table.triggered_by\n ),\n })\n)\n","/**\n * @nehorai/payments-drizzle - Provider Health Schema\n *\n * Tracks payment provider health for circuit breaker pattern.\n * Used for intelligent failover when providers experience issues.\n */\n\nimport {\n pgTable,\n uuid,\n text,\n numeric,\n timestamp,\n jsonb,\n index,\n unique,\n} from 'drizzle-orm/pg-core'\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/**\n * Circuit breaker states\n */\nexport type CircuitBreakerState = 'closed' | 'open' | 'half_open'\n\n// ============================================================================\n// Schema Definition\n// ============================================================================\n\n/**\n * Provider health table\n * Tracks payment provider health for circuit breaker pattern.\n * Used for intelligent failover when providers experience issues.\n */\nexport const providerHealth = pgTable(\n 'payment_provider_health',\n {\n id: uuid('id').defaultRandom().primaryKey(),\n\n // Provider identification\n provider: text('provider').notNull(), // stripe, hyp, cardcom\n\n // Circuit breaker state\n circuit_state: text('circuit_state')\n .$type<CircuitBreakerState>()\n .notNull()\n .default('closed'),\n\n // Failure tracking\n failure_count: text('failure_count').default('0'),\n success_count: text('success_count').default('0'),\n last_failure_at: timestamp('last_failure_at', { withTimezone: true }),\n last_success_at: timestamp('last_success_at', { withTimezone: true }),\n\n // Circuit breaker timing\n circuit_opened_at: timestamp('circuit_opened_at', { withTimezone: true }),\n next_retry_at: timestamp('next_retry_at', { withTimezone: true }),\n\n // Performance metrics\n avg_latency_ms: numeric('avg_latency_ms'),\n error_rate: numeric('error_rate'), // 0-1\n request_count_window: text('request_count_window').default('0'),\n\n // Health check results\n last_health_check_at: timestamp('last_health_check_at', { withTimezone: true }),\n health_check_result: jsonb('health_check_result').$type<{\n healthy: boolean\n latency_ms?: number\n error?: string\n }>(),\n\n // Timestamps\n created_at: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updated_at: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (table) => ({\n // One record per provider\n providerUnique: unique('provider_health_provider_unique').on(table.provider),\n // Performance indexes\n circuitStateIdx: index('provider_health_circuit_state_idx').on(\n table.circuit_state\n ),\n nextRetryIdx: index('provider_health_next_retry_idx').on(table.next_retry_at),\n })\n)\n"],"mappings":"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","names":["pgTable","uuid","text","timestamp","jsonb","index","pgTable","uuid","text","timestamp","jsonb","index","unique","pgTable","uuid","text","timestamp","jsonb","index","pgTable","uuid","text","numeric","timestamp","jsonb","index","unique"]}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/storage/index.ts
21
+ var storage_exports = {};
22
+ __export(storage_exports, {
23
+ DrizzleCircuitBreakerStorage: () => DrizzleCircuitBreakerStorage,
24
+ createDrizzleCircuitBreakerStorage: () => createDrizzleCircuitBreakerStorage
25
+ });
26
+ module.exports = __toCommonJS(storage_exports);
27
+
28
+ // src/storage/drizzle-circuit-breaker-storage.ts
29
+ var DrizzleCircuitBreakerStorage = class {
30
+ repo;
31
+ constructor(providerHealthRepository) {
32
+ this.repo = providerHealthRepository;
33
+ }
34
+ async getState(provider) {
35
+ try {
36
+ const health = await this.repo.findByProvider(provider);
37
+ if (!health) return null;
38
+ return this.mapToStateRecord(provider, health);
39
+ } catch (error) {
40
+ console.error(`[DrizzleCircuitBreakerStorage] Failed to get state for ${provider}:`, error);
41
+ return null;
42
+ }
43
+ }
44
+ async setState(provider, state) {
45
+ try {
46
+ await this.repo.getOrCreate(provider);
47
+ await this.repo.update(provider, {
48
+ circuitState: state.state,
49
+ failureCount: state.failureCount,
50
+ successCount: state.successCount,
51
+ lastFailureAt: state.lastFailure ?? void 0,
52
+ circuitOpenedAt: state.openedAt ?? void 0,
53
+ nextRetryAt: state.nextRetryAt ?? void 0
54
+ });
55
+ } catch (error) {
56
+ console.error(`[DrizzleCircuitBreakerStorage] Failed to set state for ${provider}:`, error);
57
+ throw error;
58
+ }
59
+ }
60
+ async getAllStates() {
61
+ try {
62
+ const allHealth = await this.repo.findAll();
63
+ const states = /* @__PURE__ */ new Map();
64
+ for (const health of allHealth) {
65
+ const provider = health.provider;
66
+ states.set(provider, this.mapToStateRecord(provider, health));
67
+ }
68
+ return states;
69
+ } catch (error) {
70
+ console.error("[DrizzleCircuitBreakerStorage] Failed to get all states:", error);
71
+ return /* @__PURE__ */ new Map();
72
+ }
73
+ }
74
+ async deleteState(provider) {
75
+ try {
76
+ await this.repo.resetStats(provider);
77
+ await this.repo.closeCircuit(provider);
78
+ } catch (error) {
79
+ console.error(`[DrizzleCircuitBreakerStorage] Failed to delete state for ${provider}:`, error);
80
+ throw error;
81
+ }
82
+ }
83
+ async getOpenCircuits() {
84
+ try {
85
+ const openHealth = await this.repo.findOpenCircuits();
86
+ return openHealth.map((h) => h.provider);
87
+ } catch (error) {
88
+ console.error("[DrizzleCircuitBreakerStorage] Failed to get open circuits:", error);
89
+ return [];
90
+ }
91
+ }
92
+ async isHealthy() {
93
+ try {
94
+ await this.repo.findAll();
95
+ return true;
96
+ } catch (error) {
97
+ console.error("[DrizzleCircuitBreakerStorage] Health check failed:", error);
98
+ return false;
99
+ }
100
+ }
101
+ // ==========================================================================
102
+ // Helper Methods
103
+ // ==========================================================================
104
+ mapToStateRecord(provider, health) {
105
+ return {
106
+ provider,
107
+ state: health.circuitState,
108
+ failureCount: health.failureCount,
109
+ successCount: health.successCount,
110
+ lastFailure: health.lastFailureAt,
111
+ openedAt: health.circuitOpenedAt,
112
+ nextRetryAt: health.nextRetryAt
113
+ };
114
+ }
115
+ };
116
+ function createDrizzleCircuitBreakerStorage(providerHealthRepo) {
117
+ return new DrizzleCircuitBreakerStorage(providerHealthRepo);
118
+ }
119
+ // Annotate the CommonJS export names for ESM import in node:
120
+ 0 && (module.exports = {
121
+ DrizzleCircuitBreakerStorage,
122
+ createDrizzleCircuitBreakerStorage
123
+ });
124
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/index.ts","../../src/storage/drizzle-circuit-breaker-storage.ts"],"sourcesContent":["/**\n * @nehorai/payments-drizzle - Storage Exports\n *\n * Database-backed circuit breaker storage implementations.\n */\n\nexport {\n DrizzleCircuitBreakerStorage,\n createDrizzleCircuitBreakerStorage,\n} from './drizzle-circuit-breaker-storage.js'\n","/**\n * @nehorai/payments-drizzle - Drizzle Circuit Breaker Storage\n *\n * Database-backed storage implementation using the existing provider_health table.\n * Suitable for serverless deployments and multi-instance environments.\n *\n * Benefits:\n * - State persists across deployments\n * - Shared state across multiple instances (serverless-friendly)\n * - Uses existing provider_health table (no migration needed)\n *\n * Usage:\n * ```typescript\n * const repos = createDrizzleRepositories(db);\n * const storage = new DrizzleCircuitBreakerStorage(repos.providerHealth);\n * const circuitBreaker = new CircuitBreaker({ storage });\n * ```\n */\n\nimport type { PaymentProvider } from '@nehorai/payments/types'\nimport type { IProviderHealthRepository } from '@nehorai/payments/repository'\nimport type {\n ICircuitBreakerStorage,\n CircuitBreakerStateRecord,\n StoredCircuitState,\n} from '@nehorai/payments/services'\n\n// ============================================================================\n// Drizzle Storage Implementation\n// ============================================================================\n\n/**\n * Drizzle-based implementation of ICircuitBreakerStorage\n *\n * Uses the existing provider_health table via IProviderHealthRepository.\n * Perfect for serverless environments (Vercel, AWS Lambda) where\n * in-memory state doesn't persist between invocations.\n */\nexport class DrizzleCircuitBreakerStorage implements ICircuitBreakerStorage {\n private repo: IProviderHealthRepository\n\n constructor(providerHealthRepository: IProviderHealthRepository) {\n this.repo = providerHealthRepository\n }\n\n async getState(provider: PaymentProvider): Promise<CircuitBreakerStateRecord | null> {\n try {\n const health = await this.repo.findByProvider(provider)\n if (!health) return null\n\n return this.mapToStateRecord(provider, health)\n } catch (error) {\n console.error(`[DrizzleCircuitBreakerStorage] Failed to get state for ${provider}:`, error)\n return null\n }\n }\n\n async setState(provider: PaymentProvider, state: CircuitBreakerStateRecord): Promise<void> {\n try {\n // Ensure record exists\n await this.repo.getOrCreate(provider)\n\n // Update with new state\n await this.repo.update(provider, {\n circuitState: state.state,\n failureCount: state.failureCount,\n successCount: state.successCount,\n lastFailureAt: state.lastFailure ?? undefined,\n circuitOpenedAt: state.openedAt ?? undefined,\n nextRetryAt: state.nextRetryAt ?? undefined,\n })\n } catch (error) {\n console.error(`[DrizzleCircuitBreakerStorage] Failed to set state for ${provider}:`, error)\n throw error\n }\n }\n\n async getAllStates(): Promise<Map<PaymentProvider, CircuitBreakerStateRecord>> {\n try {\n const allHealth = await this.repo.findAll()\n const states = new Map<PaymentProvider, CircuitBreakerStateRecord>()\n\n for (const health of allHealth) {\n const provider = health.provider as PaymentProvider\n states.set(provider, this.mapToStateRecord(provider, health))\n }\n\n return states\n } catch (error) {\n console.error('[DrizzleCircuitBreakerStorage] Failed to get all states:', error)\n return new Map()\n }\n }\n\n async deleteState(provider: PaymentProvider): Promise<void> {\n try {\n await this.repo.resetStats(provider)\n await this.repo.closeCircuit(provider)\n } catch (error) {\n console.error(`[DrizzleCircuitBreakerStorage] Failed to delete state for ${provider}:`, error)\n throw error\n }\n }\n\n async getOpenCircuits(): Promise<PaymentProvider[]> {\n try {\n const openHealth = await this.repo.findOpenCircuits()\n return openHealth.map((h) => h.provider as PaymentProvider)\n } catch (error) {\n console.error('[DrizzleCircuitBreakerStorage] Failed to get open circuits:', error)\n return []\n }\n }\n\n async isHealthy(): Promise<boolean> {\n try {\n // Try a simple read operation to verify database connectivity\n await this.repo.findAll()\n return true\n } catch (error) {\n console.error('[DrizzleCircuitBreakerStorage] Health check failed:', error)\n return false\n }\n }\n\n // ==========================================================================\n // Helper Methods\n // ==========================================================================\n\n private mapToStateRecord(\n provider: PaymentProvider,\n health: {\n circuitState: string\n failureCount: number\n successCount: number\n lastFailureAt: Date | null\n circuitOpenedAt: Date | null\n nextRetryAt: Date | null\n }\n ): CircuitBreakerStateRecord {\n return {\n provider,\n state: health.circuitState as StoredCircuitState,\n failureCount: health.failureCount,\n successCount: health.successCount,\n lastFailure: health.lastFailureAt,\n openedAt: health.circuitOpenedAt,\n nextRetryAt: health.nextRetryAt,\n }\n }\n}\n\n// ============================================================================\n// Factory Helper\n// ============================================================================\n\n/**\n * Create a Drizzle-based circuit breaker storage\n *\n * Convenience function for creating storage with a provider health repository.\n *\n * @param providerHealthRepo - The Drizzle provider health repository\n * @returns DrizzleCircuitBreakerStorage instance\n */\nexport function createDrizzleCircuitBreakerStorage(\n providerHealthRepo: IProviderHealthRepository\n): DrizzleCircuitBreakerStorage {\n return new DrizzleCircuitBreakerStorage(providerHealthRepo)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsCO,IAAM,+BAAN,MAAqE;AAAA,EAClE;AAAA,EAER,YAAY,0BAAqD;AAC/D,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAM,SAAS,UAAsE;AACnF,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAAK,eAAe,QAAQ;AACtD,UAAI,CAAC,OAAQ,QAAO;AAEpB,aAAO,KAAK,iBAAiB,UAAU,MAAM;AAAA,IAC/C,SAAS,OAAO;AACd,cAAQ,MAAM,0DAA0D,QAAQ,KAAK,KAAK;AAC1F,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,UAA2B,OAAiD;AACzF,QAAI;AAEF,YAAM,KAAK,KAAK,YAAY,QAAQ;AAGpC,YAAM,KAAK,KAAK,OAAO,UAAU;AAAA,QAC/B,cAAc,MAAM;AAAA,QACpB,cAAc,MAAM;AAAA,QACpB,cAAc,MAAM;AAAA,QACpB,eAAe,MAAM,eAAe;AAAA,QACpC,iBAAiB,MAAM,YAAY;AAAA,QACnC,aAAa,MAAM,eAAe;AAAA,MACpC,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,0DAA0D,QAAQ,KAAK,KAAK;AAC1F,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,eAAyE;AAC7E,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,KAAK,QAAQ;AAC1C,YAAM,SAAS,oBAAI,IAAgD;AAEnE,iBAAW,UAAU,WAAW;AAC9B,cAAM,WAAW,OAAO;AACxB,eAAO,IAAI,UAAU,KAAK,iBAAiB,UAAU,MAAM,CAAC;AAAA,MAC9D;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,4DAA4D,KAAK;AAC/E,aAAO,oBAAI,IAAI;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,UAA0C;AAC1D,QAAI;AACF,YAAM,KAAK,KAAK,WAAW,QAAQ;AACnC,YAAM,KAAK,KAAK,aAAa,QAAQ;AAAA,IACvC,SAAS,OAAO;AACd,cAAQ,MAAM,6DAA6D,QAAQ,KAAK,KAAK;AAC7F,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,kBAA8C;AAClD,QAAI;AACF,YAAM,aAAa,MAAM,KAAK,KAAK,iBAAiB;AACpD,aAAO,WAAW,IAAI,CAAC,MAAM,EAAE,QAA2B;AAAA,IAC5D,SAAS,OAAO;AACd,cAAQ,MAAM,+DAA+D,KAAK;AAClF,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,YAA8B;AAClC,QAAI;AAEF,YAAM,KAAK,KAAK,QAAQ;AACxB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,uDAAuD,KAAK;AAC1E,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,iBACN,UACA,QAQ2B;AAC3B,WAAO;AAAA,MACL;AAAA,MACA,OAAO,OAAO;AAAA,MACd,cAAc,OAAO;AAAA,MACrB,cAAc,OAAO;AAAA,MACrB,aAAa,OAAO;AAAA,MACpB,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO;AAAA,IACtB;AAAA,EACF;AACF;AAcO,SAAS,mCACd,oBAC8B;AAC9B,SAAO,IAAI,6BAA6B,kBAAkB;AAC5D;","names":[]}
@@ -0,0 +1,52 @@
1
+ import { PaymentProvider } from '@nehorai/payments/types';
2
+ import { IProviderHealthRepository } from '@nehorai/payments/repository';
3
+ import { ICircuitBreakerStorage, CircuitBreakerStateRecord } from '@nehorai/payments/services';
4
+
5
+ /**
6
+ * @nehorai/payments-drizzle - Drizzle Circuit Breaker Storage
7
+ *
8
+ * Database-backed storage implementation using the existing provider_health table.
9
+ * Suitable for serverless deployments and multi-instance environments.
10
+ *
11
+ * Benefits:
12
+ * - State persists across deployments
13
+ * - Shared state across multiple instances (serverless-friendly)
14
+ * - Uses existing provider_health table (no migration needed)
15
+ *
16
+ * Usage:
17
+ * ```typescript
18
+ * const repos = createDrizzleRepositories(db);
19
+ * const storage = new DrizzleCircuitBreakerStorage(repos.providerHealth);
20
+ * const circuitBreaker = new CircuitBreaker({ storage });
21
+ * ```
22
+ */
23
+
24
+ /**
25
+ * Drizzle-based implementation of ICircuitBreakerStorage
26
+ *
27
+ * Uses the existing provider_health table via IProviderHealthRepository.
28
+ * Perfect for serverless environments (Vercel, AWS Lambda) where
29
+ * in-memory state doesn't persist between invocations.
30
+ */
31
+ declare class DrizzleCircuitBreakerStorage implements ICircuitBreakerStorage {
32
+ private repo;
33
+ constructor(providerHealthRepository: IProviderHealthRepository);
34
+ getState(provider: PaymentProvider): Promise<CircuitBreakerStateRecord | null>;
35
+ setState(provider: PaymentProvider, state: CircuitBreakerStateRecord): Promise<void>;
36
+ getAllStates(): Promise<Map<PaymentProvider, CircuitBreakerStateRecord>>;
37
+ deleteState(provider: PaymentProvider): Promise<void>;
38
+ getOpenCircuits(): Promise<PaymentProvider[]>;
39
+ isHealthy(): Promise<boolean>;
40
+ private mapToStateRecord;
41
+ }
42
+ /**
43
+ * Create a Drizzle-based circuit breaker storage
44
+ *
45
+ * Convenience function for creating storage with a provider health repository.
46
+ *
47
+ * @param providerHealthRepo - The Drizzle provider health repository
48
+ * @returns DrizzleCircuitBreakerStorage instance
49
+ */
50
+ declare function createDrizzleCircuitBreakerStorage(providerHealthRepo: IProviderHealthRepository): DrizzleCircuitBreakerStorage;
51
+
52
+ export { DrizzleCircuitBreakerStorage, createDrizzleCircuitBreakerStorage };
@@ -0,0 +1,52 @@
1
+ import { PaymentProvider } from '@nehorai/payments/types';
2
+ import { IProviderHealthRepository } from '@nehorai/payments/repository';
3
+ import { ICircuitBreakerStorage, CircuitBreakerStateRecord } from '@nehorai/payments/services';
4
+
5
+ /**
6
+ * @nehorai/payments-drizzle - Drizzle Circuit Breaker Storage
7
+ *
8
+ * Database-backed storage implementation using the existing provider_health table.
9
+ * Suitable for serverless deployments and multi-instance environments.
10
+ *
11
+ * Benefits:
12
+ * - State persists across deployments
13
+ * - Shared state across multiple instances (serverless-friendly)
14
+ * - Uses existing provider_health table (no migration needed)
15
+ *
16
+ * Usage:
17
+ * ```typescript
18
+ * const repos = createDrizzleRepositories(db);
19
+ * const storage = new DrizzleCircuitBreakerStorage(repos.providerHealth);
20
+ * const circuitBreaker = new CircuitBreaker({ storage });
21
+ * ```
22
+ */
23
+
24
+ /**
25
+ * Drizzle-based implementation of ICircuitBreakerStorage
26
+ *
27
+ * Uses the existing provider_health table via IProviderHealthRepository.
28
+ * Perfect for serverless environments (Vercel, AWS Lambda) where
29
+ * in-memory state doesn't persist between invocations.
30
+ */
31
+ declare class DrizzleCircuitBreakerStorage implements ICircuitBreakerStorage {
32
+ private repo;
33
+ constructor(providerHealthRepository: IProviderHealthRepository);
34
+ getState(provider: PaymentProvider): Promise<CircuitBreakerStateRecord | null>;
35
+ setState(provider: PaymentProvider, state: CircuitBreakerStateRecord): Promise<void>;
36
+ getAllStates(): Promise<Map<PaymentProvider, CircuitBreakerStateRecord>>;
37
+ deleteState(provider: PaymentProvider): Promise<void>;
38
+ getOpenCircuits(): Promise<PaymentProvider[]>;
39
+ isHealthy(): Promise<boolean>;
40
+ private mapToStateRecord;
41
+ }
42
+ /**
43
+ * Create a Drizzle-based circuit breaker storage
44
+ *
45
+ * Convenience function for creating storage with a provider health repository.
46
+ *
47
+ * @param providerHealthRepo - The Drizzle provider health repository
48
+ * @returns DrizzleCircuitBreakerStorage instance
49
+ */
50
+ declare function createDrizzleCircuitBreakerStorage(providerHealthRepo: IProviderHealthRepository): DrizzleCircuitBreakerStorage;
51
+
52
+ export { DrizzleCircuitBreakerStorage, createDrizzleCircuitBreakerStorage };