Return latest state of paid or failed invoice

This commit is contained in:
ekzyis 2024-11-27 19:09:56 +01:00
parent 68513559e4
commit 9cfc18d655
4 changed files with 36 additions and 28 deletions

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'
import { gql, useApolloClient, useMutation } from '@apollo/client'
import { INVOICE } from '@/fragments/wallet'
import { useApolloClient, useMutation } from '@apollo/client'
import { CANCEL_INVOICE, INVOICE } from '@/fragments/wallet'
import Invoice from '@/components/invoice'
import { useShowModal } from './modal'
import { InvoiceCanceledError, InvoiceExpiredError } from '@/wallets/errors'
@ -10,13 +10,7 @@ export const useInvoice = () => {
const client = useApolloClient()
const [retryPaidAction] = useMutation(RETRY_PAID_ACTION)
const [cancelInvoice] = useMutation(gql`
mutation cancelInvoice($hash: String!, $hmac: String!) {
cancelInvoice(hash: $hash, hmac: $hmac) {
id
}
}
`)
const [cancelInvoice] = useMutation(CANCEL_INVOICE)
const isInvoice = useCallback(async ({ id }, that) => {
const { data, error } = await client.query({ query: INVOICE, fetchPolicy: 'network-only', variables: { id } })
@ -40,7 +34,7 @@ export const useInvoice = () => {
client.writeQuery({ query: INVOICE, variables: { id }, data: { invoice: data.invoice } })
}
return that(data.invoice)
return { invoice: data.invoice, check: that(data.invoice) }
}, [client])
const cancel = useCallback(async ({ hash, hmac }) => {
@ -49,8 +43,8 @@ export const useInvoice = () => {
}
console.log('canceling invoice:', hash)
const inv = await cancelInvoice({ variables: { hash, hmac } })
return inv
const { data } = await cancelInvoice({ variables: { hash, hmac } })
return data.cancelInvoice
}, [cancelInvoice])
const retry = useCallback(async ({ id, hash, hmac }) => {
@ -83,8 +77,8 @@ export const useQrPayment = () => {
let paid
const cancelAndReject = async (onClose) => {
if (!paid && cancelOnClose) {
await invoice.cancel(inv).catch(console.error)
reject(new InvoiceCanceledError(inv))
const updatedInv = await invoice.cancel(inv).catch(console.error)
reject(new InvoiceCanceledError(updatedInv))
}
resolve(inv)
}
@ -99,7 +93,7 @@ export const useQrPayment = () => {
waitFor={waitFor}
onExpired={inv => reject(new InvoiceExpiredError(inv))}
onCanceled={inv => { onClose(); reject(new InvoiceCanceledError(inv, inv?.actionError)) }}
onPayment={() => { paid = true; onClose(); resolve(inv) }}
onPayment={(inv) => { paid = true; onClose(); resolve(inv) }}
poll
/>,
{ keepOpen, persistOnNavigate, onClose: cancelAndReject })

View File

@ -98,26 +98,30 @@ export function usePaidMutation (mutation,
error: e instanceof InvoiceCanceledError && e.actionError ? e : undefined
})
const dataKey = Object.keys(data)[0]
// should we wait for the invoice to be paid?
if (response?.paymentMethod === 'OPTIMISTIC' && !forceWaitForPayment) {
// onCompleted is called before the invoice is paid for optimistic updates
ourOnCompleted?.(data)
// don't wait to pay the invoice
waitForPayment(invoice, { persistOnNavigate, waitFor }).then(() => {
waitForPayment(invoice, { persistOnNavigate, waitFor }).then((invoice) => {
// invoice might have been retried during payment
data = {
[dataKey]: {
...data[dataKey],
invoice
}
}
onPaid?.(client.cache, { data })
}).catch(e => {
console.error('usePaidMutation: failed to pay invoice', e)
if (e.invoice) {
// update the failed invoice for the Apollo cache update
data = {
[Object.keys(data)[0]]: {
...data,
invoice: {
...e.invoice,
actionState: 'FAILED',
cancelled: true,
cancelledAt: new Date()
}
[dataKey]: {
...data[dataKey],
invoice
}
}
}
@ -138,7 +142,7 @@ export function usePaidMutation (mutation,
// create new data object
// ( hmac is only returned on invoice creation so we need to add it back to the data )
data = {
[Object.keys(data)[0]]: {
[dataKey]: {
...paidAction,
invoice: { ...paidAction.invoice, hmac: invoice.hmac }
}

View File

@ -221,3 +221,12 @@ export const SET_WALLET_PRIORITY = gql`
setWalletPriority(id: $id, priority: $priority)
}
`
export const CANCEL_INVOICE = gql`
${INVOICE_FIELDS}
mutation cancelInvoice($hash: String!, $hmac: String!) {
cancelInvoice(hash: $hash, hmac: $hmac) {
...InvoiceFields
}
}
`

View File

@ -113,11 +113,12 @@ const invoiceController = (inv, isInvoice) => {
const signal = controller.signal
controller.wait = async (waitFor = inv => inv?.actionState === 'PAID') => {
return await new Promise((resolve, reject) => {
let updatedInvoice, paid
const interval = setInterval(async () => {
try {
const paid = await isInvoice(inv, waitFor)
({ invoice: updatedInvoice, check: paid } = await isInvoice(inv, waitFor))
if (paid) {
resolve(inv)
resolve(updatedInvoice)
clearInterval(interval)
signal.removeEventListener('abort', abort)
} else {
@ -132,7 +133,7 @@ const invoiceController = (inv, isInvoice) => {
const abort = () => {
console.info(`invoice #${inv.id}: stopped waiting`)
resolve(inv)
resolve(updatedInvoice)
clearInterval(interval)
signal.removeEventListener('abort', abort)
}