158 lines
4.4 KiB
JavaScript
158 lines
4.4 KiB
JavaScript
import { getScopes, SCOPE_READ, SCOPE_RECEIVE, SCOPE_WRITE, getWallet, request } from 'wallets/blink/common'
|
|
export * from 'wallets/blink'
|
|
|
|
export async function testSendPayment ({ apiKey, currency }, { logger }) {
|
|
logger.info('trying to fetch ' + currency + ' wallet')
|
|
const scopes = await getScopes(apiKey)
|
|
if (!scopes.includes(SCOPE_READ)) {
|
|
throw new Error('missing READ scope')
|
|
}
|
|
if (!scopes.includes(SCOPE_WRITE)) {
|
|
throw new Error('missing WRITE scope')
|
|
}
|
|
if (scopes.includes(SCOPE_RECEIVE)) {
|
|
throw new Error('RECEIVE scope must not be present')
|
|
}
|
|
|
|
currency = currency ? currency.toUpperCase() : 'BTC'
|
|
await getWallet(apiKey, currency)
|
|
|
|
logger.ok(currency + ' wallet found')
|
|
}
|
|
|
|
export async function sendPayment (bolt11, { apiKey, currency }) {
|
|
const wallet = await getWallet(apiKey, currency)
|
|
return await payInvoice(apiKey, wallet, bolt11)
|
|
}
|
|
|
|
async function payInvoice (authToken, wallet, invoice) {
|
|
const walletId = wallet.id
|
|
const out = await request(authToken, `
|
|
mutation LnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
|
|
lnInvoicePaymentSend(input: $input) {
|
|
status
|
|
errors {
|
|
message
|
|
path
|
|
code
|
|
}
|
|
transaction {
|
|
settlementVia {
|
|
... on SettlementViaIntraLedger {
|
|
preImage
|
|
}
|
|
... on SettlementViaLn {
|
|
preImage
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
{
|
|
input: {
|
|
paymentRequest: invoice,
|
|
walletId
|
|
}
|
|
})
|
|
const status = out.data.lnInvoicePaymentSend.status
|
|
const errors = out.data.lnInvoicePaymentSend.errors
|
|
if (errors && errors.length > 0) {
|
|
throw new Error('failed to pay invoice ' + errors.map(e => e.code + ' ' + e.message).join(', '))
|
|
}
|
|
|
|
// payment was settled immediately
|
|
if (status === 'SUCCESS') {
|
|
const preimage = out.data.lnInvoicePaymentSend.transaction.settlementVia.preImage
|
|
if (!preimage) throw new Error('no preimage')
|
|
return preimage
|
|
}
|
|
|
|
// payment failed immediately
|
|
if (status === 'FAILED') {
|
|
throw new Error('failed to pay invoice')
|
|
}
|
|
|
|
// payment couldn't be settled (or fail) immediately, so we wait for a result
|
|
if (status === 'PENDING') {
|
|
while (true) {
|
|
// at some point it should either be settled or fail on the backend, so the loop will exit
|
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
|
|
const txInfo = await getTxInfo(authToken, wallet, invoice)
|
|
// settled
|
|
if (txInfo.status === 'SUCCESS') {
|
|
if (!txInfo.preImage) throw new Error('no preimage')
|
|
return txInfo.preImage
|
|
}
|
|
// failed
|
|
if (txInfo.status === 'FAILED') {
|
|
throw new Error(txInfo.error || 'failed to pay invoice')
|
|
}
|
|
// still pending
|
|
// retry later
|
|
}
|
|
}
|
|
|
|
// this should never happen
|
|
throw new Error('unexpected error')
|
|
}
|
|
|
|
async function getTxInfo (authToken, wallet, invoice) {
|
|
const walletId = wallet.id
|
|
let out
|
|
try {
|
|
out = await request(authToken, `
|
|
query GetTxInfo($walletId: WalletId!, $paymentRequest: LnPaymentRequest!) {
|
|
me {
|
|
defaultAccount {
|
|
walletById(walletId: $walletId) {
|
|
transactionsByPaymentRequest(paymentRequest: $paymentRequest) {
|
|
status
|
|
direction
|
|
settlementVia {
|
|
... on SettlementViaIntraLedger {
|
|
preImage
|
|
}
|
|
... on SettlementViaLn {
|
|
preImage
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
{
|
|
paymentRequest: invoice,
|
|
walletId
|
|
})
|
|
} catch (e) {
|
|
// something went wrong during the query,
|
|
// maybe the connection was lost, so we just return
|
|
// a pending status, the caller can retry later
|
|
return {
|
|
status: 'PENDING',
|
|
preImage: null,
|
|
error: ''
|
|
}
|
|
}
|
|
const tx = out.data.me.defaultAccount.walletById.transactionsByPaymentRequest.find(t => t.direction === 'SEND')
|
|
if (!tx) {
|
|
// the transaction was not found, something went wrong
|
|
return {
|
|
status: 'FAILED',
|
|
preImage: null,
|
|
error: 'transaction not found'
|
|
}
|
|
}
|
|
const status = tx.status
|
|
const preImage = tx.settlementVia.preImage
|
|
return {
|
|
status,
|
|
preImage,
|
|
error: ''
|
|
}
|
|
}
|