122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
import { nostrService } from './nostr'
|
|
import { waitForArticlePayment as waitForArticlePaymentHelper } from './paymentPolling'
|
|
import { resolveArticleInvoice } from './invoiceResolver'
|
|
import type { Article } from '@/types/nostr'
|
|
import type { AlbyInvoice } from '@/types/alby'
|
|
|
|
export interface PaymentRequest {
|
|
article: Article
|
|
userPubkey: string
|
|
}
|
|
|
|
export interface PaymentResult {
|
|
success: boolean
|
|
invoice?: AlbyInvoice
|
|
paymentHash?: string
|
|
error?: string
|
|
}
|
|
|
|
/**
|
|
* Payment service integrating Alby/WebLN Lightning payments with Nostr articles
|
|
*/
|
|
export class PaymentService {
|
|
/**
|
|
* Create a Lightning invoice for an article payment
|
|
* First checks if author has created an invoice in the event tags, otherwise creates a new one
|
|
*/
|
|
async createArticlePayment(request: PaymentRequest): Promise<PaymentResult> {
|
|
try {
|
|
const invoice = await resolveArticleInvoice(request.article)
|
|
|
|
// Create zap request event on Nostr
|
|
await nostrService.createZapRequest(
|
|
request.article.pubkey,
|
|
request.article.id,
|
|
request.article.zapAmount
|
|
)
|
|
|
|
return {
|
|
success: true,
|
|
invoice,
|
|
paymentHash: invoice.paymentHash,
|
|
}
|
|
} catch (error) {
|
|
console.error('Payment creation error:', error)
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to create payment',
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if payment for an article has been completed
|
|
*/
|
|
async checkArticlePayment(
|
|
paymentHash: string,
|
|
articleId: string,
|
|
articlePubkey: string,
|
|
amount: number,
|
|
userPubkey?: string
|
|
): Promise<boolean> {
|
|
try {
|
|
// With Alby/WebLN, we rely on zap receipts for payment verification
|
|
// since WebLN doesn't provide payment status checking
|
|
const zapReceiptExists = await nostrService.checkZapReceipt(
|
|
articlePubkey,
|
|
articleId,
|
|
amount,
|
|
userPubkey
|
|
)
|
|
|
|
return zapReceiptExists
|
|
} catch (error) {
|
|
console.error('Payment check error:', error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for payment completion with polling
|
|
* After payment is confirmed, sends private content to the user
|
|
*/
|
|
async waitForArticlePayment(
|
|
paymentHash: string,
|
|
articleId: string,
|
|
articlePubkey: string,
|
|
amount: number,
|
|
recipientPubkey: string,
|
|
timeout: number = 300000 // 5 minutes
|
|
): Promise<boolean> {
|
|
return waitForArticlePaymentHelper(
|
|
paymentHash,
|
|
articleId,
|
|
articlePubkey,
|
|
amount,
|
|
recipientPubkey,
|
|
timeout
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Get payment URL for display/QR code generation
|
|
*/
|
|
async getPaymentUrl(request: PaymentRequest): Promise<string | null> {
|
|
try {
|
|
const result = await this.createArticlePayment(request)
|
|
|
|
if (result.success && result.invoice) {
|
|
// Return Lightning URI format
|
|
return `lightning:${result.invoice.invoice}`
|
|
}
|
|
|
|
return null
|
|
} catch (error) {
|
|
console.error('Get payment URL error:', error)
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
export const paymentService = new PaymentService()
|