I'm trying to build a crypto withdraw system and need to make sure the system doesn't duplicate, etc. so I've decided to use Agenda to help me out. The problem I've come into is the fact I need to listen for the completion of the job in order to retrieve the transaction ID from the newly-sent transaction. This will only be available after the job is complete and I can't find anything about using a listener for one job.
Here is a section of my code:
let pendingUserWithdrawJob = await Job.count({ name: "withdraw_order", "data.userId": user._id, completed: { $ne: true } });
if (pendingUserWithdrawJob > 0) return 1;
const job = global.agenda.create('withdraw_order', {userId: user._id, recipientAddress: addr, amount, txid: null });
job.save((err) => {
if (err) return false;
return msg.reply('Successfully withdrawn! TXID: ' + job.attrs.data.txid);
});
This is before any kind of checking for completion, and the txid returns null, which is expected.
Here is my agenda define code:
agenda.define('withdraw_order', async function(job, done) {
let result = await paymentProcessor.performWithdraw(job);
if (result.error) return done(result.error);
done();
});
The performWithdraw function in question is:
async performWithdraw(options) {
try {
await this.withdraw(options);
return { success: true };
} catch(e) {
this.reportException(e);
return { error: e };
}
}
this.withdraw:
async withdraw(job) {
// parameters
const userId = job.attrs.data.userId;
const recipientAddress = job.attrs.data.recipientAddress;
const amount = job.attrs.data.amount;
// Validate if user is present
let user = await models.User.findById(userId);
if (!user) throw new Error(`User ${userId} not found`);
await models.User.validateWithdrawAmount(user, amount);
// Step 1: Process transaction
let sendID;
if (job.attrs.sendStepCompleted) {
sendID = job.attrs.txid;
} else {
const sent = await this.pivxClient.send(recipientAddress, amount);
if (sent.error) throw new Error(sent.error);
await models.Job.findOneAndUpdate({ _id: job.attrs._id} , { "data.sendStepCompleted": true, "data.txid": sent.txid });
}
// Step 2: Update user balance
if (!job.attrs.userStepCompleted) {
await models.User.withdraw(user, amount);
await models.Job.findByIdAndUpdate(job.attrs._id, { "data.userStepCompleted": true });
}
// Step 3: Record Transaction
if (!job.attrs.transactionStepCompleted) {
await models.Transaction.create({ userId: userId, withdraw: amount, txid: sendID });
await models.Job.findByIdAndUpdate(job.attrs._id, { "data.transactionStepCompleted": true });
}
return sendID;
}
Apologies for the mess of code in advance, I just wanted to make sure I didn't leave anything out that could be important. How can I listen for the job to be done without using setInterval and checking for transactionStepCompleted?