lnurl-pay
Version:
Client library for lnurl-pay and lightning address
262 lines (261 loc) • 9.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decipherAES = exports.isValidPreimage = exports.getHashFromInvoice = exports.decodeInvoice = exports.sha256 = exports.getJson = exports.isValidAmount = exports.toSats = exports.checkedToSats = exports.isOnionUrl = exports.isUrl = exports.parseLnurlp = exports.isLnurlp = exports.parseLightningAddress = exports.isLightningAddress = exports.isLnurl = exports.parseLnUrl = exports.decodeUrlOrAddress = void 0;
var tslib_1 = require("tslib");
var is_url_1 = tslib_1.__importDefault(require("is-url"));
var bech32_1 = require("bech32");
var axios_1 = tslib_1.__importDefault(require("axios"));
var aes_js_1 = tslib_1.__importDefault(require("aes-js"));
var base64_js_1 = tslib_1.__importDefault(require("base64-js"));
var bolt11 = tslib_1.__importStar(require("bolt11"));
var sha256_js_1 = require("@aws-crypto/sha256-js");
var LNURL_REGEX = /^(?:http.*[&?]lightning=|lightning:)?(lnurl[0-9]{1,}[02-9ac-hj-np-z]+)/;
var LN_ADDRESS_REGEX = /^((?:[^<>()\[\]\\.,;:\s@"]+(?:\.[^<>()\[\]\\.,;:\s@"]+)*)|(?:".+"))@((?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(?:(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var ONION_REGEX = /^(http:\/\/[^/:@]+\.onion(?::\d{1,5})?)(\/.*)?$/;
var LNURLP_REGEX = /^lnurlp:\/\/([\w-]+\.)+[\w-]+(:\d{1,5})?(\/[\w-.\/?%&=]*)?$/;
/**
* Decode a bech32 encoded url (lnurl), lightning address or lnurlp url and return a url
* @method decodeUrlOrAddress
* @param lnUrlOrAddress string to decode
* @return plain url or null if is an invalid url, lightning address or lnurlp
*/
var decodeUrlOrAddress = function (lnUrlOrAddress) {
var bech32Url = (0, exports.parseLnUrl)(lnUrlOrAddress);
if (bech32Url) {
var decoded = bech32_1.bech32.decode(bech32Url, 20000);
return Buffer.from(bech32_1.bech32.fromWords(decoded.words)).toString();
}
var address = (0, exports.parseLightningAddress)(lnUrlOrAddress);
if (address) {
var username = address.username, domain = address.domain;
var protocol = domain.match(/\.onion$/) ? 'http' : 'https';
return "".concat(protocol, "://").concat(domain, "/.well-known/lnurlp/").concat(username);
}
return (0, exports.parseLnurlp)(lnUrlOrAddress);
};
exports.decodeUrlOrAddress = decodeUrlOrAddress;
/**
* Parse an url and return a bech32 encoded url (lnurl)
* @method parseLnUrl
* @param url string to parse
* @return bech32 encoded url (lnurl) or null if is an invalid url
*/
var parseLnUrl = function (url) {
if (!url)
return null;
var result = LNURL_REGEX.exec(url.toLowerCase());
return result ? result[1] : null;
};
exports.parseLnUrl = parseLnUrl;
/**
* Verify if a string is a valid lnurl value
* @method isLnurl
* @param url string to validate
* @return true if is a valid lnurl value
*/
var isLnurl = function (url) {
if (!url)
return false;
return LNURL_REGEX.test(url.toLowerCase());
};
exports.isLnurl = isLnurl;
/**
* Verify if a string is a lightning adress
* @method isLightningAddress
* @param address string to validate
* @return true if is a lightning address
*/
var isLightningAddress = function (address) {
if (!address)
return false;
return LN_ADDRESS_REGEX.test(address);
};
exports.isLightningAddress = isLightningAddress;
/**
* Parse an address and return username and domain
* @method parseLightningAddress
* @param address string to parse
* @return LightningAddress { username, domain }
*/
var parseLightningAddress = function (address) {
if (!address)
return null;
var result = LN_ADDRESS_REGEX.exec(address);
return result ? { username: result[1], domain: result[2] } : null;
};
exports.parseLightningAddress = parseLightningAddress;
/**
* Verify if a string is a lnurlp url
* @method isLnurlp
* @param url string to validate
* @return true if is a lnurlp url
*/
var isLnurlp = function (url) {
if (!url)
return false;
return LNURLP_REGEX.test(url);
};
exports.isLnurlp = isLnurlp;
/**
* Parse a lnurlp url and return an url with the proper protocol
* @method parseLnurlp
* @param url string to parse
* @return url (http or https) or null if is an invalid lnurlp
*/
var parseLnurlp = function (url) {
if (!url)
return null;
var parsedUrl = url.toLowerCase();
if (!LNURLP_REGEX.test(parsedUrl))
return null;
var protocol = parsedUrl.includes('.onion') ? 'http://' : 'https://';
return parsedUrl.replace('lnurlp://', protocol);
};
exports.parseLnurlp = parseLnurlp;
/**
* Verify if a string is an url
* @method isUrl
* @param url string to validate
* @return true if is an url
*/
var isUrl = function (url) {
if (!url)
return false;
try {
return (0, is_url_1.default)(url);
}
catch (_a) {
return false;
}
};
exports.isUrl = isUrl;
/**
* Verify if a string is an onion url
* @method isOnionUrl
* @param url string to validate
* @return true if is an onion url
*/
var isOnionUrl = function (url) {
return (0, exports.isUrl)(url) && ONION_REGEX.test(url.toLowerCase());
};
exports.isOnionUrl = isOnionUrl;
/**
* Parse a number to Satoshis
* @method checkedToSats
* @param value number to parse
* @return Satoshis or null
*/
var checkedToSats = function (value) {
if (value && value >= 0)
return (0, exports.toSats)(value);
return null;
};
exports.checkedToSats = checkedToSats;
/**
* Cast a number to Satoshis type
* @method toSats
* @param value number to cast
* @return Satoshis
*/
var toSats = function (value) {
return value;
};
exports.toSats = toSats;
var isValidAmount = function (_a) {
var amount = _a.amount, min = _a.min, max = _a.max;
var isValid = amount > 0 && amount >= min && amount <= max;
var isFixed = min === max;
return isValid && isFixed ? amount === min : isValid;
};
exports.isValidAmount = isValidAmount;
var getJson = function (_a) { return tslib_1.__awaiter(void 0, [_a], void 0, function (_b) {
var url = _b.url, params = _b.params;
return tslib_1.__generator(this, function (_c) {
return [2 /*return*/, axios_1.default.get(url, { params: params }).then(function (response) {
if (response.data.status === 'ERROR')
throw new Error(response.data.reason + '');
return response.data;
})];
});
}); };
exports.getJson = getJson;
var sha256 = function (data, encoding) {
if (encoding === void 0) { encoding = 'hex'; }
var sha256 = new sha256_js_1.Sha256();
sha256.update(Buffer.from(data, encoding));
return Buffer.from(sha256.digestSync()).toString('hex');
};
exports.sha256 = sha256;
var decodeInvoice = function (invoice) {
if (!invoice)
return null;
try {
var network = undefined;
// hack to support signet invoices, remove when it is supported in bolt11
if (invoice.startsWith('lntbs')) {
network = {
bech32: 'tbs',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0, 1],
};
}
return bolt11.decode(invoice, network);
}
catch (_a) {
return null;
}
};
exports.decodeInvoice = decodeInvoice;
var getHashFromInvoice = function (invoice) {
if (!invoice)
return null;
try {
var decoded = (0, exports.decodeInvoice)(invoice);
if (!decoded || !decoded.tags)
return null;
var hashTag = decoded.tags.find(function (value) { return value.tagName === 'payment_hash'; });
if (!hashTag || !hashTag.data)
return null;
return hashTag.data.toString();
}
catch (_a) {
return null;
}
};
exports.getHashFromInvoice = getHashFromInvoice;
var isValidPreimage = function (_a) {
var invoice = _a.invoice, preimage = _a.preimage;
if (!invoice || !preimage)
return false;
var invoiceHash = (0, exports.getHashFromInvoice)(invoice);
if (!invoiceHash)
return false;
try {
var preimageHash = (0, exports.sha256)(preimage);
return invoiceHash === preimageHash;
}
catch (_b) {
return false;
}
};
exports.isValidPreimage = isValidPreimage;
var decipherAES = function (_a) {
var successAction = _a.successAction, preimage = _a.preimage;
if (successAction.tag !== 'aes' ||
!successAction.iv ||
!successAction.ciphertext ||
!preimage)
return null;
var key = aes_js_1.default.utils.hex.toBytes(preimage);
var iv = base64_js_1.default.toByteArray(successAction.iv);
var ciphertext = base64_js_1.default.toByteArray(successAction.ciphertext);
var cbc = new aes_js_1.default.ModeOfOperation.cbc(key, iv);
var plaintext = cbc.decrypt(ciphertext);
// remove padding
var size = plaintext.length;
var pad = plaintext[size - 1];
plaintext = plaintext.slice(0, size - pad);
return aes_js_1.default.utils.utf8.fromBytes(plaintext);
};
exports.decipherAES = decipherAES;