Skip to content

Commit 8cea5af

Browse files
authored
Create INSTRUCTIONS.md
1 parent 4e6f00e commit 8cea5af

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

INSTRUCTIONS.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
These instructions should be followed **after** you read through the [README](README.md).
2+
3+
## Creating invoices ⚡️
4+
5+
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.
6+
7+
Copy the generated key.
8+
9+
Now create a new variable on your `index.js` file:
10+
11+
```js
12+
const opennodeKey = 'YOUR_API_KEY';
13+
```
14+
15+
You can start creating Lightning invoices using the OpenNode API now.
16+
17+
We want every message in our message board to be paid for using Lightning.
18+
19+
To achieve this, we need to edit the `app.post('/messages')` endpoint.
20+
21+
Remove the temporary `res.redirect('/');` and add the following snippet to create a charge:
22+
23+
```js
24+
axios.post('https://dev-api.opennode.co/v1/charges', {
25+
amount: 1,
26+
description: 'New message',
27+
order_id: id,
28+
}, { headers: { Authorization: opennodeKey } })
29+
.then(response => {
30+
res.render('payment.ejs', { payreq: response.data.data.lightning_invoice.payreq });
31+
})
32+
.catch(error => console.log(error.response.data));
33+
```
34+
35+
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.
36+
37+
## Checking for payments 💵
38+
39+
We are programatically creating Lightning invoices and displaying them to our users. Our users can take that invoice and pay them using any Lightning wallet.
40+
41+
We now need to check if the payment was made and mark the message as paid in our database.
42+
43+
OpenNode's API can send you Webhooks that inform your server of any updates in any of your invoices. Lets use that to check for payments!
44+
45+
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:
46+
47+
`const replUrl = 'https://opennode-workshop--ruigomes.repl.co';`
48+
49+
Remember to replace the actual URL with your own :)
50+
51+
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:
52+
53+
`callback_url: replUrl + '/webhook'`
54+
55+
OpenNode will call the `callback_url` field with any updates that happen with this invoice!
56+
57+
We just need to make sure we are listening to these webhooks. To do so, add the following snippet to the `index.js` file:
58+
59+
60+
```js
61+
62+
app.post('/webhook', (req, res) => {
63+
console.log('OpenNode Webhook', req.body);
64+
65+
const status = req.body.status;
66+
67+
if(status !== 'paid') {
68+
return res.send('Order not paid');
69+
}
70+
71+
db.markMessageAsPaid(req.body.order_id);
72+
73+
return res.send('Order paid');
74+
});
75+
```
76+
77+
When OpenNode sends a webhook to this endpoint, we will get the status of that webhook and make sure it is set to `paid`.
78+
79+
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.
80+
81+
## Validating callbacks 🕵️‍♂️
82+
83+
Your application 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?
84+
85+
Inside every webhook, OpenNode sends you an `hashed_order` field that is hashed using your API key. If you recreate this hashed_order field in your own application and they match, you can be sure that OpenNode sent this webhook!
86+
87+
Within the `axios.post('webhook')` method, replace:
88+
89+
```js
90+
const status = req.body.status;
91+
```
92+
93+
```js
94+
const { id, order_id, hashed_order, status } = req.body;
95+
```
96+
97+
Immediately after that line, add the following:
98+
99+
```js
100+
const ourHashedOrder = crypto
101+
.createHmac('sha256', opennodeKey)
102+
.update(id)
103+
.digest('hex');
104+
105+
if(hashed_order !== ourHashedOrder) {
106+
return res.send('Fake callback');
107+
}
108+
```
109+
110+
We are recreating the hashed_order by hasing the OpenNode invoice ID with our API key, and then we make sure they match.
111+
112+
If they don't, we return immediately, not marking the order as paid!

0 commit comments

Comments
 (0)