Skip to content

Commit

Permalink
Try WebLN provider first
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzyis committed Jan 17, 2024
1 parent 4d6c4a5 commit 2c464f8
Showing 1 changed file with 51 additions and 14 deletions.
65 changes: 51 additions & 14 deletions components/invoice.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useCallback, useEffect } from 'react'
import { useMutation, useQuery } from '@apollo/client'
import { useApolloClient, useMutation, useQuery } from '@apollo/client'
import { Button } from 'react-bootstrap'
import { gql } from 'graphql-tag'
import { numWithUnits } from '../lib/format'
Expand All @@ -12,6 +12,7 @@ import { useShowModal } from './modal'
import Countdown from './countdown'
import PayerData from './payer-data'
import Bolt11Info from './bolt11-info'
import { useWebLN } from './webln'

export function Invoice ({ invoice, modal, onPayment, info, successVerb }) {
const [expired, setExpired] = useState(new Date(invoice.expiredAt) <= new Date())
Expand Down Expand Up @@ -161,6 +162,7 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
mutation createInvoice($amount: Int!) {
createInvoice(amount: $amount, hodlInvoice: true, expireSecs: 180) {
id
bolt11
hash
hmac
expiresAt
Expand All @@ -175,6 +177,9 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
`)

const showModal = useShowModal()
const provider = useWebLN()
const client = useApolloClient()
const pollInvoice = (id) => client.query({ query: INVOICE, variables: { id } })

const onSubmitWrapper = useCallback(async ({ cost, ...formValues }, ...submitArgs) => {
// some actions require a session
Expand Down Expand Up @@ -206,24 +211,13 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
const inv = data.createInvoice

// wait until invoice is paid or modal is closed
let modalClose
await new Promise((resolve, reject) => {
showModal(onClose => {
modalClose = onClose
return (
<JITInvoice
invoice={inv}
onPayment={resolve}
/>
)
}, { keepOpen: true, onClose: reject })
})
const modalClose = await waitForPayment(inv, showModal, provider, pollInvoice)

const retry = () => onSubmit({ hash: inv.hash, hmac: inv.hmac, ...formValues }, ...submitArgs)
// first retry
try {
const ret = await retry()
modalClose()
modalClose?.()
return ret
} catch (error) {
console.error('retry error:', error)
Expand Down Expand Up @@ -255,6 +249,49 @@ export const useInvoiceable = (onSubmit, options = defaultOptions) => {
return onSubmitWrapper
}

const waitForPayment = async (inv, showModal, provider, pollInvoice) => {
try {
// try WebLN provider first
return await new Promise((resolve, reject) => {
// don't use await here since we're using HODL invoices
// and sendPaymentAsync is not supported yet.
// see https://www.webln.guide/building-lightning-apps/webln-reference/webln.sendpaymentasync
provider.sendPayment(inv.bolt11)
const interval = setInterval(async () => {
try {
const { data, error } = await pollInvoice(inv.id)
if (error) {
clearInterval(interval)
return reject(error)
}
const { invoice } = data
if (invoice.isHeld && invoice.satsReceived) {
clearInterval(interval)
resolve()
}
} catch (err) {
clearInterval(interval)
reject(err)
}
}, 1000)
})
} catch (err) {
console.error('WebLN payment failed:', err)
}

// QR code as fallback
return await new Promise((resolve, reject) => {
showModal(onClose => {
return (
<JITInvoice
invoice={inv}
onPayment={() => resolve(onClose)}
/>
)
}, { keepOpen: true, onClose: reject })
})
}

export const useInvoiceModal = (onPayment, deps) => {
const onPaymentMemo = useCallback(onPayment, deps)
return useInvoiceable(onPaymentMemo, { replaceModal: true })
Expand Down

0 comments on commit 2c464f8

Please sign in to comment.