Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 80 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ or for use without any build tools:

```html
<script type="module">
import { LightningAddress } from "https://esm.sh/@getalby/lightning-tools@5.0.0"; // jsdelivr.net, skypack.dev also work
import { LightningAddress } from "https://esm.sh/@getalby/lightning-tools@7"; // jsdelivr.net, skypack.dev also work

// use LightningAddress normally...
(async () => {
Expand Down Expand Up @@ -168,6 +168,8 @@ Native zaps without a browser extension are possible by using a Nostr Wallet Con

See [examples/zaps-nwc](examples/zaps-nwc.js)

> All examples in the [examples/](examples/) directory are runnable. See [examples/README.md](examples/README.md) for setup instructions.

### L402

L402 is a protocol standard based on the HTTP 402 Payment Required error code
Expand All @@ -181,19 +183,18 @@ This library includes a `fetchWithL402` function to consume L402 protected resou
- url: the L402 protected URL
- fetchArgs: arguments are passed to the underlying `fetch()` function used to do the HTTP request
- options:
- wallet: any object that implements `sendPayment(paymentRequest)` and returns `{ preimage }`. Used to pay the L402 invoice.
- wallet: any object (e.g. a NWC client) that implements `payInvoice({ invoice })` and returns `{ preimage }`. Used to pay the L402 invoice.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is what?

- store: a key/value store object to persiste the l402 for each URL. The store must implement a `getItem()`/`setItem()` function as the browser's localStorage. By default a memory storage is used.
- headerKey: defaults to L402 but if you need to consume an old LSAT API set this to LSAT

##### Examples

```js
import { fetchWithL402 } from "@getalby/lightning-tools/l402";

