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(params: { type: 'article' | 'review' id: string pubkey: string amount: number recipient: string platformCommission: number }): void { const logData = { [params.type === 'article' ? 'articleId' : 'reviewId']: params.id, [params.type === 'article' ? 'articlePubkey' : 'reviewerPubkey']: params.pubkey, amount: params.amount, recipient: params.recipient, platformCommission: params.platformCommission, timestamp: new Date().toISOString(), } console.warn(`Automatic transfer required${params.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 { return this.transferPortion({ type: 'article', id: articleId, pubkey: articlePubkey, recipient: authorLightningAddress, paymentAmount, computeSplit: calculateArticleSplit, getRecipientAmount: (split) => split.author, missingRecipientError: 'Author Lightning address not available', errorLogMessage: 'Error transferring author portion', }) } /** * Transfer reviewer portion after review reward payment */ async transferReviewerPortion( reviewerLightningAddress: string, reviewId: string, reviewerPubkey: string, paymentAmount: number ): Promise { return this.transferPortion({ type: 'review', id: reviewId, pubkey: reviewerPubkey, recipient: reviewerLightningAddress, paymentAmount, computeSplit: calculateReviewSplit, getRecipientAmount: (split) => split.reviewer, missingRecipientError: 'Reviewer Lightning address not available', errorLogMessage: 'Error transferring reviewer portion', }) } private async transferPortion(params: { type: 'article' | 'review' id: string pubkey: string recipient: string paymentAmount: number computeSplit: (amount: number) => TSplit getRecipientAmount: (split: TSplit) => number missingRecipientError: string errorLogMessage: string }): Promise { try { const split = params.computeSplit(params.paymentAmount) const recipientAmount = params.getRecipientAmount(split) if (!params.recipient) { return this.buildMissingRecipientResult({ error: params.missingRecipientError, recipient: params.recipient, amount: recipientAmount, }) } this.logAndTrackTransferRequirement({ type: params.type, id: params.id, pubkey: params.pubkey, recipient: params.recipient, amount: recipientAmount, platformCommission: split.platform, }) return { success: true, amount: recipientAmount, recipient: params.recipient } } catch (error) { this.logTransferError({ message: params.errorLogMessage, id: params.id, pubkey: params.pubkey, error }) return this.buildTransferError(error, params.recipient) } } private buildMissingRecipientResult(params: { error: string; recipient: string; amount: number }): TransferResult { return { success: false, error: params.error, amount: params.amount, recipient: params.recipient } } private logAndTrackTransferRequirement(params: { type: 'article' | 'review' id: string pubkey: string recipient: string amount: number platformCommission: number }): void { this.logTransferRequired({ type: params.type, id: params.id, pubkey: params.pubkey, amount: params.amount, recipient: params.recipient, platformCommission: params.platformCommission, }) this.trackTransferRequirement({ type: params.type, id: params.id, recipientPubkey: params.pubkey, amount: params.amount, recipientAddress: params.recipient, }) } private logTransferError(params: { message: string; id: string; pubkey: string; error: unknown }): void { console.error(params.message, { id: params.id, pubkey: params.pubkey, error: params.error instanceof Error ? params.error.message : 'Unknown error', timestamp: new Date().toISOString(), }) } /** * Track transfer requirement for later processing * In production, this would be stored in a database or queue */ private trackTransferRequirement( params: { 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.warn('Transfer requirement tracked', { type: params.type, id: params.id, recipientPubkey: params.recipientPubkey, amount: params.amount, recipientAddress: params.recipientAddress, timestamp: new Date().toISOString(), }) } } export const automaticTransferService = new AutomaticTransferService()