import { Event, verifyEvent, getPublicKey } from 'nostr-tools' import type { Article } from '@/types/nostr' /** * Service for verifying zap receipts and their signatures */ export class ZapVerificationService { /** * Verify a zap receipt signature */ verifyZapReceiptSignature(event: Event): boolean { try { return verifyEvent(event) } catch (error) { console.error('Error verifying zap receipt signature:', error) return false } } /** * Check if a zap receipt is valid for a specific article and user */ verifyZapReceiptForArticle( zapReceipt: Event, articleId: string, articlePubkey: string, userPubkey: string, expectedAmount: number ): boolean { // Verify signature first if (!this.verifyZapReceiptSignature(zapReceipt)) { console.warn('Zap receipt signature verification failed') return false } // Verify the zap receipt is from the expected user // In zap receipts, the 'p' tag should contain the recipient (article author) // and the event pubkey should be from the payer or zap service const recipientTag = zapReceipt.tags.find((tag) => tag[0] === 'p') if (!recipientTag || recipientTag[1] !== articlePubkey) { console.warn('Zap receipt recipient does not match article author') return false } // Verify the article ID is referenced const eventTag = zapReceipt.tags.find((tag) => tag[0] === 'e') if (!eventTag || eventTag[1] !== articleId) { console.warn('Zap receipt does not reference the correct article') return false } // Verify the amount (in millisats, so we need to check if it's >= expectedAmount * 1000) const amountTag = zapReceipt.tags.find((tag) => tag[0] === 'amount') if (amountTag) { const amountInMillisats = parseInt(amountTag[1] || '0') const expectedAmountInMillisats = expectedAmount * 1000 if (amountInMillisats < expectedAmountInMillisats) { console.warn(`Zap amount ${amountInMillisats} is less than expected ${expectedAmountInMillisats}`) return false } } else { console.warn('Zap receipt does not contain amount tag') return false } // Verify it's a zap receipt (kind 9735) if (zapReceipt.kind !== 9735) { console.warn('Event is not a zap receipt (kind 9735)') return false } return true } /** * Extract payment information from a zap receipt */ extractPaymentInfo(zapReceipt: Event): { amount: number // in satoshis recipient: string articleId: string | null payer: string } | null { try { const amountTag = zapReceipt.tags.find((tag) => tag[0] === 'amount') const recipientTag = zapReceipt.tags.find((tag) => tag[0] === 'p') const eventTag = zapReceipt.tags.find((tag) => tag[0] === 'e') if (!amountTag || !recipientTag) { return null } const amountInMillisats = parseInt(amountTag[1] || '0') const amountInSats = Math.floor(amountInMillisats / 1000) return { amount: amountInSats, recipient: recipientTag[1], articleId: eventTag ? eventTag[1] : null, payer: zapReceipt.pubkey, } } catch (error) { console.error('Error extracting payment info:', error) return null } } } export const zapVerificationService = new ZapVerificationService()