@bsv/402-pay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +111 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +51 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +104 -0
- package/dist/server.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# @bsv/402-pay
|
|
2
|
+
|
|
3
|
+
[BRC-121](https://github.com/bitcoin-sv/BRCs/blob/master/payments/0121.md) Simple 402 Payments -- server middleware and client for BSV micropayments over HTTP.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @bsv/402-pay
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependency: `@bsv/sdk >= 2.0.0`
|
|
12
|
+
|
|
13
|
+
## Server
|
|
14
|
+
|
|
15
|
+
### Express middleware
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import express from 'express'
|
|
19
|
+
import { createPaymentMiddleware } from '@bsv/402-pay/server'
|
|
20
|
+
|
|
21
|
+
const app = express()
|
|
22
|
+
|
|
23
|
+
app.use('/articles/:slug', createPaymentMiddleware({
|
|
24
|
+
wallet, // WalletInterface from @bsv/sdk
|
|
25
|
+
calculatePrice: (path) => {
|
|
26
|
+
// Return price in satoshis, or undefined to skip payment
|
|
27
|
+
return 100
|
|
28
|
+
}
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
app.get('/articles/:slug', (req, res) => {
|
|
32
|
+
// req.payment is set if payment was accepted
|
|
33
|
+
res.send('Paid content here')
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Low-level validation
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { validatePayment, send402 } from '@bsv/402-pay/server'
|
|
41
|
+
|
|
42
|
+
// In any HTTP handler:
|
|
43
|
+
const result = await validatePayment(req, wallet)
|
|
44
|
+
if (!result) {
|
|
45
|
+
send402(res, serverIdentityKey, 100)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
// Payment accepted — serve content
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Client
|
|
52
|
+
|
|
53
|
+
### Fetch wrapper
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { create402Fetch } from '@bsv/402-pay/client'
|
|
57
|
+
|
|
58
|
+
const fetch402 = create402Fetch({ wallet })
|
|
59
|
+
|
|
60
|
+
// Automatically handles 402 responses with payment
|
|
61
|
+
const response = await fetch402('https://example.com/articles/foo')
|
|
62
|
+
const html = await response.text()
|
|
63
|
+
|
|
64
|
+
// Clear the payment cache
|
|
65
|
+
fetch402.clearCache()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Headers
|
|
69
|
+
|
|
70
|
+
| Header | Direction | Description |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `x-bsv-sats` | Server → Client | Required satoshi amount |
|
|
73
|
+
| `x-bsv-server` | Server → Client | Server identity public key |
|
|
74
|
+
| `x-bsv-beef` | Client → Server | Base64-encoded BEEF transaction |
|
|
75
|
+
| `x-bsv-sender` | Client → Server | Client identity public key |
|
|
76
|
+
| `x-bsv-nonce` | Client → Server | Base64-encoded derivation prefix |
|
|
77
|
+
| `x-bsv-time` | Client → Server | Unix millisecond timestamp |
|
|
78
|
+
| `x-bsv-vout` | Client → Server | Payment output index |
|
|
79
|
+
|
|
80
|
+
## Replay Protection
|
|
81
|
+
|
|
82
|
+
Two mechanisms prevent replay attacks:
|
|
83
|
+
|
|
84
|
+
1. **Timestamp freshness** -- `x-bsv-time` must be within 30 seconds of the server's clock
|
|
85
|
+
2. **Transaction uniqueness** -- `internalizeAction` returns `isMerge: true` for previously seen transactions
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
See LICENSE
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WalletInterface } from '@bsv/sdk';
|
|
2
|
+
export interface Payment402Options {
|
|
3
|
+
/** The client's wallet instance */
|
|
4
|
+
wallet: WalletInterface;
|
|
5
|
+
/** Cache timeout in milliseconds for paid content (default: 30 minutes) */
|
|
6
|
+
cacheTimeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a fetch wrapper that automatically handles 402 Payment Required responses.
|
|
10
|
+
*
|
|
11
|
+
* When a 402 is received, the wrapper constructs a BRC-121 payment using the
|
|
12
|
+
* provided wallet and retransmits the request with payment headers.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { create402Fetch } from '@bsv/402-pay/client'
|
|
17
|
+
*
|
|
18
|
+
* const fetch402 = create402Fetch({ wallet })
|
|
19
|
+
* const response = await fetch402('https://example.com/articles/foo')
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function create402Fetch(options: Payment402Options): ((url: string, init?: RequestInit) => Promise<Response>) & {
|
|
23
|
+
clearCache: () => void;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAA;IACvB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,UAY1B,MAAM,SAAQ,WAAW,KAAQ,OAAO,CAAC,QAAQ,CAAC;;EAiGhF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { PublicKey, Utils, Random } from '@bsv/sdk';
|
|
2
|
+
import { BRC29_PROTOCOL_ID, HEADERS } from './constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a fetch wrapper that automatically handles 402 Payment Required responses.
|
|
5
|
+
*
|
|
6
|
+
* When a 402 is received, the wrapper constructs a BRC-121 payment using the
|
|
7
|
+
* provided wallet and retransmits the request with payment headers.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { create402Fetch } from '@bsv/402-pay/client'
|
|
12
|
+
*
|
|
13
|
+
* const fetch402 = create402Fetch({ wallet })
|
|
14
|
+
* const response = await fetch402('https://example.com/articles/foo')
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function create402Fetch(options) {
|
|
18
|
+
const { wallet, cacheTimeoutMs = 30 * 60 * 1000 } = options;
|
|
19
|
+
const cache = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Clears the payment cache. Call this when the user clears history
|
|
22
|
+
* or when you want to force re-payment.
|
|
23
|
+
*/
|
|
24
|
+
function clearCache() {
|
|
25
|
+
cache.clear();
|
|
26
|
+
}
|
|
27
|
+
async function fetch402(url, init = {}) {
|
|
28
|
+
// Check cache
|
|
29
|
+
const cached = cache.get(url);
|
|
30
|
+
if (cached && (Date.now() - cached.timestamp) < cacheTimeoutMs) {
|
|
31
|
+
return new Response(cached.body, {
|
|
32
|
+
status: cached.response.status,
|
|
33
|
+
headers: cached.response.headers
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Initial request
|
|
37
|
+
const res = await fetch(url, init);
|
|
38
|
+
if (res.status !== 402) {
|
|
39
|
+
return res;
|
|
40
|
+
}
|
|
41
|
+
// Read 402 headers
|
|
42
|
+
const satsHeader = res.headers.get(HEADERS.SATS);
|
|
43
|
+
const serverHeader = res.headers.get(HEADERS.SERVER);
|
|
44
|
+
if (!satsHeader || !serverHeader)
|
|
45
|
+
return res;
|
|
46
|
+
const satoshis = Number.parseInt(satsHeader);
|
|
47
|
+
if (isNaN(satoshis) || satoshis <= 0)
|
|
48
|
+
return res;
|
|
49
|
+
// Construct payment
|
|
50
|
+
const serverIdentityKey = serverHeader;
|
|
51
|
+
const nonce = Utils.toBase64(Random(8));
|
|
52
|
+
const time = String(Date.now());
|
|
53
|
+
const timeSuffixB64 = Buffer.from(time).toString('base64');
|
|
54
|
+
const originator = new URL(url).origin;
|
|
55
|
+
// Derive recipient public key via BRC-42
|
|
56
|
+
const { publicKey: derivedPubKey } = await wallet.getPublicKey({
|
|
57
|
+
protocolID: BRC29_PROTOCOL_ID,
|
|
58
|
+
keyID: `${nonce} ${timeSuffixB64}`,
|
|
59
|
+
counterparty: serverIdentityKey
|
|
60
|
+
}, originator);
|
|
61
|
+
const pkh = PublicKey.fromString(derivedPubKey).toHash('hex');
|
|
62
|
+
// Get sender identity key
|
|
63
|
+
const { publicKey: senderIdentityKey } = await wallet.getPublicKey({ identityKey: true }, originator);
|
|
64
|
+
// Create payment transaction
|
|
65
|
+
const actionResult = await wallet.createAction({
|
|
66
|
+
description: `Paid Content: ${new URL(url).pathname}`,
|
|
67
|
+
outputs: [{
|
|
68
|
+
satoshis,
|
|
69
|
+
lockingScript: `76a914${pkh}88ac`,
|
|
70
|
+
outputDescription: '402 web payment',
|
|
71
|
+
customInstructions: JSON.stringify({
|
|
72
|
+
derivationPrefix: nonce,
|
|
73
|
+
derivationSuffix: timeSuffixB64,
|
|
74
|
+
serverIdentityKey
|
|
75
|
+
}),
|
|
76
|
+
tags: ['402-payment']
|
|
77
|
+
}],
|
|
78
|
+
labels: ['402-payment'],
|
|
79
|
+
options: { randomizeOutputs: false }
|
|
80
|
+
}, originator);
|
|
81
|
+
const txBase64 = Utils.toBase64(actionResult.tx);
|
|
82
|
+
// Retransmit with payment headers
|
|
83
|
+
const paidRes = await fetch(url, {
|
|
84
|
+
...init,
|
|
85
|
+
headers: {
|
|
86
|
+
...init.headers,
|
|
87
|
+
[HEADERS.BEEF]: txBase64,
|
|
88
|
+
[HEADERS.SENDER]: senderIdentityKey,
|
|
89
|
+
[HEADERS.NONCE]: nonce,
|
|
90
|
+
[HEADERS.TIME]: time,
|
|
91
|
+
[HEADERS.VOUT]: '0'
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Cache successful responses
|
|
95
|
+
if (paidRes.ok) {
|
|
96
|
+
const body = await paidRes.text();
|
|
97
|
+
cache.set(url, {
|
|
98
|
+
response: paidRes,
|
|
99
|
+
body,
|
|
100
|
+
timestamp: Date.now()
|
|
101
|
+
});
|
|
102
|
+
return new Response(body, {
|
|
103
|
+
status: paidRes.status,
|
|
104
|
+
headers: paidRes.headers
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return paidRes;
|
|
108
|
+
}
|
|
109
|
+
return Object.assign(fetch402, { clearCache });
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAe3D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,OAA0B;IACvD,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAA;IAC3D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAA;IAE3C;;;OAGG;IACH,SAAS,UAAU;QACjB,KAAK,CAAC,KAAK,EAAE,CAAA;IACf,CAAC;IAED,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,OAAoB,EAAE;QACzD,cAAc;QACd,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC7B,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,cAAc,EAAE,CAAC;YAC/D,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE;gBAC/B,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;gBAC9B,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO;aACjC,CAAC,CAAA;QACJ,CAAC;QAED,kBAAkB;QAClB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAClC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,GAAG,CAAA;QACZ,CAAC;QAED,mBAAmB;QACnB,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QAChD,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACpD,IAAI,CAAC,UAAU,IAAI,CAAC,YAAY;YAAE,OAAO,GAAG,CAAA;QAE5C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;QAC5C,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAO,GAAG,CAAA;QAEhD,oBAAoB;QACpB,MAAM,iBAAiB,GAAG,YAAY,CAAA;QACtC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QAC/B,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAC1D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;QAEtC,yCAAyC;QACzC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC;YAC7D,UAAU,EAAE,iBAAiB;YAC7B,KAAK,EAAE,GAAG,KAAK,IAAI,aAAa,EAAE;YAClC,YAAY,EAAE,iBAAiB;SAChC,EAAE,UAAU,CAAC,CAAA;QAEd,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,CAAW,CAAA;QAEvE,0BAA0B;QAC1B,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAChE,EAAE,WAAW,EAAE,IAAI,EAAE,EACrB,UAAU,CACX,CAAA;QAED,6BAA6B;QAC7B,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC;YAC7C,WAAW,EAAE,iBAAiB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;YACrD,OAAO,EAAE,CAAC;oBACR,QAAQ;oBACR,aAAa,EAAE,SAAS,GAAG,MAAM;oBACjC,iBAAiB,EAAE,iBAAiB;oBACpC,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC;wBACjC,gBAAgB,EAAE,KAAK;wBACvB,gBAAgB,EAAE,aAAa;wBAC/B,iBAAiB;qBAClB,CAAC;oBACF,IAAI,EAAE,CAAC,aAAa,CAAC;iBACtB,CAAC;YACF,MAAM,EAAE,CAAC,aAAa,CAAC;YACvB,OAAO,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE;SACrC,EAAE,UAAU,CAAC,CAAA;QAEd,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAc,CAAC,CAAA;QAE5D,kCAAkC;QAClC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC/B,GAAG,IAAI;YACP,OAAO,EAAE;gBACP,GAAI,IAAI,CAAC,OAA8C;gBACvD,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ;gBACxB,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,iBAAiB;gBACnC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK;gBACtB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI;gBACpB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG;aACpB;SACF,CAAC,CAAA;QAEF,6BAA6B;QAC7B,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAA;YACjC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;gBACb,QAAQ,EAAE,OAAO;gBACjB,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAA;YACF,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;gBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;AAChD,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WalletProtocol } from '@bsv/sdk';
|
|
2
|
+
/** BRC-29 protocol ID for key derivation */
|
|
3
|
+
export declare const BRC29_PROTOCOL_ID: WalletProtocol;
|
|
4
|
+
/** Header prefix for all BRC-121 headers */
|
|
5
|
+
export declare const HEADER_PREFIX = "x-bsv-";
|
|
6
|
+
/** Header names */
|
|
7
|
+
export declare const HEADERS: {
|
|
8
|
+
/** Server → Client: required satoshi amount */
|
|
9
|
+
readonly SATS: "x-bsv-sats";
|
|
10
|
+
/** Server → Client: server identity public key */
|
|
11
|
+
readonly SERVER: "x-bsv-server";
|
|
12
|
+
/** Client → Server: base64-encoded BEEF transaction */
|
|
13
|
+
readonly BEEF: "x-bsv-beef";
|
|
14
|
+
/** Client → Server: client identity public key */
|
|
15
|
+
readonly SENDER: "x-bsv-sender";
|
|
16
|
+
/** Client → Server: base64-encoded derivation prefix */
|
|
17
|
+
readonly NONCE: "x-bsv-nonce";
|
|
18
|
+
/** Client → Server: Unix millisecond timestamp */
|
|
19
|
+
readonly TIME: "x-bsv-time";
|
|
20
|
+
/** Client → Server: output index (decimal string) */
|
|
21
|
+
readonly VOUT: "x-bsv-vout";
|
|
22
|
+
};
|
|
23
|
+
/** Default payment window: 30 seconds */
|
|
24
|
+
export declare const DEFAULT_PAYMENT_WINDOW_MS = 30000;
|
|
25
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9C,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,EAAE,cAAoC,CAAA;AAEpE,4CAA4C;AAC5C,eAAO,MAAM,aAAa,WAAW,CAAA;AAErC,mBAAmB;AACnB,eAAO,MAAM,OAAO;IAClB,+CAA+C;;IAE/C,kDAAkD;;IAElD,uDAAuD;;IAEvD,kDAAkD;;IAElD,wDAAwD;;IAExD,kDAAkD;;IAElD,qDAAqD;;CAE7C,CAAA;AAEV,yCAAyC;AACzC,eAAO,MAAM,yBAAyB,QAAS,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** BRC-29 protocol ID for key derivation */
|
|
2
|
+
export const BRC29_PROTOCOL_ID = [2, '3241645161d8'];
|
|
3
|
+
/** Header prefix for all BRC-121 headers */
|
|
4
|
+
export const HEADER_PREFIX = 'x-bsv-';
|
|
5
|
+
/** Header names */
|
|
6
|
+
export const HEADERS = {
|
|
7
|
+
/** Server → Client: required satoshi amount */
|
|
8
|
+
SATS: `${HEADER_PREFIX}sats`,
|
|
9
|
+
/** Server → Client: server identity public key */
|
|
10
|
+
SERVER: `${HEADER_PREFIX}server`,
|
|
11
|
+
/** Client → Server: base64-encoded BEEF transaction */
|
|
12
|
+
BEEF: `${HEADER_PREFIX}beef`,
|
|
13
|
+
/** Client → Server: client identity public key */
|
|
14
|
+
SENDER: `${HEADER_PREFIX}sender`,
|
|
15
|
+
/** Client → Server: base64-encoded derivation prefix */
|
|
16
|
+
NONCE: `${HEADER_PREFIX}nonce`,
|
|
17
|
+
/** Client → Server: Unix millisecond timestamp */
|
|
18
|
+
TIME: `${HEADER_PREFIX}time`,
|
|
19
|
+
/** Client → Server: output index (decimal string) */
|
|
20
|
+
VOUT: `${HEADER_PREFIX}vout`
|
|
21
|
+
};
|
|
22
|
+
/** Default payment window: 30 seconds */
|
|
23
|
+
export const DEFAULT_PAYMENT_WINDOW_MS = 30_000;
|
|
24
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,4CAA4C;AAC5C,MAAM,CAAC,MAAM,iBAAiB,GAAmB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAA;AAEpE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAA;AAErC,mBAAmB;AACnB,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,+CAA+C;IAC/C,IAAI,EAAE,GAAG,aAAa,MAAM;IAC5B,kDAAkD;IAClD,MAAM,EAAE,GAAG,aAAa,QAAQ;IAChC,uDAAuD;IACvD,IAAI,EAAE,GAAG,aAAa,MAAM;IAC5B,kDAAkD;IAClD,MAAM,EAAE,GAAG,aAAa,QAAQ;IAChC,wDAAwD;IACxD,KAAK,EAAE,GAAG,aAAa,OAAO;IAC9B,kDAAkD;IAClD,IAAI,EAAE,GAAG,aAAa,MAAM;IAC5B,qDAAqD;IACrD,IAAI,EAAE,GAAG,aAAa,MAAM;CACpB,CAAA;AAEV,yCAAyC;AACzC,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { HEADERS, HEADER_PREFIX, BRC29_PROTOCOL_ID, DEFAULT_PAYMENT_WINDOW_MS } from './constants.js';
|
|
2
|
+
export { createPaymentMiddleware, validatePayment, send402 } from './server.js';
|
|
3
|
+
export type { PaymentMiddlewareOptions, PaymentResult, PaymentRequest, PaymentResponse } from './server.js';
|
|
4
|
+
export { create402Fetch } from './client.js';
|
|
5
|
+
export type { Payment402Options } from './client.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAA;AACrG,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC/E,YAAY,EAAE,wBAAwB,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC3G,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// BRC-121: Simple 402 Payments
|
|
2
|
+
// https://github.com/bitcoin-sv/BRCs/blob/master/payments/0121.md
|
|
3
|
+
export { HEADERS, HEADER_PREFIX, BRC29_PROTOCOL_ID, DEFAULT_PAYMENT_WINDOW_MS } from './constants.js';
|
|
4
|
+
export { createPaymentMiddleware, validatePayment, send402 } from './server.js';
|
|
5
|
+
export { create402Fetch } from './client.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,kEAAkE;AAElE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAA;AACrG,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE/E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type WalletInterface } from '@bsv/sdk';
|
|
2
|
+
export interface PaymentResult {
|
|
3
|
+
accepted: boolean;
|
|
4
|
+
satoshisPaid: number;
|
|
5
|
+
senderIdentityKey: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PaymentMiddlewareOptions {
|
|
8
|
+
/** The server's wallet instance */
|
|
9
|
+
wallet: WalletInterface;
|
|
10
|
+
/** Function that returns the price in satoshis for a given request path. Return 0 or undefined to skip payment. */
|
|
11
|
+
calculatePrice: (path: string) => number | undefined;
|
|
12
|
+
/** Payment freshness window in milliseconds (default: 30000) */
|
|
13
|
+
paymentWindowMs?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generic request/response interface so the middleware is not coupled to Express.
|
|
17
|
+
* Works with Express, Fastify, or any framework that provides headers, path, status, and set.
|
|
18
|
+
*/
|
|
19
|
+
export interface PaymentRequest {
|
|
20
|
+
path: string;
|
|
21
|
+
headers: Record<string, string | string[] | undefined>;
|
|
22
|
+
}
|
|
23
|
+
export interface PaymentResponse {
|
|
24
|
+
status(code: number): PaymentResponse;
|
|
25
|
+
set(headers: Record<string, string>): PaymentResponse;
|
|
26
|
+
end(): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Sends a 402 Payment Required response with price and server identity headers.
|
|
30
|
+
*/
|
|
31
|
+
export declare function send402(res: PaymentResponse, serverIdentityKey: string, sats: number): void;
|
|
32
|
+
/**
|
|
33
|
+
* Validates payment headers on an incoming request.
|
|
34
|
+
* Returns a PaymentResult if the payment is valid, or null if the request should be rejected.
|
|
35
|
+
*/
|
|
36
|
+
export declare function validatePayment(req: PaymentRequest, wallet: WalletInterface, paymentWindowMs?: number): Promise<PaymentResult | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Creates an Express-compatible middleware function for BRC-121 payments.
|
|
39
|
+
*
|
|
40
|
+
* Usage:
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { createPaymentMiddleware } from '@bsv/402-pay/server'
|
|
43
|
+
*
|
|
44
|
+
* app.use('/articles/:slug', createPaymentMiddleware({
|
|
45
|
+
* wallet,
|
|
46
|
+
* calculatePrice: (path) => 100
|
|
47
|
+
* }))
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function createPaymentMiddleware(options: PaymentMiddlewareOptions): (req: any, res: any, next: any) => Promise<any>;
|
|
51
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,UAAU,CAAA;AAG5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAA;IACvB,mHAAmH;IACnH,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACpD,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAA;IACrC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,eAAe,CAAA;IACrD,GAAG,IAAI,IAAI,CAAA;CACZ;AAED;;GAEG;AACH,wBAAgB,OAAO,CACrB,GAAG,EAAE,eAAe,EACpB,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,MAAM,GACX,IAAI,CAMN;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,eAAe,EACvB,eAAe,GAAE,MAAkC,GAClD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA2C/B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,IAIzD,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,kBA+B5C"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Utils, Beef } from '@bsv/sdk';
|
|
2
|
+
import { HEADERS, DEFAULT_PAYMENT_WINDOW_MS } from './constants.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sends a 402 Payment Required response with price and server identity headers.
|
|
5
|
+
*/
|
|
6
|
+
export function send402(res, serverIdentityKey, sats) {
|
|
7
|
+
res.set({
|
|
8
|
+
[HEADERS.SATS]: String(sats),
|
|
9
|
+
[HEADERS.SERVER]: serverIdentityKey
|
|
10
|
+
});
|
|
11
|
+
res.status(402).end();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validates payment headers on an incoming request.
|
|
15
|
+
* Returns a PaymentResult if the payment is valid, or null if the request should be rejected.
|
|
16
|
+
*/
|
|
17
|
+
export async function validatePayment(req, wallet, paymentWindowMs = DEFAULT_PAYMENT_WINDOW_MS) {
|
|
18
|
+
const h = (name) => {
|
|
19
|
+
const v = req.headers[name];
|
|
20
|
+
return Array.isArray(v) ? v[0] : v;
|
|
21
|
+
};
|
|
22
|
+
const sender = h(HEADERS.SENDER);
|
|
23
|
+
const beef = h(HEADERS.BEEF);
|
|
24
|
+
const nonce = h(HEADERS.NONCE);
|
|
25
|
+
const time = h(HEADERS.TIME);
|
|
26
|
+
const vout = h(HEADERS.VOUT);
|
|
27
|
+
if (!sender || !beef || !nonce || !time || !vout)
|
|
28
|
+
return null;
|
|
29
|
+
// Validate timestamp freshness
|
|
30
|
+
const timestamp = Number(time);
|
|
31
|
+
if (isNaN(timestamp) || Math.abs(Date.now() - timestamp) > paymentWindowMs)
|
|
32
|
+
return null;
|
|
33
|
+
const beefArr = Utils.toArray(beef, 'base64');
|
|
34
|
+
Beef.fromBinary(beefArr); // validates structure
|
|
35
|
+
const result = await wallet.internalizeAction({
|
|
36
|
+
tx: beefArr,
|
|
37
|
+
outputs: [{
|
|
38
|
+
outputIndex: Number.parseInt(vout),
|
|
39
|
+
protocol: 'wallet payment',
|
|
40
|
+
paymentRemittance: {
|
|
41
|
+
derivationPrefix: nonce,
|
|
42
|
+
derivationSuffix: Buffer.from(time).toString('base64'),
|
|
43
|
+
senderIdentityKey: sender
|
|
44
|
+
}
|
|
45
|
+
}],
|
|
46
|
+
description: `Payment for ${req.path}`
|
|
47
|
+
});
|
|
48
|
+
// Reject replayed transactions
|
|
49
|
+
if (result.isMerge)
|
|
50
|
+
return null;
|
|
51
|
+
return {
|
|
52
|
+
accepted: true,
|
|
53
|
+
satoshisPaid: 0, // actual amount is validated by the wallet during internalization
|
|
54
|
+
senderIdentityKey: sender
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates an Express-compatible middleware function for BRC-121 payments.
|
|
59
|
+
*
|
|
60
|
+
* Usage:
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { createPaymentMiddleware } from '@bsv/402-pay/server'
|
|
63
|
+
*
|
|
64
|
+
* app.use('/articles/:slug', createPaymentMiddleware({
|
|
65
|
+
* wallet,
|
|
66
|
+
* calculatePrice: (path) => 100
|
|
67
|
+
* }))
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function createPaymentMiddleware(options) {
|
|
71
|
+
const { wallet, calculatePrice, paymentWindowMs } = options;
|
|
72
|
+
let identityKey = '';
|
|
73
|
+
return async (req, res, next) => {
|
|
74
|
+
try {
|
|
75
|
+
if (!identityKey) {
|
|
76
|
+
const { publicKey } = await wallet.getPublicKey({ identityKey: true });
|
|
77
|
+
identityKey = publicKey;
|
|
78
|
+
}
|
|
79
|
+
const price = calculatePrice(req.path);
|
|
80
|
+
if (!price)
|
|
81
|
+
return next();
|
|
82
|
+
const hasPayment = req.headers[HEADERS.BEEF];
|
|
83
|
+
if (!hasPayment) {
|
|
84
|
+
return send402(res, identityKey, price);
|
|
85
|
+
}
|
|
86
|
+
const result = await validatePayment(req, wallet, paymentWindowMs);
|
|
87
|
+
if (!result) {
|
|
88
|
+
return send402(res, identityKey, price);
|
|
89
|
+
}
|
|
90
|
+
req.payment = { ...result, satoshisPaid: price };
|
|
91
|
+
next();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
if (!identityKey) {
|
|
95
|
+
res.status(500).end();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const price = calculatePrice(req.path) ?? 100;
|
|
99
|
+
send402(res, identityKey, price);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bsv/402-pay",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BRC-121 Simple 402 Payments — server middleware and client for BSV micropayments over HTTP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./server": {
|
|
14
|
+
"types": "./dist/server.d.ts",
|
|
15
|
+
"import": "./dist/server.js"
|
|
16
|
+
},
|
|
17
|
+
"./client": {
|
|
18
|
+
"types": "./dist/client.d.ts",
|
|
19
|
+
"import": "./dist/client.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"bsv",
|
|
32
|
+
"402",
|
|
33
|
+
"payment",
|
|
34
|
+
"micropayments",
|
|
35
|
+
"brc-121",
|
|
36
|
+
"middleware"
|
|
37
|
+
],
|
|
38
|
+
"author": "Deggen <d.kellenschwiler@bsvassociation.org>",
|
|
39
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@bsv/sdk": ">=2.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@bsv/sdk": "^2.0.13",
|
|
45
|
+
"@types/node": "^20.14.0",
|
|
46
|
+
"typescript": "^5.5.0"
|
|
47
|
+
}
|
|
48
|
+
}
|