import { a as InvoiceStore, U as UpdateInvoiceOpts, d as InvoiceFilter, e as InvoiceStats, X as X402UsageReservation, f as X402UsageReservationResult, g as X402UsageBatchReservationResult, C as CreatePaymentLinkArgs, P as PaymentLink, h as PaymentLinkStats, O as OutboxEvent, b as OutboxRecord, c as OutboxStatus } from '../invoice-engine-BzpQXq7Y.cjs'; export { I as InvoiceEngine, i as InvoiceEngineEvents, W as WebhookSink, j as X402AuditEvent, k as X402AuditEventType } from '../invoice-engine-BzpQXq7Y.cjs'; import { W as WalletRpcClient } from '../wallet-rpc-sY1qJ24S.cjs'; export { a as WalletRpcConfig } from '../wallet-rpc-sY1qJ24S.cjs'; import { DaemonRpcClient } from '../rpc/index.cjs'; export { DaemonRpcConfig } from '../rpc/index.cjs'; import { P as Payment, I as Invoice, h as WebhookEvent, i as WebhookEventType, D as DeroChainId } from '../types-Bsy2LUTI.cjs'; import { E as EscrowClaimGuard, d as EscrowInventoryStore } from '../manager-CNcr_RGt.cjs'; export { C as CreateEscrowParams, b as EscrowContract, h as EscrowManager, i as EscrowManagerConfig, j as EscrowManagerEvents, k as EscrowOnChainState, l as EscrowRecord, n as EscrowStatus } from '../manager-CNcr_RGt.cjs'; export { O as OutboxWebhookSink, W as WebhookDeliveryWorker, b as WebhookOutbox, d as deriveDeliveryId, c as deriveDiscriminator, s as storeSupportsOutbox } from '../delivery-id-CrKP8rw1.cjs'; /** * Payment monitoring engine. * * Polls the DERO wallet RPC for incoming transactions matching * specific payment IDs and emits events on state changes. */ /** Events emitted by the payment monitor */ type PaymentMonitorEvents = { /** A new payment was detected for an invoice */ paymentDetected: (invoiceId: string, payment: Payment) => void; /** A payment reached required confirmations */ paymentConfirmed: (invoiceId: string, payment: Payment) => void; /** An invoice is now fully paid (amount met + confirmations met) */ invoiceCompleted: (invoiceId: string) => void; /** An invoice has expired */ invoiceExpired: (invoiceId: string) => void; /** An invoice received a partial payment */ invoicePartial: (invoiceId: string, amountReceived: bigint) => void; /** Monitor encountered an error */ error: (error: Error) => void; }; /** * Payment monitor that watches for incoming DERO payments. * * Usage: * ```ts * const monitor = new PaymentMonitor({ walletRpc, daemonRpc }); * monitor.on("paymentDetected", (invoiceId, payment) => { ... }); * monitor.on("invoiceCompleted", (invoiceId) => { ... }); * monitor.track(invoice); * monitor.start(); * ``` */ declare class PaymentMonitor { private walletRpc; private daemonRpc; private pollIntervalMs; private pollingTimer; private isRunning; private trackedInvoices; private listeners; constructor(options: { walletRpc: WalletRpcClient; daemonRpc: DaemonRpcClient; /** Polling interval in ms (default: 5000) */ pollIntervalMs?: number; }); /** Register an event listener */ on(event: K, callback: PaymentMonitorEvents[K]): () => void; private emit; /** * Start tracking payments for an invoice. * * The scan floor (`startHeight`, passed as `min_height` to GetTransfers) must * NEVER be re-anchored to the live current height for an invoice that already * has payments — that is the O35/O32 lost-payment bug: on restart after * downtime, a floor of `currentHeight - buffer` can sit ABOVE a payment that * landed during downtime, so it is never re-scanned and no event is ever * produced (the outbox cannot save an event that was never generated). * * UNIT CORRECTNESS: `min_height` filters `TransferEntry.height` — the BLOCK * height. `payment.height` is the persisted block height, so anchoring to it * is unit-correct. We deliberately do NOT derive the floor from a topoheight * source (daemonRpc.getHeight() returns topoheight, which runs ahead across * side-blocks and would set the floor too high). */ track(invoice: Invoice): Promise; /** * Stop tracking an invoice. */ untrack(invoiceId: string): void; /** * Update the invoice state for a tracked invoice. * Called after external state changes (e.g., store updates). */ updateInvoice(invoice: Invoice): void; /** * Start the payment monitoring loop. */ start(): void; /** * Stop the payment monitoring loop. */ stop(): void; /** Whether the monitor is currently running */ get running(): boolean; /** Number of invoices currently being tracked */ get trackedCount(): number; /** * Perform a single polling cycle. */ private poll; /** * Poll for payments on a specific invoice. */ private pollInvoice; /** * Check if any confirming payments have reached the required depth. */ private checkConfirmationUpdates; /** * Update confirmations for an already-tracked payment. */ private updatePaymentConfirmations; /** * Convert a wallet transfer entry to a Payment object. */ private entryToPayment; } /** * In-memory invoice store for development and testing. * * Data is lost when the process exits. * NOT suitable for production — use SQLite or a database-backed store. */ /** * In-memory implementation of InvoiceStore. */ declare class MemoryInvoiceStore implements InvoiceStore { private invoices; private claimGuard?; private inventoryStore?; /** Per-process claim guard (in-memory). Memoized for the store's lifetime. */ createClaimGuard(): EscrowClaimGuard; /** Per-process keeper inventory (in-memory). Memoized for the store's lifetime. */ createInventoryStore(): EscrowInventoryStore; private paymentIdIndex; private usedReceiptJtis; private x402UsageWindows; createInvoice(invoice: Invoice): Promise; getInvoice(id: string): Promise; /** * O10 — return a fully DETACHED copy so a caller mutating invoice.escrow (or * metadata) in place cannot "write through" to the stored blob by shared * reference. A shallow `{ ...invoice }` aliases the nested escrow object, which * both masks a missing persist AND defeats the updateInvoice compare-and-set * (the CAS would read the caller's in-place mutation instead of the committed * state). Deep-copying the nested blobs makes the in-memory store model the * same serialize/deserialize boundary the SQLite store has. bigint scalars are * copied by the spread; only escrow/metadata need the structured clone. */ private snapshot; getInvoiceByPaymentId(paymentId: bigint): Promise; getInvoiceByScid(scid: string): Promise; updateInvoice(id: string, updates: Partial>, opts?: UpdateInvoiceOpts): Promise; addPayment(invoiceId: string, payment: Payment): Promise; updatePayment(invoiceId: string, txid: string, updates: Partial>): Promise; listInvoices(filter?: InvoiceFilter): Promise; getActiveInvoices(): Promise; getStats(): Promise; markReceiptJtiUsed(jti: string, expiresAt: string): Promise; reserveX402Usage(reservation: X402UsageReservation): Promise; reserveX402UsageBatch(reservations: X402UsageReservation[]): Promise; close(): Promise; private pruneExpiredReceiptJtis; private pruneExpiredUsageWindows; } /** * SQLite-backed invoice store for production use. * * Uses better-sqlite3 for synchronous, fast SQLite operations. * Data persists across restarts. * * Requires `better-sqlite3` as a peer dependency: * bun add better-sqlite3 */ /** Configuration for SQLite store */ type SqliteStoreConfig = { /** Path to the SQLite database file */ path: string; /** Enable WAL mode for better concurrent read performance (default: true) */ walMode?: boolean; /** * O11 — how long a contended writer blocks for the SQLite write lock before * throwing SQLITE_BUSY (default: 5000ms). Pinned explicitly so the claim * guard's cross-process serialization does not depend on an unasserted binding * default. Set to 0 only if you deliberately want fail-fast contention. */ busyTimeoutMs?: number; }; /** * SQLite implementation of InvoiceStore. * * Uses better-sqlite3 which must be installed as a peer dependency. */ declare class SqliteInvoiceStore implements InvoiceStore { private db; private claimGuard?; private inventoryStore?; /** * Durable, multi-process claim guard sharing this store's database. Memoized * so the escrow_claims table is created once. */ createClaimGuard(): EscrowClaimGuard; /** * Durable, multi-process keeper inventory sharing this store's database (same * file as the claim guard, so pool pops are atomic across workers). Memoized so * the escrow_inventory table is created once. */ createInventoryStore(): EscrowInventoryStore; constructor(config: SqliteStoreConfig); private initSchema; private migrateInvoices; private migrateWebhookOutbox; private migratePaymentLinks; createInvoice(invoice: Invoice): Promise; getInvoice(id: string): Promise; getInvoiceByPaymentId(paymentId: bigint): Promise; getInvoiceByScid(scid: string): Promise; updateInvoice(id: string, updates: Partial>, opts?: UpdateInvoiceOpts): Promise; addPayment(invoiceId: string, payment: Payment): Promise; updatePayment(invoiceId: string, txid: string, updates: Partial>): Promise; listInvoices(filter?: InvoiceFilter): Promise; getActiveInvoices(): Promise; getStats(): Promise; markReceiptJtiUsed(jti: string, expiresAt: string): Promise; reserveX402Usage(reservation: X402UsageReservation): Promise; reserveX402UsageBatch(reservations: X402UsageReservation[]): Promise; createPaymentLink(args: CreatePaymentLinkArgs): PaymentLink; listPaymentLinks(filter?: { includeArchived?: boolean; includeRevoked?: boolean; limit?: number; }): PaymentLink[]; getPaymentLink(id: string): PaymentLink | null; getPaymentLinkBySlug(slug: string): PaymentLink | null; updatePaymentLink(id: string, patch: { name?: string; description?: string | null; amountAtomic?: bigint | null; usageLimit?: number | null; expiresAt?: number | null; redirectUrl?: string | null; metadata?: Record; invoiceTemplateId?: string | null; }): PaymentLink; revokePaymentLink(id: string): PaymentLink; incrementPaymentLinkUses(id: string): PaymentLink; recordPaymentLinkView(idOrSlug: string): PaymentLinkStats | null; getPaymentLinkStats(id: string): PaymentLinkStats; /** * Apply a newly-detected payment AND enqueue its webhook in one transaction. * * The bigint re-sum here is the SOLE writer of amount_received (invariant 1). * The caller (the engine, via OutboxWebhookSink) passes a builder that, given * the freshly-committed total, returns the outbox event to enqueue (its * deterministic id + frozen signed payload reflect that exact committed sum). * Returns the post-commit invoice + total so the caller can decide status. */ applyPaymentWithOutbox(invoiceId: string, payment: Payment, buildEvent: (committedTotal: bigint, invoice: Invoice) => OutboxEvent | null): { invoice: Invoice; total: bigint; }; /** * Apply an invoice status/amount update AND enqueue its webhook in one * transaction (the confirmation edge, the expiry edge, etc.). Same atomicity * guarantee as applyPaymentWithOutbox; does NOT recompute amount_received * unless explicitly given one. */ applyInvoiceUpdateWithOutbox(invoiceId: string, updates: Partial>, buildEvent: (invoice: Invoice) => OutboxEvent | null): { invoice: Invoice; }; /** * Status-aware UPSERT (invariant 5). The deterministic id is the PK so a * replayed logical event collapses; but the disposition depends on the * EXISTING row's status: * - {pending,delivering,delivered}: DO NOTHING (preserve live dedupe). * - 'dead': REVIVE — reset attempts/next_attempt_at/lease, clear error, and * REFRESH the frozen payload (re-signed under the current secret on the * next delivery), so a post-secret-rotation 401-cascade self-heals. * - absent: INSERT pending. * Must run inside an open transaction (called by the apply* methods). */ private upsertOutboxSync; /** Synchronous invoice read for use inside a transaction (no await). */ private getInvoiceSync; claimDueOutbox(now: number, leaseMs: number, limit: number): Promise; markOutboxDelivered(id: string, deliveredAt: number): Promise; rescheduleOutbox(id: string, nextAttemptAt: number, lastError: string): Promise; markOutboxDead(id: string, lastError: string): Promise; pruneDeliveredOutbox(olderThan: number): Promise; countOutboxByStatus(): Promise>; getOutboxRecord(id: string): Promise; close(): Promise; private rowToPaymentLink; private rowToInvoice; } /** * Webhook types for DeroPay payment notifications. */ /** Webhook delivery attempt */ type WebhookDelivery = { /** Event ID */ eventId: string; /** Target URL */ url: string; /** HTTP status code (0 if connection failed) */ statusCode: number; /** Attempt number (1-based) */ attempt: number; /** Whether delivery was successful (2xx response) */ success: boolean; /** Error message if delivery failed */ error?: string; /** When the attempt was made */ timestamp: string; }; /** Webhook configuration */ type WebhookConfig = { /** Target URL for webhook POST requests */ url: string; /** HMAC-SHA256 signing secret */ secret: string; /** Maximum retry attempts (default: 3) */ maxRetries?: number; /** Retry delay in ms (default: 5000, doubles each retry) */ retryDelayMs?: number; /** Request timeout in ms (default: 10000) */ timeoutMs?: number; }; /** * Webhook dispatcher for DeroPay payment notifications. * * Sends HMAC-signed HTTP POST requests to merchant webhook endpoints * with automatic retry and exponential backoff. * * Signature format (same pattern as Stripe/GitHub): * X-DeroPay-Signature: sha256= * X-DeroPay-Event: * X-DeroPay-Delivery: */ /** * Create a webhook event payload. */ declare function createWebhookEvent(type: WebhookEventType, invoice: Invoice, payment?: Payment): WebhookEvent; /** * Sign a webhook payload with HMAC-SHA256. * * @param payload - JSON string of the webhook event * @param secret - The webhook signing secret * @returns Hex-encoded HMAC signature */ declare function signWebhookPayload(payload: string, secret: string): string; /** * Verify a webhook signature. * Use this on the receiving end to validate webhook authenticity. * * @param payload - The raw request body string * @param signature - The X-DeroPay-Signature header value * @param secret - Your webhook signing secret * @returns Whether the signature is valid */ declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean; /** * The single HTTP attempt of a webhook delivery, with NO retry/backoff/logging * state of its own. It works purely from strings — the already-serialized * payload and the already-computed signature — so the durable delivery worker * can replay a stored frozen payload and re-send byte-identical bytes (same * signature) across restarts. The `WebhookDispatcher` retry loop and the * outbox worker both compose this one function (invariant: HMAC/timeout logic * is defined once, not reinvented). */ declare function deliverOnce(args: { url: string; payload: string; signature: string; eventType: string; deliveryId: string; timeoutMs?: number; }): Promise<{ statusCode: number; success: boolean; error?: string; }>; /** * Webhook dispatcher that delivers events to merchant endpoints. * * Usage: * ```ts * const dispatcher = new WebhookDispatcher({ * url: "https://mystore.com/webhooks/dero", * secret: process.env.WEBHOOK_SECRET!, * }); * * await dispatcher.dispatch(event); * ``` */ declare class WebhookDispatcher { private config; private deliveryLog; constructor(config: WebhookConfig); /** * Dispatch a webhook event with automatic retry. * * @returns Whether the delivery was ultimately successful */ dispatch(event: WebhookEvent): Promise; /** * Create and dispatch a webhook event in one call. */ send(type: WebhookEventType, invoice: Invoice, payment?: Payment): Promise; /** * Get the delivery log. */ getDeliveryLog(): WebhookDelivery[]; /** * Clear the delivery log. */ clearDeliveryLog(): void; private attemptDelivery; } type ReceiptSecrets = string | Record; type CreateReceiptOptions = { keyId?: string; }; type PaymentReceiptClaims = { v: 1; jti: string; invoiceId: string; resource: string; asset: "DERO"; network: DeroChainId; amountAtomic: string; confirmations: number; issuedAt: number; expiresAt: number; paymentTxid?: string; }; type VerifyReceiptOptions = { resource?: string; minAmountAtomic?: bigint; nowMs?: number; }; type IssueReceiptOptions = { resource: string; secret: string; ttlSeconds?: number; network?: DeroChainId; keyId?: string; }; declare function createPaymentReceipt(claims: Omit, secret: string, options?: CreateReceiptOptions): string; declare function verifyPaymentReceipt(token: string, secrets: ReceiptSecrets, options?: VerifyReceiptOptions): PaymentReceiptClaims | null; declare function issueReceiptFromInvoice(invoice: Invoice, options: IssueReceiptOptions): { token: string; claims: PaymentReceiptClaims; }; export { CreatePaymentLinkArgs, type CreateReceiptOptions, DaemonRpcClient, InvoiceFilter, InvoiceStats, InvoiceStore, type IssueReceiptOptions, MemoryInvoiceStore, OutboxEvent, OutboxRecord, OutboxStatus, PaymentLink, PaymentLinkStats, PaymentMonitor, type PaymentMonitorEvents, type PaymentReceiptClaims, type ReceiptSecrets, SqliteInvoiceStore, type SqliteStoreConfig, type VerifyReceiptOptions, WalletRpcClient, type WebhookConfig, type WebhookDelivery, WebhookDispatcher, createPaymentReceipt, createWebhookEvent, deliverOnce, issueReceiptFromInvoice, signWebhookPayload, verifyPaymentReceipt, verifyWebhookSignature };