@devlab.group/agent-commerce 1.2.0 → 1.4.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 +94 -66
- package/dist/ap2.d.ts +164 -0
- package/dist/ap2.js +669 -0
- package/dist/ap2.js.map +1 -0
- package/dist/authorization-CPDcpJVa.d.ts +123 -0
- package/dist/chunk-7NKWFGBY.js +68 -0
- package/dist/chunk-7NKWFGBY.js.map +1 -0
- package/dist/{chunk-5TKNTWMC.js → chunk-IUR3Q7DE.js} +3 -3
- package/dist/{chunk-5TKNTWMC.js.map → chunk-IUR3Q7DE.js.map} +1 -1
- package/dist/{chunk-EIACECVM.js → chunk-ZDVJ7EVL.js} +150 -67
- package/dist/chunk-ZDVJ7EVL.js.map +1 -0
- package/dist/cli/index.js +788 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +256 -7
- package/dist/index.js +5636 -136
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +3 -2
- package/dist/mcp.js +10 -12
- package/dist/mcp.js.map +1 -1
- package/dist/{protocol-adapter-BAKGnuZ4.d.ts → protocol-adapter-CaMZj6Wm.d.ts} +20 -3
- package/dist/{runtime-Bqg9qW3t.d.ts → runtime-CKq3d_3K.d.ts} +25 -5
- package/dist/types-CJtwLu1c.d.ts +99 -0
- package/dist/x402.d.ts +1 -1
- package/dist/x402.js +3 -3
- package/package.json +28 -4
- package/dist/chunk-EIACECVM.js.map +0 -1
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
<img alt="MCP" src="https://img.shields.io/badge/MCP-supported-6b4fbb">
|
|
12
12
|
<img alt="x402" src="https://img.shields.io/badge/x402-supported-0052ff">
|
|
13
13
|
<img alt="A2A" src="https://img.shields.io/badge/A2A-experimental-f0a30a">
|
|
14
|
+
<img alt="ACP" src="https://img.shields.io/badge/ACP-experimental-f0a30a">
|
|
15
|
+
<img alt="AP2" src="https://img.shields.io/badge/AP2-experimental-f0a30a">
|
|
14
16
|
</p>
|
|
15
17
|
|
|
16
18
|
## What it is, in ten seconds
|
|
@@ -21,19 +23,19 @@ You already have an HTTP API. AI agents want to **discover** it, **call** it and
|
|
|
21
23
|
Agent Commerce Gateway sits in front of your existing API, in **your**
|
|
22
24
|
infrastructure, and does that for you. You describe an endpoint in a YAML file -
|
|
23
25
|
or generate that description from an OpenAPI document you already have - and
|
|
24
|
-
agents get an MCP tool and an x402 paywall.
|
|
26
|
+
agents get an MCP tool and an x402 paywall. Switch on the experimental adapters
|
|
27
|
+
and the same resource is also an A2A skill, or an ACP checkout session. Switch
|
|
28
|
+
on AP2 and a paid resource can also demand a signed mandate: proof the human
|
|
29
|
+
behind the agent approved that exact purchase. The money goes straight to your
|
|
25
30
|
wallet - the gateway never holds it, and never holds your keys.
|
|
26
31
|
|
|
27
32
|
```text
|
|
28
33
|
Your existing API → Agent Commerce Gateway → AI Agent
|
|
29
|
-
|
|
34
|
+
MCP · A2A · ACP · x402 · AP2 · receipts · doctor
|
|
30
35
|
```
|
|
31
36
|
|
|
32
37
|
## Demo
|
|
33
38
|
|
|
34
|
-
<!-- TODO(release): 15-30s GIF - left: buyer agent terminal, right: dashboard,
|
|
35
|
-
overlay: on-chain settlement. Replace this block before tagging. -->
|
|
36
|
-
|
|
37
39
|
```text
|
|
38
40
|
[agent] Discovering resources over MCP...
|
|
39
41
|
[agent] Found: market_report - Premium Market Report (0.01 USDC)
|
|
@@ -54,11 +56,9 @@ Your existing API → Agent Commerce Gateway → AI Agent
|
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
The dashboard at <http://localhost:5173> shows the same request as it happens.
|
|
57
|
-
It polls the authenticated events route
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
header-capable client, never by a browser. Polling is the dashboard's intended
|
|
61
|
-
path, not a degraded mode.
|
|
59
|
+
It polls the authenticated events route rather than streaming, because a browser
|
|
60
|
+
cannot send the admin token over `EventSource`; see
|
|
61
|
+
[why the stream is polled](SECURITY.md#the-live-event-stream-is-polled-not-streamed).
|
|
62
62
|
|
|
63
63
|
## Install
|
|
64
64
|
|
|
@@ -90,17 +90,18 @@ const { url } = await gateway.listen();
|
|
|
90
90
|
|
|
91
91
|
### Optional peers - install only the rails you use
|
|
92
92
|
|
|
93
|
-
The MCP adapter
|
|
94
|
-
needs a dependency the rest of the package does not -
|
|
95
|
-
whole EVM signing and RPC stack, which a gateway
|
|
96
|
-
has no business installing.
|
|
93
|
+
The MCP adapter, the x402 provider and AP2 verification live on their own
|
|
94
|
+
subpaths, because each needs a dependency the rest of the package does not -
|
|
95
|
+
the x402 rail brings the whole EVM signing and RPC stack, which a gateway
|
|
96
|
+
serving a free HTTP resource has no business installing.
|
|
97
97
|
|
|
98
|
-
| You want
|
|
99
|
-
|
|
|
100
|
-
| gateway, config, receipts, CLI
|
|
101
|
-
| expose resources as MCP tools
|
|
102
|
-
| accept x402 payments
|
|
103
|
-
|
|
|
98
|
+
| You want | Install | Import |
|
|
99
|
+
| --------------------------------------- | ---------------------------------- | ------------------------------------------ |
|
|
100
|
+
| gateway, config, receipts, CLI | `@devlab.group/agent-commerce` | `from '@devlab.group/agent-commerce'` |
|
|
101
|
+
| expose resources as MCP tools | `+ @modelcontextprotocol/sdk` | `from '@devlab.group/agent-commerce/mcp'` |
|
|
102
|
+
| accept x402 payments | `+ @x402/core @x402/evm viem` | `from '@devlab.group/agent-commerce/x402'` |
|
|
103
|
+
| verify AP2 mandates, sign checkout JWTs | `+ jose @sd-jwt/core canonicalize` | `from '@devlab.group/agent-commerce/ap2'` |
|
|
104
|
+
| authenticate to a CDP facilitator | `+ @coinbase/x402` | (no import - loaded on demand) |
|
|
104
105
|
|
|
105
106
|
```bash
|
|
106
107
|
npm install @devlab.group/agent-commerce @modelcontextprotocol/sdk @x402/core @x402/evm viem
|
|
@@ -109,8 +110,14 @@ npm install @devlab.group/agent-commerce @modelcontextprotocol/sdk @x402/core @x
|
|
|
109
110
|
```ts
|
|
110
111
|
import { mcp } from '@devlab.group/agent-commerce/mcp';
|
|
111
112
|
import { x402 } from '@devlab.group/agent-commerce/x402';
|
|
113
|
+
import { ap2 } from '@devlab.group/agent-commerce/ap2';
|
|
112
114
|
```
|
|
113
115
|
|
|
116
|
+
The AP2 three are small - about 1.3 MB installed between them, against roughly
|
|
117
|
+
63 MB for the x402 stack - but they stay optional on the same principle: a
|
|
118
|
+
deployment that gates nothing on a mandate should not carry a JOSE stack and an
|
|
119
|
+
SD-JWT parser to serve a resource.
|
|
120
|
+
|
|
114
121
|
Peers are pinned exactly: x402's schemas and EIP-712 domains cross this
|
|
115
122
|
boundary, so a version skew is a correctness problem rather than a convenience
|
|
116
123
|
one. Import a subpath without its
|
|
@@ -157,30 +164,33 @@ To stop and wipe state: `docker compose down -v`.
|
|
|
157
164
|
## How it works
|
|
158
165
|
|
|
159
166
|
```text
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
│
|
|
172
|
-
│
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
168
|
+
│ AI Agent │
|
|
169
|
+
└───────────────────────────────────────┬──────────────────────────────────────┘
|
|
170
|
+
│ MCP · A2A · ACP · HTTP
|
|
171
|
+
│ PAYMENT-SIGNATURE
|
|
172
|
+
│ Agent-Authorization
|
|
173
|
+
┌───────────────────────────────────────▼──────────────────────────────────────┐
|
|
174
|
+
│ Agent Commerce Gateway (yours) │
|
|
175
|
+
│ │
|
|
176
|
+
│ protocol adapters → ExecutionPipeline │
|
|
177
|
+
│ │ │
|
|
178
|
+
│ ┌────────────────────┬──────────────┴──┬────────────────┐ │
|
|
179
|
+
│ ▼ ▼ ▼ ▼ │
|
|
180
|
+
│ AuthorizationProvider PaymentProvider BackendExecutor ReceiptStore │
|
|
181
|
+
│ (ap2) (x402) (bounded HTTP) (SQLite) │
|
|
182
|
+
└───────────────────────────────────────┬──────────────────────────────────────┘
|
|
183
|
+
│
|
|
184
|
+
┌──────────▼─────────┐
|
|
185
|
+
│ Your backend API │
|
|
186
|
+
└────────────────────┘
|
|
178
187
|
```
|
|
179
188
|
|
|
180
189
|
Every protocol adapter converges on **one execution pipeline**. That is what
|
|
181
190
|
makes payment enforcement a property of the system rather than something each
|
|
182
|
-
adapter has to remember.
|
|
183
|
-
|
|
191
|
+
adapter has to remember. Money never passes through the box: the buyer pays the
|
|
192
|
+
merchant directly on chain, and the gateway holds neither the funds nor a key.
|
|
193
|
+
Full detail in [docs/architecture.md](docs/architecture.md).
|
|
184
194
|
|
|
185
195
|
## Configure a resource
|
|
186
196
|
|
|
@@ -219,29 +229,39 @@ It writes a reviewable `resources:` fragment - path, query and JSON body
|
|
|
219
229
|
mapped, schemas converted to what the gateway actually enforces - and
|
|
220
230
|
deliberately leaves `pricing` and `expose` out, because an OpenAPI document has
|
|
221
231
|
no opinion on what an operation costs or who may see it. Credentials are never
|
|
222
|
-
imported. See [
|
|
223
|
-
supported subset.
|
|
232
|
+
imported. See [OpenAPI import](docs/openapi-import.md) for the exact supported subset.
|
|
224
233
|
|
|
225
234
|
See [docs/configuration.md](docs/configuration.md).
|
|
226
235
|
|
|
227
236
|
## Protocol support
|
|
228
237
|
|
|
229
|
-
| Protocol
|
|
230
|
-
|
|
|
231
|
-
| **MCP**
|
|
232
|
-
| **x402**
|
|
233
|
-
| **HTTP**
|
|
234
|
-
| **A2A**
|
|
235
|
-
|
|
|
236
|
-
|
|
|
238
|
+
| Protocol | Status | Pinned revision |
|
|
239
|
+
| --------- | ------------ | -------------------------------------------------------- |
|
|
240
|
+
| **MCP** | Supported | `@modelcontextprotocol/sdk@1.30.0` |
|
|
241
|
+
| **x402** | Supported | x402 v2 (`@x402/core`, `@x402/evm`), scheme `exact`, EVM |
|
|
242
|
+
| **HTTP** | Supported | native routes |
|
|
243
|
+
| **A2A** | Experimental | A2A v1.0.0, binding `JSONRPC`, method `SendMessage` |
|
|
244
|
+
| **ACP** | Experimental | ACP `2026-04-17`, REST checkout + discovery |
|
|
245
|
+
| **AP2** | Experimental | AP2 `v0.2.0`, Direct Checkout Mandate verification |
|
|
246
|
+
| UCP · MPP | Planned | - |
|
|
247
|
+
|
|
248
|
+
[AP2](docs/ap2.md) is in that table because people look there, but it is an **authorization**
|
|
249
|
+
method rather than a transport: it gates settlement on a resource that still
|
|
250
|
+
takes a real payment, and it is the verifying half only - the gateway holds no
|
|
251
|
+
signing key and issues no Checkout Receipt.
|
|
237
252
|
|
|
238
253
|
"Planned" means **no code ships for it**. "Experimental" means the code ships,
|
|
239
|
-
is tested against the official
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
254
|
+
is tested against the protocol's own official artifacts, and serves a narrow
|
|
255
|
+
named subset: [A2A](docs/protocols.md#a2a) and [ACP](docs/protocols.md#acp) are
|
|
256
|
+
both off by default and documented in full there, as are
|
|
257
|
+
[MCP](docs/protocols.md#mcp) and [x402](docs/protocols.md#x402). ACP serves the
|
|
258
|
+
five stable checkout operations and advertises `services: ["checkout"]` and
|
|
259
|
+
nothing more; its `payment_data` stays with the merchant's own checkout and is
|
|
260
|
+
never turned into an x402 payment.
|
|
261
|
+
|
|
262
|
+
Each adapter reports its own `supportedSpec`, `capabilities` and `unsupported`
|
|
263
|
+
list at runtime through `GET /.well-known/agent-commerce` and
|
|
264
|
+
`agent-commerce doctor`, so the claim is checkable rather than marketing.
|
|
245
265
|
|
|
246
266
|
## Payment model
|
|
247
267
|
|
|
@@ -256,8 +276,10 @@ checkable, not marketing. Detail: [docs/protocols.md](docs/protocols.md).
|
|
|
256
276
|
- **Real settlement in CI.** The end-to-end test asserts the buyer's balance
|
|
257
277
|
falls and the merchant's rises by exactly the price, with a real transaction
|
|
258
278
|
hash in the receipt. A log line saying "payment successful" would not count.
|
|
259
|
-
|
|
260
|
-
|
|
279
|
+
- **Authorization is separate from payment.** A resource can also require an
|
|
280
|
+
AP2 mandate, verified before settlement and spendable exactly once. It never
|
|
281
|
+
moves money and never unlocks a resource on its own - the payment still has
|
|
282
|
+
to be real.
|
|
261
283
|
|
|
262
284
|
## Public networks
|
|
263
285
|
|
|
@@ -371,8 +393,10 @@ $ npm run agent-commerce -- doctor --config config-demo.yaml
|
|
|
371
393
|
PASS Config valid - 2 resource(s), merchant "Demo Data Store" (using local chain manifest .deploy/local.json for X402_ASSET, X402_ASSET_NAME, X402_ASSET_VERSION, X402_ASSET_DECIMALS, MERCHANT_WALLET, X402_FACILITATOR_PRIVATE_KEY)
|
|
372
394
|
PASS Gateway healthy and ready at http://127.0.0.1:8080
|
|
373
395
|
PASS Backend 2/2 backend host(s) reachable
|
|
374
|
-
PASS Protocols http=on mcp=on (/mcp) a2a=off
|
|
396
|
+
PASS Protocols http=on mcp=on (/mcp) a2a=off acp=off
|
|
375
397
|
INFO A2A disabled
|
|
398
|
+
INFO ACP disabled
|
|
399
|
+
INFO AP2 disabled
|
|
376
400
|
PASS Payments x402 v2 (scheme=exact) enabled - LOCAL dev chain (eip155:84532, chain id shared with Base Sepolia), destination=0x7099…79C8, facilitator=local
|
|
377
401
|
INFO Payments (MPP) planned - not implemented in this release
|
|
378
402
|
PASS Storage sqlite schema v1 writable; receipts=2
|
|
@@ -393,8 +417,9 @@ Exits non-zero if anything fails. `--json` for machines.
|
|
|
393
417
|
The demo binds everything to `127.0.0.1`. Before putting the gateway anywhere
|
|
394
418
|
reachable by anyone else, know the split:
|
|
395
419
|
|
|
396
|
-
- **Agent routes** (`/api/resources/:id/invoke`, `/mcp
|
|
397
|
-
design - paid resources are protected by payment, not by a
|
|
420
|
+
- **Agent routes** (`/api/resources/:id/invoke`, `/mcp`, the A2A mount) are
|
|
421
|
+
unauthenticated by design - paid resources are protected by payment, not by a
|
|
422
|
+
password. ACP is the exception: its checkout routes require a bearer token.
|
|
398
423
|
- **Operator routes** (`/api/receipts`, `/api/events`, `/api/events/stream`) are
|
|
399
424
|
the merchant's commerce ledger: payer addresses, amounts, settlement hashes.
|
|
400
425
|
They require `server.adminToken`, and return **404** if none is configured.
|
|
@@ -418,12 +443,14 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
418
443
|
|
|
419
444
|
## Roadmap
|
|
420
445
|
|
|
421
|
-
**Now
|
|
422
|
-
|
|
423
|
-
|
|
446
|
+
**Now** - MCP, x402 v2, settlement on the local chain, Base Sepolia and Base
|
|
447
|
+
mainnet, receipts, doctor, deterministic demo, experimental A2A v1.0.0 and ACP
|
|
448
|
+
`2026-04-17` checkout adapters, experimental AP2 v0.2.0 mandate verification,
|
|
449
|
+
and experimental OpenAPI import.
|
|
424
450
|
|
|
425
|
-
**Next** - a
|
|
426
|
-
|
|
451
|
+
**Next** - a `doctor` GitHub Action · UCP · MPP · autonomous-mode AP2 (open
|
|
452
|
+
mandates, agent key binding, constraint evaluation) · more of ACP (carts, feed,
|
|
453
|
+
delegated payment) · Shopify and WooCommerce examples · PostgreSQL · richer
|
|
427
454
|
observability · multi-file and remote OpenAPI sources.
|
|
428
455
|
|
|
429
456
|
New protocols land only after the adapter model survives real use. Scope
|
|
@@ -436,6 +463,7 @@ discipline is a release requirement, not a mood.
|
|
|
436
463
|
| [Architecture](docs/architecture.md) | how the pieces fit |
|
|
437
464
|
| [Payment flow](docs/payment-flow.md) | the paid round trip, and every way it fails |
|
|
438
465
|
| [Protocols](docs/protocols.md) | exactly what is and is not supported |
|
|
466
|
+
| [AP2](docs/ap2.md) | mandate verification and the trust model |
|
|
439
467
|
| [Configuration](docs/configuration.md) | `config.yaml` reference |
|
|
440
468
|
| [OpenAPI import](docs/openapi-import.md) | generate resources from an existing API |
|
|
441
469
|
| [Security model](docs/security.md) | trust boundaries, and what we do not defend |
|
package/dist/ap2.d.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { E as EnabledAp2Config } from './types-CJtwLu1c.js';
|
|
2
|
+
export { A as AP2_CHECKOUT_MANDATE_VCT, a as AP2_CHECKOUT_PROFILE, b as AP2_DEFAULT_CLOCK_SKEW_SECONDS, c as AP2_MAX_CLOCK_SKEW_SECONDS, d as AP2_SIGNING_ALGORITHM, e as AP2_SPEC_VERSION, f as Ap2AuthorizationConfig, g as Ap2Mode, h as Ap2TrustedIssuer, i as Ap2TrustedKey } from './types-CJtwLu1c.js';
|
|
3
|
+
import { A as AuthorizationProvider } from './authorization-CPDcpJVa.js';
|
|
4
|
+
import { C as Clock, L as Logger } from './runtime-CKq3d_3K.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A private ES256 key: either a private JWK (the pair of the public one in the
|
|
8
|
+
* gateway's `checkoutIssuers`) or a PKCS#8 PEM, as `openssl` emits it.
|
|
9
|
+
*/
|
|
10
|
+
type Ap2SigningKey = Readonly<Record<string, unknown>> | string;
|
|
11
|
+
interface CreateCheckoutJwtOptions {
|
|
12
|
+
readonly privateKey: Ap2SigningKey;
|
|
13
|
+
/** Must match a `kid` configured under the gateway's `checkoutIssuers` */
|
|
14
|
+
readonly kid: string;
|
|
15
|
+
/** Must match that issuer's configured `issuer` */
|
|
16
|
+
readonly issuer: string;
|
|
17
|
+
/** Must match that issuer's configured `audience` */
|
|
18
|
+
readonly audience: string;
|
|
19
|
+
readonly resourceId: string;
|
|
20
|
+
/**
|
|
21
|
+
* The resource input this purchase is for, exactly as the buyer will send
|
|
22
|
+
* it: no reserved fields, no request id, no transport metadata.
|
|
23
|
+
*/
|
|
24
|
+
readonly input: unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Decimal string, never a number, and compared as a string: `0.10` and `0.1`
|
|
27
|
+
* are different mandates. Take it from your own catalogue rather than from
|
|
28
|
+
* whatever the agent asked for.
|
|
29
|
+
*/
|
|
30
|
+
readonly amount: string;
|
|
31
|
+
readonly currency: string;
|
|
32
|
+
/** The rail that will settle, e.g. `x402` */
|
|
33
|
+
readonly paymentMethod: string;
|
|
34
|
+
/**
|
|
35
|
+
* Settlement coordinates. Required whenever the gateway's requirement names
|
|
36
|
+
* them, which under x402 is always. A mandate silent about the chain will
|
|
37
|
+
* not unlock a settlement on one.
|
|
38
|
+
*/
|
|
39
|
+
readonly destination?: string;
|
|
40
|
+
readonly network?: string;
|
|
41
|
+
readonly asset?: string;
|
|
42
|
+
/** Defaults to a random UUID. Recorded on the receipt and used for replay defence */
|
|
43
|
+
readonly jwtId?: string;
|
|
44
|
+
/** Defaults to 900: a human approval sits inside this window */
|
|
45
|
+
readonly expiresInSeconds?: number;
|
|
46
|
+
/** Injectable so a test need not move the wall clock */
|
|
47
|
+
readonly now?: Date;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns the compact JWT to hand to the agent, which wraps it in the Checkout
|
|
51
|
+
* Mandate the buyer signs.
|
|
52
|
+
*/
|
|
53
|
+
declare function createCheckoutJwt(options: CreateCheckoutJwtOptions): Promise<string>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Adapter self-description.
|
|
57
|
+
*
|
|
58
|
+
* Imports nothing but core types and the pins, so `doctor` can report AP2
|
|
59
|
+
* without pulling `jose` or `@sd-jwt/core` into the CLI bundle.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/** What this provider actually verifies */
|
|
63
|
+
declare const AP2_CAPABILITIES: readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* Everything an AP2 client may reasonably expect and will not get here.
|
|
66
|
+
* Complete on purpose: a short list reads as "mostly compatible", which is the
|
|
67
|
+
* blanket claim alpha honesty forbids.
|
|
68
|
+
*/
|
|
69
|
+
declare const AP2_UNSUPPORTED: readonly string[];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Every failure the AP2 verifier can report.
|
|
73
|
+
*
|
|
74
|
+
* `CommerceError.message` reaches the client, and a mandate carries the
|
|
75
|
+
* buyer's purchase and often personal data, so messages are fixed phrases from
|
|
76
|
+
* the list below. The machine-readable code goes in `details.reason` and the
|
|
77
|
+
* original exception on `cause`, which is never serialised.
|
|
78
|
+
*
|
|
79
|
+
* Three codes: AUTHORIZATION_INVALID when the buyer's mandate is bad,
|
|
80
|
+
* AUTHORIZATION_REPLAYED when it is good but spent, and
|
|
81
|
+
* AUTHORIZATION_PROVIDER_UNAVAILABLE when our verifier never reached a
|
|
82
|
+
* verdict. Blaming the buyer for our outage refuses a good mandate.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Coarse on purpose: a client learns roughly where its mandate was refused,
|
|
87
|
+
* not which check failed. Finer detail is an oracle for probing trust policy.
|
|
88
|
+
*/
|
|
89
|
+
declare const AP2_REJECTION_REASONS: readonly ["malformed_presentation", "untrusted_issuer", "unknown_key", "invalid_signature", "unsupported_algorithm", "invalid_claims", "expired", "wrong_audience", "unsupported_mandate_type", "checkout_binding_failed", "purchase_mismatch"];
|
|
90
|
+
type Ap2RejectionReason = (typeof AP2_REJECTION_REASONS)[number];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `released` is "nothing happened" and the only state a mandate can be
|
|
94
|
+
* presented from again. `uncertain` is a settlement whose outcome we never
|
|
95
|
+
* learned: not reusable, but findable by an operator reconciling by hand.
|
|
96
|
+
*/
|
|
97
|
+
type Ap2AuthorizationState = 'reserved' | 'consumed' | 'released' | 'uncertain';
|
|
98
|
+
/**
|
|
99
|
+
* A digest and a few identifiers: everything replay defence needs, and nothing
|
|
100
|
+
* a leaked database would hand an attacker
|
|
101
|
+
*/
|
|
102
|
+
interface Ap2ReservationRequest {
|
|
103
|
+
/**
|
|
104
|
+
* Digest of the ISSUER-SIGNED TOKEN, not of the presentation. Selective
|
|
105
|
+
* disclosure gives one mandate many presentation strings, so keying on the
|
|
106
|
+
* presentation would let it be spent once per disclosed subset.
|
|
107
|
+
*/
|
|
108
|
+
readonly reference: string;
|
|
109
|
+
/** `jti` of the merchant checkout JWT the mandate binds */
|
|
110
|
+
readonly checkoutJti: string;
|
|
111
|
+
readonly mandateIssuer: string;
|
|
112
|
+
readonly checkoutIssuer: string;
|
|
113
|
+
readonly resourceId: string;
|
|
114
|
+
readonly requestId: string;
|
|
115
|
+
}
|
|
116
|
+
type Ap2ReservationResult = {
|
|
117
|
+
readonly kind: 'reserved';
|
|
118
|
+
}
|
|
119
|
+
/** Already reserved, consumed, or of uncertain outcome. Never re-spendable */
|
|
120
|
+
| {
|
|
121
|
+
readonly kind: 'replayed';
|
|
122
|
+
readonly state: Ap2AuthorizationState;
|
|
123
|
+
};
|
|
124
|
+
interface Ap2ReplayStore {
|
|
125
|
+
/** Atomically claim a mandate, or report that something already holds it */
|
|
126
|
+
reserve(request: Ap2ReservationRequest): Ap2ReservationResult;
|
|
127
|
+
/** Settlement succeeded: spend it permanently */
|
|
128
|
+
consume(reference: string): void;
|
|
129
|
+
/** Nothing happened: hand it back so a corrected retry can use it */
|
|
130
|
+
release(reference: string): void;
|
|
131
|
+
/** Settlement outcome unknown. Not reusable, and flagged for a human */
|
|
132
|
+
markUncertain(reference: string): void;
|
|
133
|
+
/** Current state, for tests and diagnostics */
|
|
134
|
+
stateOf(reference: string): Ap2AuthorizationState | undefined;
|
|
135
|
+
close(): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The AP2 authorization provider: the seam between core's generic contract and
|
|
140
|
+
* the mandate machinery.
|
|
141
|
+
*
|
|
142
|
+
* Owns the replay database, so the gateway owns this object's lifetime and
|
|
143
|
+
* closes it on shutdown.
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
interface Ap2AuthorizationProviderOptions {
|
|
147
|
+
readonly config: EnabledAp2Config;
|
|
148
|
+
readonly clock?: Clock;
|
|
149
|
+
readonly logger?: Logger;
|
|
150
|
+
/** Injectable so tests need not touch the filesystem */
|
|
151
|
+
readonly replayStore?: Ap2ReplayStore;
|
|
152
|
+
}
|
|
153
|
+
interface Ap2AuthorizationProvider extends AuthorizationProvider {
|
|
154
|
+
/** Closes the replay database */
|
|
155
|
+
close(): void;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A reservation is keyed by the mandate reference, so the handle and the
|
|
159
|
+
* receipt's identity are the same digest. Kept as one name rather than two
|
|
160
|
+
* fields that must never disagree.
|
|
161
|
+
*/
|
|
162
|
+
declare function createAp2AuthorizationProvider(options: Ap2AuthorizationProviderOptions): Ap2AuthorizationProvider;
|
|
163
|
+
|
|
164
|
+
export { AP2_CAPABILITIES, AP2_REJECTION_REASONS, AP2_UNSUPPORTED, type Ap2AuthorizationProvider, type Ap2AuthorizationProviderOptions, type Ap2RejectionReason, type Ap2SigningKey, type CreateCheckoutJwtOptions, EnabledAp2Config, createAp2AuthorizationProvider as ap2, createAp2AuthorizationProvider, createCheckoutJwt };
|