// pass a wallet that implements sendPayment()
// pass a wallet that implements payInvoice()
// the tokens/preimage data will be stored in the browser's localStorage and used for any following request
await fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
"https://l402.example.com/protected-resource",
{},
{ wallet: myWallet, store: window.localStorage },
)
Expand All @@ -203,32 +204,94 @@ await fetchWithL402(

```js
import { fetchWithL402 } from "@getalby/lightning-tools/l402";
import { NostrWebLNProvider } from "@getalby/sdk";
import { NWCClient } from "@getalby/sdk";

// use a NWC provider as the wallet to do the payments
const nwc = new NostrWebLNProvider({
nostrWalletConnectUrl: loadNWCUrl(),
// use a NWC client as the wallet to do the payments
const nwc = new NWCClient({
nostrWalletConnectUrl: "nostr+walletconnect://...",
});

// this will fetch the resource and pay the invoice using the NWC wallet
await fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
"https://l402.example.com/protected-resource",
{},
{ wallet: nwc },
)
.then((res) => res.json())
.then(console.log);
.then(console.log)
.finally(() => nwc.close());
```

### X402

Similar to L402 X402 is an open protocol for machine-to-machine payments built on the HTTP 402 Payment Required status code.
It enables APIs and resources to request payments inline, without prior registration or authentication.

This library includes a `fetchWithX402` function to consume X402-protected resources that support the lightning network.
(Note: X402 works also with other coins and network. This library supports X402 resources that accept Bitcoin on the lightning network)

#### fetchWithX402(url: string, fetchArgs, options)

- url: the X402 protected URL
- fetchArgs: arguments are passed to the underlying `fetch()` function used to do the HTTP request
- options:
- wallet: any object (e.g. a NWC client) that implements `payInvoice({ invoice })` and returns `{ preimage }`. Used to pay the X402 invoice.
- store: a key/value store object to persist the payment proof for each URL. The store must implement a `getItem()`/`setItem()` function as the browser's localStorage. By default a memory storage is used.

##### Examples

```js
import { fetchWithL402, NoStorage } from "@getalby/lightning-tools/l402";
import { fetchWithX402 } from "@getalby/lightning-tools/x402";
import { NWCClient } from "@getalby/sdk";

// do not store the tokens
await fetchWithL402(
"https://lsat-weather-api.getalby.repl.co/kigali",
// use a NWC client as the wallet to do the payments
const nwc = new NWCClient({
nostrWalletConnectUrl: "nostr+walletconnect://...",
});

// the payment proof will not be stored by default. to reuse the proofs for subsequent requests provide a storage
await fetchWithX402(
"https://x402.example.com/protected-resource",
{},
{ wallet: nwc, store: window.localStorage },
)
.then((res) => res.json())
.then(console.log)
.finally(() => nwc.close());
```

### fetch402

`fetch402` is a single function that transparently handles both L402 and X402 protected resources. Use it when you don't know or don't care which protocol the server uses — it will detect the protocol from the response headers and pay accordingly.

#### fetch402(url: string, fetchArgs, options)

- url: the protected URL
- fetchArgs: arguments are passed to the underlying `fetch()` function used to do the HTTP request
- options:
- wallet: any object that implements `payInvoice({ invoice })` and returns `{ preimage }`. Used to pay L402 and X402 invoices.
- store: a key/value store object to persist the payment proof for each URL. The store must implement a `getItem()`/`setItem()` function as the browser's localStorage. By default no storage is used - pass `window.localStorage` or a similar store to enable caching.

##### Examples


```js
import { fetch402 } from "@getalby/lightning-tools/402";
import { NWCClient } from "@getalby/sdk";

const nwc = new NWCClient({
nostrWalletConnectUrl: "nostr+walletconnect://...",
});

// use a NWC wallet — works for both L402 and X402
await fetch402(
"https://example.com/protected-resource",
{},
{ store: new NoStorage() },
);
{ wallet: nwc, store: window.localStorage },
)
.then((res) => res.json())
.then(console.log)
.finally(() => nwc.close());
```

### Basic invoice decoding
Expand Down
21 changes: 21 additions & 0 deletions examples/402.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { fetch402 } from "@getalby/lightning-tools/402";
import { NWCClient } from "@getalby/sdk";

// fetch402 works with both L402 and X402 endpoints —
// it detects the protocol from the server's response headers automatically.
const url = process.env.URL || "https://x402.albylabs.com/demo/quote";

const nostrWalletConnectUrl = process.env.NWC_URL;

if (!nostrWalletConnectUrl) {
throw new Error("Please set a NWC_URL env variable");
}

const nwc = new NWCClient({ nostrWalletConnectUrl });

fetch402(url, {}, { wallet: nwc })
.then((response) => response.json())
.then((data) => {
console.info(data);
})
.finally(() => nwc.close());
52 changes: 52 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Examples

Runnable examples for `@getalby/lightning-tools`.

## Setup

```bash
cd examples
yarn
```

> `yarn` links the local package via `file:..` — if you change library source, rebuild first:
>
> ```bash
> cd .. && yarn build && cd examples
> ```

## Running examples

### x402 / L402 — paid API fetch

These examples fetch a URL protected by an HTTP 402 payment wall. You need an [NWC](https://www.nwc.dev) connection string.

```bash
NWC_URL="nostr+walletconnect://..." yarn 402 # auto-detects L402 or x402
NWC_URL="nostr+walletconnect://..." yarn x402 # x402 only
NWC_URL="nostr+walletconnect://..." yarn l402 # L402 only
```

Override the default URL:

```bash
URL="https://your-402-endpoint.example.com" NWC_URL="nostr+walletconnect://..." yarn 402
```

### Lightning Address — request invoice

Fetches LNURL-pay data for a lightning address and requests an invoice. No credentials needed.

```bash
yarn request-invoice
```

### Zaps via NWC

Sends a zap to a lightning address using NWC for payment and a Nostr key for signing.

```bash
NOSTR_PRIVATE_KEY="your-hex-private-key" NWC_URL="nostr+walletconnect://..." yarn zaps-nwc
```

> **`zaps.js`** is a browser-only example (uses `window.webln`) and is not runnable as a Node script.
16 changes: 5 additions & 11 deletions examples/l402.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import { fetchWithL402 } from "@getalby/lightning-tools/l402";
import { NostrWebLNProvider } from "@getalby/sdk";
import "websocket-polyfill";
import { NWCClient } from "@getalby/sdk";

const url = "https://lsat-weather-api.getalby.repl.co/kigali";
const url = process.env.URL || "https://l402.example.com/protected-resource";

const nostrWalletConnectUrl = process.env.NWC_URL;

if (!nostrWalletConnectUrl) {
throw new Error("Please set a NWC_URL env variable");
}

const nwc = new NostrWebLNProvider({
nostrWalletConnectUrl,
});
nwc.on("sendPayment", (response) => {
console.info(`payment response:`, response);
});
const nwc = new NWCClient({ nostrWalletConnectUrl });

fetchWithL402(url, {}, { wallet: nwc })
.then((response) => response.json())
.then((data) => {
console.info(data);
nwc.close();
});
})
.finally(() => nwc.close());
18 changes: 18 additions & 0 deletions examples/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@getalby/lightning-tools-examples",
"private": true,
"type": "module",
"scripts": {
"402": "node 402.js",
"l402": "node l402.js",
"x402": "node x402.js",
"request-invoice": "node request-invoice.js",
"zaps-nwc": "node zaps-nwc.js"
},
"dependencies": {
"@getalby/lightning-tools": "file:..",
"@getalby/sdk": "^7.0.0",
"@noble/hashes": "^2.0.1",
"nostr-tools": "^2.16.1"
}
}
19 changes: 19 additions & 0 deletions examples/x402.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fetchWithX402 } from "@getalby/lightning-tools/x402";
import { NWCClient } from "@getalby/sdk";

