import { Event, EventTemplate, finalizeEvent } from 'nostr-tools' import { hexToBytes } from 'nostr-tools/utils' import { nostrService } from './nostr' import { PLATFORM_NPUB } from './platformConfig' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' import { getPrimaryRelaySync } from './config' export interface SponsoringTracking { transactionId: string authorPubkey: string authorMainnetAddress: string amount: number authorAmount: number platformCommission: number timestamp: number confirmed: boolean confirmations: number } /** * Sponsoring tracking service * Publishes tracking events on Nostr for sponsoring payments */ export class SponsoringTrackingService { private readonly platformPubkey: string = PLATFORM_NPUB private readonly trackingKind = 30079 // Custom kind for sponsoring tracking /** * Track sponsoring payment * Publishes event on Nostr with commission information */ private buildSponsoringTrackingTags(tracking: SponsoringTracking): string[][] { return [ ['p', this.platformPubkey], ['transaction', tracking.transactionId], ['author', tracking.authorPubkey], ['author_address', tracking.authorMainnetAddress], ['amount', tracking.amount.toString()], ['author_amount', tracking.authorAmount.toString()], ['platform_commission', tracking.platformCommission.toString()], ['confirmed', tracking.confirmed ? 'true' : 'false'], ['confirmations', tracking.confirmations.toString()], ['timestamp', tracking.timestamp.toString()], ] } private buildSponsoringTrackingEvent( tracking: SponsoringTracking, _authorPubkey: string, authorPrivateKey: string ): Event { const eventTemplate: EventTemplate = { kind: this.trackingKind, created_at: Math.floor(Date.now() / 1000), tags: this.buildSponsoringTrackingTags(tracking), content: JSON.stringify({ transactionId: tracking.transactionId, authorPubkey: tracking.authorPubkey, authorMainnetAddress: tracking.authorMainnetAddress, amount: tracking.amount, authorAmount: tracking.authorAmount, platformCommission: tracking.platformCommission, confirmed: tracking.confirmed, confirmations: tracking.confirmations, timestamp: tracking.timestamp, }), } const secretKey = hexToBytes(authorPrivateKey) return finalizeEvent(eventTemplate, secretKey) } private async publishSponsoringTrackingEvent(event: Event): Promise { const pool = nostrService.getPool() if (!pool) { throw new Error('Pool not initialized') } const poolWithSub = pool as SimplePoolWithSub const relayUrl = getPrimaryRelaySync() const pubs = poolWithSub.publish([relayUrl], event) await Promise.all(pubs) } async trackSponsoringPayment( tracking: SponsoringTracking, authorPrivateKey: string ): Promise { try { const pool = nostrService.getPool() if (!pool) { console.error('Pool not initialized for sponsoring tracking') return null } const authorPubkey = nostrService.getPublicKey() if (!authorPubkey) { console.error('Author public key not available for tracking') return null } const event = this.buildSponsoringTrackingEvent(tracking, authorPubkey, authorPrivateKey) await this.publishSponsoringTrackingEvent(event) console.log('Sponsoring payment tracked', { eventId: event.id, transactionId: tracking.transactionId, authorPubkey: tracking.authorPubkey, authorAmount: tracking.authorAmount, platformCommission: tracking.platformCommission, timestamp: new Date().toISOString(), }) return event.id } catch (error) { console.error('Error tracking sponsoring payment', { transactionId: tracking.transactionId, authorPubkey: tracking.authorPubkey, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) return null } } /** * Update author presentation article with new total sponsoring */ async updatePresentationSponsoring( presentationArticleId: string, newTotalSponsoring: number, _authorPrivateKey: string ): Promise { try { // In production, this would: // 1. Fetch the presentation article // 2. Update the total_sponsoring tag // 3. Publish updated event console.log('Presentation sponsoring updated', { presentationArticleId, newTotalSponsoring, timestamp: new Date().toISOString(), }) return true } catch (error) { console.error('Error updating presentation sponsoring', { presentationArticleId, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) return false } } } export const sponsoringTrackingService = new SponsoringTrackingService()