172 lines
4.8 KiB
TypeScript
172 lines
4.8 KiB
TypeScript
import { useState } from 'react'
|
|
import type { Article } from '@/types/nostr'
|
|
import type { AlbyInvoice } from '@/types/alby'
|
|
import { paymentService } from '@/lib/payment'
|
|
import { nostrService } from '@/lib/nostr'
|
|
|
|
type UseArticlePaymentResult = {
|
|
loading: boolean
|
|
error: string | null
|
|
paymentInvoice: AlbyInvoice | null
|
|
handleUnlock: () => Promise<void>
|
|
handlePaymentComplete: () => Promise<void>
|
|
handleCloseModal: () => void
|
|
}
|
|
|
|
export function useArticlePayment(
|
|
article: Article,
|
|
pubkey: string | null,
|
|
onUnlockSuccess?: () => void,
|
|
connect?: () => Promise<void>
|
|
): UseArticlePaymentResult {
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [paymentInvoice, setPaymentInvoice] = useState<AlbyInvoice | null>(null)
|
|
const [paymentHash, setPaymentHash] = useState<string | null>(null)
|
|
|
|
const handleUnlock = (): Promise<void> =>
|
|
unlockArticlePayment({
|
|
article,
|
|
pubkey,
|
|
connect,
|
|
onUnlockSuccess,
|
|
setLoading,
|
|
setError,
|
|
setPaymentInvoice,
|
|
setPaymentHash,
|
|
})
|
|
|
|
const handlePaymentComplete = (): Promise<void> =>
|
|
checkPaymentAndUnlock({
|
|
article,
|
|
pubkey,
|
|
paymentHash,
|
|
onUnlockSuccess,
|
|
setError,
|
|
setPaymentInvoice,
|
|
setPaymentHash,
|
|
})
|
|
|
|
const handleCloseModal = (): void => resetPaymentModalState({ setPaymentInvoice, setPaymentHash })
|
|
|
|
return {
|
|
loading,
|
|
error,
|
|
paymentInvoice,
|
|
handleUnlock,
|
|
handlePaymentComplete,
|
|
handleCloseModal,
|
|
}
|
|
}
|
|
|
|
async function unlockArticlePayment(params: {
|
|
article: Article
|
|
pubkey: string | null
|
|
connect: (() => Promise<void>) | undefined
|
|
onUnlockSuccess: (() => void) | undefined
|
|
setLoading: (value: boolean) => void
|
|
setError: (value: string | null) => void
|
|
setPaymentInvoice: (value: AlbyInvoice | null) => void
|
|
setPaymentHash: (value: string | null) => void
|
|
}): Promise<void> {
|
|
if (!params.pubkey) {
|
|
await ensureConnectedOrError({
|
|
connect: params.connect,
|
|
setLoading: params.setLoading,
|
|
setError: params.setError,
|
|
})
|
|
return
|
|
}
|
|
|
|
params.setLoading(true)
|
|
params.setError(null)
|
|
try {
|
|
const paymentResult = await paymentService.createArticlePayment({
|
|
article: params.article,
|
|
userPubkey: params.pubkey,
|
|
})
|
|
if (!paymentResult.success || !paymentResult.invoice || !paymentResult.paymentHash) {
|
|
params.setError(paymentResult.error ?? 'Failed to create payment invoice')
|
|
return
|
|
}
|
|
params.setPaymentInvoice(paymentResult.invoice)
|
|
params.setPaymentHash(paymentResult.paymentHash)
|
|
void checkPaymentAndUnlock({
|
|
article: params.article,
|
|
pubkey: params.pubkey,
|
|
paymentHash: paymentResult.paymentHash,
|
|
onUnlockSuccess: params.onUnlockSuccess,
|
|
setError: params.setError,
|
|
setPaymentInvoice: params.setPaymentInvoice,
|
|
setPaymentHash: params.setPaymentHash,
|
|
})
|
|
} catch (e) {
|
|
const errorMessage = e instanceof Error ? e.message : 'Failed to process payment'
|
|
console.error('Payment processing error:', e)
|
|
params.setError(errorMessage)
|
|
} finally {
|
|
params.setLoading(false)
|
|
}
|
|
}
|
|
|
|
async function ensureConnectedOrError(params: {
|
|
connect: (() => Promise<void>) | undefined
|
|
setLoading: (value: boolean) => void
|
|
setError: (value: string | null) => void
|
|
}): Promise<void> {
|
|
if (!params.connect) {
|
|
params.setError('Please connect with Nostr first')
|
|
return
|
|
}
|
|
params.setLoading(true)
|
|
try {
|
|
await params.connect()
|
|
} finally {
|
|
params.setLoading(false)
|
|
}
|
|
}
|
|
|
|
async function checkPaymentAndUnlock(params: {
|
|
article: Article
|
|
pubkey: string | null
|
|
paymentHash: string | null
|
|
onUnlockSuccess: (() => void) | undefined
|
|
setError: (value: string | null) => void
|
|
setPaymentInvoice: (value: AlbyInvoice | null) => void
|
|
setPaymentHash: (value: string | null) => void
|
|
}): Promise<void> {
|
|
if (!params.paymentHash || !params.pubkey) {
|
|
return
|
|
}
|
|
try {
|
|
const hasPaid = await paymentService.waitForArticlePayment({
|
|
paymentHash: params.paymentHash,
|
|
articleId: params.article.id,
|
|
articlePubkey: params.article.pubkey,
|
|
amount: params.article.zapAmount,
|
|
recipientPubkey: params.pubkey,
|
|
timeout: 300000,
|
|
})
|
|
if (!hasPaid) {
|
|
return
|
|
}
|
|
const content = await nostrService.getPrivateContent(params.article.id, params.article.pubkey)
|
|
if (!content) {
|
|
params.setError('Content not available. Please contact the author.')
|
|
return
|
|
}
|
|
resetPaymentModalState({ setPaymentInvoice: params.setPaymentInvoice, setPaymentHash: params.setPaymentHash })
|
|
params.onUnlockSuccess?.()
|
|
} catch (e) {
|
|
console.error('Payment check error:', e)
|
|
}
|
|
}
|
|
|
|
function resetPaymentModalState(params: {
|
|
setPaymentInvoice: (value: AlbyInvoice | null) => void
|
|
setPaymentHash: (value: string | null) => void
|
|
}): void {
|
|
params.setPaymentInvoice(null)
|
|
params.setPaymentHash(null)
|
|
}
|