BSV Blockchain service monetization express middleware
npm install @bsv/payment-express-middlewareAccept BSV micropayments in your Express.js API by seamlessly integrating 402 Payment Required flows with BRC-103 and BRC-104 mutual authentication. This middleware builds upon the Auth middleware—thus letting you identify and authenticate the payer before receiving BSV to monetize your services.
- Monetize your APIs via BSV micropayments.
- Automatically handle 402 Payment Required logic by providing the amount owed, plus a derivation prefix for the payer to build the transaction.
- Integrates seamlessly after the BRC-103 Auth middleware to ensure the user’s identity is established.
- Extensible pricing logic via a user-defined function.
1. Background
2. Features
3. Installation
4. Pre-requisites
5. Quick Start
6. Detailed Usage
- Creating the Payment Middleware
- Installing the Payment Middleware in Express
- Custom Pricing Logic
- Detailed Flow
7. API Reference
8. Example Payment Flows
- 0 Satoshis (Free Request)
- Paid Request
9. Security Considerations
10. Resources & References
11. License
---
The BRC-103 authentication framework and its BRC-104 HTTP transport extension provide mutual authentication and selective field disclosure. Building on top of these, we can now monetize interactions by requiring micropayments for certain requests. By layering a Payment Middleware after the Auth middleware, your service can signal “402 Payment Required” to the client, prompting them to respond with a BSV transaction that pays for that request.
---
- Simple 402 Payment Flows: Easily return a 402 status if payment is required.
- Configurable Pricing: Provide a calculateRequestPrice function to dynamically determine how many satoshis are owed for each request.
- Nonce-Based: Uses a derivation prefix to ensure the final payment is bound to your session, preventing replay attacks.
- Auth Integration: Leverages the user’s identity key from the preceding Auth middleware to track who is paying.
- Automatic Transaction Handling: On the server side, calls your wallet instance’s internalizeAction() to process the transaction.
---
``bash`
npm i @bsv/payment-express-middleware
Note: You must also have @bsv/auth-express-middleware installed and set up before using this payment middleware.
---
1. BRC-103 / BRC-104–based Auth Middleware
You must install and configure @bsv/auth-express-middleware first. This ensures every request has a valid req.auth.identityKey.
2. BSV Wallet
A wallet capable of receiving, verifying, and broadcasting transactions. This middleware leverages the standard, bRC-100 wallet.internalizeAction() to handle submitting the payment transaction. derivationPrefix
- This can be your own custom wallet logic that implements these interfaces.
- The wallet should also be able to verify that the and derivationSuffix properly correspond to keys in the output script, as per BRC-29.@bsv/sdk
- If you use the wallet implementation from , these details are handled automatically.
3. Client with 402 Support
On the client side, you need a user agent (e.g., AuthFetch from @bsv/sdk, or a custom approach) that automatically responds to 402 challenges by constructing a BSV transaction to make the payment.
---
Below is the minimal example integrating the payment middleware with the Auth middleware:
`ts
import express from 'express'
import bodyParser from 'body-parser'
import { createAuthMiddleware } from '@bsv/auth-express-middleware'
import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
import { Wallet } from '@your/bsv-wallet'
// 1. Create a BSV wallet that can manage transactions
const wallet = new Wallet({ / config / })
// 2. Create the Auth middleware (BRC-103/104)
const authMiddleware = createAuthMiddleware({ wallet })
// 3. Create the Payment middleware
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
// e.g., 50 satoshis per request
return 50
}
})
const app = express()
app.use(bodyParser.json())
// 4. Place Auth middleware first, then Payment middleware
app.use(authMiddleware)
app.use(paymentMiddleware)
// 5. Define your routes as normal
app.post('/somePaidEndpoint', (req, res) => {
// If we got here, the request is authenticated and the payment (if required) was accepted.
res.json({ message: 'Payment received, request authorized', amount: req.payment.satoshisPaid })
})
app.listen(3000, () => {
console.log('Payment-enabled server is listening on port 3000')
})
`
In this setup:
- Auth middleware ensures req.auth is set. Payment middleware
- checks if payment is required (based on calculateRequestPrice). If yes, the client must supply a valid x-bsv-payment header with a BSV transaction referencing the specified derivation prefix. Otherwise, the middleware returns a 402 Payment Required response, prompting the client to pay.
---
`ts
import { createPaymentMiddleware } from '@bsv/payment-express-middleware'
const paymentMiddleware = createPaymentMiddleware({
wallet: myWallet,
calculateRequestPrice: (req) => {
// Your logic to return satoshis required for this request
return 100 // e.g. 100 satoshis
}
})
`
Options:
- wallet (required): A wallet object that can process and broadcast BSV transactions. Must expose an internalizeAction method. calculateRequestPrice
- (optional): A function (req) => number | Promise that returns how many satoshis the request should cost. Defaults to 100.
1. Order: Must run after the Auth middleware.
2. Usage:
`ts`
app.use(authMiddleware) // from @bsv/auth-express-middleware
app.use(paymentMiddleware) // from @bsv/payment-express-middleware
3. Effects:
- On each request, it first checks req.auth.identityKey. If undefined, returns an error (the Payment middleware requires you to be authenticated). 0
- Determines the price. If , no payment is required—proceeds immediately. x-bsv-payment
- Otherwise, checks the header from the client. 402 Payment Required
- If the header is missing or invalid, responds with along with the x-bsv-payment-satoshis-required and a nonce in x-bsv-payment-derivation-prefix. wallet.internalizeAction()
- If the header is present, tries to finalize the transaction via . req.payment
- On success, sets with the transaction details and calls next().
You can define any logic for calculating the cost of each request, such as:
- A flat fee for all requests (return 100) 0
- Per-endpoint pricing
- Different costs based on request size or complexity
- Free requests (return ) for certain routes or conditions
`ts`
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
if (req.path === '/premium') return 500 // cost 500 satoshis
return 0 // free for everything else
}
})
1. Authenticated request arrives.
2. Payment middleware calls calculateRequestPrice(req). price = 0
3. If , continue. x-bsv-payment
4. Else check header: 402 Payment Required
- If missing → respond with + nonce (derivation prefix). wallet.internalizeAction()
- If present → parse JSON, verify the nonce, call . req.payment.satoshisPaid = price
- If successful, sets .
- Continue to your route handler.
---
Returns an Express middleware function that:
1. Checks for req.auth.identityKey. x-bsv-payment
2. Calculates the request’s price.
3. Enforces payment via if price > 0. req.payment = { satoshisPaid, accepted, tx }
4. On success, attaches .
PaymentMiddlewareOptions:
| Property | Type | Required | Description |
|-------------------------|--------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| calculateRequestPrice | (req: Request) => number \| Promise | No | Determines how many satoshis are needed to serve the request. Defaults to 100. |wallet
| | Wallet | Yes | A wallet instance with a internalizeAction() function to finalize the BSV transaction. |
#### PaymentMiddleware
Once invoked:
- If price = 0, sets req.payment = { satoshisPaid: 0 } and calls next(). price > 0
- If , requires the x-bsv-payment header containing a valid BSV transaction (plus a derivation prefix & suffix). req.payment = { satoshisPaid:
- If successful, sets .
---
`ts
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: () => 0
})
app.use(authMiddleware)
app.use(paymentMiddleware)
// => All requests are free, effectively ignoring payment logic, but the pipeline remains consistent.
`
`ts`
const paymentMiddleware = createPaymentMiddleware({
wallet,
calculateRequestPrice: async (req) => {
// Example: cost is 100 satoshis unless "POST" method, which costs 200
return req.method === 'POST' ? 200 : 100
}
})
When the client tries to call a route, the server may respond with:
`json`
{
"status": "error",
"code": "ERR_PAYMENT_REQUIRED",
"satoshisRequired": 200,
"description": "A BSV payment is required to complete this request."
}`
along with the header:`
x-bsv-payment-satoshis-required: 200
x-bsv-payment-derivation-prefix: `
The client then constructs a BSV transaction paying the appropriate amount to the server’s wallet, referencing the derivation prefix in the transaction metadata. Once complete, the client re-sends the request including:`
"x-bsv-payment": JSON.stringify({
derivationPrefix:
derivationSuffix:
transaction:
})
If accepted, the request proceeds.
---
1. Run after Auth
This middleware relies on req.auth.identityKey from the preceding BRC-103 authentication. If you skip Auth, the identity is unknown, which can break the payment system.
2. Nonce Handling
Uses a derivationPrefix to ensure each payment is unique to the request context. The library verifies the prefix is bound to the server private key. internalizeAction()
- You should ensure your wallet is robust to replay attacks, e.g., by only accepting each prefix once inside of .internalizeAction()
- Don't accept the same transaction twice, even if it's still valid! Ensure your wallet throws an error if is called with the same payment multiple times.
3. Error Handling
Non-compliant or missing x-bsv-payment data results in a 4xx error (often 402 Payment Required or 400 Bad Request).
4. Transaction Acceptance
The final acceptance or rejection of a transaction is performed by your wallet.internalizeAction(). Ensure your wallet’s logic is secure and robust.
---
- BRC-103 Spec – Mutual authentication & certificate exchange.
- BRC-104 Spec – HTTP transport for BRC-103.
- @bsv/auth-express-middleware – The prerequisite middleware for authentication.
- BRC-29 key derivation protocol – The specification covering derivationPrefix and derivationSuffix` as related to the exchange of BSV payments.
- 402 Payment Required – The HTTP status code used to signal that payment is required.
---
---
Happy Building! If you have questions, run into issues, or want to contribute improvements, feel free to open issues or PRs in our repository.