Alby JS SDK
Introduction
This JavaScript SDK for the Alby OAuth2 Wallet API and the Nostr Wallet Connect API.
Installing
npm install @getalby/sdk
or
yarn add @getalby/sdk
or for use without any build tools:
<script type="module">
import { nwc, webln } from "https://esm.sh/@getalby/sdk@3.7.0";
</script>
NodeJS
This library relies on a global fetch() function which will work in browsers and node v18.x or newer. (In older versions you have to use a polyfill.)
Content
Nostr Wallet Connect Documentation
Nostr Wallet Connect is an open protocol enabling applications to interact with bitcoin lightning wallets. It allows users to connect their existing wallets to your application allowing developers to easily integrate bitcoin lightning functionality.
The Alby JS SDK allows you to easily integrate Nostr Wallet Connect into any JavaScript based application.
There are two interfaces you can use to access NWC:
- The
NWCClient
exposes the NWC interface directly, which is more powerful than the WebLN interface and is recommended if you plan to create an application outside of the web (e.g. native mobile/command line/server backend etc.). You can explore all the examples here. - The
NostrWebLNProvider
exposes the WebLN interface to execute lightning wallet functionality through Nostr Wallet Connect, such as sending payments, making invoices and getting the node balance. You can explore all the examples here. See also Bitcoin Connect if you are developing a frontend web application.
NWCClient
Initialization Options
providerName
: name of the provider to load the default options. currently alby
(default)nostrWalletConnectUrl
: full Nostr Wallet Connect URL as defined by the specrelayUrl
: URL of the Nostr relay to be used (e.g. wss://nostr-relay.getalby.com)walletPubkey
: pubkey of the Nostr Wallet Connect appsecret
: secret key to sign the request event (if not available window.nostr will be used)authorizationUrl
: URL to the NWC interface for the user to and the app connection
Quick start example
import { nwc } from "@getalby/sdk";
const nwc = new nwc.NWCClient({
nostrWalletConnectUrl: loadNWCUrl(),
});
const response = await nwc.payInvoice({ invoice });
See the NWC client examples directory for a full list of examples.
NostrWebLNProvider (aliased as NWC) Options
providerName
: name of the provider to load the default options. currently alby
(default)nostrWalletConnectUrl
: full Nostr Wallet Connect URL as defined by the specrelayUrl
: URL of the Nostr relay to be used (e.g. wss://nostr-relay.getalby.com)walletPubkey
: pubkey of the Nostr Wallet Connect appsecret
: secret key to sign the request event (if not available window.nostr will be used)authorizationUrl
: URL to the NWC interface for the user to and the app connection
Quick start example
import { webln } from "@getalby/sdk";
const nwc = new webln.NostrWebLNProvider({
nostrWalletConnectUrl: loadNWCUrl(),
});
const nwc = new webln.NWC({ nostrWalletConnectUrl: loadNWCUrl });
await nwc.enable();
const response = await nwc.sendPayment(invoice);
You can use NWC as a webln compatible object in your web app:
if (!window.webln) {
window.webln = new webln.NostrWebLNProvider({
nostrWalletConnectUrl: loadNWCUrl,
});
}
NostrWebLNProvider Functions
The goal of the Nostr Wallet Connect provider is to be API compatible with webln. Currently not all methods are supported - see the examples/nwc directory for a list of supported methods.
static withNewSecret()
Initialized a new NostrWebLNProvider
instance but generates a new random secret. The pubkey of that secret then needs to be authorized by the user (this can be initiated by redirecting the user to the getAuthorizationUrl()
URL or calling initNWC()
to open an authorization popup.
Example
const nwc = NostrWebLNProvider.withNewSecret();
await nwc.initNWC();
sendPayment(invice: string)
Takes a bolt11 invoice and calls the NWC pay_invoice
function.
It returns a promise object that is resolved with an object with the preimage or is rejected with an error
Example
const nwc = new NostrWebLNProvider({ nostrWalletConnectUrl: loadNWCUrl });
await nwc.enable();
const response = await nwc.sendPayment(invoice);
console.log(response);
getNostrWalletConnectUrl()
Returns the nostr+walletconnect://
URL which includes all the connection information (walletPubkey
, relayUrl
, secret
)
This can be used to get and persist the string for later use.
initNWC({name: string})
Opens a new window prompt with the getAuthorizationUrl()
(the user's NWC UI) to ask the user to authorize the app connection.
The promise resolves when the connection is authorized and the popup sends a nwc:success
message or rejects when the prompt is closed.
Pass a name
to the NWC provider describing the application.
const nwc = NostrWebLNProvider.withNewSecret();
try {
await nwc.initNWC({name: 'ACME app' );
} catch(e) {
console.warn("Prompt closed");
}
await nwc.enable();
let response;
try {
response = await nwc.sendPayment(invoice);
console.info(`payment successful, the preimage is ${response.preimage}`);
}
catch (e) {
console.error(e.error || e);
}
React Native (Expo)
Look at our NWC React Native Expo Demo app for how to use NWC in a React Native expo project.
For Node.js
To use this on Node.js you first must install websocket-polyfill
and import it:
import "websocket-polyfill";
if you get an crypto is not defined
error, either upgrade to node.js 20 or above, or import it manually:
import * as crypto from 'crypto';
globalThis.crypto = crypto as any;
Examples
Defaults
import { NostrWebLNProvider } from "@getalby/sdk";
const webln = new NostrWebLNProvider();
await webln.enable();
const response = await webln.sendPayment(invoice);
console.log(response.preimage);
webln.close();
Use a custom, user provided Nostr Wallet Connect URL
import { NostrWebLNProvider } from '@getalby/sdk';
const webln = new NostrWebLNProvider({ nostrWalletConnectUrl: 'nostr+walletconnect://69effe7b49a6dd5cf525bd0905917a5005ffe480b58eeb8e861418cf3ae760d9?relay=wss://nostr.bitcoiner.social&secret=c60320b3ecb6c15557510d1518ef41194e9f9337c82621ddef3f979f668bfebd');
await webln.enable();
const response = await webln.sendPayment(invoice);
console.log(response.preimage);
webln.close();
Generate a new NWC connect url using a locally-generated secret
const webln = webln.NostrWebLNProvider.withNewSecret();
webln.getConnectUrl({ name: `My app name` });
webln.getConnectUrl({
name: `My app name`,
returnTo: document.location.toString(),
});
await webln.initNWC("alby", {
name: `My app name`,
});
OAuth API Documentation
Please have a look a the Alby OAuth2 Wallet API:
https://guides.getalby.com/alby-wallet-api/reference/getting-started
Avalilable methods
- accountBalance
- accountSummary
- accountInformation
- accountValue4Value
- invoices
- incomingInvoices
- outgoingInvoices
- getInvoice
- createInvoice
- decodeInvoice
- keysend
- sendPayment
- sendBoostagram
- sendBoostagramToAlbyAccount
- createWebhookEndpoint
- deleteWebhookEndpoint
Examples
Full OAuth Authentication flow
const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
callback: "http://localhost:8080/callback",
scopes: [
"invoices:read",
"account:read",
"balance:read",
"invoices:create",
"invoices:read",
"payments:send",
],
token: {
access_token: undefined,
refresh_token: undefined,
expires_at: undefined,
},
});
const authUrl = await authClient.generateAuthURL({
code_challenge_method: "S256",
});
await authClient.requestAccessToken(code);
console.log(authClient.token);
const client = new Client(authClient);
const result = await client.accountBalance();
Initialize a client from existing token details
const token = loadTokenForUser();
const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
callback: "http://localhost:8080/callback",
scopes: [
"invoices:read",
"account:read",
"balance:read",
"invoices:create",
"invoices:read",
"payments:send",
],
token: token,
});
const client = new Client(authClient);
const result = await client.createInvoice({ amount: 1000 });
Handling refresh token
Access tokens do expire. If an access token is about to expire, this library will automatically use a refresh token to retrieve a fresh one. Utilising the tokenRefreshed event is a simple approach to guarantee that you always save the most recent tokens.
If token refresh fails, you can restart the OAuth Authentication flow or log the error by listening for the tokenRefreshFailed event.
(Note: To prevent losing access to the user's token, only initialize one instance of the client per token pair at a time)
const token = loadTokenForUser();
const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
callback: "http://localhost:8080/callback",
scopes: [
"invoices:read",
"account:read",
"balance:read",
"invoices:create",
"invoices:read",
"payments:send",
],
token: token,
});
authClient.on("tokenRefreshed", (tokens) => {
console.log(tokens);
});
authClient.on("tokenRefreshFailed", (error) => {
console.error("Token refresh failed:", error.message);
});
Sending payments
const token = loadTokenForUser();
const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
callback: "http://localhost:8080/callback",
scopes: [
"invoices:read",
"account:read",
"balance:read",
"invoices:create",
"invoices:read",
"payments:send",
],
token: token,
});
const client = new Client(authClient);
await client.sendPayment({ invoice: bolt11 });
await client.keysend({
destination: nodekey,
amount: 10,
memo: memo,
});
Send a boostagram
refer also to the boostagram spec: https://github.com/lightning/blips/blob/master/blip-0010.md
const token = loadTokenForUser();
const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
callback: "http://localhost:8080/callback",
scopes: ["payments:send"],
token: token,
});
const client = new Client(authClient);
await client.sendBoostagram({
recipient: {
address:
"030a58b8653d32b99200a2334cfe913e51dc7d155aa0116c176657a4f1722677a3",
customKey: "696969",
customValue: "bNVHj0WZ0aLPPAesnn9M",
},
amount: 10,
boostagram: {
app_name: "Alby SDK Demo",
value_msat_total: 49960,
value_msat: 2121,
url: "https://feeds.buzzsprout.com/xxx.rss",
podcast: "Podcast title",
action: "boost",
episode: "The episode title",
episode_guid: "Buzzsprout-xxx",
ts: 574,
name: "Podcaster - the recipient name",
sender_name: "Satoshi - the sender/listener name",
},
});
await client.keysend({
destination: nodekey,
amount: 10,
customRecords: {
7629169: JSON.stringify(boostagram),
696969: "user",
},
});
Send multiple boostagrams
You often want to send a boostagram for multiple splits. You can do this with one API call. Simply pass in an array of boostagrams. See example above.
const response = await client.sendBoostagram([
boostagram1,
boostagram2,
boostagram3,
]);
console.log(response.keysends);
response.keysends
is an array of objects that either has an error
key if a payment faild or the keysend
key if everything succeeded.
{
"keysends": [
{
"keysend": {
"amount": 10,
"fee": 0,
"destination": "xx",
"payment_preimage": "xx",
"payment_hash": "xx"
}
},
{
"keysend": {
"amount": 10,
"fee": 0,
"destination": "xxx",
"payment_preimage": "xxx",
"payment_hash": "xxx"
}
}
]
}
Decoding an invoice
For quick invoice decoding without an API request please see Alby's Lightning Tools package.
For more invoice details you can use the Alby Wallet API:
const decodedInvoice = await client.decodeInvoice(paymentRequest);
const {payment_hash, amount, description, ...} = decodedInvoice;
fetch() dependency
This library relies on a global fetch()
function which will only work in browsers and node v18.x or newer. In older versions you can manually install a global fetch option or polyfill if needed.
For example:
import fetch from "cross-fetch";
globalThis.fetch = fetch;
import "cross-fetch/polyfill";
Full usage examples
You can find examples in the examples/ directory.
Need help?
We are happy to help, please contact us or create an issue.
Thanks
The client and the setup is inspired and based on the twitter-api-typescript-sdk.
License
MIT