188 lines
5.0 KiB
JavaScript
188 lines
5.0 KiB
JavaScript
import { galoyBlinkUrl } from 'wallets/blink'
|
|
export * from 'wallets/blink'
|
|
|
|
export async function testSendPayment ({ apiKey, currency }, { logger }) {
|
|
currency = currency ? currency.toUpperCase() : 'BTC'
|
|
logger.info('trying to fetch ' + currency + ' wallet')
|
|
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: ''
|
|
}
|
|
}
|
|
|
|
async function getWallet (authToken, currency) {
|
|
const out = await request(authToken, `
|
|
query me {
|
|
me {
|
|
defaultAccount {
|
|
wallets {
|
|
id
|
|
walletCurrency
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`, {})
|
|
const wallets = out.data.me.defaultAccount.wallets
|
|
for (const wallet of wallets) {
|
|
if (wallet.walletCurrency === currency) {
|
|
return wallet
|
|
}
|
|
}
|
|
throw new Error(`wallet ${currency} not found`)
|
|
}
|
|
|
|
async function request (authToken, query, variables = {}) {
|
|
const options = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-API-KEY': authToken
|
|
},
|
|
body: JSON.stringify({ query, variables })
|
|
}
|
|
const res = await fetch(galoyBlinkUrl, options)
|
|
if (res.status >= 400 && res.status <= 599) {
|
|
if (res.status === 401) {
|
|
throw new Error('unauthorized')
|
|
} else {
|
|
throw new Error('API responded with HTTP ' + res.status)
|
|
}
|
|
}
|
|
return res.json()
|
|
}
|