Learn JavaScript programming fundamentals and NodeJS development to build practical applications and tools.
Learn to code your first lightning app
- NodeJs >= 8
- LND >= 9
- Express for our webserver
- Pug templates + bootstrap for our frontend
Operating system
$ sudo npm install express-generator -g
$ express --view=pug lnapp
$ cd myapp $ npm install
$ npm start
. ├── app.js ├── bin │ └── www ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views ├── error.pug ├── index.pug └── layout.pug
Polar
Nodemon
$ npm install nodemon -D
"scripts": { "start": "node ./bin/www", "dev": "nodemon ./bin/www" },
$ npm run dev
Connecting to LND
$ npm install ln-service dotenv
LND_GRPC_HOST='' LND_CERT_BASE64='' LND_MACAROON_BASE64=''
require('dotenv').config();
const lnservice = require('ln-service');
router.get('/info', async function(req, res, next) { try { // authenticate with lnd const { lnd } = lnservice.authenticatedLndGrpc({ cert: process.env.LND_CERT_BASE64, macaroon: process.env.LND_MACAROON_BASE64, socket: process.env.LND_GRPC_HOST, }); // get node information const info = await lnservice.getWalletInfo({ lnd }); // display info in json format res.send(` <h1>Node info</h1> <pre>${JSON.stringify(info, null, 2)}</pre> `); next(); } catch (e) { console.log(e); } });
$ npm install uuid
const { v4: uuidv4 } = require('uuid');' class Post { constructor() { this.posts = []; } add({ time, name, content, paid, hash, preimage, request }) { const post = { id: uuidv4(), time: time || new Date(), name, content, paid: paid || false, hash: hash || null, preimage: preimage || null, request: request || null, }; this.posts.push(post); return post; } find(id) { return this.posts.find(p => p.id === id); } findByHash(hash) { return this.posts.find(p => p.hash === hash); } findAll() { return this.posts; } findAllPaid() { return this.posts .filter(p => !!p.paid) .sort((a, b) => b.time - a.time); } paid(hash) { let updatedPost; this.posts = this.posts.map(p => { if (p.hash === hash) { updatedPost = { ...p, paid: true }; return updatedPost; } return p; }); if (updatedPost) { return true; } return false; } } const posts = new Post(); module.exports = posts;
Prepare the view
doctype html html head title= title link(rel='stylesheet', href='https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css') link(rel='stylesheet', href='/stylesheets/style.css') body block content block scripts script(src="https://code.jquery.com/jquery-3.4.1.min.js") script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js") script(src="/javascripts/main.js")
Creating a Post
.collapse#post-form form h2 Escriba un artículo .form-group label(for="name") Nombre input(id="name").form-control .form-group label(for="content") Contenido textarea(id="content").form-control button.btn.btn-primary#send-btn(type="button") Enviar
Javascript in the frontend
const App = { endpoint: 'http://localhost:3000/api', interval: null, }; App.init = () => { $('#post-form').collapse('show'); $('#send-btn').click(App.sendBtn); } App.sendBtn = () => { console.log('!hola'); }; $(() => App.init());
App.sendBtn = async () => { const name = $('#name').val(); const content = $('#content').val(); const response = await App.makeRequest({ api: "post", post: { name, content }, }); if (!response) console.error('Error getting data!'); if (response.success) { // Do something with the response } };
// Print the data that comes from the API to the console console.log(response.data); } };
App.makeRequest = ({api, post}) => { const type = !post ? 'GET' : 'POST'; const data = !!post ? JSON.stringify(post) : null; return $.ajax(`${App.endpoint}/${api}`, { type, data, contentType: 'application/json', dataType: 'json', }); };
Create the API
const express = require('express'); const lnservice = require('ln-service'); const router = express.Router(); const post = require('../models/Post'); router.post('/post', (req, res) => { const { name, content } = req.body; return res.json({ success: true, data: { name, content }, }); }); module.exports = router;
const apiRouter = require('./routes/api'); app.use('/api', apiRouter);
Create the invoice
router.post('/post', async (req, res) => { // Authenticate with lnd const { lnd } = lnservice.authenticatedLndGrpc({ cert: process.env.LND_CERT_BASE64, macaroon: process.env.LND_MACAROON_BASE64, socket: process.env.LND_GRPC_HOST, }); const { name, content } = req.body; try { const invoice = await lnservice.createInvoice({ lnd, tokens: 1000, description: name, }); if (!!invoice) { const p = post.add({ name, content, hash: invoice.id, request: invoice.request, preimage: invoice.secret, }); return res.json({ success: true, data: p, }); } } catch (e) { console.log(e); } });
{ "success":true, "data":{ "id":"0fb1544a-d7f9-487d-961a-d0004ecc515c", "time":"2020-09-02T21:29:53.747Z", "name":"epale", "content":"contenido bla bla", "paid":false, "hash":"0e3c7a1151d8f8f202bc7264db83e554c9009f9bd32c0fcb0412772b310b520e", "preimage":null, }
New invoice view
.collapse#invoice-form form .h2 Pay this invoice .form-group textarea( id="invoice", readonly, rows="5" ).form-control
extends layout block content h1 Lightning App include form.pug include invoice.pug
Modifying App.sendBtn()
App.sendBtn = async () => { const name = $('#name').val(); const content = $('#content').val(); const response = await App.makeRequest({ api: "post", post: { name, content }, }); if (!response) console.error('Error getting data!'); if (response.success) { $('#post-form').collapse('hide'); $('#invoice-form').collapse('show'); $('#invoice').val(response.data.request); } };
Receiving the payment
// require lnservice and our post table const lnservice = require('ln-service'); const post = require('./models/Post'); // authenticate with lnd const { lnd } = lnservice.authenticatedLndGrpc({ cert: process.env.LND_CERT_BASE64, macaroon: process.env.LND_MACAROON_BASE64, socket: process.env.LND_GRPC_HOST, }); // check if the invoice has been paid every time an invoice is updated const subscribeInvoices = async () => { try { const sub = lnservice.subscribeToInvoices({lnd}); sub.on('invoice_updated', async invoice => { if (!invoice.is_confirmed) return; // mark the invoice as 'paid' const paid = post.paid(invoice.id); if (paid) console.log('Invoice paid!'); }); } catch (e) { console.log(e); return e; } }; // start the invoice subscription subscribeInvoices();
router.get('/post/:hash', (req, res) => { const { hash } = req.params;' ```javascript const data = post.findByHash(hash); if (!!data) { return res.json({ success: true, data, }); } else { return res.json({ success: false, data: null, }); } });
App.waitPayment = async (hash) => { const response = await App.makeRequest({ api: `post/${hash}`, }); if (response.success && response.data.paid) { console.log("Payment made"); } };
App.waitPayment = async (hash) => { const response = await App.makeRequest({ api: `post/${hash}`, }); if (response.success && response.data.paid) { clearInterval(App.interval); App.interval = null; $("#invoice-form").collapse("hide"); $("#preimage").text(response.data.preimage); $("#success").collapse("show"); } }; App.sendBtn = async () => { const name = $("#name").val(); const content = $("#content").val(); const response = await App.makeRequest({ api: "post", post: { name, content }, }); if (!response) console.error("Error getting data!"); if (response.success) { $("#post-form").collapse("hide"); $("#invoice-form").collapse("show"); $("#invoice").val(response.data.request); App.interval = setInterval(App.waitPayment, 1000, response.data.hash); } };
.collapse#success h2 Payment successful div Payment proof: #preimage
extends layout block content h1 Lightning App include form.pug include invoice.pug include success.pug
Author
This tutorial has been written by Francisco Calderón
You can say thanks by tipping the professor.
I'm a software engineer, Open source & Bitcoin supporter. I spend my time contributing to open source Bitcoin and Ligthning Network related projects, I want to help make Bitcoin better money and be easy to use for non technical people. I've been working on Lightning Network projects since Q1 2018, currently I'm an independent consultant on Bitcoin/Lightning Network related projects, if you want to talk just DM me.
Credits
This tutorial has been proofread by Asi0Flammeus
Even if this content is in its original language, human review is necessary to ensure its accuracy.
Asi0Flammeus1 464 sats732 satsEvery content on the platform is the result of a collaborative effort: each lesson, translation, and revision is made possible by the work of contributors. For this reason, we are always looking for proofreaders who can review our content in many languages. If you want to participate in the proofreading process, please reach out in our Telegram group and read our tutorial. We remind you that this content is open-source - licensed under CC BY-SA - so it can be freely shared and used, as long as the original source is credited.

