BuzzPay
BuzzPay is what Nostr Wallet Connect looks like at a counter: a tiny browser-based checkout that lets a team create Lightning invoices from a connected wallet without handing custody to a payment processor.
A counter app, not a payment processor account
BuzzPay is deliberately small. The live app is a point-of-sale screen for Bitcoin Lightning payments. The README calls it Alby's super simple self-custodial PoS powered by Nostr Wallet Connect. That phrase is accurate because BuzzPay is not trying to become a merchant bank, a full store builder or an accounting suite. It is the screen a shop, event table, food stall, meet-up desk or small team can open when someone needs to charge a customer in sats or a fiat-denominated amount.
The shape is familiar. A cashier opens the app, enters an amount on a large keypad, optionally works in a local fiat currency, presses the charge button, shows a QR code, and waits until the invoice is paid. The interesting part is where the invoice comes from. BuzzPay does not hold a balance. It does not run a central BuzzPay custody account. It connects to a Lightning wallet through Nostr Wallet Connect and asks that wallet to make and look up invoices.
That makes BuzzPay a useful Commerce project rather than a wallet brand. The wallet remains somewhere else: Alby Hub, an Alby-connected wallet, or another NWC-compatible wallet. BuzzPay is the counter surface. Nostr is the connection layer that lets a browser app request the invoice and check settlement without owning the user's funds.
Why NWC fits a point of sale
A point-of-sale app needs a narrow kind of wallet access. It needs to know enough about the wallet to create an invoice. It needs to know whether that invoice was paid. It does not normally need permission to spend the merchant's funds. Nostr Wallet Connect fits that shape because a wallet can grant app-specific methods rather than handing the entire wallet UI to the counter device.
BuzzPay's connection setup reflects that. The home page initializes Bitcoin Connect with the app name BuzzPay PoS, filters for NWC, hides balance display and requests `get_info`, `make_invoice` and `lookup_invoice`. It also asks for an isolated connection and includes `buzzpay` as app metadata. Those details matter because the app is meant to run on devices that may be used by staff, not only by the wallet owner.
The code checks the connected provider before it proceeds. It calls `getInfo`, confirms that `makeInvoice` and `lookupInvoice` are available, and expects the provider to be a NostrWebLNProvider. If the connection reports a send-payment method, BuzzPay shows a confirmation warning that sharing the PoS URL with others could lead to lost funds. This is not decorative copy. It is the most important security boundary in the product.
The wallet URL is the credential
After a successful connection, BuzzPay stores the NWC URL in browser localStorage under `pos:nwcUrl`. The wallet page later reads that value, creates a new `NostrWebLNProvider` from the Alby SDK and calls `enable`. If the provider cannot load, the user is sent back to the home page. This design keeps the app light. A static site can become a working PoS because the wallet connection is carried by the browser.
The same simplicity creates the main operational risk. The NWC URL is not a harmless preference. It is a credential for a wallet service. What it can do depends on the wallet's permission grant. If it can only make and look up invoices, a co-worker can receive payments into the merchant's wallet but cannot spend from it. If it can send payments, the same shared counter link becomes dangerous. BuzzPay's warning is there because the app itself cannot make a broad wallet credential safe.
Readers should treat the wallet URL like a password with limited powers. Do not paste a high-permission NWC string into a shared register device. Do not put the share link in a public chat. Do not reuse one connection across unrelated teams. If a staff member leaves or a device is lost, revoke the NWC connection at the wallet side and create a new BuzzPay share link.
The team link is useful and blunt
BuzzPay's team feature is one of its most practical ideas. A shop owner can connect the wallet once, open the menu, choose the co-worker share screen and display a QR code or copy a URI. A co-worker scans or opens that link, and the receiving device can restore the same wallet connection. For small teams, this is much easier than asking every person to set up wallet infrastructure or handle an nsec.
The implementation is direct. The share page reads the stored NWC URL, base64-encodes it with `btoa`, and places it in a query parameter named `nwc`. It also adds the stored label and currency. The home page reads that `nwc` parameter, decodes it with `atob`, stores the wallet URL locally and routes the user to the new invoice screen. This is clever because it works for a static PWA and helps around iOS storage boundaries, but base64 is not encryption. Anyone with the link can recover the NWC URL.
That bluntness is acceptable only when the wallet permissions are bluntly limited. A receive-only counter URL is a convenient instrument. A spend-capable counter URL is an incident waiting for a distracted cashier, a shared screenshot or a stale bookmark. BuzzPay gives the workflow a warning, but the merchant has to enforce the permission model in the wallet that issued the NWC connection.
The invoice flow is small enough to audit
BuzzPay's invoice screen is not a maze. The `New` page keeps an amount, a total, a sats total, a currency and a label. If the user works in fiat, it asks Alby's rates endpoint and the Lightning Tools fiat helper for satoshi conversion. If the user works directly in sats, the satoshi total is the keypad total. When the form is submitted, the app calls `provider.makeInvoice` with the amount and a default memo based on the label.
That memo matters in a shop setting. BuzzPay lets the user change the visible label, and the label is added to the payment request. A counter might use the label for a booth name, a staff station, a product group or a short event marker. It is visible to the customer, so it should be boring and clear rather than a private internal note.
After creating the invoice, BuzzPay routes to a payment screen with the BOLT11 payment request in the URL. The payment page decodes the invoice with Lightning Tools, displays the amount and description, renders a QR code, lets the cashier copy the invoice text and checks every three seconds whether the invoice has been paid. When `lookupInvoice` returns paid, the app moves to a payment received screen. It is a simple loop, which is exactly why the required NWC methods are so narrow.
Fiat display is a convenience, not settlement
BuzzPay supports more than raw sats entry. It fetches fiat rate data from Alby, sorts currencies by priority and lets the cashier choose a fiat code from the selector. The keypad then treats input as the smallest unit of that currency and converts to sats before creating the invoice. This is useful when a store prices in euros, dollars, baht or another local unit but wants the wallet to receive Lightning.
The conversion should be understood as a checkout convenience. BuzzPay is not doing treasury management, exchange execution or tax accounting. It turns a displayed fiat amount into a satoshi invoice amount at the time of entry. If the merchant needs formal records, refunds, end-of-day reports or currency-rate audit trails, BuzzPay should sit beside other bookkeeping processes rather than replace them.
That distinction also helps avoid customer confusion. The QR code is a Lightning invoice denominated in sats. The app may show a fiat equivalent, but the wallet pays the BOLT11 invoice. For tiny counters and pop-up events, that is enough. For larger businesses, it is a reason to test BuzzPay as a receiving surface before relying on it as the only checkout system.
PWA behavior is part of the product
BuzzPay is built with Vite, React and the Vite PWA plugin. The manifest names the app BuzzPay PoS, uses the short name BuzzPay, sets a standalone display mode, and uses the yellow BuzzPay visual identity. The README tells users to save it to the homescreen and share it with a team. The app is therefore not just a web page that happens to work on mobile. It is designed to be installed as a lightweight register screen.
This matters most on mobile devices. A cashier does not want a browser tab farm while a customer is waiting. A homescreen PWA can open straight into the counter workflow once the wallet URL is stored. The app also includes an import wallet URL prompt because iOS PWA storage does not always share state with Safari. That little workaround tells you a lot about the intended use: get the wallet connection onto the device, then keep the counter app easy to reopen.
The PWA model also means device hygiene matters. If the phone or tablet is shared, stolen, sold or repurposed, localStorage may still contain the NWC URL until the app is logged out, storage is cleared or the wallet-side connection is revoked. BuzzPay's logout path removes `pos:nwcUrl`, but operational safety should not depend only on a UI menu. Revoke old connections from the wallet when devices change hands.
There is no server to blame
BuzzPay is hosted as a static app, and the repository does not contain a backend service that stores pending invoices, customer data or staff accounts. Most state lives in the browser and in the connected wallet service. That is a feature. A merchant can use the app without signing up for a separate BuzzPay account or trusting BuzzPay with custody.
It also means the operator needs to know where responsibilities actually sit. If invoice creation fails, the cause may be the NWC connection, the wallet service, relay reachability, the browser, or the app's own provider initialization. If an invoice is paid but the screen does not advance, the lookup permission, lookup response or polling path may be involved. If a co-worker cannot load the register, the share URL may have been copied incorrectly or the wallet connection may have been revoked.
This is the self-custody trade in miniature. BuzzPay removes a payment processor account from the middle, but it cannot remove operational thinking. The app is pleasant because it is small. That smallness asks the merchant to keep the wallet, permissions, devices and backup plan understandable.
Where the Nostr part actually happens
BuzzPay does not publish social posts or maintain a relay-based profile. Its Nostr relationship is narrower: NWC uses Nostr-style events and relays to let the app communicate with a wallet service. The user experiences that as a payment connection, not as a feed. That is why BuzzPay belongs near payment tools even though the protocol under the hood is Nostr.
The Alby SDK's `NostrWebLNProvider` gives BuzzPay a WebLN-like interface backed by NWC. The app can ask for `makeInvoice` and `lookupInvoice` instead of implementing the event encryption and wallet communication itself. Bitcoin Connect handles the connection UX. NWC supplies the wallet-control protocol. BuzzPay supplies the counter interface that a human can operate in seconds.
That division is healthy. Nostr should not make every app look like a social app. Sometimes it is the quiet transport behind a cashier screen. BuzzPay is a good example because the UI has almost no protocol vocabulary. The customer sees an invoice. The staff member sees a keypad and QR code. The wallet owner sees an NWC permission grant. Each person gets the layer they need.
What a merchant should lock down
The first setting to lock down is the NWC permission set. The connection should allow invoice creation and invoice lookup. It should not allow send payments for a shared PoS unless there is a very specific reason and a very small budget. BuzzPay can warn when it detects a send method, but the safest policy is to avoid that condition entirely.
The second setting is the wallet-side revoke path. Before using BuzzPay with staff, the wallet owner should know exactly where to revoke the NWC connection. Test revocation once. Create a connection, open BuzzPay, confirm it works, revoke it, and verify the counter stops working. That rehearsal makes the real incident response much faster when a device is lost or a link is shared too widely.
The third setting is labeling. A default label of BuzzPay is fine for a test, but real counters benefit from specific labels such as event names, booth names or merchant names. The label is visible to the customer and can appear in invoice descriptions. Keep it short, public and easy to recognize later.
What BuzzPay is not trying to solve
BuzzPay is not a full BTCPay replacement. It does not manage a product catalog, inventory, taxes, customer records, refunds, order states, user roles or accounting exports. It is not a marketplace and it is not a wallet. It is closer to a tiny Lightning receive terminal for teams that already have a wallet and want a practical counter screen.
That scope makes it attractive for the right use case. A conference booth can accept zaps and Lightning payments without a laptop. A cafe experiment can test Lightning at the register. A creator can let a helper take payments at an event without handing over the main wallet app. A meetup can put a shared donation screen on a phone. Those are real workflows, and they do not need a complex commerce suite.
The same scope should keep expectations realistic. If the business needs reporting, permissions per staff member, reconciliation or customer support logs, BuzzPay should be paired with a more complete system. If the business needs only a fast and self-custodial Lightning invoice screen, the smallness is the point.
How to test it before a real counter
Start with a fresh receive-only NWC connection. Use a wallet that clearly shows the allowed methods and budget. Connect BuzzPay, enter a small amount in sats, create an invoice and pay it from another wallet. Wait for the paid screen. Then create a second invoice and cancel it before payment so the staff can see how to restart the flow.
Next, test the co-worker link. Open the share screen, scan the QR code on another device and confirm that the second device can create invoices but cannot spend. If the wallet reports send permissions, stop and create a narrower connection. Copying a base64 link into a chat should feel like handling a key, because that is what it is.
Then test PWA behavior. Save BuzzPay to the homescreen, close it, reopen it, and confirm the wallet still loads. On iOS, use the import wallet URL flow if the PWA cannot see the connection created in Safari. Finally, revoke the NWC connection at the wallet and verify that both the original device and the shared device lose access. That last step is the difference between a demo and an operational checkout.
Sources worth opening
Read the live app, the README, the React pages and the Alby/NWC documentation together. BuzzPay is small enough that the exact browser flow matters more than a marketing description.
- BuzzPay live app
- BuzzPay GitHub repository
- BuzzPay README
- BuzzPay package dependencies
- BuzzPay home and NWC connection flow
- BuzzPay wallet provider setup
- BuzzPay new invoice page
- BuzzPay payment QR and invoice lookup page
- BuzzPay paid confirmation page
- BuzzPay co-worker share page
- BuzzPay about page copy
- BuzzPay app routes
- BuzzPay browser storage keys
- BuzzPay PWA configuration
- BuzzPay manifest icon
- Alby developer page
- Alby NWC JS SDK guide
- Alby JS SDK repository
- Alby SDK npm package
- Alby Bitcoin Connect repository
- NWC code examples
- NWC introduction
- NWC reference API overview
- NIP-47 Nostr Wallet Connect
- Alby Lightning Tools repository
- Alby Lightning Tools README
- WebLN specification
- Lightning BOLT 11 invoice specification
- Vite PWA documentation
- qrcode.react repository
- Zustand state library





