feat-4abranch to see how we can implement this feature.
postInvoice()which will receive the post’s
idfrom the url. It then gets the full post object from the database in order to get the node that should receive the payment. We use the
pubkeystored in the post to retrieve the node’s connection information from our database. Next, it calls the AddInvoice
lndendpoint to create a new invoice for 100 sats. Once the invoice is created, we send the generated payment request and the invoice
hashback to the client. The
hashis what is used to uniquely identify this invoice on the node. We’ll need this later to check if the invoice is paid.
createInvoice()function to the API wrapper on the frontend which is used to make the http request to the backend.
showPaymentRequest()which accepts a post object. It makes the API request to the backend to create the invoice, then sets the state variables with the data returned in the API response. It also sets
truein order to display the payment request on screen. We’ll discuss
pmtErrora little later.
hidePaymentRequest()function is straight-forward. It just resets the state variables and sets
falseto hide the payment request screen.
PostListcomponent to display a newly added
PayModalcomponent, which is shown below.
PayModal. This component will be displayed when
store.showPayModalis true. It overlays the full screen displaying a textbox containing the invoice’s payment request and a message informing the user that we are waiting on them to send the payment. If
pmtSuccessMsghas a value then the message will be displayed instead of the payment request.
VoteButtoncomponent. When the “Upvote” button is clicked, we call the
showPaymentRequest()function on the store instead of the
upvotePost()function. This way, we collect the payment before submitting the upvote.
feat-4bbranch to view the new changes to the code.
upvotePost()route handler to now require the invoice
hashin the request. This change was made because we do not want to allow the user to upvote a post unless we can verify that the invoice identified by the
hashhas been paid already. We use
lnd’s LookupInvoice endpoint to find the invoice using the
hash. The returned invoice has a
settledflag indicating if it has been paid. If it is not paid then we throw an error. Otherwise, we continue to increment the number of votes.
hashover and over again. This can be fixed by storing a list of previously used invoice hashes in our database, then preventing them from being used more than once. We’ll leave fixing this as an exercise for the reader.
NodeManagerclass, we’ve added a
listenForPaymentsfunction. This showcases a powerful feature of
lnd, streaming API endpoints. There are some API endpoints that return a stream of responses instead of a single response. When you subscribe to these streams, the app will hold a connection open to the node and will periodically receive data as events occur in
lnd. In our case here, we are using the SubscribeInvoices streaming endpoint. This endpoint will send a message to us any time an invoice is created or settled (paid). We are only interested in knowing when an invoice is paid, so we check if the
settledflag is true on the incoming invoice. If so, then we grab the
amountfrom the invoice and call
this.emit()with information about the payment.
emit()function is doing here. If you are not familiar with EventEmitters, let’s briefly explain what this is. Similar to how we described
lnd’s streaming endpoints above, NodeJS has a similar concept internally which allows you to create an object that can notify other portions of your app when certain events occur. Those other portions of your app can listen for these events and take action accordingly. There is a great explainer blog post over at freecodecamp.org that goes into much more detail.
NodeManagerclass inherit from
EventEmitterso that our expressjs API server can listen for these
invoicePaidevents and send them down to the web clients as they happen. Let’s look at the code for this next.
nodeManager.on()function adds a listener to execute the
paymentsListenerfunction whenever the
invoicePaidevent is triggered. In
paymentsListener, we simply forward that event to the connected browsers.
onSocketMessagefunction which listened for
postUpdatedmessages. We added another check for
invoicePaidmessages. If we receive one of them, and the
hashof the received payment matches the
hashof the payment request we just paid, then we call the
upvotePost()function in the store.
upvotePost()function to send the invoice’s
hashalong with the post id. Remember we updated the backend to require the
hashto verify that the payment was made. If the upvote is successful, we set
pmtSuccessMsgso that it is displayed to the user in the modal.
upvotePost()function in the API wrapper module to accept the
hashas a parameter and send it to the backend in the http request body.
feat-4cbranch to view the new changes to the code.
onSocketMessage()function to check if the recipient of the payment is the currently connected node. If so, it calls the
_incrementBalance()function. This function just updates the state variable
balance, which will automatically update what is displayed in the Navbar. It also sets a new
makeItRainflag to true for 3 seconds. Let’s see what that will do.
react-confettinpm package. This just displays a specified number of confetti flakes on the screen. We use the
makeItRainflag to determine if we display 1000 or 0. With the change we made to the mobx store, this component will now display confetti on the payment recipient’s browser for 3 seconds after they receive a payment due to their post being upvoted.