Use invoice.cancelledAt to determine if invoice expired or was canceled by user (#1631)
* Set and resolve invoice.cancelledAt * Don't close modal if invoice expired
This commit is contained in:
parent
96e5c6c51c
commit
923ddb74ca
|
@ -108,6 +108,7 @@ const typeDefs = `
|
|||
bolt11: String!
|
||||
expiresAt: Date!
|
||||
cancelled: Boolean!
|
||||
cancelledAt: Date
|
||||
confirmedAt: Date
|
||||
satsReceived: Int
|
||||
satsRequested: Int!
|
||||
|
|
|
@ -43,7 +43,7 @@ export function CompactLongCountdown (props) {
|
|||
? ` ${props.formatted.hours}:${props.formatted.minutes}:${props.formatted.seconds}`
|
||||
: Number(props.formatted.minutes) > 0
|
||||
? ` ${props.formatted.minutes}:${props.formatted.seconds}`
|
||||
: Number(props.formatted.seconds) > 0
|
||||
: Number(props.formatted.seconds) >= 0
|
||||
? ` ${props.formatted.seconds}s`
|
||||
: ' '}
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { numWithUnits } from '@/lib/format'
|
||||
import AccordianItem from './accordian-item'
|
||||
import Qr, { QrSkeleton } from './qr'
|
||||
|
@ -18,10 +18,9 @@ import { Badge } from 'react-bootstrap'
|
|||
import styles from './invoice.module.css'
|
||||
|
||||
export default function Invoice ({
|
||||
id, query = INVOICE, modal, onPayment, onCanceled, info, successVerb = 'deposited',
|
||||
id, query = INVOICE, modal, onPayment, onExpired, onCanceled, info, successVerb = 'deposited',
|
||||
heldVerb = 'settling', useWallet = true, walletError, poll, waitFor, ...props
|
||||
}) {
|
||||
const [expired, setExpired] = useState(false)
|
||||
const { data, error } = useQuery(query, SSR
|
||||
? {}
|
||||
: {
|
||||
|
@ -33,6 +32,8 @@ export default function Invoice ({
|
|||
|
||||
const invoice = data?.invoice
|
||||
|
||||
const expired = invoice?.cancelledAt && new Date(invoice.expiresAt) < new Date(invoice.cancelledAt)
|
||||
|
||||
useEffect(() => {
|
||||
if (!invoice) {
|
||||
return
|
||||
|
@ -40,11 +41,12 @@ export default function Invoice ({
|
|||
if (waitFor?.(invoice)) {
|
||||
onPayment?.(invoice)
|
||||
}
|
||||
if (invoice.cancelled || invoice.actionError) {
|
||||
if (expired) {
|
||||
onExpired?.(invoice)
|
||||
} else if (invoice.cancelled || invoice.actionError) {
|
||||
onCanceled?.(invoice)
|
||||
}
|
||||
setExpired(new Date(invoice.expiredAt) <= new Date())
|
||||
}, [invoice, onPayment, setExpired])
|
||||
}, [invoice, expired, onExpired, onCanceled, onPayment])
|
||||
|
||||
if (error) {
|
||||
return <div>{error.message}</div>
|
||||
|
@ -78,6 +80,10 @@ export default function Invoice ({
|
|||
</>
|
||||
)
|
||||
useWallet = false
|
||||
} else if (expired) {
|
||||
variant = 'failed'
|
||||
status = 'expired'
|
||||
useWallet = false
|
||||
} else if (invoice.cancelled) {
|
||||
variant = 'failed'
|
||||
status = 'cancelled'
|
||||
|
@ -90,18 +96,10 @@ export default function Invoice ({
|
|||
</div>
|
||||
)
|
||||
useWallet = false
|
||||
} else if (expired) {
|
||||
variant = 'failed'
|
||||
status = 'expired'
|
||||
useWallet = false
|
||||
} else {
|
||||
variant = 'pending'
|
||||
status = (
|
||||
<CompactLongCountdown
|
||||
date={invoice.expiresAt} onComplete={() => {
|
||||
setExpired(true)
|
||||
}}
|
||||
/>
|
||||
<CompactLongCountdown date={invoice.expiresAt} />
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,12 @@ export const useInvoice = () => {
|
|||
throw error
|
||||
}
|
||||
|
||||
const { hash, cancelled, actionError, actionState } = data.invoice
|
||||
const { hash, cancelled, cancelledAt, actionError, actionState, expiresAt } = data.invoice
|
||||
|
||||
const expired = cancelledAt && new Date(expiresAt) < new Date(cancelledAt)
|
||||
if (expired) {
|
||||
throw new InvoiceExpiredError(hash)
|
||||
}
|
||||
|
||||
if (cancelled || actionError) {
|
||||
throw new InvoiceCanceledError(hash, actionError)
|
||||
|
@ -170,6 +175,7 @@ export const useQrPayment = () => {
|
|||
useWallet={false}
|
||||
walletError={walletError}
|
||||
waitFor={waitFor}
|
||||
onExpired={inv => reject(new InvoiceExpiredError(inv?.hash))}
|
||||
onCanceled={inv => { onClose(); reject(new InvoiceCanceledError(inv?.hash, inv?.actionError)) }}
|
||||
onPayment={() => { paid = true; onClose(); resolve() }}
|
||||
poll
|
||||
|
|
|
@ -11,6 +11,7 @@ export const INVOICE_FIELDS = gql`
|
|||
satsRequested
|
||||
satsReceived
|
||||
cancelled
|
||||
cancelledAt
|
||||
confirmedAt
|
||||
expiresAt
|
||||
nostr
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Invoice" ADD COLUMN "cancelledAt" TIMESTAMP(3);
|
||||
|
||||
UPDATE "Invoice" SET "cancelledAt" = "expiresAt" WHERE "cancelled" = true;
|
|
@ -909,6 +909,7 @@ model Invoice {
|
|||
confirmedAt DateTime?
|
||||
confirmedIndex BigInt?
|
||||
cancelled Boolean @default(false)
|
||||
cancelledAt DateTime?
|
||||
msatsRequested BigInt
|
||||
msatsReceived BigInt?
|
||||
desc String?
|
||||
|
|
|
@ -456,7 +456,8 @@ export async function paidActionFailed ({ data: { invoiceId, ...args }, models,
|
|||
await paidActions[dbInvoice.actionType].onFail?.({ invoice: dbInvoice }, { models, tx, lnd })
|
||||
|
||||
return {
|
||||
cancelled: true
|
||||
cancelled: true,
|
||||
cancelledAt: new Date()
|
||||
}
|
||||
},
|
||||
...args
|
||||
|
|
|
@ -182,7 +182,8 @@ export async function checkInvoice ({ data: { hash, invoice }, boss, models, lnd
|
|||
hash: inv.id
|
||||
},
|
||||
data: {
|
||||
cancelled: true
|
||||
cancelled: true,
|
||||
cancelledAt: new Date()
|
||||
}
|
||||
}), { models }
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue