How to build a simple LAPP(lightning-app) using BTCPay/c-lightning/lightning-charge

> Dear Reader, This is my first Medium post EVER. I finally built up the courage to write something, and share it with anyone willing to read. If you find parts of this article confusing, misleading or outright incorrect please feel free to shout at me in the comment section. :)

I’ve written this blog post because I spent the past 2 weeks trying to build a simple lightning-app, and found the entire process to be wildly confusing. In this post I will attempt to share my findings and hopefully encourage fellow developers. Please keep I am not an expert, and my methods may not be the best.

We’ll be using BTCPay to setup and configure Bitcoin and Lightning(c-lightning). Then we’ll use lightning-charge server to expose a simple HTTP REST API to our lightning node. We’ll then use lightning-charge-client to create lightning invoices through our web application.

At a high level, here’s the stack we’ll be working with:

  • Backend:Express
  • Front-end:React
  • Bitcoin:Bitcoin
  • BTCPay:BTCPay
  • Lightning-libraries: lightning-charge and lightning-charge-client-js
  • Lightning-Implementation: c-lightning (maybe, i’ll write about lnd in another post)
  • Coffee?:Yes Please
Image for post
Image for post

Before we begin, some story telling :)

I began attempting to build a lightning-app… Here’s was my initial thought process:

First, I’ll want to have Bitcoin installed, configured and synced up to chain-tip on a machine of my choosing.

Then, I’ll want to have a Lightning implementation installed and configured. There are 2 major lightning implementations:

  1. LND(
  2. c-lightning(

Then I’ll want to create a progressive web-application. I’ll use express as back-end/server and react as front-end framework.

Once I have lightning installed and configured I can begin writing custom Javascript code to create invoices. Then I’ll need webhooks that tell my application back-end when invoices are paid out in full so that the application perform some sort of action (maybe inform the user or grant access to something).

At this point, it became evident there were 2 major paths: write custom JS code that communicates with my lightning-node or use a library. Writing custom JS code seemed like a fun way to get my hands dirty, but I had no idea how to grant access/allow my web-app to communicate directly with my remote lightning-node. Plus the fact that exposing an HTTP API is dangerous, and I would likely have major leaks/bugs!

So, as any developer, I begin researching implementations and tools. Amongst all the tools, libraries, examples and resource I find, I ended up much more confused than when I began. Thankfully, I joined some relevant slack/communication channels to seek guidance, the Bitcoin/Lightning community really is helpful!

  • LND(
  • Zap(
  • BTCPay(
Image for post
Image for post

Anyways, enough story telling, let’s build something!

Step 1: Set up BTCPay/Bitcoin/Lightning

You’re going to use BTCPay server to deploy and manage your Bitcoin/Lightning implementation. Why? Because BTCPay is awesome! BTCPay makes it really easy to setup a proper and secure Bitcoin/Lightning environment equipped to handle communication with an external application. Alternatively, if you’re a skeptic you can choose to install Bitcoin on your own and then tell BTCPay where your Bitcoin `data-dir` is (same goes for your lightning implementation).

Image for post
Image for post

To install BTCPay you have 3 major options

  1. Manual Install (not recommended, and really complicated)
  2. Manual Docker Deployment (my favorite, relatively easy)
  3. One-Click Azure Deployment (I personally dislike azure ui)
  4. One-Click Lunanode Deployment(accepts Bitcoin!)

In this article, we’ll discuss the 2nd option Manual Docker Deployment. Here’s the link for the github repo (

I’ll give a small intro on how to install BTCPay manually through docker, but you really should just follow a proper guide like this one → click me!

First, you’ll want to setup a VPS on a hosting platform of your choosing. Once your server is up and running, follow these instructions (from btcpayserver-docker documentation)

# Login as root
sudo su -
# Create a folder for BTCPay
mkdir BTCPayServer
cd BTCPayServer
# Clone this repository
git clone
cd btcpayserver-docker
# Run with the right parameters
export BTCPAY_HOST=””
export NBITCOIN_NETWORK=”mainnet”
export BTCPAYGEN_CRYPTO1=”btc”
export BTCPAYGEN_LIGHTNING=”clightning”
. ./ -i

Now your BTCPay will begin downloading Bitcoin mainnet blocks, this will take around ~1 day.

We wait…

Image for post
Image for post

Now with your BTCPay server setup and configured, you’ll want to make sure that you configured your Lightning implementation correctly, if you’re confused check the official btcpay docs.

In the services section you want to make sure that c-lightning charge-server appears.

Image for post
Image for post
We’re going to be using lightning charge-server

Start building web application

Now let’s move on to our web application. Let’s make a directory to hold our application, then create an express backend using express generator and a react front-end using react-create-app. If you have your own methods, ignore this.

mkdir myawesomeappcd myawesomeappexpress back-endnpx create-react-app front-end

Next, we’ll install the required npm packages.

| Front-End   |        Back-End         || ----------- | :---------------------: || axios       | lightning-charge-client || qrcode      |          cors           || material-ui |                         |

Now, we’ll set up our lightning-charge-client. In this step you’ll need to grab the credentials from your btcpay server. You’ll access this in server settings → services → C-Lightning (Charge server) → see information.

Image for post
Image for post

In our back-end we’ll set up lightning-charge-client using credentials from above, like this

// Initialize the client
const ChargeClient = require(“lightning-charge-client”);
// new is optional
const ln_charge = new ChargeClient(

For this basic example, we’ll create 2 simple routes. 1 route will create an Invoice, the other will wait until that invoice is payed. I’ve kept them simple, but you can imaging passing data into the Invoice and adjusting the satoshis based on your lApps need.

The first back-end route will simply call lightning-charge-client to create an invoice with given parameters“/createInvoice”, async (req, res, next) => {
let invoice = await ln_charge.invoice({
msatoshi: 10000,
description: `My awesome lApp`
return res.json({ data: invoice });

The second back-end route will wait until that invoice is payed and eventually return a payload

router.get(“/fetchInvoice/:id/wait”, async (req, res, next) => {
// Long poll payment updates for a specific invoice
let invoice =;
const paid = null;
do {
const paid = await ln_charge.wait(
/* timeout: */ 600 /* seconds */
if (paid) return res.json({ success: true, data: invoice });
else if (paid === false)
console.log(“invoice expired and can no longer be paid”);
else if (paid === null)
console.log(“timeout reached without payment”);
} while (paid === null);

I hope that you’re already thinking about the possibilities!

Image for post
Image for post

Now in our front end, we’re going to make a button that when clicked will request an invoice from our back-end, pass the invoice payload into a QR code, display a popup, and lastly wait until invoice is paid.

Here we request an invoice from our back-end then render a modal, you can imagine sending data in the post and creating a custom-invoice as per your needs.

Note: using react-hooks

First, let’s make a button that when clicked requests our back-end to create a lightning invoice

async function onButtonClick() { 
let invoice =
const BOLT11 = “lightning:” +;

Then we’ll pass the invoice data over to a QR

async function loadQr(BOLT11) {
const qrCode =
await qrcode.toDataURL(BOLT11, {margin: 2, width: 500});

Lastly, wait until that invoice is paid in full, and return some action to user.

async function waitForPayment(invoiceId) {
let payment = await axios.get(
return payment ? paymentSuccess() : waitForPayment(invoiceId);

That’s all! You’ve just built a basic lightning-app using BTCPay/c-lightning/lightning-charge. PLEASE keep in mind I kept this simple for demonstration purposes only. I hope you enjoyed reading!

Here’s the finished product!

Image for post
Image for post

Full code is posted on Github here →



Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store