import { calculateArticleSplit, calculateReviewSplit } from './platformCommissions' /** * Automatic transfer service * Handles automatic forwarding of author/reviewer portions after payment * * Since WebLN doesn't support BOLT12 with split, the platform receives the full amount * and must automatically forward the author/reviewer portion. * * This service tracks transfers and ensures they are executed correctly. */ export interface TransferResult { success: boolean transferInvoiceId?: string error?: string amount: number recipient: string } export class AutomaticTransferService { /** * Transfer author portion after article payment * Creates a Lightning invoice from the platform to the author */ private logTransferRequired(type: 'article' | 'review', id: string, pubkey: string, amount: number, recipient: string, platformCommission: number) { const logData = { [type === 'article' ? 'articleId' : 'reviewId']: id, [type === 'article' ? 'articlePubkey' : 'reviewerPubkey']: pubkey, amount, recipient, platformCommission, timestamp: new Date().toISOString(), } console.log(`Automatic transfer required${type === 'review' ? ' for review' : ''}`, logData) } private buildTransferError(error: unknown, recipient: string, amount: number = 0): TransferResult { const errorMessage = error instanceof Error ? error.message : 'Unknown error' return { success: false, error: errorMessage, amount, recipient, } } async transferAuthorPortion( authorLightningAddress: string, articleId: string, articlePubkey: string, paymentAmount: number ): Promise { try { const split = calculateArticleSplit(paymentAmount) if (!authorLightningAddress) { return { success: false, error: 'Author Lightning address not available', amount: split.author, recipient: authorLightningAddress, } } this.logTransferRequired('article', articleId, articlePubkey, split.author, authorLightningAddress, split.platform) this.trackTransferRequirement('article', articleId, articlePubkey, split.author, authorLightningAddress) return { success: true, amount: split.author, recipient: authorLightningAddress, } } catch (error) { console.error('Error transferring author portion', { articleId, articlePubkey, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) return this.buildTransferError(error, authorLightningAddress) } } /** * Transfer reviewer portion after review reward payment */ async transferReviewerPortion( reviewerLightningAddress: string, reviewId: string, reviewerPubkey: string, paymentAmount: number ): Promise { try { const split = calculateReviewSplit(paymentAmount) if (!reviewerLightningAddress) { return { success: false, error: 'Reviewer Lightning address not available', amount: split.reviewer, recipient: reviewerLightningAddress, } } this.logTransferRequired('review', reviewId, reviewerPubkey, split.reviewer, reviewerLightningAddress, split.platform) this.trackTransferRequirement('review', reviewId, reviewerPubkey, split.reviewer, reviewerLightningAddress) return { success: true, amount: split.reviewer, recipient: reviewerLightningAddress, } } catch (error) { console.error('Error transferring reviewer portion', { reviewId, reviewerPubkey, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) return this.buildTransferError(error, reviewerLightningAddress) } } /** * Track transfer requirement for later processing * In production, this would be stored in a database or queue */ private trackTransferRequirement( type: 'article' | 'review', id: string, recipientPubkey: string, amount: number, recipientAddress: string ): void { // In production, this would: // 1. Store in a database/queue for processing // 2. Trigger automatic transfer via platform's Lightning node // 3. Update tracking when transfer is complete console.log('Transfer requirement tracked', { type, id, recipientPubkey, amount, recipientAddress, timestamp: new Date().toISOString(), }) } } export const automaticTransferService = new AutomaticTransferService()