From d3100a1390830db555658af3f4091946d3fc8dd4 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sun, 6 Jan 2019 14:35:14 +0000 Subject: [PATCH] FIX: occasional duplicate payments (closes #3) --- controllers/api.js | 23 +++++++++++++++++++++++ doc/schema.md | 1 + 2 files changed, 24 insertions(+) diff --git a/controllers/api.js b/controllers/api.js index 3d55f26..ccdfc49 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -113,6 +113,7 @@ router.post('/payinvoice', async function(req, res) { if (!req.body.invoice) return errorBadArguments(res); let freeAmount = false; if (req.body.amount) freeAmount = parseInt(req.body.amount); + const lock_key = 'invoice_paying_for_' + u.getUserId(); let userBalance = await u.getBalance(); @@ -133,6 +134,12 @@ router.post('/payinvoice', async function(req, res) { let userid_payee = await u.getUseridByPaymentHash(info.payment_hash); if (!userid_payee) return errorGeneralServerError(res); + if (await redis.get(lock_key)) { + return errorTryAgainLater(res); + } + await redis.set(lock_key, 1); + await redis.expire(lock_key, 2 * 60); + let UserPayee = new User(redis); UserPayee._userid = userid_payee; // hacky, fixme let payee_balance = await UserPayee.getBalance(); @@ -152,6 +159,7 @@ router.post('/payinvoice', async function(req, res) { await UserPayee.setPaymentHashPaid(info.payment_hash); + await redis.del(lock_key, 1); return res.send(info); } @@ -164,9 +172,11 @@ router.post('/payinvoice', async function(req, res) { payment.pay_req = req.body.invoice; payment.decoded = info; u.savePaidLndInvoice(payment); + redis.del(lock_key); res.send(payment); } else { // payment failed + redis.del(lock_key); return errorLnd(res); } }); @@ -176,6 +186,11 @@ router.post('/payinvoice', async function(req, res) { } let inv = { payment_request: req.body.invoice, amt: info.num_satoshis }; // amt is used only for 'tip' invoices try { + if (await redis.get(lock_key)) { + return errorTryAgainLater(res); + } + await redis.set(lock_key, 1); + await redis.expire(lock_key, 2 * 60); logger.log('/payinvoice', [req.id, 'before write', JSON.stringify(inv)]); call.write(inv); } catch (Err) { @@ -362,3 +377,11 @@ function errorBadArguments(res) { message: 'Bad arguments', }); } + +function errorTryAgainLater(res) { + return res.send({ + error: true, + code: 9, + message: 'Try again later', + }); +} diff --git a/doc/schema.md b/doc/schema.md index 7c645d1..ec5fb10 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -10,6 +10,7 @@ User storage schema * userid_for_{refresh_token} = {userid} * refresh_token_for_{userid} = {access_token} * importing_{txid} = 1 `atomic lock when processing topup tx` +* invoice_paying_for_{userid} = 1 `lock for when payinvoice is in progress`