Micropayments in Action
Walkthrough of how to leverage BSV’s unique micropayment capabilities, covering real-world applications, key BRC standards, and best practices to help developers integrate fast, low-cost payments into their services or products.
Below is an example tutorial that walks you through integrating @bsv/auth-express-middleware and @bsv/payment-express-middleware in an Express application for micropayments, and then shows how to call that API from a client using the AuthFetch client. In this sample use-case, you’ll create a simple “premium” API endpoint that costs a small fee in satoshis. The server will automatically enforce authentication and micropayment collection, and you can then view and manage those payments with the Wallet UI admin panel.
First-run note: This is a real payment-middleware tutorial. It requires a server wallet root key, wallet storage, and funded wallets. If you want the no-spend 402 flow first, run the maintained starter at use.bsv.tools before using this page.
1. Overview
What You’ll Build
-
Server (Express):
- Auth middleware ensures only authenticated users can even invoke your endpoint.
- Payment middleware charges the user a small micropayment (in satoshis) whenever they call your endpoint.
- A backend wallet (using
@bsv/wallet-toolbox) collects and manages these micropayments automatically.
-
Client:
- User’s wallet is installed locally (e.g., the BSV Desktop or another BRC-100–compatible wallet).
- AuthFetch library automatically handles authentication (BRC-103 handshake) and responds to
402 Payment Requiredfrom the server, paying the required satoshis if the user agrees.
What You’ll Need
- Node.js environment
- An existing BSV private key for your server or create one (64 hex chars, 256-bit). This is the server’s “wallet root key.”
- Access to the Babbage wallet storage service or your own self-hosted storage.
- A local BSV wallet on the client side, such as the BSV Desktop.
- Some BSV in your server’s wallet (and in your user’s wallet) to handle small fees.
2. Setting Up the Server
Below is a simple Express server named server.ts. It uses:
createAuthMiddlewarefrom @bsv/auth-express-middlewarecreatePaymentMiddlewarefrom @bsv/payment-express-middleware- A server-side wallet from @bsv/wallet-toolbox to both authenticate routes and receive micropayments.
2.1 Install Dependencies
1npm install express body-parser dotenv
2npm install @bsv/auth-express-middleware @bsv/payment-express-middleware
3npm install @bsv/wallet-toolbox2.2 Server Code (server.ts)
1import express, { Request, Response, NextFunction } from 'express'
2import bodyParser from 'body-parser'
3import { Setup } from '@bsv/wallet-toolbox'
4import { createAuthMiddleware } from '@bsv/auth-express-middleware'
5import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
6
7// Optionally load environment variables from a .env file
8import dotenv from 'dotenv'
9dotenv.config()
10
11// Your environment variables:
12const {
13 SERVER_PRIVATE_KEY = 'YOUR_64_CHAR_HEX_PRIVATE_KEY',
14 WALLET_STORAGE_URL = 'https://storage.babbage.systems',
15 BSV_NETWORK = 'main',
16 HTTP_PORT = 8080
17} = process.env
18
19async function main() {
20 // 1) Create the Express app
21 const app = express()
22 // 2) Use body-parser to parse JSON request bodies
23 app.use(bodyParser.json({ limit: '1mb' }))
24
25 // 3) Create a server wallet (the “payee” server) using @bsv/wallet-toolbox
26 const wallet = await Setup.createWalletClientNoEnv({
27 chain: BSV_NETWORK as 'main' | 'test',
28 rootKeyHex: SERVER_PRIVATE_KEY,
29 storageUrl: WALLET_STORAGE_URL
30 })
31
32 // 4) Auth Middleware: If "allowUnauthenticated" is false, server requires authentication for all routes.
33 app.use(
34 createAuthMiddleware({
35 wallet,
36 allowUnauthenticated: false, // require BRC-103-based mutual auth
37 logger: console, // optional logging
38 logLevel: 'info'
39 })
40 )
41
42 // 5) Payment Middleware: Automatically responds with 402 Payment Required if user doesn't include micropayment
43 app.use(
44 createPaymentMiddleware({
45 wallet,
46 calculateRequestPrice: async (req: Request) => {
47 // Basic example: a flat 5 satoshi charge:
48 return 5
49 }
50 })
51 )
52
53 // 6) Example route: A “premium” endpoint that costs 5 satoshis per request
54 app.get('/api/premium', (req: Request, res: Response) => {
55 // By the time code reaches here:
56 // - The user is authenticated
57 // - The micropayment for 5 satoshis is validated & confirmed (server has the transaction)
58 return res.json({
59 status: 'success',
60 message: 'Hello from your premium endpoint!',
61 yourCostInSats: 5
62 })
63 })
64
65 // 7) Start listening
66 app.listen(HTTP_PORT, () => {
67 console.log(`Micropayment API listening on port ${HTTP_PORT}`)
68 })
69}
70
71main().catch(err => {
72 console.error('Failed to start server:', err)
73})2.3 Explanation
-
Wallet Setup: You create a server wallet by providing:
- a root key (
SERVER_PRIVATE_KEY) - a chain (e.g.
mainortest) - a storage URL (
WALLET_STORAGE_URL)
- a root key (
-
Auth Middleware (
createAuthMiddleware) This enforces that only BRC-103–authenticated users (using mutual auth) can access your endpoints. -
Payment Middleware (
createPaymentMiddleware) Checks each incoming request:- Decides how many satoshis are required (via
calculateRequestPrice). - If the user didn’t provide a valid micropayment in the request, it sends back
402 Payment Required. - If they did pay, the server’s wallet processes the transaction.
- Decides how many satoshis are required (via
-
Endpoint: You expose a route
GET /api/premium. Anyone hitting that route must (1) authenticate and (2) pay 5 sats. Success means you respond with the resource or data they paid for.
3. Using AuthFetch on the Client Side
Users must have a local wallet installed, but your front-end can automate requests with the combination of WalletClient + AuthFetch. When the server says “402 Payment Required,” AuthFetch automatically builds a payment transaction using the user’s local wallet and re-sends the request.
3.1 Install the @bsv/sdk
On your front-end or client environment:
1npm install @bsv/sdk3.2 Client Side Example (auth-fetch-demo.js)
Below is a small snippet illustrating how to call the protected endpoint using AuthFetch.
1import { AuthFetch, WalletClient } from '@bsv/sdk'
2
3async function callPremiumEndpoint() {
4 try {
5 // 1) Connect to the local BRC-100 wallet using automatic substrate detection
6 const wallet = new WalletClient('auto')
7
8 // 2) Create an AuthFetch instance
9 const client = new AuthFetch(wallet)
10
11 // 3) Attempt our Premium GET request
12 const response = await client.fetch('http://localhost:8080/api/premium', {
13 method: 'GET'
14 })
15
16 // 4) Parse server result
17 const result = await response.json()
18 console.log('Server result:', result)
19 } catch (error) {
20 console.error('Failed to call premium endpoint:', error)
21 }
22}
23
24callPremiumEndpoint()How It Works
AuthFetchtries the request. The server sees no micropayment yet, responds with402 Payment Required.AuthFetchnotices the402and automatically constructs a BSV transaction paying the required satoshis to the server’s derived address.AuthFetchre-sends the request with thex-bsv-paymentheader plus the transaction.- The server verifies the micropayment, sees user is authenticated, and grants access to
/api/premium.
No boilerplate of building the transaction directly is needed on the front end—AuthFetch + WalletClient do it all for you.
4. Viewing & Managing Payments in the Admin Panel (WUI)
Your server wallet from @bsv/wallet-toolbox is accessible in (at least) two ways:
-
API:
- You can perform advanced operations: query the wallet, list actions, sign transactions, etc.
-
Web UI:
- https://wui.bapp.dev
- Or run it from source yourself
- Choose “Add New,” supply your server’s root key and correct storage URL.
- The admin panel will load your wallet data, let you see all micropayments received, show any unspent outputs, etc.
By monitoring the wallet with the WUI, you can confirm you’re indeed collecting those 5-satoshi fees from each client request.
Tip: In a real-world scenario, you’ll often want to keep your wallet’s root key secure. You can optionally split it or require multiple factors. For demonstration and development, though, simply import the root key into the WUI for quick management.
5. Tangible Example: “Paid Weather Data” Endpoint
Below is a more fleshed-out (though simplified) example scenario:
- Goal: Provide a “/weatherStats” endpoint that returns special data behind a micropaywall.
- Selling: Users pay 1 sat to get your exclusive weather data.
- Authenticating: They must also be BRC-103–authenticated (no account/password needed, just their wallet).
Server (snippet)
1import express, { Request, Response } from 'express'
2import bodyParser from 'body-parser'
3import { Setup } from '@bsv/wallet-toolbox'
4import { createAuthMiddleware } from '@bsv/auth-express-middleware'
5import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
6
7async function startServer() {
8 const app = express()
9 app.use(bodyParser.json())
10
11 const wallet = await Setup.createWalletClientNoEnv({
12 chain: 'main',
13 rootKeyHex: process.env.SERVER_PRIVATE_KEY,
14 storageUrl: 'https://storage.babbage.systems'
15 })
16
17 app.use(createAuthMiddleware({ wallet, allowUnauthenticated: false }))
18 app.use(createPaymentMiddleware({
19 wallet,
20 calculateRequestPrice: async () => 1 // Just 1 sat per request!
21 }))
22
23 app.get('/weatherStats', async (req: Request, res: Response) => {
24 // The user is authenticated and has paid 1 sat
25 // This is the part where you get creative: plug in your IoT home weather station here!
26 // req.auth describes the identity of the user
27 // req.payment is the payment they sent
28 const weatherData = {
29 temperature: '15 C',
30 conditions: 'Clear skies',
31 region: 'Downtown Mars' // Hypothetical data
32 }
33 res.json(weatherData)
34 })
35
36 const port = 8080
37 app.listen(port, () => {
38 console.log(`Paid Weather API listening on port ${port}`)
39 })
40}
41startServer().catch(console.error)Client (snippet)
1import { AuthFetch, WalletClient } from '@bsv/sdk'
2
3async function getWeatherData() {
4 const wallet = new WalletClient('auto')
5 const client = new AuthFetch(wallet)
6
7 try {
8 const resp = await client.fetch('http://localhost:8080/weatherStats')
9 if (!resp.ok) {
10 throw new Error(`Server error: ${resp.status}`)
11 }
12 const data = await resp.json()
13 console.log('Weather data:', data)
14 } catch (err) {
15 console.error('Could not fetch weather data:', err)
16 }
17}
18
19getWeatherData()Whenever fetch('http://localhost:8080/weatherStats') is called, the server demands 1 sat. AuthFetch automatically builds the payment transaction with the user’s wallet. The user is prompted to confirm or must have auto-approval settings. Finally, the server sees the paid & authenticated request, returns the weather data, and you can confirm in the WUI that you earned 1 sat!
6. Next Steps
- Expand Payment Logic: Add dynamic fees, e.g. per request size or user type.
- Certificate-Based Access: Use
certificatesToRequestincreateAuthMiddlewareto require a user certificate (like “cool user only”). - Explore BRC-105: This is the specification behind BRC-105 HTTP micropayments (the 402 Payment Required flow).
- Enhance Security: Use multi-factor setups for your server wallet root key.
- Deploy: Put your Express server with micropayments on a server for the world to use.
7. Conclusion
With just a few lines of code:
- You’ve enforced user authentication (BRC-103).
- You’ve monetized each request with a micropayment (BRC-105).
- You’ve made the developer experience easy on both sides, thanks to
AuthFetch+WalletClienton front-end, and a simple Express chain ofAuthMiddleware -> PaymentMiddlewarehooking in your server’s wallet.
All micropayments are automatically deposited in your server’s wallet, which you can view and manage in the Wallet UI admin panel by importing your root key. That’s all it takes to stand up an authenticated, pay-per-call API on BSV—giving you a foundation for building more advanced use-cases and profitable services. Enjoy building!
