fbpx
Guides

Building a Lightning Network app with the OpenNode API5 min read

October 22, 2020 4 min read

author:

Building a Lightning Network app with the OpenNode API5 min read

Reading Time: 4 minutes

Like physical infrastructure, digital infrastructure requires solid foundations and the right tools. At OpenNode, we believe that our API is the perfect tool for building an optimized payment solution supported by our trusted, reliable platform. With 10 lines of code, our API can be integrated by any business or app to power Bitcoin payments and payouts for any number of use cases. 

In this guide, we will walk through the creation of a simple application powered by our Lightning Network Bitcoin API. By the end of this guide, you will be able to create a public message board where every message published is paid for using Bitcoin over the Lightning Network. Check out what we will end up with: Demo Message Board

Working on Bitcoin’s Testnet

Testing development changes using real money is risky and unnecessary. We strongly encourage you to use the Lightning Network test-net. To easily pay for test-net Lightning invoices, check out the HTLC.me wallet.

The first step is to create an account on OpenNode Dev – https://dev.opennode.co. If you can see the “Dev Environment” tag next to the OpenNode logo you’re in the right place!

Note:

  • API keys generated on a regular OpenNode account will not work on the test-net.
  • To run this app on mainnet instead, create a regular OpenNode account at https://opennode.co/signup and change the API base URL from https://dev-api.opennode.co to https://api.opennode.com

Creating invoices ⚡️

Creating an invoice is Lightning Network lingo for “creating a charge” or “creating a payment request”. First things first, you will need an OpenNode API key. You can get yours at https://dev.opennode.co/settings/api. Click on “Add key” and select the “Invoices” permission-type.

Copy the generated key. Now create a new variable on your index.js file:

const opennodeKey = 'YOUR_API_KEY';

You can start creating Lightning invoices using the OpenNode API now. We want every message in our message board to be paid for using Lightning. To achieve this, we need to edit the app.post(‘/messages’) endpoint. Remove the temporary res.redirect(‘/’); and add the following snippet to create a charge:

axios.post('https://dev-api.opennode.co/v1/charges', {
    amount: 1,
    description: 'New message',
    order_id: id,
  }, { headers: { Authorization: opennodeKey } })
    .then(response => {
      res.render('payment.ejs', { payreq: response.data.data.lightning_invoice.payreq });
    })
    .catch(error => console.log(error.response.data));

This will call the OpenNode API and request a new invoice for 1 satoshi. It will also pass in the created message ID to the order_id field, which we will use to mark the message as paid in a later stage.

Checking for payments 💵

We are programmatically creating Lightning invoices and displaying them to our users. Our users can pay invoices using any Lightning wallet. We now need to check if the payment was made and mark the message as paid in our database. OpenNode’s API can send you Webhooks that inform your server of any updates in any of your invoices. Let’s use that to check for payments!

Our Repl.it instance is open to the internet, which means that the OpenNode API can reach it. Lets create a variable that holds our public Repl URL:

const replUrl = 'https://opennode-workshop--ruigomes.repl.co';

Remember to replace the actual URL with your own 🙂

Now we need to tell OpenNode where to send the webhooks. To do so, let’s edit the axios.post() sent data, and right below order_id: id, add the following:

callback_url: replUrl + '/webhook'

OpenNode will call the callback_url field with any updates that happen with this invoice! We just need to make sure we are listening to these webhooks. To do so, add the following snippet to the index.js file:

app.post('/webhook', (req, res) => {
  console.log('OpenNode Webhook', req.body);

  const status = req.body.status;

  if(status !== 'paid') {
    return res.send('Order not paid');
  }

  db.markMessageAsPaid(req.body.order_id);

  return res.send('Order paid');
});

When OpenNode sends a webhook to this endpoint, we will get the status of that webhook and make sure it is set to paid.

If it is, we will then fetch the order_id which we previously set to our internal message ID and mark that message as paid.

Validating callbacks 🕵️‍♂️

Your app is now handling Lightning invoices, but how can you be sure that it was OpenNode sending you the webhook, and not a malicious third-party?

Inside every webhook, OpenNode sends you a hashed_order field that is hashed using your API key. If you recreate this hashed_order field in your own application and verify that they match, you can be sure that OpenNode sent this webhook!

Within the axios.post(‘webhook’) method, replace:

const status = req.body.status;

const { id, order_id, hashed_order, status } = req.body;

Immediately after that line, add the following:

const ourHashedOrder = crypto
    .createHmac('sha256', opennodeKey)
    .update(id)
    .digest('hex');

  if(hashed_order !== ourHashedOrder) {
    return res.send('Fake callback');
  }

We are recreating the hashed_order by hashing the OpenNode invoice ID with our API key, and then we make sure they match. If they don’t, we return immediately, not marking the order as paid!

And there you have it, you just created a simple app powered by Lightning Network Bitcoin payments! Now that you know how to power an application with our API we encourage you to keep building. Bitcoin is a new technology and the Lightning Network is even newer. The possibilities for new applications are uncharted limitless. 


With OpenNode guides, our goal is to complement developer tools with education and resources for businesses. We’re always looking for new and valuable ways to expand these resources and we’re open to your suggestions. 

Are there guides or resources that you would like to see added to OpenNode guides? Send us an email at hello@opennode.com or send us a direct message on Twitter and we’re happy to hear your ideas!