import type { Event } from 'nostr-tools' import { SimplePool } from 'nostr-tools' import { createSubscription } from '@/types/nostr-tools-extended' import { getPrimaryRelaySync } from './config' function createZapFilters(targetPubkey: string, targetEventId: string, userPubkey: string): Array<{ kinds: number[] '#p': string[] '#e': string[] authors: string[] }> { return [ { kinds: [9735], // Zap receipt '#p': [targetPubkey], '#e': [targetEventId], authors: [userPubkey], // Filter by the payer's pubkey }, ] } async function isValidZapReceipt( params: { event: Event targetEventId: string targetPubkey: string userPubkey: string amount: number } ): Promise { // Import verification service dynamically to avoid circular dependencies const { zapVerificationService } = await import('./zapVerification') return zapVerificationService.verifyZapReceiptForArticle({ zapReceipt: params.event, articleId: params.targetEventId, articlePubkey: params.targetPubkey, userPubkey: params.userPubkey, expectedAmount: params.amount, }) } /** * Check if user has paid for an article by looking for zap receipts */ function handleZapReceiptEvent( params: { event: Event targetEventId: string targetPubkey: string userPubkey: string amount: number finalize: (value: boolean) => void resolved: { current: boolean } } ): void { if (params.resolved.current) { return } void isValidZapReceipt({ event: params.event, targetEventId: params.targetEventId, targetPubkey: params.targetPubkey, userPubkey: params.userPubkey, amount: params.amount, }).then((isValid) => { if (isValid) { params.finalize(true) } }) } export function checkZapReceipt( params: { pool: SimplePool targetPubkey: string targetEventId: string amount: number userPubkey: string } ): Promise { return setupZapReceiptCheck(params) } function setupZapReceiptCheck(params: { pool: SimplePool targetPubkey: string targetEventId: string amount: number userPubkey: string }): Promise { return new Promise((resolve) => { const relayUrl = getPrimaryRelaySync() const sub = createSubscription(params.pool, [relayUrl], createZapFilters(params.targetPubkey, params.targetEventId, params.userPubkey)) const state = createZapReceiptState({ sub, resolve }) registerZapReceiptHandlers({ sub, params, state }) }) } function createZapReceiptState(params: { sub: import('@/types/nostr-tools-extended').Subscription resolve: (value: boolean) => void }): { resolvedRef: { current: boolean }; finalize: (value: boolean) => void } { const resolvedRef = { current: false } const finalize = (value: boolean): void => { if (resolvedRef.current) { return } resolvedRef.current = true params.sub.unsub() params.resolve(value) } return { resolvedRef, finalize } } function registerZapReceiptHandlers(params: { sub: import('@/types/nostr-tools-extended').Subscription params: { targetPubkey: string; targetEventId: string; amount: number; userPubkey: string } state: { resolvedRef: { current: boolean }; finalize: (value: boolean) => void } }): void { params.sub.on('event', (event: Event): void => { handleZapReceiptEvent({ event, targetEventId: params.params.targetEventId, targetPubkey: params.params.targetPubkey, userPubkey: params.params.userPubkey, amount: params.params.amount, finalize: params.state.finalize, resolved: params.state.resolvedRef, }) }) const end = (): void => { params.state.finalize(false) } params.sub.on('eose', end) setTimeout(end, 3000) }