pago/main.js

168 lines
4.8 KiB
JavaScript

const dotenv = require('dotenv')
const express = require('express')
const cors = require('cors')
const Wallet = require('monero-wallet-rpc-js')
const {createServer} = require('node:http')
const {Server} = require('socket.io')
;(async () => {
const deAtomize = (atomic) => Number(atomic) * 0.000000000001
dotenv.config()
const WALLET_ACCOUNT_INDEX = Number(process.env.WALLET_ACCOUNT_INDEX)
const WALLET_AUTOSAVE_SEC = Number(process.env.WALLET_AUTOSAVE_SEC)
const WALLET_REFRESH_SEC = Number(process.env.WALLET_REFRESH_SEC)
// Load monero wallet
const wallet = await new Wallet({
url: process.env.WALLET_RPC_URI,
filename: process.env.WALLET_FILENAME,
password: process.env.WALLET_PASSWORD,
address: process.env.WALLET_ADDRESS,
viewkey: process.env.WALLET_VIEWKEY,
restore_height: process.env.WALLET_RESTORE_HEIGHT,
})
// Set auto save
if (WALLET_AUTOSAVE_SEC > 0)
setInterval(wallet.store(), WALLET_AUTOSAVE_SEC)
// Set auto refresh
try {
await wallet.rpc('auto_refresh', {
enable:true, period:WALLET_REFRESH_SEC,
})
console.log(`Auto-refreshing wallet every ${process.env.WALLET_REFRESH_SEC} seconds. `)
} catch (err) {
console.error(`Failed to set auto_refresh to ${process.env.WALLET_REFRESH_SEC}: ${err}`)
}
// Confirm sync
try {
console.log(`Wallet synced to block ${await wallet.getHeight()}.`)
} catch (err) {
console.error(`Failed to get blockchain height: ${err}`)
}
// Check accounts, create account if needed
// TODO: Validate that the admin didn't set WALLET_ACCOUNT_INDEX too high
const getAccount = async (i) => {
try {
accounts = (await wallet.getAccounts()).subaddress_accounts
} catch (err) {
console.error(`Failed to get account listing from rpc: ${err}`)
}
account = accounts.find(a => a.account_index == i)
if (account===undefined) {
console.log(`There is no account #${i}. Creating account...`)
try {
const newAccount = await wallet.createAccount()
console.log(`Created account #${newAccount.account_index}.`)
} catch (err) {
console.error(`Failed to create new account: ${err}`)
}
return getAccount(i)
} else {
console.log(`Using account #${i} (balance: ${deAtomize(account.balance)} XMR).`)
return account
}
}; getAccount(WALLET_ACCOUNT_INDEX)
// Server
const app = express()
app.use(express.json())
app.use(cors({
// origin: 'http://example.com',
}))
const server = createServer(app)
const io = new Server(server)
server.listen(80)
// Test websockets
io.on('connection', (socket) => {
console.log('a user connected')
socket.on('disconnect', () => {
console.log('user disconnected')
})
})
// Healthchecks
app.get('/wallet/height', async (req, res) =>
res.send((await wallet.getHeight()).toString())
)
app.get('/wallet/balance', async (req, res) => {
let result; try {
result = await wallet.getBalance({
account_index: WALLET_ACCOUNT_INDEX,
})
} catch (err) {
console.error(`Failed to get balance: ${err}`)
return res.sendStatus(500)
}
return res.send(`${deAtomize(result.balance)} XMR`)
})
// New payment
app.post('/payment', async (req, res) => {
let result; try {
result = await wallet.createAddress({
account_index: 1,
label: req.body.label,
})
} catch (err) {
console.error(`Failed to create address "${req.body.label}": ${err}`)
return res.sendStatus(500)
}
console.log(`Created new payment at subaddress #${result.address_index}`)
return res.redirect(`/payment/${result.address}`)
})
// Check payment
app.get('/payment/:addr', async (req, res) => {
let subaddr_index; try {
subaddr_index = (await wallet.getAddressIndex(req.params.addr)).index.minor
} catch (err) {
console.error(`Failed to get index of subaddress: ${err}`)
return res.sendStatus(500)
}
let transfers; try {
transfers = Object.values(await wallet.getTransfers({
all_accounts: true,
in: true,
out: false,
pending: true,
failed: true,
pool: true,
account_index: WALLET_ACCOUNT_INDEX,
subaddr_indices: [subaddr_index],
})).flat()
} catch (err) {
console.error(`Failed to get transactions for subaddress: ${err}`)
return res.sendStatus(500)
}
return res.json(transfers)
})
// Listen for new transactions
app.get('/new_tx/:txid', async (req, res) => {
// TODO: Retry, sleep, retry, give up
console.log(`New transaction: ${req.params.txid}`)
let result; try {
result = await wallet.getTransfersByTxid({
account_index: WALLET_ACCOUNT_INDEX,
txid: req.params.txid,
})
if (result.transfer===undefined) throw new Error('Multiple transfers?')
} catch (err) {
console.error(`Failed to query transaction: ${err}`)
return res.sendStatus(500)
}
console.log(`Transfer received at ${result.transfer.subaddr_index.minor} for ${deAtomize(result.transfer.amount)} XMR to ${result.transfer.address}`)
// TODO: Send websocket
return res.sendStatus(200)
})
})()