story-research-zapwall/lib/paymentNotes.ts
2026-01-06 14:17:55 +01:00

239 lines
7.0 KiB
TypeScript

import { nostrService } from './nostr'
import { buildTags } from './nostrTagSystem'
import { PLATFORM_SERVICE } from './platformConfig'
import { generatePurchaseHashId, generateReviewTipHashId, generateSponsoringHashId } from './hashIdGenerator'
import { buildObjectId } from './urlGenerator'
import type { Event, EventTemplate } from 'nostr-tools'
/**
* Publish an explicit payment note (kind 1) for a purchase
* This note is published in addition to the zap receipt (kind 9735)
*/
export async function publishPurchaseNote(params: {
articleId: string
authorPubkey: string
payerPubkey: string
amount: number
paymentHash: string
zapReceiptId?: string
category?: 'science-fiction' | 'scientific-research'
seriesId?: string
payerPrivateKey: string
}): Promise<Event | null> {
const category = params.category === 'science-fiction' ? 'sciencefiction' : params.category === 'scientific-research' ? 'research' : 'sciencefiction'
const purchaseData = {
payerPubkey: params.payerPubkey,
articleId: params.articleId,
authorPubkey: params.authorPubkey,
amount: params.amount,
paymentHash: params.paymentHash,
}
const hashId = await generatePurchaseHashId(purchaseData)
const id = buildObjectId(hashId, 0, 0)
const tags = buildTags({
type: 'payment',
category,
id: hashId,
service: PLATFORM_SERVICE,
version: 0,
hidden: false,
payment: true,
paymentType: 'purchase',
amount: params.amount,
payerPubkey: params.payerPubkey,
recipientPubkey: params.authorPubkey,
paymentHash: params.paymentHash,
zapReceiptId: params.zapReceiptId,
articleId: params.articleId,
...(params.seriesId ? { seriesId: params.seriesId } : {}),
})
// Build JSON metadata
const paymentJson = JSON.stringify({
type: 'purchase',
id,
hash: hashId,
version: 0,
index: 0,
...purchaseData,
})
tags.push(['json', paymentJson])
const eventTemplate: EventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags,
content: `Purchase confirmed: ${params.amount} sats for article ${params.articleId}`,
}
nostrService.setPrivateKey(params.payerPrivateKey)
return await nostrService.publishEvent(eventTemplate)
}
/**
* Publish an explicit payment note (kind 1) for a review tip
* This note is published in addition to the zap receipt (kind 9735)
*/
export async function publishReviewTipNote(params: {
articleId: string
reviewId: string
authorPubkey: string
reviewerPubkey: string
payerPubkey: string
amount: number
paymentHash: string
zapReceiptId?: string
category?: 'science-fiction' | 'scientific-research'
seriesId?: string
text?: string
payerPrivateKey: string
}): Promise<Event | null> {
const category = params.category === 'science-fiction' ? 'sciencefiction' : params.category === 'scientific-research' ? 'research' : 'sciencefiction'
const tipData = {
payerPubkey: params.payerPubkey,
articleId: params.articleId,
reviewId: params.reviewId,
reviewerPubkey: params.reviewerPubkey,
authorPubkey: params.authorPubkey,
amount: params.amount,
paymentHash: params.paymentHash,
}
const hashId = await generateReviewTipHashId(tipData)
const id = buildObjectId(hashId, 0, 0)
const tags = buildTags({
type: 'payment',
category,
id: hashId,
service: PLATFORM_SERVICE,
version: 0,
hidden: false,
payment: true,
paymentType: 'review_tip',
amount: params.amount,
payerPubkey: params.payerPubkey,
recipientPubkey: params.reviewerPubkey,
paymentHash: params.paymentHash,
zapReceiptId: params.zapReceiptId,
articleId: params.articleId,
reviewId: params.reviewId,
...(params.seriesId ? { seriesId: params.seriesId } : {}),
...(params.text ? { text: params.text } : {}),
})
// Build JSON metadata
const paymentJson = JSON.stringify({
type: 'review_tip',
id,
hash: hashId,
version: 0,
index: 0,
...tipData,
...(params.text ? { text: params.text } : {}),
})
tags.push(['json', paymentJson])
const content = params.text
? `Review tip confirmed: ${params.amount} sats for review ${params.reviewId}\n\n${params.text}`
: `Review tip confirmed: ${params.amount} sats for review ${params.reviewId}`
const eventTemplate: EventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags,
content,
}
nostrService.setPrivateKey(params.payerPrivateKey)
return await nostrService.publishEvent(eventTemplate)
}
/**
* Publish an explicit payment note (kind 1) for a sponsoring
* This note is published in addition to the zap receipt (kind 9735) if applicable
*/
export async function publishSponsoringNote(params: {
authorPubkey: string
payerPubkey: string
amount: number
paymentHash: string
category?: 'science-fiction' | 'scientific-research'
seriesId?: string
articleId?: string
text?: string
transactionId?: string // Bitcoin transaction ID for mainnet payments
payerPrivateKey: string
}): Promise<Event | null> {
const category = params.category === 'science-fiction' ? 'sciencefiction' : params.category === 'scientific-research' ? 'research' : 'sciencefiction'
const sponsoringData = {
payerPubkey: params.payerPubkey,
authorPubkey: params.authorPubkey,
amount: params.amount,
paymentHash: params.paymentHash,
...(params.seriesId ? { seriesId: params.seriesId } : {}),
...(params.articleId ? { articleId: params.articleId } : {}),
}
const hashId = await generateSponsoringHashId(sponsoringData)
const id = buildObjectId(hashId, 0, 0)
const tags = buildTags({
type: 'payment',
category,
id: hashId,
service: PLATFORM_SERVICE,
version: 0,
hidden: false,
payment: true,
paymentType: 'sponsoring',
amount: params.amount,
payerPubkey: params.payerPubkey,
recipientPubkey: params.authorPubkey,
paymentHash: params.paymentHash,
...(params.seriesId ? { seriesId: params.seriesId } : {}),
...(params.articleId ? { articleId: params.articleId } : {}),
...(params.text ? { text: params.text } : {}),
})
// Add transaction ID if provided (for Bitcoin mainnet payments)
if (params.transactionId) {
tags.push(['transaction_id', params.transactionId])
}
// Build JSON metadata
const paymentJson = JSON.stringify({
type: 'sponsoring',
id,
hash: hashId,
version: 0,
index: 0,
...sponsoringData,
...(params.text ? { text: params.text } : {}),
...(params.transactionId ? { transactionId: params.transactionId } : {}),
})
tags.push(['json', paymentJson])
const content = params.text
? `Sponsoring confirmed: ${params.amount} sats for author ${params.authorPubkey.substring(0, 16)}...\n\n${params.text}`
: `Sponsoring confirmed: ${params.amount} sats for author ${params.authorPubkey.substring(0, 16)}...`
const eventTemplate: EventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags,
content,
}
nostrService.setPrivateKey(params.payerPrivateKey)
return await nostrService.publishEvent(eventTemplate)
}