@reapp-sdk/express-middleware 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 +206 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +8 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +243 -0
- package/dist/middleware.js.map +1 -0
- package/dist/proof-store.d.ts +13 -0
- package/dist/proof-store.d.ts.map +1 -0
- package/dist/proof-store.js +21 -0
- package/dist/proof-store.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verification.d.ts +72 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +327 -0
- package/dist/verification.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# @reapp-sdk/express-middleware 0.1.0
|
|
2
|
+
|
|
3
|
+
Fail-closed Express middleware for REAPP payment-gated APIs on Stellar.
|
|
4
|
+
|
|
5
|
+
The middleware issues an x402-style `402` challenge, accepts an `X-PAYMENT`
|
|
6
|
+
settlement proof, and independently verifies the successful Stellar transaction
|
|
7
|
+
before a protected handler runs. The transaction must contain one unambiguous
|
|
8
|
+
payment event from the configured MandateRegistry and one matching SEP-41
|
|
9
|
+
transfer from the mandate user to the configured merchant. Header claims never
|
|
10
|
+
replace contract evidence.
|
|
11
|
+
|
|
12
|
+
Supports Express 4 and Express 5. Ships as ESM with TypeScript declarations.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @reapp-sdk/express-middleware express
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import express from "express";
|
|
24
|
+
import {
|
|
25
|
+
InMemoryRedemptionStore,
|
|
26
|
+
createReappPaymentMiddleware,
|
|
27
|
+
getVerifiedPayment,
|
|
28
|
+
} from "@reapp-sdk/express-middleware";
|
|
29
|
+
|
|
30
|
+
const app = express();
|
|
31
|
+
|
|
32
|
+
const requirePayment = createReappPaymentMiddleware({
|
|
33
|
+
merchant: process.env.REAPP_MERCHANT_ADDRESS!,
|
|
34
|
+
sourceAccount: process.env.REAPP_READ_SOURCE_ADDRESS!,
|
|
35
|
+
amount: "1.00",
|
|
36
|
+
resource: "/source/market",
|
|
37
|
+
// Local development and one-process demos only. See Production replay safety.
|
|
38
|
+
redemptionStore: new InMemoryRedemptionStore(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
app.get("/source/market", requirePayment, (_request, response) => {
|
|
42
|
+
const payment = getVerifiedPayment(response);
|
|
43
|
+
response.json({
|
|
44
|
+
source: "verified research data",
|
|
45
|
+
settlement: payment?.txHash,
|
|
46
|
+
mandate: payment?.mandateId,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
app.listen(4021);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`sourceAccount` is a funded Stellar `G...` address used only to simulate the
|
|
54
|
+
read-only `get_mandate` call. The verifier never signs or submits a transaction.
|
|
55
|
+
|
|
56
|
+
## What is verified
|
|
57
|
+
|
|
58
|
+
For each paid retry, the default verifier requires all of the following:
|
|
59
|
+
|
|
60
|
+
1. The configured RPC reports the exact configured network passphrase.
|
|
61
|
+
2. The transaction hash is valid, successful, and inside the configured ledger
|
|
62
|
+
freshness window.
|
|
63
|
+
3. Exactly one contract event is emitted by the configured MandateRegistry with
|
|
64
|
+
topics `payment` and the configured merchant, plus a 32-byte mandate id and
|
|
65
|
+
an amount meeting the request price.
|
|
66
|
+
4. The event-derived mandate currently stored by the contract names the same
|
|
67
|
+
merchant and SEP-41 asset and supplies the user and agent identities.
|
|
68
|
+
5. The same transaction contains exactly one matching transfer emitted by that
|
|
69
|
+
asset, from the mandate user to the merchant, for the exact event amount.
|
|
70
|
+
6. A shared redemption store atomically consumes the network + registry +
|
|
71
|
+
transaction key before the protected handler runs.
|
|
72
|
+
|
|
73
|
+
Only the transaction hash crosses from the HTTP adapter into the verifier.
|
|
74
|
+
Caller-supplied `amount` and `mandateId` fields remain wire-format hints; the
|
|
75
|
+
verified values placed in `response.locals` come from Stellar and the contract.
|
|
76
|
+
|
|
77
|
+
## Production replay safety
|
|
78
|
+
|
|
79
|
+
`InMemoryRedemptionStore` is intentionally limited to one Node.js process. A
|
|
80
|
+
production deployment must provide a durable `RedemptionStore` whose
|
|
81
|
+
`consumeOnce` operation is linearizable across every worker and host:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import type {
|
|
85
|
+
RedemptionRecord,
|
|
86
|
+
RedemptionStore,
|
|
87
|
+
} from "@reapp-sdk/express-middleware";
|
|
88
|
+
|
|
89
|
+
class SharedRedemptionStore implements RedemptionStore {
|
|
90
|
+
async consumeOnce(
|
|
91
|
+
record: Readonly<RedemptionRecord>,
|
|
92
|
+
): Promise<"consumed" | "duplicate"> {
|
|
93
|
+
// Perform one atomic set-if-absent in your durable database.
|
|
94
|
+
// Never delete a consumed key merely because downstream delivery failed.
|
|
95
|
+
return atomicInsert(record.key, record) ? "consumed" : "duplicate";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Only the caller that receives `consumed` reaches the protected handler. A store
|
|
101
|
+
error returns `503` and never serves the resource.
|
|
102
|
+
|
|
103
|
+
## Response behavior
|
|
104
|
+
|
|
105
|
+
| Condition | Status | Behavior |
|
|
106
|
+
|---|---:|---|
|
|
107
|
+
| No proof or malformed/invalid settlement | `402` | Returns a fresh payment requirement. |
|
|
108
|
+
| Settlement already consumed | `409` | Does not invite a second payment. |
|
|
109
|
+
| RPC, mandate lookup, or redemption store unavailable | `503` | Fails closed with `Retry-After`; retry the same proof. |
|
|
110
|
+
| Verified and atomically consumed | next handler | Stores chain-derived evidence in `response.locals.reappPayment`. |
|
|
111
|
+
|
|
112
|
+
Challenges and successful responses receive `Cache-Control: private, no-store`
|
|
113
|
+
and `Vary: X-PAYMENT` so a shared cache cannot bypass the payment boundary.
|
|
114
|
+
|
|
115
|
+
## Request-specific prices
|
|
116
|
+
|
|
117
|
+
`amount` and `resource` may be resolver functions. The middleware snapshots the
|
|
118
|
+
resolved requirement before any asynchronous verification:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const requirePayment = createReappPaymentMiddleware({
|
|
122
|
+
merchant: MERCHANT,
|
|
123
|
+
sourceAccount: READ_SOURCE,
|
|
124
|
+
amount: (request) => priceFor(request.params.id),
|
|
125
|
+
resource: (request) => `/source/${request.params.id}`,
|
|
126
|
+
redemptionStore,
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Amounts are decimal strings and are converted to integer stroops. Floating-point
|
|
131
|
+
amounts, scientific notation, over-precision, zero, and values outside i128 fail
|
|
132
|
+
closed.
|
|
133
|
+
|
|
134
|
+
## API
|
|
135
|
+
|
|
136
|
+
### `createReappPaymentMiddleware(options)`
|
|
137
|
+
|
|
138
|
+
Creates an Express 4/5 `RequestHandler`.
|
|
139
|
+
|
|
140
|
+
| Option | Type | Meaning |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `merchant` | `string` | Required Stellar address that must receive the transfer. |
|
|
143
|
+
| `amount` | `string \| (request) => string` | Required decimal price. |
|
|
144
|
+
| `redemptionStore` | `RedemptionStore` | Required atomic consume-once store. |
|
|
145
|
+
| `sourceAccount` | `string?` | Funded `G...` address for read-only simulations; required by the default verifier. |
|
|
146
|
+
| `resource` | `string \| (request) => string` | Challenge resource id; defaults to `request.originalUrl`. |
|
|
147
|
+
| `asset` | `string?` | Required SEP-41 emitter; defaults to the configured network's native SAC. |
|
|
148
|
+
| `networkConfig` | `NetworkConfig?` | RPC, passphrase, registry, and native SAC; defaults to REAPP testnet. |
|
|
149
|
+
| `scheme` | `string?` | Wire scheme; defaults to `reapp-soroban`. |
|
|
150
|
+
| `network` | `string?` | Wire network label; defaults to `stellar-testnet`. |
|
|
151
|
+
| `decimals` | `number?` | Asset decimals; defaults to `7`. |
|
|
152
|
+
| `maxProofAgeLedgers` | `number?` | Settlement freshness window; defaults to `120`. |
|
|
153
|
+
| `pollAttempts` | `number?` | `NOT_FOUND` retries; defaults to `15`. |
|
|
154
|
+
| `pollIntervalMs` | `number?` | Retry delay; defaults to `1000`. |
|
|
155
|
+
| `maxHeaderBytes` | `number?` | Strict `X-PAYMENT` size limit; defaults to `8192`. |
|
|
156
|
+
| `allowHttpRpc` | `boolean?` | Development-only plaintext RPC escape hatch; defaults to `false`. |
|
|
157
|
+
| `verifier` | `PaymentVerifier?` | Explicit trusted verifier injection for tests or alternate RPC infrastructure. |
|
|
158
|
+
|
|
159
|
+
### Runtime exports
|
|
160
|
+
|
|
161
|
+
| Export | Purpose |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `createReappPaymentMiddleware` | Build the payment boundary. |
|
|
164
|
+
| `getVerifiedPayment(response)` | Read the chain-derived `VerifiedPayment` after the gate. |
|
|
165
|
+
| `InMemoryRedemptionStore` | One-process development/test store. |
|
|
166
|
+
| `createStellarPaymentVerifier` | Build the independent Stellar verifier. |
|
|
167
|
+
| `buildChallenge` | Build the isolated x402-style response object. |
|
|
168
|
+
| `createRedemptionKey` | Create the normalized cross-network replay key. |
|
|
169
|
+
| `extractContractEvents`, `interpretEvents` | Strict V3/V4 Stellar event decoding helpers. |
|
|
170
|
+
| `selectPayment`, `selectTransfer` | Pure fail-closed event selection helpers. |
|
|
171
|
+
|
|
172
|
+
The package also exports TypeScript types for requirements, verified payments,
|
|
173
|
+
verifiers, redemption records and stores, middleware options, and decoded event
|
|
174
|
+
evidence.
|
|
175
|
+
|
|
176
|
+
## Wire-format isolation
|
|
177
|
+
|
|
178
|
+
x402 is evolving. REAPP keeps the HTTP challenge/proof shape in the isolated
|
|
179
|
+
adapter inside `@reapp-sdk/core`; MandateRegistry does not depend on that shape.
|
|
180
|
+
An x402 version change can replace the adapter while the same contract methods,
|
|
181
|
+
storage, and verifier invariants remain intact.
|
|
182
|
+
|
|
183
|
+
## Current protocol limits
|
|
184
|
+
|
|
185
|
+
- The settlement proof is bearer data. Atomic consumption prevents two
|
|
186
|
+
successful redemptions, but a copied proof can race the legitimate requester.
|
|
187
|
+
- The challenge resource is not committed into the current contract event.
|
|
188
|
+
Exact requester/resource binding requires an authenticated nonce/signature or
|
|
189
|
+
a future contract/event commitment; this package does not claim that property.
|
|
190
|
+
- `get_mandate` reads current contract state, not a historical state snapshot.
|
|
191
|
+
Same-transaction registry and token events prove settlement, while current
|
|
192
|
+
state binds the mandate identities and asset.
|
|
193
|
+
|
|
194
|
+
## Current contract evidence
|
|
195
|
+
|
|
196
|
+
The testnet default is the upgradeable simple MandateRegistry:
|
|
197
|
+
|
|
198
|
+
- Contract: [`CC6JMPDHRPBR2HBLJKRCIKV54HXDV2RFXDKW6MALQKWM6JEAJQHICRWE`](https://stellar.expert/explorer/testnet/contract/CC6JMPDHRPBR2HBLJKRCIKV54HXDV2RFXDKW6MALQKWM6JEAJQHICRWE)
|
|
199
|
+
- WASM SHA-256: `13f7023d4a361b6e49d3d39f61f55c5eeece51a602013a3cddae420d2ce8552b`
|
|
200
|
+
- Reproducible release: [`simple-v0.2.0`](https://github.com/reapp-protocol/reapp-protocol-contracts/releases/tag/simple-v0.2.0_contracts_simple_mandate_registry_mandate-registry_pkg0.2.0_cli25.1.0)
|
|
201
|
+
|
|
202
|
+
The contract checks and consumes authorization inside `execute_payment` before
|
|
203
|
+
the token transfer. Testnet operations include pause, authority rotation, and a
|
|
204
|
+
24-hour timelocked same-address upgrade path.
|
|
205
|
+
|
|
206
|
+
Apache-2.0.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RequestHandler, Response } from "express";
|
|
2
|
+
import type { PaymentRequirement, ReappPaymentMiddlewareOptions, VerifiedPayment, X402Challenge } from "./types.js";
|
|
3
|
+
export declare const REAPP_PAYMENT_LOCALS_KEY: "reappPayment";
|
|
4
|
+
export declare function buildChallenge(requirement: PaymentRequirement): X402Challenge;
|
|
5
|
+
export declare function getVerifiedPayment(response: Response): VerifiedPayment | undefined;
|
|
6
|
+
export declare function createRedemptionKey(networkPassphrase: string, registryId: string, txHash: string): string;
|
|
7
|
+
export declare function createReappPaymentMiddleware(options: ReappPaymentMiddlewareOptions): RequestHandler;
|
|
8
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAyB,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,KAAK,EACV,kBAAkB,EAClB,6BAA6B,EAE7B,eAAe,EACf,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,wBAAwB,EAAG,cAAuB,CAAC;AA6DhE,wBAAgB,cAAc,CAAC,WAAW,EAAE,kBAAkB,GAAG,aAAa,CAe7E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAElF;AAED,wBAAgB,mBAAmB,CACjC,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,MAAM,CAGR;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,cAAc,CAkLhB"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Buffer } from "buffer";
|
|
3
|
+
import { Address, StrKey } from "@stellar/stellar-sdk";
|
|
4
|
+
import { X_PAYMENT_HEADER, decodePaymentProof, toStroops } from "@reapp-sdk/core";
|
|
5
|
+
import { TESTNET } from "@reapp-sdk/stellar";
|
|
6
|
+
import { createStellarPaymentVerifier } from "./verification.js";
|
|
7
|
+
export const REAPP_PAYMENT_LOCALS_KEY = "reappPayment";
|
|
8
|
+
const DEFAULT_MAX_HEADER_BYTES = 8_192;
|
|
9
|
+
const DEFAULT_MAX_PROOF_AGE_LEDGERS = 120;
|
|
10
|
+
function exactText(label, value) {
|
|
11
|
+
if (typeof value !== "string" || value.length === 0 || value.trim() !== value) {
|
|
12
|
+
throw new Error(`${label} must be a non-empty string without surrounding whitespace.`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function stellarAddress(label, value) {
|
|
17
|
+
const address = exactText(label, value);
|
|
18
|
+
try {
|
|
19
|
+
Address.fromString(address);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new Error(`${label} must be a valid Stellar address.`);
|
|
23
|
+
}
|
|
24
|
+
return address;
|
|
25
|
+
}
|
|
26
|
+
function resolveRequestValue(label, value, request) {
|
|
27
|
+
return exactText(label, typeof value === "function" ? value(request) : value);
|
|
28
|
+
}
|
|
29
|
+
function setPrivateResponseHeaders(response) {
|
|
30
|
+
response.set("cache-control", "private, no-store");
|
|
31
|
+
response.vary("X-PAYMENT");
|
|
32
|
+
}
|
|
33
|
+
function json(response, status, body, retryAfter) {
|
|
34
|
+
setPrivateResponseHeaders(response);
|
|
35
|
+
response.status(status);
|
|
36
|
+
if (retryAfter)
|
|
37
|
+
response.set("retry-after", retryAfter);
|
|
38
|
+
response.json(body);
|
|
39
|
+
}
|
|
40
|
+
function canonicalPaymentHeader(request, maxBytes) {
|
|
41
|
+
const values = [];
|
|
42
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
43
|
+
const name = request.rawHeaders[index];
|
|
44
|
+
const value = request.rawHeaders[index + 1];
|
|
45
|
+
if (name?.toLowerCase() === X_PAYMENT_HEADER && value !== undefined)
|
|
46
|
+
values.push(value);
|
|
47
|
+
}
|
|
48
|
+
if (values.length === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
if (values.length !== 1)
|
|
51
|
+
throw new Error("multiple X-PAYMENT headers are not allowed");
|
|
52
|
+
const header = values[0];
|
|
53
|
+
if (Buffer.byteLength(header, "utf8") > maxBytes) {
|
|
54
|
+
throw new Error("X-PAYMENT header exceeds the configured size limit");
|
|
55
|
+
}
|
|
56
|
+
if (header.length === 0
|
|
57
|
+
|| header.includes(",")
|
|
58
|
+
|| !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(header)
|
|
59
|
+
|| Buffer.from(header, "base64").toString("base64") !== header) {
|
|
60
|
+
throw new Error("X-PAYMENT header is not canonical base64");
|
|
61
|
+
}
|
|
62
|
+
return header;
|
|
63
|
+
}
|
|
64
|
+
export function buildChallenge(requirement) {
|
|
65
|
+
return {
|
|
66
|
+
x402Version: 1,
|
|
67
|
+
accepts: [
|
|
68
|
+
{
|
|
69
|
+
scheme: requirement.scheme,
|
|
70
|
+
network: requirement.network,
|
|
71
|
+
maxAmountRequired: requirement.amount,
|
|
72
|
+
asset: requirement.asset,
|
|
73
|
+
payTo: requirement.merchant,
|
|
74
|
+
resource: requirement.resource,
|
|
75
|
+
extra: { contract: requirement.registryId },
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function getVerifiedPayment(response) {
|
|
81
|
+
return response.locals[REAPP_PAYMENT_LOCALS_KEY];
|
|
82
|
+
}
|
|
83
|
+
export function createRedemptionKey(networkPassphrase, registryId, txHash) {
|
|
84
|
+
const networkId = createHash("sha256").update(networkPassphrase, "utf8").digest("hex");
|
|
85
|
+
return `${networkId}:${registryId.toLowerCase()}:${txHash.toLowerCase()}`;
|
|
86
|
+
}
|
|
87
|
+
export function createReappPaymentMiddleware(options) {
|
|
88
|
+
if (!options || typeof options !== "object") {
|
|
89
|
+
throw new Error("REAPP payment middleware options are required.");
|
|
90
|
+
}
|
|
91
|
+
if (!options.redemptionStore || typeof options.redemptionStore.consumeOnce !== "function") {
|
|
92
|
+
throw new Error("redemptionStore with an atomic consumeOnce(record) operation is required.");
|
|
93
|
+
}
|
|
94
|
+
const networkConfig = options.networkConfig ?? TESTNET;
|
|
95
|
+
const merchant = stellarAddress("merchant", options.merchant);
|
|
96
|
+
const registryId = exactText("networkConfig.mandateRegistryId", networkConfig.mandateRegistryId);
|
|
97
|
+
if (!StrKey.isValidContract(registryId)) {
|
|
98
|
+
throw new Error("networkConfig.mandateRegistryId must be a valid Stellar contract address.");
|
|
99
|
+
}
|
|
100
|
+
const asset = exactText("asset", options.asset ?? networkConfig.nativeSac);
|
|
101
|
+
if (!StrKey.isValidContract(asset)) {
|
|
102
|
+
throw new Error("asset must be a valid Stellar contract address.");
|
|
103
|
+
}
|
|
104
|
+
const scheme = exactText("scheme", options.scheme ?? "reapp-soroban");
|
|
105
|
+
const network = exactText("network", options.network ?? "stellar-testnet");
|
|
106
|
+
const decimals = options.decimals ?? 7;
|
|
107
|
+
const maxHeaderBytes = options.maxHeaderBytes ?? DEFAULT_MAX_HEADER_BYTES;
|
|
108
|
+
const maxProofAgeLedgers = options.maxProofAgeLedgers ?? DEFAULT_MAX_PROOF_AGE_LEDGERS;
|
|
109
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 38) {
|
|
110
|
+
throw new Error("decimals must be an integer from 0 through 38.");
|
|
111
|
+
}
|
|
112
|
+
if (!Number.isInteger(maxHeaderBytes) || maxHeaderBytes < 256 || maxHeaderBytes > 65_536) {
|
|
113
|
+
throw new Error("maxHeaderBytes must be an integer from 256 through 65536.");
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isInteger(maxProofAgeLedgers) || maxProofAgeLedgers < 0 || maxProofAgeLedgers > 1_000_000) {
|
|
116
|
+
throw new Error("maxProofAgeLedgers must be an integer from 0 through 1000000.");
|
|
117
|
+
}
|
|
118
|
+
if (typeof options.amount === "string") {
|
|
119
|
+
const staticAmount = toStroops(exactText("amount", options.amount), decimals);
|
|
120
|
+
if (staticAmount <= 0n)
|
|
121
|
+
throw new Error("amount must be greater than zero.");
|
|
122
|
+
}
|
|
123
|
+
if (typeof options.resource === "string")
|
|
124
|
+
exactText("resource", options.resource);
|
|
125
|
+
const verifier = options.verifier ?? createStellarPaymentVerifier({
|
|
126
|
+
networkConfig,
|
|
127
|
+
sourceAccount: options.sourceAccount ?? merchant,
|
|
128
|
+
pollAttempts: options.pollAttempts,
|
|
129
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
130
|
+
maxProofAgeLedgers,
|
|
131
|
+
allowHttpRpc: options.allowHttpRpc,
|
|
132
|
+
});
|
|
133
|
+
const handle = async (request, response, next) => {
|
|
134
|
+
let requirement;
|
|
135
|
+
try {
|
|
136
|
+
const amount = resolveRequestValue("amount", options.amount, request);
|
|
137
|
+
const amountStroops = toStroops(amount, decimals);
|
|
138
|
+
if (amountStroops <= 0n)
|
|
139
|
+
throw new Error("amount must be greater than zero.");
|
|
140
|
+
const resource = options.resource === undefined
|
|
141
|
+
? exactText("resource", request.originalUrl || request.url)
|
|
142
|
+
: resolveRequestValue("resource", options.resource, request);
|
|
143
|
+
requirement = Object.freeze({
|
|
144
|
+
scheme,
|
|
145
|
+
network,
|
|
146
|
+
resource,
|
|
147
|
+
merchant,
|
|
148
|
+
asset,
|
|
149
|
+
amount,
|
|
150
|
+
amountStroops,
|
|
151
|
+
registryId,
|
|
152
|
+
decimals,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
json(response, 500, { error: "payment middleware configuration failed closed" });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const challenge = buildChallenge(requirement);
|
|
160
|
+
let header;
|
|
161
|
+
try {
|
|
162
|
+
header = canonicalPaymentHeader(request, maxHeaderBytes);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
json(response, 402, {
|
|
166
|
+
error: error instanceof Error ? error.message : "malformed X-PAYMENT proof",
|
|
167
|
+
...challenge,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (!header) {
|
|
172
|
+
json(response, 402, challenge);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
let proof;
|
|
176
|
+
try {
|
|
177
|
+
proof = decodePaymentProof(header);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
json(response, 402, { error: "malformed X-PAYMENT proof", ...challenge });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (proof.scheme !== requirement.scheme) {
|
|
184
|
+
json(response, 402, { error: "payment proof scheme does not match this API", ...challenge });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (proof.network !== requirement.network) {
|
|
188
|
+
json(response, 402, { error: "payment proof network does not match this API", ...challenge });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (!/^[0-9a-f]{64}$/i.test(proof.txHash)) {
|
|
192
|
+
json(response, 402, { error: "payment proof transaction hash is invalid", ...challenge });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
let verdict;
|
|
196
|
+
try {
|
|
197
|
+
// txHash is the only caller-supplied field allowed across this boundary.
|
|
198
|
+
verdict = await verifier.verify(proof.txHash.toLowerCase(), requirement);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
json(response, 503, { error: "payment verification is temporarily unavailable; retry with the same proof", retryable: true }, "1");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (!verdict.ok) {
|
|
205
|
+
if (verdict.kind === "unavailable") {
|
|
206
|
+
json(response, 503, { error: `payment verification unavailable: ${verdict.reason}`, retryable: true }, "1");
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
json(response, 402, { error: `payment not verified on-chain: ${verdict.reason}`, ...challenge });
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const redemptionKey = createRedemptionKey(networkConfig.networkPassphrase, requirement.registryId, verdict.payment.txHash);
|
|
214
|
+
let consumed;
|
|
215
|
+
try {
|
|
216
|
+
consumed = await options.redemptionStore.consumeOnce(Object.freeze({
|
|
217
|
+
key: redemptionKey,
|
|
218
|
+
payment: Object.freeze({ ...verdict.payment }),
|
|
219
|
+
acceptedThroughLedger: verdict.payment.ledger + maxProofAgeLedgers,
|
|
220
|
+
}));
|
|
221
|
+
if (consumed !== "consumed" && consumed !== "duplicate") {
|
|
222
|
+
throw new Error("redemption store returned an unsupported result");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
json(response, 503, { error: "payment redemption store is unavailable; retry with the same proof", retryable: true }, "1");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (consumed === "duplicate") {
|
|
230
|
+
json(response, 409, { error: "this payment was already redeemed" });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
response.locals[REAPP_PAYMENT_LOCALS_KEY] = verdict.payment;
|
|
234
|
+
setPrivateResponseHeaders(response);
|
|
235
|
+
next();
|
|
236
|
+
};
|
|
237
|
+
// Express 4 does not forward rejected async middleware promises. Always end
|
|
238
|
+
// the internal promise explicitly and hand unexpected faults to next(error).
|
|
239
|
+
return (request, response, next) => {
|
|
240
|
+
void handle(request, response, next).catch(next);
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RedemptionRecord, RedemptionStore } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Atomic inside one JavaScript process only. It intentionally never expires or
|
|
4
|
+
* releases a consumed payment. Multi-worker and production deployments must use
|
|
5
|
+
* a durable shared store implementing the same atomic consume-once contract.
|
|
6
|
+
*/
|
|
7
|
+
export declare class InMemoryRedemptionStore implements RedemptionStore {
|
|
8
|
+
private readonly records;
|
|
9
|
+
consumeOnce(record: Readonly<RedemptionRecord>): "consumed" | "duplicate";
|
|
10
|
+
has(key: string): boolean;
|
|
11
|
+
get(key: string): Readonly<RedemptionRecord> | undefined;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=proof-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-store.d.ts","sourceRoot":"","sources":["../src/proof-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiD;IAEzE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,UAAU,GAAG,WAAW;IAMzE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC,GAAG,SAAS;CAGzD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic inside one JavaScript process only. It intentionally never expires or
|
|
3
|
+
* releases a consumed payment. Multi-worker and production deployments must use
|
|
4
|
+
* a durable shared store implementing the same atomic consume-once contract.
|
|
5
|
+
*/
|
|
6
|
+
export class InMemoryRedemptionStore {
|
|
7
|
+
records = new Map();
|
|
8
|
+
consumeOnce(record) {
|
|
9
|
+
if (this.records.has(record.key))
|
|
10
|
+
return "duplicate";
|
|
11
|
+
this.records.set(record.key, Object.freeze({ ...record }));
|
|
12
|
+
return "consumed";
|
|
13
|
+
}
|
|
14
|
+
has(key) {
|
|
15
|
+
return this.records.has(key);
|
|
16
|
+
}
|
|
17
|
+
get(key) {
|
|
18
|
+
return this.records.get(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=proof-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-store.js","sourceRoot":"","sources":["../src/proof-store.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,uBAAuB;IACjB,OAAO,GAAG,IAAI,GAAG,EAAsC,CAAC;IAEzE,WAAW,CAAC,MAAkC;QAC5C,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;YAAE,OAAO,WAAW,CAAC;QACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { Request } from "express";
|
|
2
|
+
import type { NetworkConfig } from "@reapp-sdk/stellar";
|
|
3
|
+
/** A request-specific price and the immutable deployment it must settle through. */
|
|
4
|
+
export interface PaymentRequirement {
|
|
5
|
+
scheme: string;
|
|
6
|
+
network: string;
|
|
7
|
+
resource: string;
|
|
8
|
+
merchant: string;
|
|
9
|
+
asset: string;
|
|
10
|
+
amount: string;
|
|
11
|
+
amountStroops: bigint;
|
|
12
|
+
registryId: string;
|
|
13
|
+
decimals: number;
|
|
14
|
+
}
|
|
15
|
+
/** Settlement evidence derived from Stellar, never from caller-supplied proof fields. */
|
|
16
|
+
export interface VerifiedPayment {
|
|
17
|
+
txHash: string;
|
|
18
|
+
ledger: number;
|
|
19
|
+
mandateId: string;
|
|
20
|
+
user: string;
|
|
21
|
+
agent: string;
|
|
22
|
+
amount: string;
|
|
23
|
+
amountStroops: bigint;
|
|
24
|
+
merchant: string;
|
|
25
|
+
asset: string;
|
|
26
|
+
registryId: string;
|
|
27
|
+
scheme: string;
|
|
28
|
+
network: string;
|
|
29
|
+
}
|
|
30
|
+
export type VerificationResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
payment: VerifiedPayment;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
kind: "invalid" | "unavailable";
|
|
36
|
+
reason: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* A verifier receives only a transaction hash. Header claims such as amount and
|
|
40
|
+
* mandate id are deliberately absent so they cannot become authorization data.
|
|
41
|
+
*/
|
|
42
|
+
export interface PaymentVerifier {
|
|
43
|
+
verify(txHash: string, requirement: PaymentRequirement): Promise<VerificationResult>;
|
|
44
|
+
}
|
|
45
|
+
export type RequestValue = string | ((request: Request) => string);
|
|
46
|
+
export interface RedemptionRecord {
|
|
47
|
+
/** Network-passphrase hash, registry id, and normalized transaction hash. */
|
|
48
|
+
key: string;
|
|
49
|
+
payment: Readonly<VerifiedPayment>;
|
|
50
|
+
/** The proof was accepted only inside this verifier freshness window. */
|
|
51
|
+
acceptedThroughLedger: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Production implementations must make consumeOnce linearizable across every
|
|
55
|
+
* process and host. Only the caller receiving `consumed` may serve the resource.
|
|
56
|
+
*/
|
|
57
|
+
export interface RedemptionStore {
|
|
58
|
+
consumeOnce(record: Readonly<RedemptionRecord>): "consumed" | "duplicate" | Promise<"consumed" | "duplicate">;
|
|
59
|
+
}
|
|
60
|
+
export interface ReappPaymentMiddlewareOptions {
|
|
61
|
+
/** Merchant address that must receive the contract-authorized transfer. */
|
|
62
|
+
merchant: string;
|
|
63
|
+
/** Price as a human decimal string, or a request-specific resolver. */
|
|
64
|
+
amount: RequestValue;
|
|
65
|
+
/** Resource identifier placed in the 402 challenge. Defaults to originalUrl. */
|
|
66
|
+
resource?: RequestValue;
|
|
67
|
+
/** SEP-41 asset contract. Defaults to networkConfig.nativeSac. */
|
|
68
|
+
asset?: string;
|
|
69
|
+
/** Contract/RPC configuration. Defaults to REAPP testnet. */
|
|
70
|
+
networkConfig?: NetworkConfig;
|
|
71
|
+
/** x402 network label. Defaults to stellar-testnet. */
|
|
72
|
+
network?: string;
|
|
73
|
+
/** Settlement scheme. Defaults to reapp-soroban. */
|
|
74
|
+
scheme?: string;
|
|
75
|
+
/** Asset decimals. Defaults to 7. */
|
|
76
|
+
decimals?: number;
|
|
77
|
+
/** Funded G-address used only for read-only contract simulations. */
|
|
78
|
+
sourceAccount?: string;
|
|
79
|
+
/** Required atomic redemption store. */
|
|
80
|
+
redemptionStore: RedemptionStore;
|
|
81
|
+
/** Optional verifier injection for tests or alternate trusted RPC infrastructure. */
|
|
82
|
+
verifier?: PaymentVerifier;
|
|
83
|
+
/** Transaction NOT_FOUND retries for the default verifier. Defaults to 15. */
|
|
84
|
+
pollAttempts?: number;
|
|
85
|
+
/** Delay between transaction reads for the default verifier. Defaults to 1000ms. */
|
|
86
|
+
pollIntervalMs?: number;
|
|
87
|
+
/** Maximum accepted age in ledgers. Defaults to 120. */
|
|
88
|
+
maxProofAgeLedgers?: number;
|
|
89
|
+
/** Maximum X-PAYMENT header bytes. Defaults to 8192. */
|
|
90
|
+
maxHeaderBytes?: number;
|
|
91
|
+
/** Explicit development escape hatch for an http:// RPC URL. Defaults to false. */
|
|
92
|
+
allowHttpRpc?: boolean;
|
|
93
|
+
}
|
|
94
|
+
export interface X402Challenge {
|
|
95
|
+
x402Version: 1;
|
|
96
|
+
accepts: Array<{
|
|
97
|
+
scheme: string;
|
|
98
|
+
network: string;
|
|
99
|
+
maxAmountRequired: string;
|
|
100
|
+
asset: string;
|
|
101
|
+
payTo: string;
|
|
102
|
+
resource: string;
|
|
103
|
+
extra: {
|
|
104
|
+
contract: string;
|
|
105
|
+
};
|
|
106
|
+
}>;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Buffer } from "buffer";
|
|
2
|
+
import { xdr } from "@stellar/stellar-sdk";
|
|
3
|
+
import { type NetworkConfig } from "@reapp-sdk/stellar";
|
|
4
|
+
import type { PaymentVerifier } from "./types.js";
|
|
5
|
+
export interface DecodedValue {
|
|
6
|
+
/** Original ScVal discriminant, retained so string/symbol and numeric types cannot blur. */
|
|
7
|
+
type: string;
|
|
8
|
+
value: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface DecodedEvent {
|
|
11
|
+
type: string;
|
|
12
|
+
contractId: string | null;
|
|
13
|
+
topics: readonly DecodedValue[];
|
|
14
|
+
data: DecodedValue;
|
|
15
|
+
}
|
|
16
|
+
export interface PaymentCheck {
|
|
17
|
+
merchant: string;
|
|
18
|
+
registryId: string;
|
|
19
|
+
priceStroops: bigint;
|
|
20
|
+
}
|
|
21
|
+
export type PaymentSelection = {
|
|
22
|
+
ok: true;
|
|
23
|
+
amount: bigint;
|
|
24
|
+
mandateId: Buffer;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
export interface LoadedTransaction {
|
|
30
|
+
status: string;
|
|
31
|
+
ledger?: number;
|
|
32
|
+
latestLedger?: number;
|
|
33
|
+
events?: readonly DecodedEvent[];
|
|
34
|
+
}
|
|
35
|
+
export interface LoadedMandate {
|
|
36
|
+
user: string;
|
|
37
|
+
agent: string;
|
|
38
|
+
merchant: string;
|
|
39
|
+
asset: string;
|
|
40
|
+
}
|
|
41
|
+
export interface StellarVerifierOptions {
|
|
42
|
+
networkConfig: NetworkConfig;
|
|
43
|
+
sourceAccount?: string;
|
|
44
|
+
pollAttempts?: number;
|
|
45
|
+
pollIntervalMs?: number;
|
|
46
|
+
maxProofAgeLedgers?: number;
|
|
47
|
+
allowHttpRpc?: boolean;
|
|
48
|
+
/** Injection points used by deterministic tests and alternate trusted RPC stacks. */
|
|
49
|
+
loadNetworkPassphrase?: () => Promise<string>;
|
|
50
|
+
loadTransaction?: (txHash: string) => Promise<LoadedTransaction>;
|
|
51
|
+
loadMandate?: (mandateId: Buffer) => Promise<LoadedMandate>;
|
|
52
|
+
wait?: (milliseconds: number) => Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export declare function extractContractEvents(meta: xdr.TransactionMeta): xdr.ContractEvent[];
|
|
55
|
+
export declare function interpretEvents(events: readonly xdr.ContractEvent[]): DecodedEvent[];
|
|
56
|
+
/** Pure, fail-closed selection of one unambiguous registry payment event. */
|
|
57
|
+
export declare function selectPayment(decoded: readonly DecodedEvent[], check: PaymentCheck): PaymentSelection;
|
|
58
|
+
export interface TransferCheck {
|
|
59
|
+
asset: string;
|
|
60
|
+
user: string;
|
|
61
|
+
merchant: string;
|
|
62
|
+
amount: bigint;
|
|
63
|
+
}
|
|
64
|
+
/** Require exactly one matching SEP-41 transfer emitted by the configured asset. */
|
|
65
|
+
export declare function selectTransfer(decoded: readonly DecodedEvent[], check: TransferCheck): {
|
|
66
|
+
ok: true;
|
|
67
|
+
} | {
|
|
68
|
+
ok: false;
|
|
69
|
+
reason: string;
|
|
70
|
+
};
|
|
71
|
+
export declare function createStellarPaymentVerifier(options: StellarVerifierOptions): PaymentVerifier;
|
|
72
|
+
//# sourceMappingURL=verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { Buffer } from "buffer";
|
|
2
|
+
import { rpc, scValToNative, StrKey } from "@stellar/stellar-sdk";
|
|
3
|
+
import { Client } from "@reapp-sdk/stellar";
|
|
4
|
+
export function extractContractEvents(meta) {
|
|
5
|
+
try {
|
|
6
|
+
return meta.v4().operations().flatMap((operation) => operation.events());
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
try {
|
|
10
|
+
return meta.v3().sorobanMeta()?.events() ?? [];
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error("unsupported Stellar transaction metadata version");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function decodeScVal(value) {
|
|
18
|
+
const type = value.switch().name;
|
|
19
|
+
if (type === "scvVec") {
|
|
20
|
+
return {
|
|
21
|
+
type,
|
|
22
|
+
value: (value.vec() ?? []).map((entry) => decodeScVal(entry)),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return { type, value: scValToNative(value) };
|
|
26
|
+
}
|
|
27
|
+
export function interpretEvents(events) {
|
|
28
|
+
return events.map((event) => {
|
|
29
|
+
const rawContractId = event.contractId();
|
|
30
|
+
const contractId = rawContractId
|
|
31
|
+
? StrKey.encodeContract(rawContractId)
|
|
32
|
+
: null;
|
|
33
|
+
try {
|
|
34
|
+
const body = event.body().v0();
|
|
35
|
+
return {
|
|
36
|
+
type: event.type().name,
|
|
37
|
+
contractId,
|
|
38
|
+
topics: body.topics().map((topic) => decodeScVal(topic)),
|
|
39
|
+
data: decodeScVal(body.data()),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return {
|
|
44
|
+
type: event.type().name,
|
|
45
|
+
contractId,
|
|
46
|
+
topics: [],
|
|
47
|
+
data: { type: "malformed", value: null },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function exactValue(value, type, expected) {
|
|
53
|
+
return value?.type === type && value.value === expected;
|
|
54
|
+
}
|
|
55
|
+
function paymentPayload(event) {
|
|
56
|
+
if (event.data.type !== "scvVec" || !Array.isArray(event.data.value))
|
|
57
|
+
return undefined;
|
|
58
|
+
const values = event.data.value;
|
|
59
|
+
if (values.length !== 2)
|
|
60
|
+
return undefined;
|
|
61
|
+
const idValue = values[0];
|
|
62
|
+
const amountValue = values[1];
|
|
63
|
+
if (idValue?.type !== "scvBytes" || amountValue?.type !== "scvI128")
|
|
64
|
+
return undefined;
|
|
65
|
+
if (!(Buffer.isBuffer(idValue.value) || idValue.value instanceof Uint8Array))
|
|
66
|
+
return undefined;
|
|
67
|
+
if (typeof amountValue.value !== "bigint")
|
|
68
|
+
return undefined;
|
|
69
|
+
const mandateId = Buffer.from(idValue.value);
|
|
70
|
+
if (mandateId.length !== 32 || amountValue.value <= 0n)
|
|
71
|
+
return undefined;
|
|
72
|
+
return { mandateId, amount: amountValue.value };
|
|
73
|
+
}
|
|
74
|
+
/** Pure, fail-closed selection of one unambiguous registry payment event. */
|
|
75
|
+
export function selectPayment(decoded, check) {
|
|
76
|
+
if (decoded.length === 0) {
|
|
77
|
+
return { ok: false, reason: "transaction carried no Soroban contract events" };
|
|
78
|
+
}
|
|
79
|
+
const eligible = [];
|
|
80
|
+
let largestUnderpayment;
|
|
81
|
+
for (const event of decoded) {
|
|
82
|
+
if (event.type !== "contract" || event.contractId !== check.registryId)
|
|
83
|
+
continue;
|
|
84
|
+
if (event.topics.length !== 2)
|
|
85
|
+
continue;
|
|
86
|
+
if (!exactValue(event.topics[0], "scvSymbol", "payment"))
|
|
87
|
+
continue;
|
|
88
|
+
if (!exactValue(event.topics[1], "scvAddress", check.merchant))
|
|
89
|
+
continue;
|
|
90
|
+
const payload = paymentPayload(event);
|
|
91
|
+
if (!payload)
|
|
92
|
+
continue;
|
|
93
|
+
if (payload.amount < check.priceStroops) {
|
|
94
|
+
if (largestUnderpayment === undefined || payload.amount > largestUnderpayment) {
|
|
95
|
+
largestUnderpayment = payload.amount;
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
eligible.push(payload);
|
|
100
|
+
}
|
|
101
|
+
if (eligible.length > 1) {
|
|
102
|
+
return { ok: false, reason: "transaction contains multiple eligible registry payments" };
|
|
103
|
+
}
|
|
104
|
+
const selected = eligible[0];
|
|
105
|
+
if (selected)
|
|
106
|
+
return { ok: true, ...selected };
|
|
107
|
+
if (largestUnderpayment !== undefined) {
|
|
108
|
+
return { ok: false, reason: `paid ${largestUnderpayment} stroops, below the price` };
|
|
109
|
+
}
|
|
110
|
+
return { ok: false, reason: "no trusted registry payment to this merchant in that transaction" };
|
|
111
|
+
}
|
|
112
|
+
/** Require exactly one matching SEP-41 transfer emitted by the configured asset. */
|
|
113
|
+
export function selectTransfer(decoded, check) {
|
|
114
|
+
let matches = 0;
|
|
115
|
+
for (const event of decoded) {
|
|
116
|
+
if (event.type !== "contract" || event.contractId !== check.asset)
|
|
117
|
+
continue;
|
|
118
|
+
if (event.topics.length < 3)
|
|
119
|
+
continue;
|
|
120
|
+
if (!exactValue(event.topics[0], "scvSymbol", "transfer"))
|
|
121
|
+
continue;
|
|
122
|
+
if (!exactValue(event.topics[1], "scvAddress", check.user))
|
|
123
|
+
continue;
|
|
124
|
+
if (!exactValue(event.topics[2], "scvAddress", check.merchant))
|
|
125
|
+
continue;
|
|
126
|
+
if (event.data.type !== "scvI128" || event.data.value !== check.amount)
|
|
127
|
+
continue;
|
|
128
|
+
matches += 1;
|
|
129
|
+
}
|
|
130
|
+
if (matches === 1)
|
|
131
|
+
return { ok: true };
|
|
132
|
+
if (matches > 1) {
|
|
133
|
+
return { ok: false, reason: "transaction contains multiple matching asset transfers" };
|
|
134
|
+
}
|
|
135
|
+
return { ok: false, reason: "transaction lacks the matching transfer from mandate user to merchant" };
|
|
136
|
+
}
|
|
137
|
+
function formatStroops(value, decimals) {
|
|
138
|
+
if (decimals === 0)
|
|
139
|
+
return value.toString();
|
|
140
|
+
const scale = 10n ** BigInt(decimals);
|
|
141
|
+
const whole = value / scale;
|
|
142
|
+
const fraction = (value % scale).toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
143
|
+
return fraction.length === 0 ? whole.toString() : `${whole}.${fraction}`;
|
|
144
|
+
}
|
|
145
|
+
const defaultWait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
146
|
+
export function createStellarPaymentVerifier(options) {
|
|
147
|
+
const pollAttempts = options.pollAttempts ?? 15;
|
|
148
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1_000;
|
|
149
|
+
const maxProofAgeLedgers = options.maxProofAgeLedgers ?? 120;
|
|
150
|
+
if (!Number.isInteger(pollAttempts) || pollAttempts < 0) {
|
|
151
|
+
throw new Error("pollAttempts must be a non-negative integer.");
|
|
152
|
+
}
|
|
153
|
+
if (!Number.isInteger(pollIntervalMs) || pollIntervalMs < 0) {
|
|
154
|
+
throw new Error("pollIntervalMs must be a non-negative integer.");
|
|
155
|
+
}
|
|
156
|
+
if (!Number.isInteger(maxProofAgeLedgers) || maxProofAgeLedgers < 0) {
|
|
157
|
+
throw new Error("maxProofAgeLedgers must be a non-negative integer.");
|
|
158
|
+
}
|
|
159
|
+
const network = options.networkConfig;
|
|
160
|
+
let rpcUrl;
|
|
161
|
+
try {
|
|
162
|
+
rpcUrl = new URL(network.rpcUrl);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new Error("networkConfig.rpcUrl must be an absolute URL.");
|
|
166
|
+
}
|
|
167
|
+
const allowHttp = options.allowHttpRpc === true;
|
|
168
|
+
if (rpcUrl.protocol !== "https:" && !(allowHttp && rpcUrl.protocol === "http:")) {
|
|
169
|
+
throw new Error("RPC must use https:// unless allowHttpRpc is explicitly enabled for development.");
|
|
170
|
+
}
|
|
171
|
+
const server = new rpc.Server(network.rpcUrl, { allowHttp });
|
|
172
|
+
const loadNetworkPassphrase = options.loadNetworkPassphrase
|
|
173
|
+
?? (async () => (await server.getNetwork()).passphrase);
|
|
174
|
+
const defaultTransactionLoader = async (txHash) => {
|
|
175
|
+
const transaction = await server.getTransaction(txHash);
|
|
176
|
+
if (transaction.status !== "SUCCESS") {
|
|
177
|
+
return {
|
|
178
|
+
status: transaction.status,
|
|
179
|
+
latestLedger: transaction.latestLedger,
|
|
180
|
+
ledger: "ledger" in transaction ? transaction.ledger : undefined,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
status: transaction.status,
|
|
185
|
+
ledger: transaction.ledger,
|
|
186
|
+
latestLedger: transaction.latestLedger,
|
|
187
|
+
events: interpretEvents(extractContractEvents(transaction.resultMetaXdr)),
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
let defaultMandateLoader;
|
|
191
|
+
if (!options.loadMandate) {
|
|
192
|
+
if (!options.sourceAccount || !StrKey.isValidEd25519PublicKey(options.sourceAccount)) {
|
|
193
|
+
throw new Error("sourceAccount must be a funded Stellar account address (G...).");
|
|
194
|
+
}
|
|
195
|
+
const refuseSigning = async () => {
|
|
196
|
+
throw new Error("REAPP payment verification is read-only and never signs.");
|
|
197
|
+
};
|
|
198
|
+
const client = new Client({
|
|
199
|
+
contractId: network.mandateRegistryId,
|
|
200
|
+
rpcUrl: network.rpcUrl,
|
|
201
|
+
networkPassphrase: network.networkPassphrase,
|
|
202
|
+
publicKey: options.sourceAccount,
|
|
203
|
+
signTransaction: refuseSigning,
|
|
204
|
+
allowHttp,
|
|
205
|
+
});
|
|
206
|
+
defaultMandateLoader = async (mandateId) => {
|
|
207
|
+
const mandate = (await client.get_mandate({ mandate_id: mandateId })).result.unwrap();
|
|
208
|
+
return {
|
|
209
|
+
user: mandate.user,
|
|
210
|
+
agent: mandate.agent,
|
|
211
|
+
merchant: mandate.merchant,
|
|
212
|
+
asset: mandate.asset,
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const loadTransaction = options.loadTransaction ?? defaultTransactionLoader;
|
|
217
|
+
const loadMandate = options.loadMandate ?? defaultMandateLoader;
|
|
218
|
+
if (!loadMandate)
|
|
219
|
+
throw new Error("loadMandate or sourceAccount is required.");
|
|
220
|
+
const wait = options.wait ?? defaultWait;
|
|
221
|
+
return {
|
|
222
|
+
async verify(txHash, requirement) {
|
|
223
|
+
const normalizedTxHash = txHash.toLowerCase();
|
|
224
|
+
if (!/^[0-9a-f]{64}$/.test(normalizedTxHash)) {
|
|
225
|
+
return { ok: false, kind: "invalid", reason: "proof transaction hash is not 64 hex characters" };
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const rpcPassphrase = await loadNetworkPassphrase();
|
|
229
|
+
if (rpcPassphrase !== network.networkPassphrase) {
|
|
230
|
+
return { ok: false, kind: "unavailable", reason: "RPC network passphrase does not match configuration" };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
kind: "unavailable",
|
|
237
|
+
reason: `RPC network identity read failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
let transaction;
|
|
241
|
+
try {
|
|
242
|
+
for (let attempt = 0; attempt <= pollAttempts; attempt += 1) {
|
|
243
|
+
transaction = await loadTransaction(normalizedTxHash);
|
|
244
|
+
if (transaction.status !== "NOT_FOUND")
|
|
245
|
+
break;
|
|
246
|
+
if (attempt < pollAttempts)
|
|
247
|
+
await wait(pollIntervalMs);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
kind: "unavailable",
|
|
254
|
+
reason: `transaction RPC read failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
if (!transaction || transaction.status === "NOT_FOUND") {
|
|
258
|
+
return { ok: false, kind: "unavailable", reason: "transaction was not found before the verification timeout" };
|
|
259
|
+
}
|
|
260
|
+
if (transaction.status !== "SUCCESS") {
|
|
261
|
+
return { ok: false, kind: "invalid", reason: `transaction is ${transaction.status}, not SUCCESS` };
|
|
262
|
+
}
|
|
263
|
+
if (!Number.isSafeInteger(transaction.ledger) || !Number.isSafeInteger(transaction.latestLedger)) {
|
|
264
|
+
return { ok: false, kind: "unavailable", reason: "RPC omitted safe transaction freshness ledger numbers" };
|
|
265
|
+
}
|
|
266
|
+
const ledger = transaction.ledger;
|
|
267
|
+
const latestLedger = transaction.latestLedger;
|
|
268
|
+
if (ledger > latestLedger) {
|
|
269
|
+
return { ok: false, kind: "unavailable", reason: "transaction ledger is ahead of the RPC latest ledger" };
|
|
270
|
+
}
|
|
271
|
+
if (latestLedger - ledger > maxProofAgeLedgers) {
|
|
272
|
+
return { ok: false, kind: "invalid", reason: "transaction is outside the accepted proof freshness window" };
|
|
273
|
+
}
|
|
274
|
+
const events = transaction.events ?? [];
|
|
275
|
+
const selected = selectPayment(events, {
|
|
276
|
+
merchant: requirement.merchant,
|
|
277
|
+
registryId: requirement.registryId,
|
|
278
|
+
priceStroops: requirement.amountStroops,
|
|
279
|
+
});
|
|
280
|
+
if (!selected.ok)
|
|
281
|
+
return { ok: false, kind: "invalid", reason: selected.reason };
|
|
282
|
+
let mandate;
|
|
283
|
+
try {
|
|
284
|
+
mandate = await loadMandate(selected.mandateId);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
return {
|
|
288
|
+
ok: false,
|
|
289
|
+
kind: "unavailable",
|
|
290
|
+
reason: `mandate read failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (mandate.merchant !== requirement.merchant) {
|
|
294
|
+
return { ok: false, kind: "invalid", reason: "stored mandate merchant does not match this API" };
|
|
295
|
+
}
|
|
296
|
+
if (mandate.asset !== requirement.asset) {
|
|
297
|
+
return { ok: false, kind: "invalid", reason: "stored mandate asset does not match this API" };
|
|
298
|
+
}
|
|
299
|
+
const transfer = selectTransfer(events, {
|
|
300
|
+
asset: mandate.asset,
|
|
301
|
+
user: mandate.user,
|
|
302
|
+
merchant: mandate.merchant,
|
|
303
|
+
amount: selected.amount,
|
|
304
|
+
});
|
|
305
|
+
if (!transfer.ok)
|
|
306
|
+
return { ok: false, kind: "invalid", reason: transfer.reason };
|
|
307
|
+
return {
|
|
308
|
+
ok: true,
|
|
309
|
+
payment: {
|
|
310
|
+
txHash: normalizedTxHash,
|
|
311
|
+
ledger,
|
|
312
|
+
mandateId: selected.mandateId.toString("hex"),
|
|
313
|
+
user: mandate.user,
|
|
314
|
+
agent: mandate.agent,
|
|
315
|
+
amount: formatStroops(selected.amount, requirement.decimals),
|
|
316
|
+
amountStroops: selected.amount,
|
|
317
|
+
merchant: mandate.merchant,
|
|
318
|
+
asset: mandate.asset,
|
|
319
|
+
registryId: requirement.registryId,
|
|
320
|
+
scheme: requirement.scheme,
|
|
321
|
+
network: requirement.network,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=verification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../src/verification.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reapp-sdk/express-middleware",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fail-closed Express middleware for REAPP x402 APIs with independent on-chain settlement verification.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"reapp",
|
|
7
|
+
"express",
|
|
8
|
+
"middleware",
|
|
9
|
+
"x402",
|
|
10
|
+
"stellar",
|
|
11
|
+
"soroban",
|
|
12
|
+
"agent-payments",
|
|
13
|
+
"payment-verification"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": ["dist"],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"test": "node --import tsx --test src/middleware.test.ts src/verification.test.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@reapp-sdk/core": "^0.2.2",
|
|
31
|
+
"@reapp-sdk/stellar": "^0.2.1",
|
|
32
|
+
"@stellar/stellar-sdk": "^14.5.0",
|
|
33
|
+
"buffer": "6.0.3"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"express": "^4.18.0 || ^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.10.2",
|
|
40
|
+
"@types/express": "^5.0.6",
|
|
41
|
+
"express": "^5.2.1",
|
|
42
|
+
"typescript": "^5.7.2"
|
|
43
|
+
},
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|