lint fix wip
This commit is contained in:
parent
d2124e24aa
commit
38941147cb
@ -64,9 +64,9 @@ export async function createPreviewEvent(
|
|||||||
draft: params.draft,
|
draft: params.draft,
|
||||||
invoice: params.invoice,
|
invoice: params.invoice,
|
||||||
authorPubkey: params.authorPubkey,
|
authorPubkey: params.authorPubkey,
|
||||||
authorPresentationId: params.authorPresentationId,
|
...(params.authorPresentationId ? { authorPresentationId: params.authorPresentationId } : {}),
|
||||||
extraTags: params.extraTags,
|
...(params.extraTags ? { extraTags: params.extraTags } : {}),
|
||||||
encryptedKey: params.encryptedKey,
|
...(params.encryptedKey ? { encryptedKey: params.encryptedKey } : {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -95,13 +95,17 @@ async function buildParsedArticleFromDraft(
|
|||||||
return { article, hash, version, index }
|
return { article, hash, version, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publishPreviewWithInvoice(
|
interface PublishPreviewWithInvoiceParams {
|
||||||
draft: ArticleDraft,
|
draft: ArticleDraft
|
||||||
invoice: AlbyInvoice,
|
invoice: AlbyInvoice
|
||||||
authorPubkey: string,
|
authorPubkey: string
|
||||||
presentationId: string,
|
presentationId: string
|
||||||
extraTags?: string[][],
|
extraTags?: string[][]
|
||||||
customArticle?: Article
|
customArticle?: Article
|
||||||
|
}
|
||||||
|
|
||||||
|
async function publishPreviewWithInvoice(
|
||||||
|
params: PublishPreviewWithInvoiceParams
|
||||||
): Promise<import('nostr-tools').Event | null> {
|
): Promise<import('nostr-tools').Event | null> {
|
||||||
// Build parsed article object (use custom article if provided, e.g., for updates with version)
|
// Build parsed article object (use custom article if provided, e.g., for updates with version)
|
||||||
let article: Article
|
let article: Article
|
||||||
@ -109,22 +113,22 @@ async function publishPreviewWithInvoice(
|
|||||||
let version: number
|
let version: number
|
||||||
let index: number
|
let index: number
|
||||||
|
|
||||||
if (customArticle) {
|
if (params.customArticle) {
|
||||||
;({ hash, version } = customArticle)
|
;({ hash, version } = params.customArticle)
|
||||||
article = customArticle
|
article = params.customArticle
|
||||||
index = customArticle.index ?? 0
|
index = params.customArticle.index ?? 0
|
||||||
} else {
|
} else {
|
||||||
const built = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
|
const built = await buildParsedArticleFromDraft(params.draft, params.invoice, params.authorPubkey)
|
||||||
;({ article, hash, version, index } = built)
|
;({ article, hash, version, index } = built)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build event template
|
// Build event template
|
||||||
const previewEventTemplate = await createPreviewEvent({
|
const previewEventTemplate = await createPreviewEvent({
|
||||||
draft,
|
draft: params.draft,
|
||||||
invoice,
|
invoice: params.invoice,
|
||||||
authorPubkey,
|
authorPubkey: params.authorPubkey,
|
||||||
authorPresentationId: presentationId,
|
authorPresentationId: params.presentationId,
|
||||||
extraTags,
|
...(params.extraTags ? { extraTags: params.extraTags } : {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set private key in orchestrator
|
// Set private key in orchestrator
|
||||||
@ -479,42 +483,43 @@ async function buildReviewEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildUpdateTags(
|
async function buildUpdateTags(params: {
|
||||||
draft: ArticleDraft,
|
draft: ArticleDraft
|
||||||
originalArticleId: string,
|
originalArticleId: string
|
||||||
newCategory: 'sciencefiction' | 'research',
|
newCategory: 'sciencefiction' | 'research'
|
||||||
authorPubkey: string,
|
authorPubkey: string
|
||||||
currentVersion: number = 0
|
currentVersion?: number
|
||||||
): Promise<string[][]> {
|
}): Promise<string[][]> {
|
||||||
// Generate hash ID from publication data
|
// Generate hash ID from publication data
|
||||||
const hashId = await generatePublicationHashId({
|
const hashId = await generatePublicationHashId({
|
||||||
pubkey: authorPubkey,
|
pubkey: params.authorPubkey,
|
||||||
title: draft.title,
|
title: params.draft.title,
|
||||||
preview: draft.preview,
|
preview: params.draft.preview,
|
||||||
category: newCategory,
|
category: params.newCategory,
|
||||||
seriesId: draft.seriesId ?? undefined,
|
seriesId: params.draft.seriesId ?? undefined,
|
||||||
bannerUrl: draft.bannerUrl ?? undefined,
|
bannerUrl: params.draft.bannerUrl ?? undefined,
|
||||||
zapAmount: draft.zapAmount,
|
zapAmount: params.draft.zapAmount,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Increment version for update
|
// Increment version for update
|
||||||
|
const currentVersion = params.currentVersion ?? 0
|
||||||
const nextVersion = currentVersion + 1
|
const nextVersion = currentVersion + 1
|
||||||
|
|
||||||
const updateTags = buildTags({
|
const updateTags = buildTags({
|
||||||
type: 'publication',
|
type: 'publication',
|
||||||
category: newCategory,
|
category: params.newCategory,
|
||||||
id: hashId,
|
id: hashId,
|
||||||
service: PLATFORM_SERVICE,
|
service: PLATFORM_SERVICE,
|
||||||
version: nextVersion,
|
version: nextVersion,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
paywall: true,
|
paywall: true,
|
||||||
title: draft.title,
|
title: params.draft.title,
|
||||||
preview: draft.preview,
|
preview: params.draft.preview,
|
||||||
zapAmount: draft.zapAmount,
|
zapAmount: params.draft.zapAmount,
|
||||||
...(draft.seriesId ? { seriesId: draft.seriesId } : {}),
|
...(params.draft.seriesId ? { seriesId: params.draft.seriesId } : {}),
|
||||||
...(draft.bannerUrl ? { bannerUrl: draft.bannerUrl } : {}),
|
...(params.draft.bannerUrl ? { bannerUrl: params.draft.bannerUrl } : {}),
|
||||||
})
|
})
|
||||||
updateTags.push(['e', originalArticleId], ['replace', 'article-update'])
|
updateTags.push(['e', params.originalArticleId], ['replace', 'article-update'])
|
||||||
return updateTags
|
return updateTags
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +550,13 @@ async function publishUpdate(
|
|||||||
|
|
||||||
// Use current version from original article
|
// Use current version from original article
|
||||||
const currentVersion = originalArticle.version ?? 0
|
const currentVersion = originalArticle.version ?? 0
|
||||||
const updateTags = await buildUpdateTags(draft, originalArticleId, newCategory, authorPubkey, currentVersion)
|
const updateTags = await buildUpdateTags({
|
||||||
|
draft,
|
||||||
|
originalArticleId,
|
||||||
|
newCategory,
|
||||||
|
authorPubkey,
|
||||||
|
currentVersion,
|
||||||
|
})
|
||||||
|
|
||||||
// Build parsed article with incremented version
|
// Build parsed article with incremented version
|
||||||
const { article } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
|
const { article } = await buildParsedArticleFromDraft(draft, invoice, authorPubkey)
|
||||||
@ -554,7 +565,14 @@ async function publishUpdate(
|
|||||||
version: currentVersion + 1, // Increment version for update
|
version: currentVersion + 1, // Increment version for update
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedEvent = await publishPreviewWithInvoice(draft, invoice, authorPubkey, presentationId, updateTags, updatedArticle)
|
const publishedEvent = await publishPreviewWithInvoice({
|
||||||
|
draft,
|
||||||
|
invoice,
|
||||||
|
authorPubkey,
|
||||||
|
presentationId,
|
||||||
|
extraTags: updateTags,
|
||||||
|
customArticle: updatedArticle,
|
||||||
|
})
|
||||||
if (!publishedEvent) {
|
if (!publishedEvent) {
|
||||||
return updateFailure(originalArticleId, 'Failed to publish article update')
|
return updateFailure(originalArticleId, 'Failed to publish article update')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -243,7 +243,7 @@ export class ArticlePublisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build event template
|
// Build event template
|
||||||
const eventTemplate = await buildPresentationEvent(draft, authorPubkey, authorName, category, version, index)
|
const eventTemplate = await buildPresentationEvent({ draft, authorPubkey, authorName, category, version, index })
|
||||||
|
|
||||||
// Set private key in orchestrator
|
// Set private key in orchestrator
|
||||||
writeOrchestrator.setPrivateKey(authorPrivateKey)
|
writeOrchestrator.setPrivateKey(authorPrivateKey)
|
||||||
|
|||||||
@ -33,27 +33,27 @@ export function buildPrivateMessageEvent(recipientPubkey: string, articleId: str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publishEncryptedMessage(
|
async function publishEncryptedMessage(params: {
|
||||||
articleId: string,
|
articleId: string
|
||||||
recipientPubkey: string,
|
recipientPubkey: string
|
||||||
authorPubkey: string,
|
authorPubkey: string
|
||||||
authorPrivateKey: string,
|
authorPrivateKey: string
|
||||||
keyData: string
|
keyData: string
|
||||||
): Promise<{ eventId: string } | null> {
|
}): Promise<{ eventId: string } | null> {
|
||||||
const encryptedKey = await Promise.resolve(nip04.encrypt(authorPrivateKey, recipientPubkey, keyData))
|
const encryptedKey = await Promise.resolve(nip04.encrypt(params.authorPrivateKey, params.recipientPubkey, params.keyData))
|
||||||
const privateMessageEvent = buildPrivateMessageEvent(recipientPubkey, articleId, encryptedKey)
|
const privateMessageEvent = buildPrivateMessageEvent(params.recipientPubkey, params.articleId, encryptedKey)
|
||||||
const publishedEvent = await nostrService.publishEvent(privateMessageEvent)
|
const publishedEvent = await nostrService.publishEvent(privateMessageEvent)
|
||||||
|
|
||||||
if (!publishedEvent) {
|
if (!publishedEvent) {
|
||||||
console.error('Failed to publish private message event', { articleId, recipientPubkey, authorPubkey })
|
console.error('Failed to publish private message event', { articleId: params.articleId, recipientPubkey: params.recipientPubkey, authorPubkey: params.authorPubkey })
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
console.warn('Private message published', {
|
console.warn('Private message published', {
|
||||||
messageEventId: publishedEvent.id,
|
messageEventId: publishedEvent.id,
|
||||||
articleId,
|
articleId: params.articleId,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
authorPubkey,
|
authorPubkey: params.authorPubkey,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
return { eventId: publishedEvent.id }
|
return { eventId: publishedEvent.id }
|
||||||
@ -70,7 +70,13 @@ export async function sendEncryptedContent(
|
|||||||
nostrService.setPublicKey(storedContent.authorPubkey)
|
nostrService.setPublicKey(storedContent.authorPubkey)
|
||||||
|
|
||||||
const keyData = prepareKeyData(storedContent)
|
const keyData = prepareKeyData(storedContent)
|
||||||
const publishResult = await publishEncryptedMessage(articleId, recipientPubkey, storedContent.authorPubkey, authorPrivateKey, keyData)
|
const publishResult = await publishEncryptedMessage({
|
||||||
|
articleId,
|
||||||
|
recipientPubkey,
|
||||||
|
authorPubkey: storedContent.authorPubkey,
|
||||||
|
authorPrivateKey,
|
||||||
|
keyData,
|
||||||
|
})
|
||||||
if (!publishResult) {
|
if (!publishResult) {
|
||||||
return { success: false, error: 'Failed to publish private message event' }
|
return { success: false, error: 'Failed to publish private message event' }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,25 +11,32 @@ import { generateObjectUrl, buildObjectId, parseObjectId } from './urlGenerator'
|
|||||||
import { getLatestVersion } from './versionManager'
|
import { getLatestVersion } from './versionManager'
|
||||||
import { objectCache } from './objectCache'
|
import { objectCache } from './objectCache'
|
||||||
|
|
||||||
|
interface BuildPresentationEventParams {
|
||||||
|
draft: AuthorPresentationDraft
|
||||||
|
authorPubkey: string
|
||||||
|
authorName: string
|
||||||
|
category?: 'sciencefiction' | 'research'
|
||||||
|
version?: number
|
||||||
|
index?: number
|
||||||
|
}
|
||||||
|
|
||||||
export async function buildPresentationEvent(
|
export async function buildPresentationEvent(
|
||||||
draft: AuthorPresentationDraft,
|
params: BuildPresentationEventParams
|
||||||
authorPubkey: string,
|
|
||||||
authorName: string,
|
|
||||||
category: 'sciencefiction' | 'research' = 'sciencefiction',
|
|
||||||
version: number = 0,
|
|
||||||
index: number = 0
|
|
||||||
): Promise<{
|
): Promise<{
|
||||||
kind: 1
|
kind: 1
|
||||||
created_at: number
|
created_at: number
|
||||||
tags: string[][]
|
tags: string[][]
|
||||||
content: string
|
content: string
|
||||||
}> {
|
}> {
|
||||||
|
const category = params.category ?? 'sciencefiction'
|
||||||
|
const version = params.version ?? 0
|
||||||
|
const index = params.index ?? 0
|
||||||
// Extract presentation and contentDescription from draft.content
|
// Extract presentation and contentDescription from draft.content
|
||||||
// Format: "${presentation}\n\n---\n\nDescription du contenu :\n${contentDescription}"
|
// Format: "${presentation}\n\n---\n\nDescription du contenu :\n${contentDescription}"
|
||||||
const separator = '\n\n---\n\nDescription du contenu :\n'
|
const separator = '\n\n---\n\nDescription du contenu :\n'
|
||||||
const separatorIndex = draft.content.indexOf(separator)
|
const separatorIndex = params.draft.content.indexOf(separator)
|
||||||
const presentation = separatorIndex !== -1 ? draft.content.substring(0, separatorIndex) : draft.presentation
|
const presentation = separatorIndex !== -1 ? params.draft.content.substring(0, separatorIndex) : params.draft.presentation
|
||||||
let contentDescription = separatorIndex !== -1 ? draft.content.substring(separatorIndex + separator.length) : draft.contentDescription
|
let contentDescription = separatorIndex !== -1 ? params.draft.content.substring(separatorIndex + separator.length) : params.draft.contentDescription
|
||||||
|
|
||||||
// Remove Bitcoin address from contentDescription if present (should not be visible in note content)
|
// Remove Bitcoin address from contentDescription if present (should not be visible in note content)
|
||||||
// Remove lines matching "Adresse Bitcoin mainnet (pour le sponsoring) : ..."
|
// Remove lines matching "Adresse Bitcoin mainnet (pour le sponsoring) : ..."
|
||||||
@ -43,12 +50,12 @@ export async function buildPresentationEvent(
|
|||||||
|
|
||||||
// Generate hash ID from author data first (needed for URL)
|
// Generate hash ID from author data first (needed for URL)
|
||||||
const hashId = await generateAuthorHashId({
|
const hashId = await generateAuthorHashId({
|
||||||
pubkey: authorPubkey,
|
pubkey: params.authorPubkey,
|
||||||
authorName,
|
authorName: params.authorName,
|
||||||
presentation,
|
presentation,
|
||||||
contentDescription,
|
contentDescription,
|
||||||
mainnetAddress: draft.mainnetAddress ?? undefined,
|
mainnetAddress: params.draft.mainnetAddress ?? undefined,
|
||||||
pictureUrl: draft.pictureUrl ?? undefined,
|
pictureUrl: params.draft.pictureUrl ?? undefined,
|
||||||
category,
|
category,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -56,13 +63,14 @@ export async function buildPresentationEvent(
|
|||||||
const profileUrl = generateObjectUrl('author', hashId, index, version)
|
const profileUrl = generateObjectUrl('author', hashId, index, version)
|
||||||
|
|
||||||
// Encode pubkey to npub (for metadata JSON)
|
// Encode pubkey to npub (for metadata JSON)
|
||||||
const npub = nip19.npubEncode(authorPubkey)
|
const npub = nip19.npubEncode(params.authorPubkey)
|
||||||
|
|
||||||
// Build visible content message
|
// Build visible content message
|
||||||
// If picture exists, use it as preview image for the link (markdown format)
|
// If picture exists, use it as preview image for the link (markdown format)
|
||||||
// Note: The image will display at full size in most Nostr clients, not as a thumbnail
|
// Note: The image will display at full size in most Nostr clients, not as a thumbnail
|
||||||
|
const {draft} = params
|
||||||
const linkWithPreview = draft.pictureUrl
|
const linkWithPreview = draft.pictureUrl
|
||||||
? `[](${profileUrl})`
|
? `[](${profileUrl})`
|
||||||
: profileUrl
|
: profileUrl
|
||||||
|
|
||||||
const visibleContent = [
|
const visibleContent = [
|
||||||
@ -74,9 +82,9 @@ export async function buildPresentationEvent(
|
|||||||
|
|
||||||
// Build profile JSON for metadata (stored in tag, not in content)
|
// Build profile JSON for metadata (stored in tag, not in content)
|
||||||
const profileJson = JSON.stringify({
|
const profileJson = JSON.stringify({
|
||||||
authorName,
|
authorName: params.authorName,
|
||||||
npub,
|
npub,
|
||||||
pubkey: authorPubkey,
|
pubkey: params.authorPubkey,
|
||||||
presentation,
|
presentation,
|
||||||
contentDescription,
|
contentDescription,
|
||||||
mainnetAddress: draft.mainnetAddress,
|
mainnetAddress: draft.mainnetAddress,
|
||||||
|
|||||||
@ -101,9 +101,9 @@ export async function publishPreview(
|
|||||||
invoice,
|
invoice,
|
||||||
authorPubkey,
|
authorPubkey,
|
||||||
authorPresentationId: presentationId,
|
authorPresentationId: presentationId,
|
||||||
extraTags,
|
...(extraTags ? { extraTags } : {}),
|
||||||
encryptedContent,
|
...(encryptedContent ? { encryptedContent } : {}),
|
||||||
encryptedKey,
|
...(encryptedKey ? { encryptedKey } : {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set private key in orchestrator
|
// Set private key in orchestrator
|
||||||
|
|||||||
@ -22,16 +22,23 @@ export class AutomaticTransferService {
|
|||||||
* Transfer author portion after article payment
|
* Transfer author portion after article payment
|
||||||
* Creates a Lightning invoice from the platform to the author
|
* 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): void {
|
private logTransferRequired(params: {
|
||||||
|
type: 'article' | 'review'
|
||||||
|
id: string
|
||||||
|
pubkey: string
|
||||||
|
amount: number
|
||||||
|
recipient: string
|
||||||
|
platformCommission: number
|
||||||
|
}): void {
|
||||||
const logData = {
|
const logData = {
|
||||||
[type === 'article' ? 'articleId' : 'reviewId']: id,
|
[params.type === 'article' ? 'articleId' : 'reviewId']: params.id,
|
||||||
[type === 'article' ? 'articlePubkey' : 'reviewerPubkey']: pubkey,
|
[params.type === 'article' ? 'articlePubkey' : 'reviewerPubkey']: params.pubkey,
|
||||||
amount,
|
amount: params.amount,
|
||||||
recipient,
|
recipient: params.recipient,
|
||||||
platformCommission,
|
platformCommission: params.platformCommission,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
console.warn(`Automatic transfer required${type === 'review' ? ' for review' : ''}`, logData)
|
console.warn(`Automatic transfer required${params.type === 'review' ? ' for review' : ''}`, logData)
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildTransferError(error: unknown, recipient: string, amount: number = 0): TransferResult {
|
private buildTransferError(error: unknown, recipient: string, amount: number = 0): TransferResult {
|
||||||
@ -62,8 +69,21 @@ export class AutomaticTransferService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logTransferRequired('article', articleId, articlePubkey, split.author, authorLightningAddress, split.platform)
|
this.logTransferRequired({
|
||||||
this.trackTransferRequirement('article', articleId, articlePubkey, split.author, authorLightningAddress)
|
type: 'article',
|
||||||
|
id: articleId,
|
||||||
|
pubkey: articlePubkey,
|
||||||
|
amount: split.author,
|
||||||
|
recipient: authorLightningAddress,
|
||||||
|
platformCommission: split.platform,
|
||||||
|
})
|
||||||
|
this.trackTransferRequirement({
|
||||||
|
type: 'article',
|
||||||
|
id: articleId,
|
||||||
|
recipientPubkey: articlePubkey,
|
||||||
|
amount: split.author,
|
||||||
|
recipientAddress: authorLightningAddress,
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -102,8 +122,21 @@ export class AutomaticTransferService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logTransferRequired('review', reviewId, reviewerPubkey, split.reviewer, reviewerLightningAddress, split.platform)
|
this.logTransferRequired({
|
||||||
this.trackTransferRequirement('review', reviewId, reviewerPubkey, split.reviewer, reviewerLightningAddress)
|
type: 'review',
|
||||||
|
id: reviewId,
|
||||||
|
pubkey: reviewerPubkey,
|
||||||
|
amount: split.reviewer,
|
||||||
|
recipient: reviewerLightningAddress,
|
||||||
|
platformCommission: split.platform,
|
||||||
|
})
|
||||||
|
this.trackTransferRequirement({
|
||||||
|
type: 'review',
|
||||||
|
id: reviewId,
|
||||||
|
recipientPubkey: reviewerPubkey,
|
||||||
|
amount: split.reviewer,
|
||||||
|
recipientAddress: reviewerLightningAddress,
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@ -126,11 +159,13 @@ export class AutomaticTransferService {
|
|||||||
* In production, this would be stored in a database or queue
|
* In production, this would be stored in a database or queue
|
||||||
*/
|
*/
|
||||||
private trackTransferRequirement(
|
private trackTransferRequirement(
|
||||||
type: 'article' | 'review',
|
params: {
|
||||||
id: string,
|
type: 'article' | 'review'
|
||||||
recipientPubkey: string,
|
id: string
|
||||||
amount: number,
|
recipientPubkey: string
|
||||||
recipientAddress: string
|
amount: number
|
||||||
|
recipientAddress: string
|
||||||
|
}
|
||||||
): void {
|
): void {
|
||||||
// In production, this would:
|
// In production, this would:
|
||||||
// 1. Store in a database/queue for processing
|
// 1. Store in a database/queue for processing
|
||||||
@ -138,11 +173,11 @@ export class AutomaticTransferService {
|
|||||||
// 3. Update tracking when transfer is complete
|
// 3. Update tracking when transfer is complete
|
||||||
|
|
||||||
console.warn('Transfer requirement tracked', {
|
console.warn('Transfer requirement tracked', {
|
||||||
type,
|
type: params.type,
|
||||||
id,
|
id: params.id,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
amount,
|
amount: params.amount,
|
||||||
recipientAddress,
|
recipientAddress: params.recipientAddress,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,15 +78,17 @@ function setupContentDeliveryHandlers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createContentDeliverySubscription(
|
function createContentDeliverySubscription(
|
||||||
pool: import('nostr-tools').SimplePool,
|
params: {
|
||||||
authorPubkey: string,
|
pool: import('nostr-tools').SimplePool
|
||||||
recipientPubkey: string,
|
authorPubkey: string
|
||||||
articleId: string,
|
recipientPubkey: string
|
||||||
messageEventId: string
|
articleId: string
|
||||||
|
messageEventId: string
|
||||||
|
}
|
||||||
): import('@/types/nostr-tools-extended').Subscription {
|
): import('@/types/nostr-tools-extended').Subscription {
|
||||||
const filters = createContentDeliveryFilters(authorPubkey, recipientPubkey, articleId, messageEventId)
|
const filters = createContentDeliveryFilters(params.authorPubkey, params.recipientPubkey, params.articleId, params.messageEventId)
|
||||||
const relayUrl = getPrimaryRelaySync()
|
const relayUrl = getPrimaryRelaySync()
|
||||||
return createSubscription(pool, [relayUrl], filters)
|
return createSubscription(params.pool, [relayUrl], filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createContentDeliveryPromise(
|
function createContentDeliveryPromise(
|
||||||
@ -129,7 +131,7 @@ export function verifyContentDelivery(
|
|||||||
return Promise.resolve(status)
|
return Promise.resolve(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sub = createContentDeliverySubscription(pool, authorPubkey, recipientPubkey, articleId, messageEventId)
|
const sub = createContentDeliverySubscription({ pool, authorPubkey, recipientPubkey, articleId, messageEventId })
|
||||||
return createContentDeliveryPromise(sub, status)
|
return createContentDeliveryPromise(sub, status)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
status.error = error instanceof Error ? error.message : 'Unknown error'
|
status.error = error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
|||||||
@ -10,21 +10,23 @@ export function scheduleNextCheck(checkConfirmation: () => void, interval: numbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function checkTransactionStatus(
|
export async function checkTransactionStatus(
|
||||||
txid: string,
|
params: {
|
||||||
startTime: number,
|
txid: string
|
||||||
timeout: number,
|
startTime: number
|
||||||
interval: number,
|
timeout: number
|
||||||
resolve: (value: TransactionVerificationResult | null) => void,
|
interval: number
|
||||||
checkConfirmation: () => void
|
resolve: (value: TransactionVerificationResult | null) => void
|
||||||
|
checkConfirmation: () => void
|
||||||
|
}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (Date.now() - startTime > timeout) {
|
if (Date.now() - params.startTime > params.timeout) {
|
||||||
resolve(null)
|
params.resolve(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const transaction = await getTransaction(txid)
|
const transaction = await getTransaction(params.txid)
|
||||||
if (!transaction) {
|
if (!transaction) {
|
||||||
scheduleNextCheck(checkConfirmation, interval)
|
scheduleNextCheck(params.checkConfirmation, params.interval)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,15 +35,15 @@ export async function checkTransactionStatus(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!authorOutput) {
|
if (!authorOutput) {
|
||||||
scheduleNextCheck(checkConfirmation, interval)
|
scheduleNextCheck(params.checkConfirmation, params.interval)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await verifySponsoringTransaction(txid, authorOutput.scriptpubkey_address)
|
const result = await verifySponsoringTransaction(params.txid, authorOutput.scriptpubkey_address)
|
||||||
if (result.confirmed && result.valid) {
|
if (result.confirmed && result.valid) {
|
||||||
resolve(result)
|
params.resolve(result)
|
||||||
} else {
|
} else {
|
||||||
scheduleNextCheck(checkConfirmation, interval)
|
scheduleNextCheck(params.checkConfirmation, params.interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ export async function waitForConfirmation(
|
|||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const checkConfirmation = (): void => {
|
const checkConfirmation = (): void => {
|
||||||
void checkTransactionStatus(txid, startTime, timeout, interval, resolve, checkConfirmation)
|
void checkTransactionStatus({ txid, startTime, timeout, interval, resolve, checkConfirmation })
|
||||||
}
|
}
|
||||||
checkConfirmation()
|
checkConfirmation()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -32,33 +32,35 @@ export function findTransactionOutputs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildVerificationResult(
|
export function buildVerificationResult(
|
||||||
valid: boolean,
|
params: {
|
||||||
confirmed: boolean,
|
valid: boolean
|
||||||
confirmations: number,
|
confirmed: boolean
|
||||||
authorOutput?: MempoolTransaction['vout'][0],
|
confirmations: number
|
||||||
platformOutput?: MempoolTransaction['vout'][0]
|
authorOutput?: MempoolTransaction['vout'][0]
|
||||||
|
platformOutput?: MempoolTransaction['vout'][0]
|
||||||
|
}
|
||||||
): TransactionVerificationResult {
|
): TransactionVerificationResult {
|
||||||
const result: TransactionVerificationResult = {
|
const result: TransactionVerificationResult = {
|
||||||
valid,
|
valid: params.valid,
|
||||||
confirmed,
|
confirmed: params.confirmed,
|
||||||
confirmations,
|
confirmations: params.confirmations,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!params.valid) {
|
||||||
result.error = 'Transaction outputs do not match expected split'
|
result.error = 'Transaction outputs do not match expected split'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authorOutput) {
|
if (params.authorOutput) {
|
||||||
result.authorOutput = {
|
result.authorOutput = {
|
||||||
address: authorOutput.scriptpubkey_address,
|
address: params.authorOutput.scriptpubkey_address,
|
||||||
amount: authorOutput.value,
|
amount: params.authorOutput.value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platformOutput) {
|
if (params.platformOutput) {
|
||||||
result.platformOutput = {
|
result.platformOutput = {
|
||||||
address: platformOutput.scriptpubkey_address,
|
address: params.platformOutput.scriptpubkey_address,
|
||||||
amount: platformOutput.value,
|
amount: params.platformOutput.value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,13 +158,13 @@ export async function verifySponsoringTransaction(
|
|||||||
logVerificationFailure(txid, authorMainnetAddress, split, transaction)
|
logVerificationFailure(txid, authorMainnetAddress, split, transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildVerificationResult(
|
return buildVerificationResult({
|
||||||
validation.valid,
|
valid: validation.valid,
|
||||||
validation.confirmed,
|
confirmed: validation.confirmed,
|
||||||
validation.confirmations,
|
confirmations: validation.confirmations,
|
||||||
validation.authorOutput,
|
...(validation.authorOutput ? { authorOutput: validation.authorOutput } : {}),
|
||||||
validation.platformOutput
|
...(validation.platformOutput ? { platformOutput: validation.platformOutput } : {}),
|
||||||
)
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleVerificationError(txid, authorMainnetAddress, error)
|
return handleVerificationError(txid, authorMainnetAddress, error)
|
||||||
}
|
}
|
||||||
|
|||||||
24
lib/nostr.ts
24
lib/nostr.ts
@ -285,7 +285,13 @@ class NostrService {
|
|||||||
throw new Error('Private key not set or pool not initialized')
|
throw new Error('Private key not set or pool not initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
return getPrivateContentFromPool(this.pool, eventId, authorPubkey, this.privateKey, this.publicKey)
|
return getPrivateContentFromPool({
|
||||||
|
pool: this.pool,
|
||||||
|
eventId,
|
||||||
|
authorPubkey,
|
||||||
|
privateKey: this.privateKey,
|
||||||
|
publicKey: this.publicKey,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -298,7 +304,13 @@ class NostrService {
|
|||||||
if (!this.privateKey || !this.pool || !this.publicKey) {
|
if (!this.privateKey || !this.pool || !this.publicKey) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return getDecryptionKey(this.pool, eventId, authorPubkey, this.privateKey, this.publicKey)
|
return getDecryptionKey({
|
||||||
|
pool: this.pool,
|
||||||
|
eventId,
|
||||||
|
authorPubkey,
|
||||||
|
recipientPrivateKey: this.privateKey,
|
||||||
|
recipientPublicKey: this.publicKey,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDecryptedArticleContent(eventId: string, authorPubkey: string): Promise<string | null> {
|
async getDecryptedArticleContent(eventId: string, authorPubkey: string): Promise<string | null> {
|
||||||
@ -447,7 +459,13 @@ class NostrService {
|
|||||||
// Use provided userPubkey or fall back to current public key
|
// Use provided userPubkey or fall back to current public key
|
||||||
const checkPubkey = userPubkey ?? this.publicKey
|
const checkPubkey = userPubkey ?? this.publicKey
|
||||||
|
|
||||||
return checkZapReceiptHelper(this.pool, targetPubkey, targetEventId, amount, checkPubkey)
|
return checkZapReceiptHelper({
|
||||||
|
pool: this.pool,
|
||||||
|
targetPubkey,
|
||||||
|
targetEventId,
|
||||||
|
amount,
|
||||||
|
userPubkey: checkPubkey,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -33,20 +33,22 @@ function decryptContent(privateKey: string, event: Event): Promise<string | null
|
|||||||
* This function now returns the decryption key instead of the full content
|
* This function now returns the decryption key instead of the full content
|
||||||
*/
|
*/
|
||||||
export function getPrivateContent(
|
export function getPrivateContent(
|
||||||
pool: SimplePool,
|
params: {
|
||||||
eventId: string,
|
pool: SimplePool
|
||||||
authorPubkey: string,
|
eventId: string
|
||||||
privateKey: string,
|
authorPubkey: string
|
||||||
publicKey: string
|
privateKey: string
|
||||||
|
publicKey: string
|
||||||
|
}
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
if (!privateKey || !pool || !publicKey) {
|
if (!params.privateKey || !params.pool || !params.publicKey) {
|
||||||
throw new Error('Private key not set or pool not initialized')
|
throw new Error('Private key not set or pool not initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<string | null>((resolve) => {
|
return new Promise<string | null>((resolve) => {
|
||||||
let resolved = false
|
let resolved = false
|
||||||
const relayUrl = getPrimaryRelaySync()
|
const relayUrl = getPrimaryRelaySync()
|
||||||
const sub = createSubscription(pool, [relayUrl], createPrivateMessageFilters(eventId, publicKey, authorPubkey))
|
const sub = createSubscription(params.pool, [relayUrl], createPrivateMessageFilters(params.eventId, params.publicKey, params.authorPubkey))
|
||||||
|
|
||||||
const finalize = (result: string | null): void => {
|
const finalize = (result: string | null): void => {
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
@ -58,7 +60,7 @@ export function getPrivateContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub.on('event', (event: Event): void => {
|
sub.on('event', (event: Event): void => {
|
||||||
void decryptContent(privateKey, event)
|
void decryptContent(params.privateKey, event)
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
if (content) {
|
if (content) {
|
||||||
finalize(content)
|
finalize(content)
|
||||||
@ -109,20 +111,22 @@ function handleDecryptionKeyEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getDecryptionKey(
|
export async function getDecryptionKey(
|
||||||
pool: SimplePool,
|
params: {
|
||||||
eventId: string,
|
pool: SimplePool
|
||||||
authorPubkey: string,
|
eventId: string
|
||||||
recipientPrivateKey: string,
|
authorPubkey: string
|
||||||
recipientPublicKey: string
|
recipientPrivateKey: string
|
||||||
|
recipientPublicKey: string
|
||||||
|
}
|
||||||
): Promise<DecryptionKey | null> {
|
): Promise<DecryptionKey | null> {
|
||||||
if (!recipientPrivateKey || !pool || !recipientPublicKey) {
|
if (!params.recipientPrivateKey || !params.pool || !params.recipientPublicKey) {
|
||||||
throw new Error('Private key not set or pool not initialized')
|
throw new Error('Private key not set or pool not initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<DecryptionKey | null>((resolve) => {
|
return new Promise<DecryptionKey | null>((resolve) => {
|
||||||
let resolved = false
|
let resolved = false
|
||||||
const relayUrl = getPrimaryRelaySync()
|
const relayUrl = getPrimaryRelaySync()
|
||||||
const sub = createSubscription(pool, [relayUrl], createPrivateMessageFilters(eventId, recipientPublicKey, authorPubkey))
|
const sub = createSubscription(params.pool, [relayUrl], createPrivateMessageFilters(params.eventId, params.recipientPublicKey, params.authorPubkey))
|
||||||
|
|
||||||
const finalize = (result: DecryptionKey | null): void => {
|
const finalize = (result: DecryptionKey | null): void => {
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
@ -134,7 +138,7 @@ export async function getDecryptionKey(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub.on('event', (event: Event): void => {
|
sub.on('event', (event: Event): void => {
|
||||||
handleDecryptionKeyEvent(event, recipientPrivateKey, finalize)
|
handleDecryptionKeyEvent(event, params.recipientPrivateKey, finalize)
|
||||||
})
|
})
|
||||||
sub.on('eose', (): void => {
|
sub.on('eose', (): void => {
|
||||||
finalize(null)
|
finalize(null)
|
||||||
|
|||||||
@ -20,55 +20,67 @@ function createZapFilters(targetPubkey: string, targetEventId: string, userPubke
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function isValidZapReceipt(
|
async function isValidZapReceipt(
|
||||||
event: Event,
|
params: {
|
||||||
targetEventId: string,
|
event: Event
|
||||||
targetPubkey: string,
|
targetEventId: string
|
||||||
userPubkey: string,
|
targetPubkey: string
|
||||||
amount: number
|
userPubkey: string
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// Import verification service dynamically to avoid circular dependencies
|
// Import verification service dynamically to avoid circular dependencies
|
||||||
const { zapVerificationService } = await import('./zapVerification')
|
const { zapVerificationService } = await import('./zapVerification')
|
||||||
|
|
||||||
return zapVerificationService.verifyZapReceiptForArticle(event, targetEventId, targetPubkey, userPubkey, amount)
|
return zapVerificationService.verifyZapReceiptForArticle(params.event, params.targetEventId, params.targetPubkey, params.userPubkey, params.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if user has paid for an article by looking for zap receipts
|
* Check if user has paid for an article by looking for zap receipts
|
||||||
*/
|
*/
|
||||||
function handleZapReceiptEvent(
|
function handleZapReceiptEvent(
|
||||||
event: Event,
|
params: {
|
||||||
targetEventId: string,
|
event: Event
|
||||||
targetPubkey: string,
|
targetEventId: string
|
||||||
userPubkey: string,
|
targetPubkey: string
|
||||||
amount: number,
|
userPubkey: string
|
||||||
finalize: (value: boolean) => void,
|
amount: number
|
||||||
resolved: { current: boolean }
|
finalize: (value: boolean) => void
|
||||||
|
resolved: { current: boolean }
|
||||||
|
}
|
||||||
): void {
|
): void {
|
||||||
if (resolved.current) {
|
if (params.resolved.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
void isValidZapReceipt(event, targetEventId, targetPubkey, userPubkey, amount).then((isValid) => {
|
void isValidZapReceipt({
|
||||||
|
event: params.event,
|
||||||
|
targetEventId: params.targetEventId,
|
||||||
|
targetPubkey: params.targetPubkey,
|
||||||
|
userPubkey: params.userPubkey,
|
||||||
|
amount: params.amount,
|
||||||
|
}).then((isValid) => {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
finalize(true)
|
params.finalize(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkZapReceipt(
|
export function checkZapReceipt(
|
||||||
pool: SimplePool,
|
params: {
|
||||||
targetPubkey: string,
|
pool: SimplePool
|
||||||
targetEventId: string,
|
targetPubkey: string
|
||||||
amount: number,
|
targetEventId: string
|
||||||
userPubkey: string
|
amount: number
|
||||||
|
userPubkey: string
|
||||||
|
}
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (!pool) {
|
if (!params.pool) {
|
||||||
return Promise.resolve(false)
|
return Promise.resolve(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
let resolved = false
|
let resolved = false
|
||||||
const relayUrl = getPrimaryRelaySync()
|
const relayUrl = getPrimaryRelaySync()
|
||||||
const sub = createSubscription(pool, [relayUrl], createZapFilters(targetPubkey, targetEventId, userPubkey))
|
const sub = createSubscription(params.pool, [relayUrl], createZapFilters(params.targetPubkey, params.targetEventId, params.userPubkey))
|
||||||
|
|
||||||
const finalize = (value: boolean): void => {
|
const finalize = (value: boolean): void => {
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
@ -81,7 +93,15 @@ export function checkZapReceipt(
|
|||||||
|
|
||||||
const resolvedRef = { current: resolved }
|
const resolvedRef = { current: resolved }
|
||||||
sub.on('event', (event: Event): void => {
|
sub.on('event', (event: Event): void => {
|
||||||
handleZapReceiptEvent(event, targetEventId, targetPubkey, userPubkey, amount, finalize, resolvedRef)
|
handleZapReceiptEvent({
|
||||||
|
event,
|
||||||
|
targetEventId: params.targetEventId,
|
||||||
|
targetPubkey: params.targetPubkey,
|
||||||
|
userPubkey: params.userPubkey,
|
||||||
|
amount: params.amount,
|
||||||
|
finalize,
|
||||||
|
resolved: resolvedRef,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const end = (): void => {
|
const end = (): void => {
|
||||||
|
|||||||
@ -29,15 +29,15 @@ export async function sendPrivateContentAfterPayment(
|
|||||||
if (result.success && result.messageEventId) {
|
if (result.success && result.messageEventId) {
|
||||||
verifyPaymentAmount(amount, articleId)
|
verifyPaymentAmount(amount, articleId)
|
||||||
|
|
||||||
const trackingData = createTrackingData(
|
const trackingData = createTrackingData({
|
||||||
articleId,
|
articleId,
|
||||||
validation.storedContent.authorPubkey,
|
authorPubkey: validation.storedContent.authorPubkey,
|
||||||
recipientPubkey,
|
recipientPubkey,
|
||||||
result.messageEventId,
|
messageEventId: result.messageEventId,
|
||||||
amount,
|
amount,
|
||||||
result.verified ?? false,
|
verified: result.verified ?? false,
|
||||||
zapReceiptId
|
...(zapReceiptId ? { zapReceiptId } : {}),
|
||||||
)
|
})
|
||||||
|
|
||||||
await platformTracking.trackContentDelivery(trackingData, validation.authorPrivateKey)
|
await platformTracking.trackContentDelivery(trackingData, validation.authorPrivateKey)
|
||||||
await triggerAutomaticTransfer(validation.storedContent.authorPubkey, articleId, amount)
|
await triggerAutomaticTransfer(validation.storedContent.authorPubkey, articleId, amount)
|
||||||
|
|||||||
@ -3,31 +3,33 @@ import { lightningAddressService } from './lightningAddress'
|
|||||||
import { automaticTransferService } from './automaticTransfer'
|
import { automaticTransferService } from './automaticTransfer'
|
||||||
|
|
||||||
export function createTrackingData(
|
export function createTrackingData(
|
||||||
articleId: string,
|
params: {
|
||||||
authorPubkey: string,
|
articleId: string
|
||||||
recipientPubkey: string,
|
authorPubkey: string
|
||||||
messageEventId: string,
|
recipientPubkey: string
|
||||||
amount: number,
|
messageEventId: string
|
||||||
verified: boolean,
|
amount: number
|
||||||
zapReceiptId?: string
|
verified: boolean
|
||||||
|
zapReceiptId?: string
|
||||||
|
}
|
||||||
): import('./platformTracking').ContentDeliveryTracking {
|
): import('./platformTracking').ContentDeliveryTracking {
|
||||||
const expectedSplit = calculateArticleSplit()
|
const expectedSplit = calculateArticleSplit()
|
||||||
const timestamp = Math.floor(Date.now() / 1000)
|
const timestamp = Math.floor(Date.now() / 1000)
|
||||||
|
|
||||||
const trackingData: import('./platformTracking').ContentDeliveryTracking = {
|
const trackingData: import('./platformTracking').ContentDeliveryTracking = {
|
||||||
articleId,
|
articleId: params.articleId,
|
||||||
articlePubkey: authorPubkey,
|
articlePubkey: params.authorPubkey,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
messageEventId,
|
messageEventId: params.messageEventId,
|
||||||
amount,
|
amount: params.amount,
|
||||||
authorAmount: expectedSplit.author,
|
authorAmount: expectedSplit.author,
|
||||||
platformCommission: expectedSplit.platform,
|
platformCommission: expectedSplit.platform,
|
||||||
timestamp,
|
timestamp,
|
||||||
verified,
|
verified: params.verified,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zapReceiptId) {
|
if (params.zapReceiptId) {
|
||||||
trackingData.zapReceiptId = zapReceiptId
|
trackingData.zapReceiptId = params.zapReceiptId
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackingData
|
return trackingData
|
||||||
@ -74,34 +76,36 @@ export async function triggerAutomaticTransfer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logPaymentSuccess(
|
export function logPaymentSuccess(
|
||||||
articleId: string,
|
params: {
|
||||||
recipientPubkey: string,
|
articleId: string
|
||||||
amount: number,
|
recipientPubkey: string
|
||||||
messageEventId: string,
|
amount: number
|
||||||
verified: boolean
|
messageEventId: string
|
||||||
|
verified: boolean
|
||||||
|
}
|
||||||
): void {
|
): void {
|
||||||
const expectedSplit = calculateArticleSplit()
|
const expectedSplit = calculateArticleSplit()
|
||||||
console.warn('Article payment processed with commission', {
|
console.warn('Article payment processed with commission', {
|
||||||
articleId,
|
articleId: params.articleId,
|
||||||
totalAmount: amount,
|
totalAmount: params.amount,
|
||||||
authorPortion: expectedSplit.author,
|
authorPortion: expectedSplit.author,
|
||||||
platformCommission: expectedSplit.platform,
|
platformCommission: expectedSplit.platform,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (verified) {
|
if (params.verified) {
|
||||||
console.warn('Private content sent and verified on relay', {
|
console.warn('Private content sent and verified on relay', {
|
||||||
articleId,
|
articleId: params.articleId,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
messageEventId,
|
messageEventId: params.messageEventId,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.warn('Private content sent but not yet verified on relay', {
|
console.warn('Private content sent but not yet verified on relay', {
|
||||||
articleId,
|
articleId: params.articleId,
|
||||||
recipientPubkey,
|
recipientPubkey: params.recipientPubkey,
|
||||||
messageEventId,
|
messageEventId: params.messageEventId,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -114,7 +118,13 @@ export function logPaymentResult(
|
|||||||
amount: number
|
amount: number
|
||||||
): boolean {
|
): boolean {
|
||||||
if (result.success && result.messageEventId) {
|
if (result.success && result.messageEventId) {
|
||||||
logPaymentSuccess(articleId, recipientPubkey, amount, result.messageEventId, result.verified ?? false)
|
logPaymentSuccess({
|
||||||
|
articleId,
|
||||||
|
recipientPubkey,
|
||||||
|
amount,
|
||||||
|
messageEventId: result.messageEventId,
|
||||||
|
verified: result.verified ?? false,
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
console.error('Failed to send private content, but payment was confirmed', {
|
console.error('Failed to send private content, but payment was confirmed', {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user