Live status of transaction
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
import { IInvoice } from "../models/invoice/invoice.interface";
|
||||
import { Subscriber } from 'zeromq';
|
||||
import { logger, rpcClient } from "../app";
|
||||
import { logger, rpcClient, socketManager } from "../app";
|
||||
import { invoiceRouter } from "../routes/invoice";
|
||||
import { Invoice } from "../models/invoice/invoice.model";
|
||||
import { PaymentStatus } from "./types";
|
||||
import { CryptoUnits, PaymentStatus } from "./types";
|
||||
import { config } from "../../config";
|
||||
|
||||
export class InvoiceScheduler {
|
||||
private pendingInvoices: IInvoice[];
|
||||
private unconfirmedTranscations: IInvoice[];
|
||||
private knownConfirmations: Map<string, number>; // Invoice id / confirmation cound
|
||||
private sock: Subscriber;
|
||||
|
||||
constructor() {
|
||||
this.unconfirmedTranscations = [];
|
||||
this.pendingInvoices = [];
|
||||
this.knownConfirmations = new Map<string, number>();
|
||||
|
||||
// Get all pending transcations
|
||||
Invoice.find({ status: PaymentStatus.PENDING }).then(invoices => {
|
||||
@@ -44,7 +47,7 @@ export class InvoiceScheduler {
|
||||
logger.info('Now listing for incoming transaction to any invoices ...');
|
||||
for await (const [topic, msg] of this.sock) {
|
||||
const rawtx = msg.toString('hex');
|
||||
logger.debug(`New tx: ${rawtx}`);
|
||||
//logger.debug(`New tx: ${rawtx}`);
|
||||
rpcClient.request('decoderawtransaction', [rawtx], (err, decoded) => {
|
||||
if (err) {
|
||||
logger.error(`Error while decoding raw tx: ${err.message}`);
|
||||
@@ -54,19 +57,29 @@ export class InvoiceScheduler {
|
||||
decoded.result.vout.forEach(output => {
|
||||
// Loop over each output and check if the address of one matches the one of an invoice.
|
||||
this.pendingInvoices.forEach(invoice => {
|
||||
// We found our transaction
|
||||
if (output.scriptPubKey.addresses === undefined) return; // Sometimes (weird) transaction don't have any addresses
|
||||
|
||||
// We found our transaction (https://developer.bitcoin.org/reference/rpc/decoderawtransaction.html)
|
||||
if (output.scriptPubKey.addresses.indexOf(invoice.receiveAddress) !== -1) {
|
||||
invoice.paid += output.value;
|
||||
logger.info(`Transcation for invoice ${invoice.id} received! (${decoded.result.hash})`);
|
||||
|
||||
// Change state in database
|
||||
invoice.status = PaymentStatus.UNCONFIRMED;
|
||||
invoice.transcationHash = decoded.result.txid;
|
||||
invoice.save();
|
||||
|
||||
// Push to array & remove from pending
|
||||
this.unconfirmedTranscations.push(invoice);
|
||||
this.pendingInvoices.splice(this.pendingInvoices.indexOf(invoice), 1);
|
||||
const price = invoice.paymentMethods.find((item) => { return item.method === CryptoUnits.BITCOIN }).amount;
|
||||
if (invoice.paid < price - config.transcations.acceptMargin) {
|
||||
const left = price - output.value;
|
||||
invoice.status = PaymentStatus.PARTIALLY;
|
||||
invoice.save();
|
||||
logger.info(`Transcation for invoice ${invoice.id} received but there are still ${left} BTC missing (${decoded.result.hash})`);
|
||||
} else {
|
||||
invoice.status = PaymentStatus.UNCONFIRMED;
|
||||
invoice.transcationHashes.push(decoded.result.txid);
|
||||
invoice.save();
|
||||
|
||||
// Push to array & remove from pending
|
||||
this.unconfirmedTranscations.push(invoice);
|
||||
this.pendingInvoices.splice(this.pendingInvoices.indexOf(invoice), 1);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -81,22 +94,40 @@ export class InvoiceScheduler {
|
||||
private watchConfirmations() {
|
||||
setInterval(() => {
|
||||
this.unconfirmedTranscations.forEach(invoice => {
|
||||
rpcClient.request('gettransaction', [invoice.transcationHash], (err, message) => {
|
||||
if (err) {
|
||||
logger.error(`Error while fetching confirmation state of ${invoice.transcationHash}: ${err.message}`);
|
||||
return;
|
||||
}
|
||||
if (invoice.transcationHashes.length === 0) return;
|
||||
let trustworthy = true; // Will be true if all transactions are above threshold.
|
||||
|
||||
if (Number(message.result.confirmations) > 2) {
|
||||
logger.info(`Transaction (${invoice.transcationHash}) has reached more then 2 confirmations and can now be trusted!`);
|
||||
invoice.status = PaymentStatus.DONE;
|
||||
invoice.save(); // This will trigger a post save hook that will notify the user.
|
||||
for (let i = 0; i < invoice.transcationHashes.length; i++) {
|
||||
const transcation = invoice.transcationHashes[i];
|
||||
|
||||
rpcClient.request('gettransaction', [transcation], (err, message) => {
|
||||
if (err) {
|
||||
logger.error(`Error while fetching confirmation state of ${transcation}: ${err.message}`);
|
||||
trustworthy = false;
|
||||
|
||||
this.unconfirmedTranscations.splice(this.unconfirmedTranscations.indexOf(invoice), 1);
|
||||
} else {
|
||||
logger.debug(`Transcation (${invoice.transcationHash}) has not reached his threshold yet.`);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.knownConfirmations.get(invoice.id) != message.result.confirmations) {
|
||||
this.knownConfirmations.set(invoice.id, message.result.confirmations);
|
||||
socketManager.getSocketByInvoice(invoice).emit('confirmationUpdate', { count: Number(message.result.confirmations) });
|
||||
}
|
||||
|
||||
if (Number(message.result.confirmations) > 0) {
|
||||
logger.info(`Transaction (${transcation}) has reached more then 2 confirmations and can now be trusted!`);
|
||||
|
||||
this.unconfirmedTranscations.splice(this.unconfirmedTranscations.indexOf(invoice), 1);
|
||||
} else {
|
||||
trustworthy = false;
|
||||
logger.debug(`Transcation (${transcation}) has not reached his threshold yet.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (trustworthy) {
|
||||
invoice.status = PaymentStatus.DONE;
|
||||
invoice.save(); // This will trigger a post save hook that will notify the user.
|
||||
}
|
||||
});
|
||||
}, 2_000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user