Skip to content

Commit d73198d

Browse files
Greenfield: Node.js example (#1205)
* adding Node.JS example documentation * Minor format updates * Fix links * Add to navigation Co-authored-by: Paul Willen <[email protected]>
1 parent ed30a23 commit d73198d

File tree

2 files changed

+151
-1
lines changed

2 files changed

+151
-1
lines changed

docs/.vuepress/config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ const sidebarDevelopment = [
251251
children: [
252252
[`${baseUrl}/API/Greenfield/v1`, 'Greenfield API v1', { type: 'external' }],
253253
'/BTCPayServer/greenfield-authorization',
254-
'/Development/GreenFieldExample'
254+
'/Development/GreenFieldExample',
255+
'/Development/GreenFieldExample-NodeJS'
255256
]
256257
},
257258
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# GreenField API example with Node.JS
2+
3+
The **[GreenField API](https://docs.btcpayserver.org/API/Greenfield/v1/)** (also available on your instance on `/docs`) allows you to operate BTCPay Server via an easy to use REST API.
4+
5+
Note that you can partially generate clients in the language of your choice by using the [Swagger file](https://docs.btcpayserver.org/API/Greenfield/v1/swagger.json).
6+
7+
In this guide, we will give dome examples how to use the GreenField API with Node.JS.
8+
Make sure that the token you're using has the permissions to execute the request.
9+
10+
## Create a new user
11+
12+
Creating a new user can be done by using [this endpoint](https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Users_CreateUser).
13+
14+
```js
15+
const btcpayserverurl = "https://mainnet.demo.btcpayserver.org"
16+
const apiendpoint = "/api/v1/users"
17+
const token = "APIKEYTOKEN"
18+
const headers = {
19+
"Content-Type": "application/json",
20+
"Authorization": "token " + token
21+
}
22+
const user = {
23+
"email": "[email protected]",
24+
"password": "NOTVERYSECURE",
25+
"isAdministrator": false
26+
}
27+
28+
fetch(btcpayserverurl + apiendpoint, {
29+
method: "POST",
30+
headers: headers,
31+
body: JSON.stringify(user)
32+
})
33+
.then((response) => response.json())
34+
.then((data) => { console.log(data) })
35+
```
36+
37+
## Create a new API key
38+
39+
While we can use basic authentication to access the greenfield API, it is recommended to use API Keys to limit the scope of the credentials.
40+
41+
For example: If we want to [create a new store](https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Stores_CreateStore) we need the `btcpay.store.canmodifystoresettings` permission for the API key.
42+
43+
You can do it through BTCPay Server UI (by browsing `/account/apikeys` of your instance), but let's do it via command line using [this endpoint](https://docs.btcpayserver.org/API/Greenfield/v1/#operation/ApiKeys_CreateApiKey).
44+
45+
```js
46+
const btcpayserverUrl = "https://mainnet.demo.btcpayserver.org"
47+
const apiEndpoint = "/api/v1/api-keys"
48+
const permission = "btcpay.store.canmodifystoresettings"
49+
const token = "APIKEYTOKEN"
50+
const headers = {
51+
"Content-Type": "application/json",
52+
"Authorization": "token " + token
53+
}
54+
const apikey = {
55+
"label": "LABELNAME",
56+
"permissions": [permission]
57+
}
58+
59+
fetch(btcpayserverUrl + apiEndpoint, {
60+
method: "POST",
61+
headers: headers,
62+
body: JSON.stringify(apikey)
63+
})
64+
.then((response) => response.json())
65+
.then((data) => { console.log(data) })
66+
```
67+
68+
## Create a new store
69+
70+
Now, we can use the api key to [create a new store](https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Stores_CreateStore).
71+
72+
```js
73+
const btcpayserverUrl = "https://mainnet.demo.btcpayserver.org"
74+
const apiEndpoint = "/api/v1/stores"
75+
const token = "APIKEYTOKEN"
76+
const headers = {
77+
"Content-Type": "application/json",
78+
"Authorization": "token " + token
79+
}
80+
const store = {
81+
"Name": "STORENAME"
82+
}
83+
84+
fetch(btcpayserverurl + apiendpoint, {
85+
method: "POST",
86+
headers: headers,
87+
body: JSON.stringify(store),
88+
})
89+
.then((response) => response.json())
90+
.then((data) => { console.log(data) })
91+
```
92+
93+
## Webhook implementation with Node.JS + Express
94+
95+
You can use your Node.JS Express web application to receive webhook requests from your BTCPay Server.
96+
97+
First you need a route so that your Node.JS application can receive POST requests.
98+
Based on how you set up the express server this should look somthing like underneath.
99+
100+
```js
101+
app.post('/btcpayserverwebhook', (req, res) => {
102+
//do stuff here
103+
})
104+
```
105+
106+
What's important is that the webhook (as statet in the documentation) delivers an HTTP-Header `BTCPAY-SIG`.
107+
You should in above function compare the `BTCPAY-SIG` with the actual data from the request body (as bytes).
108+
In your app.js (or similar) add following where you include requirements:
109+
110+
```js
111+
const bodyParser = require('body-parser')
112+
```
113+
114+
and add following
115+
116+
```js
117+
app.use(bodyParser.json({
118+
verify: (req, res, buf) => {
119+
req.rawBody = buf
120+
}
121+
}))
122+
```
123+
124+
This makes sure that in req.rawBody the correct content is parsed so that you can compare the hashed req.rawBody with the `BTCPAY-SIG` header value.
125+
126+
Edit your router function like this: (Obviously change `webhookSecret`)
127+
128+
```js
129+
app.post('/btcpayserverwebhook', (req, res) => {
130+
const sigHashAlg = "sha256"
131+
const sigHeaderName = "BTCPAY-SIG"
132+
const webhookSecret = "VERYVERYSECRET"
133+
if (!req.rawBody) {
134+
return next('Request body empty')
135+
}
136+
const sig = Buffer.from(req.get(sigHeaderName) || '', 'utf8')
137+
const hmac = crypto.createHmac(sigHashAlg, webhookSecret)
138+
const digest = Buffer.from(sigHashAlg + '=' + hmac.update(req.rawBody).digest('hex'), 'utf8')
139+
const checksum = Buffer.from(sig, 'utf8')
140+
if (checksum.length !== digest.length || !crypto.timingSafeEqual(digest, checksum)) {
141+
console.log(`Request body digest (${digest}) did not match ${sigHeaderName} (${checksum})`)
142+
return next(`Request body digest (${digest}) did not match ${sigHeaderName} (${checksum})`)
143+
}
144+
else {
145+
// Do More Stuff here
146+
res.status(200).send('Request body was signed')
147+
}
148+
})
149+
```

0 commit comments

Comments
 (0)