Node.js module to support LNURL offline devices. Provides set of helper functions to verify signed URLs and decrypt XOR-encrypted payloads generated by LNURL offline devices such as the Bleskomat ATM, bleskomat-diy, LNPoS, and LNURLVend.
LNURL offline devices can be grouped by whether they use lnurl-withdraw (LUD-03) or lnurl-pay (LUD-06) - ie. whether they facilitate receiving or sending Lightning payments:
-
Point-of-sale terminals and Vending machines use lnurl-pay with an encrypted payload containing the amount and a random PIN. The PIN and amount are learned by the web server by decrypting the payload using a shared secret with the device. After decryption of the payload, the normal lnurl-pay flow is followed. The PIN is revealed to the customer's mobile wallet app once the associated Lightning invoice has been paid and acts as a proof of payment that is easy and quick to read and verify by a human.
-
ATMs use lnurl-withdraw with a signature to ensure the integrity of the amount and fiat currency symbol. The web server can verify the signature by using a shared secret key with the device. A Lightning payment is made by the server only if the signature can be validated.
Add to your application via npm
:
npm install lnurl-offline
Below is an example decrypting a XOR-encrypted payload generated by an LNPoS or LNURLVend device:
const { xor } = require('lnurl-offline');
const url = require('url');
const exampleUrl = 'http://localhost:3000/lnurlpos/device-id?p=AQhnxmlzUf9K7AWNqCRW3RbzzGjqckZA';
const parsedUrl = url.parse(exampleUrl, true);
const { query } = parsedUrl;
const { p } = query;
const key = Buffer.from('super-secret-key', 'utf8');
const payload = Buffer.from(p, 'base64');
const { pin, amount } = xor.decrypt(key, payload);
console.log({ pin, amount });// pin = 1234, amount = 21
Below is an example HMAC-SHA256 signature check for a signed URL generated by a Bleskomat ATM or DIY device:
const { isValidSignedQuery } = require('lnurl-offline');
const url = require('url');
const apiKey = {
id: '7f6e30c87012dc3b7bf8',
key: '5375baf547756f91225dbfcc40b18e7ddba478c4cdf6f18a668a8efa33a0e3b3',
encoding: 'hex',
};
const exampleUrl = 'http://localhost:3000/lnurl?id=7f6e30c87012dc3b7bf8&n=ac0284a9560f9abe760c&s=bb183dcf8b17fd12641722e718fb75209816a43a60238ecb13db1e4748960dbb&t=w&pn=1000&px=1000&pd=';
const { query } = url.parse(exampleUrl, true);
const key = Buffer.from(apiKey.key, apiKey.encoding);
console.log(isValidSignedQuery(query, key) ? 'OK' : 'INVALID SIGNATURE');
Run automated tests as follows:
npm test
See CHANGELOG.md
This software is MIT licensed:
A short, permissive software license. Basically, you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source. There are many variations of this license in use.