diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index b1a1ac8f..bb590285 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -509,6 +509,7 @@ class Mediator { Action.GENERATE_NEW_HANDSHAKE_NODE, this.generateHandshakeNode ) + this.socket.on('GENERATE_ORDER_ADDRESS', this.generateOrderAddress) this.socket.on(Action.SEND_HANDSHAKE_REQUEST, this.sendHandshakeRequest) this.socket.on( Action.SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG, @@ -745,6 +746,32 @@ class Mediator { } } + /** + * @param {Readonly<{ token: string }>} body + */ + generateOrderAddress = async body => { + try { + const { token } = body + + await throwOnInvalidToken(token) + + await API.Actions.generateOrderAddress(user) + + this.socket.emit('GENERATE_ORDER_ADDRESS', { + ok: true, + msg: null, + origBody: body + }) + } catch (err) { + logger.info(err) + this.socket.emit('GENERATE_ORDER_ADDRESS', { + ok: false, + msg: err.message, + origBody: body + }) + } + } + /** * @param {Readonly<{ recipientPublicKey: string , token: string }>} body */ @@ -861,15 +888,20 @@ class Mediator { } /** - * @param {Readonly<{ uuid: string, recipientPub: string, amount: number, memo: string, token: string }>} reqBody + * @param {Readonly<{ uuid: string, recipientPub: string, amount: number, memo: string, token: string, feeLimit:number }>} reqBody */ sendPayment = async reqBody => { try { - const { recipientPub, amount, memo, token } = reqBody + const { recipientPub, amount, memo, feeLimit, token } = reqBody await throwOnInvalidToken(token) - const preimage = await API.Actions.sendPayment(recipientPub, amount, memo) + const preimage = await API.Actions.sendPayment( + recipientPub, + amount, + memo, + feeLimit + ) this.socket.emit(Action.SEND_PAYMENT, { ok: true, diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 227f0a89..8fbd3521 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -902,11 +902,21 @@ const sendHRWithInitialMsg = async ( * @param {string} to * @param {number} amount * @param {string} memo + * @param {number} feeLimit + * @param {number=} maxParts + * @param {number=} timeoutSeconds * @throws {Error} If no response in less than 20 seconds from the recipient, or * lightning cannot find a route for the payment. * @returns {Promise} The payment's preimage. */ -const sendPayment = async (to, amount, memo) => { +const sendPayment = async ( + to, + amount, + memo, + feeLimit, + maxParts = 3, + timeoutSeconds = 5 +) => { try { const SEA = require('../Mediator').mySEA const getUser = () => require('../Mediator').getUser() @@ -1037,7 +1047,7 @@ const sendPayment = async (to, amount, memo) => { } const { - services: { lightning } + services: { router } } = LightningServices /** @@ -1047,50 +1057,46 @@ const sendPayment = async (to, amount, memo) => { /** * Partial - * https://api.lightning.community/#grpc-response-sendresponse-2 + * https://api.lightning.community/#sendpaymentv2 * @typedef {object} SendResponse - * @prop {string|null} payment_error - * @prop {any[]|null} payment_route + * @prop {string} failure_reason * @prop {string} payment_preimage */ logger.info('Will now send payment through lightning') - const sendPaymentSyncArgs = { + const sendPaymentV2Args = { /** @type {string} */ - payment_request: orderResponse.response + payment_request: orderResponse.response, + max_parts: maxParts, + timeout_seconds: timeoutSeconds, + no_inflight_updates: true, + fee_limit_sat: feeLimit } - /** @type {string} */ - const preimage = await new Promise((resolve, rej) => { - lightning.sendPaymentSync(sendPaymentSyncArgs, ( - /** @type {SendErr=} */ err, - /** @type {SendResponse} */ res - ) => { - if (err) { - rej(new Error(err.details)) - } else if (res) { - if (res.payment_error) { - rej( - new Error( - `sendPaymentSync error response: ${JSON.stringify(res)}` - ) - ) - } else if (!res.payment_route || !res.payment_preimage) { - rej( - new Error( - `sendPaymentSync no payment route response or preimage: ${JSON.stringify( - res - )}` - ) - ) - } else { - resolve(res.payment_preimage) - } + const preimage = await new Promise((res, rej) => { + const sentPaymentStream = router.sendPaymentV2(sendPaymentV2Args) + /** + * @param {SendResponse} response + */ + const dataCB = response => { + logger.info('SendPayment Data:', response) + if (response.failure_reason !== 'FAILURE_REASON_NONE') { + rej(new Error(response.failure_reason)) } else { - rej(new Error('no error or response received from sendPaymentSync')) + res(response.payment_preimage) } - }) + } + sentPaymentStream.on('data', dataCB) + /** + * + * @param {SendErr} err + */ + const errCB = err => { + logger.error('SendPayment Error:', err) + rej(err.details) + } + sentPaymentStream.on('error', errCB) }) if (Utils.successfulHandshakeAlreadyExists(to)) { diff --git a/src/routes.js b/src/routes.js index f36c39e4..70d056bc 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1488,7 +1488,13 @@ module.exports = async ( const sentPayment = router.sendPaymentV2(paymentRequest) sentPayment.on('data', response => { logger.info('SendPayment Data:', response) - res.json(response) + if (response.failure_reason !== 'FAILURE_REASON_NONE') { + res.status(500).json({ + errorMessage: response.failure_reason + }) + } else { + res.json(response) + } }) sentPayment.on('status', status => { @@ -1993,6 +1999,53 @@ module.exports = async ( }) //////////////////////////////////////////////////////////////////////////////// + app.post(`/api/gun/sendpayment`, async (req, res) => { + const { + recipientPub, + amount, + memo, + maxParts, + timeoutSeconds, + feeLimit, + sessionUuid + } = req.body + logger.info('handling spont pay') + if (!feeLimit) { + logger.error( + 'please provide a "feeLimit" to the send spont payment request' + ) + return res.status(500).json({ + errorMessage: + 'please provide a "feeLimit" to the send spont payment request' + }) + } + if (!recipientPub || !amount) { + logger.info( + 'please provide a "recipientPub" and "amount" to the send spont payment request' + ) + return res.status(500).json({ + errorMessage: + 'please provide a "recipientPub" and "amount" to the send spont payment request' + }) + } + try { + const preimage = await GunActions.sendPayment( + recipientPub, + amount, + memo, + feeLimit, + maxParts, + timeoutSeconds + ) + res.json({ preimage, sessionUuid }) + } catch (err) { + logger.info('spont pay err:', err) + return res.status(500).json({ + errorMessage: err.message + }) + } + }) + app.get(`/api/gun/wall/:publicKey?`, async (req, res) => { try { const { page } = req.query