Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions src/invoicing.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,18 +255,26 @@ class PurpleInvoiceManager {

// Checks the status of an invoice once. Returns true if paid, false otherwise.
async check_invoice_is_paid(label) {
const params = { label }
const timeout_ms = parseInt(process.env.LN_INVOICE_CHECK_TIMEOUT_MS) || 60000
let timeout_id = null

const timeout_promise = new Promise((resolve) => {
timeout_id = setTimeout(() => {
resolve(undefined)
}, timeout_ms)
})

const waitinvoice_promise = this.ln_rpc({ method: "waitinvoice", params })
.then((res) => res.error ? false : true)
.catch(() => undefined)

try {
const params = { label }
return new Promise(async (resolve, reject) => {
setTimeout(() => {
resolve(undefined)
}, parseInt(process.env.LN_INVOICE_CHECK_TIMEOUT_MS) || 60000)
const res = await this.ln_rpc({ method: "waitinvoice", params })
resolve(res.error ? false : true)
})
}
catch {
return undefined
return await Promise.race([waitinvoice_promise, timeout_promise])
} finally {
if (timeout_id) {
clearTimeout(timeout_id)
}
}
}

Expand Down
26 changes: 26 additions & 0 deletions test/ln_flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,29 @@ test('LN Flow — Background polling processes paid invoices without client chec

t.end();
});

test('LN Flow — check_invoice_is_paid handles LN RPC rejection without unhandled rejection', async (t) => {
const purple_api_controller = await PurpleTestController.new(t);
const manager = purple_api_controller.purple_api.invoice_manager;
manager.ln_rpc = async () => {
// Simulate the production failure mode where a promise rejection reason can be undefined.
throw undefined
}

let unhandledRejectionReceived = false
const handleUnhandledRejection = () => {
unhandledRejectionReceived = true
}
process.on('unhandledRejection', handleUnhandledRejection)

try {
const result = await manager.check_invoice_is_paid('some-label')
await new Promise((resolve) => setImmediate(resolve))
t.same(result, undefined)
t.equal(unhandledRejectionReceived, false)
} finally {
process.removeListener('unhandledRejection', handleUnhandledRejection)
}

t.end()
})