140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import { getAlbyService } from './alby'
|
|
import { calculateArticleSplit, PLATFORM_COMMISSIONS } from './platformCommissions'
|
|
import { buildTags } from './nostrTagSystem'
|
|
import { PLATFORM_SERVICE } from './platformConfig'
|
|
import { generatePublicationHashId } from './hashIdGenerator'
|
|
import type { AlbyInvoice } from '@/types/alby'
|
|
import type { ArticleDraft } from './articlePublisher'
|
|
|
|
/**
|
|
* Create Lightning invoice for article with automatic commission split
|
|
* The invoice is created for the full amount (800 sats) which includes:
|
|
* - 700 sats for the author
|
|
* - 100 sats commission for the platform
|
|
*
|
|
* The commission is automatically tracked and the split is enforced.
|
|
* Requires Alby/WebLN to be available and enabled
|
|
*/
|
|
export async function createArticleInvoice(draft: ArticleDraft): Promise<AlbyInvoice> {
|
|
// Verify amount matches expected commission structure
|
|
if (draft.zapAmount !== PLATFORM_COMMISSIONS.article.total) {
|
|
throw new Error(
|
|
`Invalid article payment amount: ${draft.zapAmount} sats. Expected ${PLATFORM_COMMISSIONS.article.total} sats (700 to author, 100 commission)`
|
|
)
|
|
}
|
|
|
|
const split = calculateArticleSplit()
|
|
|
|
// Get author's Lightning address from their profile or use platform address as fallback
|
|
// For now, we'll create the invoice through the platform's wallet
|
|
// The platform will forward the author's portion after payment
|
|
const alby = getAlbyService()
|
|
await alby.enable()
|
|
|
|
const invoice = await alby.createInvoice({
|
|
amount: split.total,
|
|
description: `Article: ${draft.title} (${split.author} sats author, ${split.platform} sats commission)`,
|
|
expiry: 86400, // 24 hours
|
|
})
|
|
|
|
return invoice
|
|
}
|
|
|
|
/**
|
|
* Create preview event with invoice tags
|
|
* If encryptedContent is provided, it will be used instead of preview
|
|
*/
|
|
export async function createPreviewEvent(
|
|
draft: ArticleDraft,
|
|
invoice: AlbyInvoice,
|
|
authorPubkey: string,
|
|
authorPresentationId?: string,
|
|
extraTags: string[][] = [],
|
|
encryptedContent?: string,
|
|
encryptedKey?: string
|
|
): Promise<{
|
|
kind: 1
|
|
created_at: number
|
|
tags: string[][]
|
|
content: string
|
|
}> {
|
|
const tags = await buildPreviewTags(draft, invoice, authorPubkey, authorPresentationId, extraTags, encryptedKey)
|
|
|
|
return {
|
|
kind: 1 as const,
|
|
created_at: Math.floor(Date.now() / 1000),
|
|
tags,
|
|
content: encryptedContent ?? draft.preview,
|
|
}
|
|
}
|
|
|
|
async function buildPreviewTags(
|
|
draft: ArticleDraft,
|
|
invoice: AlbyInvoice,
|
|
authorPubkey: string,
|
|
_authorPresentationId?: string,
|
|
extraTags: string[][] = [],
|
|
encryptedKey?: string
|
|
): Promise<string[][]> {
|
|
// Map category to new system
|
|
const category = draft.category === 'science-fiction' ? 'sciencefiction' : draft.category === 'scientific-research' ? 'research' : 'sciencefiction'
|
|
|
|
// Generate hash ID from publication data
|
|
const hashId = await generatePublicationHashId({
|
|
pubkey: authorPubkey,
|
|
title: draft.title,
|
|
preview: draft.preview,
|
|
category,
|
|
seriesId: draft.seriesId ?? undefined,
|
|
bannerUrl: draft.bannerUrl ?? undefined,
|
|
zapAmount: draft.zapAmount,
|
|
})
|
|
|
|
// Build tags using new system
|
|
const newTags = buildTags({
|
|
type: 'publication',
|
|
category,
|
|
id: hashId,
|
|
service: PLATFORM_SERVICE,
|
|
version: 0, // New object
|
|
hidden: false,
|
|
paywall: true, // Publications are paid
|
|
title: draft.title,
|
|
preview: draft.preview,
|
|
zapAmount: draft.zapAmount,
|
|
invoice: invoice.invoice,
|
|
paymentHash: invoice.paymentHash,
|
|
...(draft.seriesId ? { seriesId: draft.seriesId } : {}),
|
|
...(draft.bannerUrl ? { bannerUrl: draft.bannerUrl } : {}),
|
|
...(encryptedKey ? { encryptedKey } : {}),
|
|
})
|
|
|
|
// Build JSON metadata
|
|
const publicationJson = JSON.stringify({
|
|
type: 'publication',
|
|
pubkey: authorPubkey,
|
|
title: draft.title,
|
|
preview: draft.preview,
|
|
category,
|
|
seriesId: draft.seriesId,
|
|
bannerUrl: draft.bannerUrl,
|
|
zapAmount: draft.zapAmount,
|
|
invoice: invoice.invoice,
|
|
paymentHash: invoice.paymentHash,
|
|
id: hashId,
|
|
version: 0,
|
|
index: 0,
|
|
...(draft.pages && draft.pages.length > 0 ? { pages: draft.pages } : {}),
|
|
})
|
|
|
|
// Add JSON metadata as a tag
|
|
newTags.push(['json', publicationJson])
|
|
|
|
// Add any extra tags (for backward compatibility)
|
|
if (extraTags.length > 0) {
|
|
newTags.push(...extraTags)
|
|
}
|
|
|
|
return newTags
|
|
}
|