- **Motivations :** Assurer passage du lint strict et clarifier la logique paiements/publications. - **Root causes :** Fonctions trop longues, promesses non gérées et typages WebLN/Nostr incomplets. - **Correctifs :** Refactor PaymentModal (handlers void), extraction helpers articlePublisher, simplification polling sponsoring/zap, corrections curly et awaits. - **Evolutions :** Nouveau module articlePublisherHelpers pour présentation/aiguillage contenu privé. - **Page affectées :** components/PaymentModal.tsx, lib/articlePublisher.ts, lib/articlePublisherHelpers.ts, lib/paymentPolling.ts, lib/sponsoring.ts, lib/nostrZapVerification.ts et dépendances liées.
118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { Event, verifyEvent } from 'nostr-tools'
|
|
|
|
/**
|
|
* Service for verifying zap receipts and their signatures
|
|
*/
|
|
export class ZapVerificationService {
|
|
/**
|
|
* Verify a zap receipt signature
|
|
*/
|
|
verifyZapReceiptSignature(event: Event): boolean {
|
|
try {
|
|
return verifyEvent(event)
|
|
} catch (error) {
|
|
console.error('Error verifying zap receipt signature:', error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a zap receipt is valid for a specific article and user
|
|
*/
|
|
verifyZapReceiptForArticle(
|
|
zapReceipt: Event,
|
|
articleId: string,
|
|
articlePubkey: string,
|
|
userPubkey: string,
|
|
expectedAmount: number
|
|
): boolean {
|
|
if (!this.verifyZapReceiptSignature(zapReceipt)) {
|
|
console.warn('Zap receipt signature verification failed')
|
|
return false
|
|
}
|
|
|
|
return (
|
|
this.isRecipientValid(zapReceipt, articlePubkey) &&
|
|
this.isArticleReferenced(zapReceipt, articleId) &&
|
|
this.isAmountValid(zapReceipt, expectedAmount) &&
|
|
this.isZapKind(zapReceipt)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Extract payment information from a zap receipt
|
|
*/
|
|
extractPaymentInfo(zapReceipt: Event): {
|
|
amount: number // in satoshis
|
|
recipient: string
|
|
articleId: string | null
|
|
payer: string
|
|
} | null {
|
|
try {
|
|
const amountTag = zapReceipt.tags.find((tag) => tag[0] === 'amount')
|
|
const recipientTag = zapReceipt.tags.find((tag) => tag[0] === 'p')
|
|
const eventTag = zapReceipt.tags.find((tag) => tag[0] === 'e')
|
|
|
|
if (!amountTag || !recipientTag) {
|
|
return null
|
|
}
|
|
|
|
const amountInMillisats = parseInt(amountTag[1] ?? '0')
|
|
const amountInSats = Math.floor(amountInMillisats / 1000)
|
|
|
|
return {
|
|
amount: amountInSats,
|
|
recipient: recipientTag[1],
|
|
articleId: eventTag?.[1] ?? null,
|
|
payer: zapReceipt.pubkey,
|
|
}
|
|
} catch (error) {
|
|
console.error('Error extracting payment info:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
private isRecipientValid(zapReceipt: Event, articlePubkey: string): boolean {
|
|
const recipient = zapReceipt.tags.find((tag) => tag[0] === 'p')?.[1]
|
|
if (recipient !== articlePubkey) {
|
|
console.warn('Zap receipt recipient does not match article author')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
private isArticleReferenced(zapReceipt: Event, articleId: string): boolean {
|
|
const eventIdTag = zapReceipt.tags.find((tag) => tag[0] === 'e')?.[1]
|
|
if (eventIdTag !== articleId) {
|
|
console.warn('Zap receipt does not reference the correct article')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
private isAmountValid(zapReceipt: Event, expectedAmount: number): boolean {
|
|
const amountTag = zapReceipt.tags.find((tag) => tag[0] === 'amount')?.[1]
|
|
if (!amountTag) {
|
|
console.warn('Zap receipt does not contain amount tag')
|
|
return false
|
|
}
|
|
const amountInMillisats = parseInt(amountTag ?? '0')
|
|
const expectedAmountInMillisats = expectedAmount * 1000
|
|
if (amountInMillisats < expectedAmountInMillisats) {
|
|
console.warn(`Zap amount ${amountInMillisats} is less than expected ${expectedAmountInMillisats}`)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
private isZapKind(zapReceipt: Event): boolean {
|
|
if (zapReceipt.kind !== 9735) {
|
|
console.warn('Event is not a zap receipt (kind 9735)')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
export const zapVerificationService = new ZapVerificationService()
|