const url = process.env.URL || "https://x402.albylabs.com/demo/quote";

const nostrWalletConnectUrl = process.env.NWC_URL;

if (!nostrWalletConnectUrl) {
throw new Error("Please set a NWC_URL env variable");
}

const nwc = new NWCClient({ nostrWalletConnectUrl });

fetchWithX402(url, {}, { wallet: nwc })
.then((response) => response.json())
.then((data) => {
console.info(data);
})
.finally(() => nwc.close());
76 changes: 76 additions & 0 deletions examples/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@getalby/lightning-tools@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-6.1.0.tgz#558b90a83b961cb6aa760e62de69f5b5ceeb2fe9"
integrity sha512-rGurar9X4Gm+9xwoNYS8s9YLK7ZYqvbqv4KbHLYV0LEeB0HxZHRgmxblGqg+fYfp6iiYHx+edIgUpt9rS3VwFw==

"@getalby/lightning-tools@file:..":
version "7.0.2"

"@getalby/sdk@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@getalby/sdk/-/sdk-7.0.0.tgz#6ab17f27bd9e762d383b70cfabd0cdeeded6bd53"
integrity sha512-0c8gyvFbRDHZIgHmOD/dfyPukxZLeidx/hx7SXlMIS/hsx4mXpKpo9Gx1zW90buElnd3k9TVB/S/bnFSEZPE7w==
dependencies:
"@getalby/lightning-tools" "^6.0.0"
nostr-tools "^2.17.0"

"@noble/ciphers@2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.1.1.tgz#c8c74fcda8c3d1f88797d0ecda24f9fc8b92b052"
integrity sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==

"@noble/curves@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-2.0.1.tgz#64ba8bd5e8564a02942655602515646df1cdb3ad"
integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==
dependencies:
"@noble/hashes" "2.0.1"

"@noble/hashes@2.0.1", "@noble/hashes@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e"
integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==

"@scure/base@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98"
integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==

"@scure/bip32@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-2.0.1.tgz#4ceea207cee8626d3fe8f0b6ab68b6af8f81c482"
integrity sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==
dependencies:
"@noble/curves" "2.0.1"
"@noble/hashes" "2.0.1"
"@scure/base" "2.0.0"

"@scure/bip39@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-2.0.1.tgz#47a6dc15e04faf200041239d46ae3bb7c3c96add"
integrity sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==
dependencies:
"@noble/hashes" "2.0.1"
"@scure/base" "2.0.0"

nostr-tools@^2.16.1, nostr-tools@^2.17.0:
version "2.23.3"
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.23.3.tgz#1a7501988b72499cf27c8f3951f00d11d9ac6025"
integrity sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA==
dependencies:
"@noble/ciphers" "2.1.1"
"@noble/curves" "2.0.1"
"@noble/hashes" "2.0.1"
"@scure/base" "2.0.0"
"@scure/bip32" "2.0.1"
"@scure/bip39" "2.0.1"
nostr-wasm "0.1.0"

nostr-wasm@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94"
integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==
8 changes: 2 additions & 6 deletions examples/zaps-nwc.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { LightningAddress } from "@getalby/lightning-tools/lnurl";
import { NostrWebLNProvider } from "@getalby/sdk";
import "websocket-polyfill";
import { finalizeEvent, getPublicKey } from "nostr-tools";
import { hexToBytes } from "@noble/hashes/utils";

// your private key is required to sign zap request events
const nostrPrivateKey = process.env.NOSTR_PRIVATE_KEY;

// NWC url will be used to pay the zap invoice.
// It can be created in advanced at nwc.getalby.com,
// or use webln.NostrWebLNProvider.withNewSecret() to generate a new one
const nostrWalletConnectUrl = process.env.NWC_URL;

if (!nostrPrivateKey || !nostrWalletConnectUrl) {
throw new Error("Please set .env variables");
}

(async () => {
const nostrWeblnProvider = new NostrWebLNProvider({
nostrWalletConnectUrl,
});
const nostrWeblnProvider = new NostrWebLNProvider({ nostrWalletConnectUrl });
// or use nostrWeblnProvider.initNWC(); to get a new NWC url
const nostrProvider = {
getPublicKey: () =>
Expand Down
Loading
Loading