@mooring-live/x402-client 0.0.0-stage → 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 +151 -2
- package/dist/card.d.ts +60 -0
- package/dist/card.js +67 -0
- package/dist/card.js.map +1 -0
- package/dist/client.d.ts +75 -0
- package/dist/client.js +252 -0
- package/dist/client.js.map +1 -0
- package/dist/denial.d.ts +81 -0
- package/dist/denial.js +116 -0
- package/dist/denial.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/precheck.d.ts +12 -0
- package/dist/precheck.js +25 -0
- package/dist/precheck.js.map +1 -0
- package/dist/reconcile.d.ts +37 -0
- package/dist/reconcile.js +44 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/scheme.d.ts +87 -0
- package/dist/scheme.js +220 -0
- package/dist/scheme.js.map +1 -0
- package/dist/signer.d.ts +13 -0
- package/dist/signer.js +31 -0
- package/dist/signer.js.map +1 -0
- package/package.json +69 -4
package/README.md
CHANGED
|
@@ -1,3 +1,152 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mooring-live/x402-client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pay [x402](https://x402.org)-protected APIs from a **Mooring spending card** on Stellar.
|
|
4
|
+
|
|
5
|
+
A card is a Soroban custom account (`__check_auth`) that holds USDC and enforces a spending
|
|
6
|
+
policy — periodic budget, per-payment cap, merchant allowlist, expiry, freeze/cancel — inside the
|
|
7
|
+
authorization path. This package is the agent-side client: it detects a `402`, checks the card's
|
|
8
|
+
policy locally, signs the payment with the agent key so the **card** is the payer, and turns every
|
|
9
|
+
refusal into a typed error instead of a bare `402` — `CardPolicyDenied` when the card's policy
|
|
10
|
+
said no, `PaymentError` for everything else.
|
|
11
|
+
|
|
12
|
+
The server side stays stock x402 (`@x402/express` + `@x402/stellar` + any facilitator built on
|
|
13
|
+
them — OpenZeppelin Channels and Rail402 are both verified); only the client scheme is Mooring's,
|
|
14
|
+
because the stock one cannot sign for a contract account.
|
|
15
|
+
|
|
16
|
+
Apache-2.0. **Stellar testnet only** — not audited, not on mainnet.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm i @mooring-live/x402-client @stellar/stellar-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`@stellar/stellar-sdk` is a dependency of the client, but you import `Keypair` from it yourself,
|
|
25
|
+
so install it alongside. The CLI (`npm i -g @mooring-live/cli`) wraps the same client for shell use.
|
|
26
|
+
|
|
27
|
+
Requires Node.js 20+.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Keypair } from "@stellar/stellar-sdk";
|
|
33
|
+
import { CardPolicyDenied, PaymentError, createMooringFetch, getSettlement } from "@mooring-live/x402-client";
|
|
34
|
+
|
|
35
|
+
const fetchWithPayment = createMooringFetch({
|
|
36
|
+
card: "CAJPWJBF…AHCJ", // card contract address (the payer)
|
|
37
|
+
agent: Keypair.fromSecret(process.env.AGENT_SECRET!), // the card's registered signer
|
|
38
|
+
network: "stellar:testnet",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const res = await fetchWithPayment("https://api.example.com/weather");
|
|
42
|
+
console.log(await res.json());
|
|
43
|
+
console.log("settled:", getSettlement(res)?.transaction);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That is the whole integration: `fetchWithPayment` is a drop-in `fetch`. A `402` is answered by
|
|
47
|
+
paying from the card and retrying; a `200` carries a `PAYMENT-RESPONSE` header that
|
|
48
|
+
`getSettlement` decodes into `{ success, transaction, network, payer }`, where `payer` is the
|
|
49
|
+
card.
|
|
50
|
+
|
|
51
|
+
## Handling a refusal
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
try {
|
|
55
|
+
await fetchWithPayment(url);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err instanceof CardPolicyDenied) {
|
|
58
|
+
// err.reason: "over_budget" | "not_allowlisted" | "expired" | …
|
|
59
|
+
// err.stage: "precheck" | "simulate" | "verify" | "settle"
|
|
60
|
+
// err.contractError: the card's error code, when the contract spoke (1–8)
|
|
61
|
+
console.error(`payment refused: ${err.reason} at ${err.stage}`);
|
|
62
|
+
} else if (err instanceof PaymentError) {
|
|
63
|
+
// Not the card's verdict. err.kind: "rejected" | "unconfirmed"
|
|
64
|
+
console.error(`payment ${err.kind}: ${err.detail}`, err.transaction);
|
|
65
|
+
} else {
|
|
66
|
+
throw err; // a network/server problem the client did not classify
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Failed payments are never retried automatically**, at any stage.
|
|
72
|
+
|
|
73
|
+
`CardPolicyDenied` means exactly one thing: **the card's policy refused**. Everything else is a
|
|
74
|
+
`PaymentError`, so a caller can treat the first as a policy decision without further checks.
|
|
75
|
+
|
|
76
|
+
| `PaymentError.kind` | When | Was the card debited? |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `rejected` | the facilitator refused before submitting — a malformed payload, a fee above its ceiling, an expiration too far out, a 5xx — or the client's own CAP-71 credential guard fired before signing | no |
|
|
79
|
+
| `unconfirmed` | the settlement state is unknown or contradictory: the server reported a failure for a transaction the ledger accepted, the transaction was never observed within the reconciliation window, or the server answered a bare `402` with no `PAYMENT-REQUIRED` and no `PAYMENT-RESPONSE` | possibly — reconcile `err.transaction` and the card's `spent` before retrying |
|
|
80
|
+
|
|
81
|
+
The facilitator answers `settle_exact_stellar_transaction_failed` both for a transaction the
|
|
82
|
+
ledger rejected and for one it stopped waiting for, so a settle failure carrying a hash is
|
|
83
|
+
reconciled against the RPC (~10 s) before it is named: a transaction the ledger accepted is never
|
|
84
|
+
reported as a policy denial.
|
|
85
|
+
|
|
86
|
+
### Stages
|
|
87
|
+
|
|
88
|
+
| Stage | When | Reached the network? |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `precheck` | the local policy mirror refused, before anything was signed | two read-only simulations of the card's views |
|
|
91
|
+
| `simulate` | the card's own `__check_auth` refused, in the enforcing simulation the client runs after signing | no — nothing left the process, no fee paid |
|
|
92
|
+
| `verify` | the facilitator refused the payload | yes; nothing submitted on-chain |
|
|
93
|
+
| `settle` | the settlement transaction failed on-chain — confirmed with the RPC, and attributed to the card only when the failure's diagnostics name it | yes; a transaction was submitted and failed |
|
|
94
|
+
|
|
95
|
+
### Reasons
|
|
96
|
+
|
|
97
|
+
`frozen`, `cancelled`, `expired`, `not_allowlisted`, `over_per_tx_cap`, `over_budget`,
|
|
98
|
+
`wrong_token`, `insufficient_balance`, `bad_signature`, `wrong_context`, `unknown`.
|
|
99
|
+
|
|
100
|
+
Card contract codes map as #1 `bad_signature`, #2 `wrong_context`, #3 `frozen`, #4 `cancelled`,
|
|
101
|
+
#5 `expired`, #6 `not_allowlisted`, #7 `over_per_tx_cap`, #8 `over_budget`. `wrong_token` and
|
|
102
|
+
`insufficient_balance` are pre-check-only verdicts. `unknown` is **not a policy verdict** — only
|
|
103
|
+
an unclassified one: the card refused but the client could not read which rule. It covers a card
|
|
104
|
+
that could not be read (the pre-check fails **closed**), a facilitator `simulation_failed` the
|
|
105
|
+
re-run pre-check no longer explains, and an on-chain failure whose diagnostics do not name the
|
|
106
|
+
card.
|
|
107
|
+
|
|
108
|
+
## Options
|
|
109
|
+
|
|
110
|
+
| Option | Type | Default | Meaning |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `card` | `string` | — | card contract address (`C…`); it is the payer |
|
|
113
|
+
| `agent` | `Keypair` | — | the card's registered signer |
|
|
114
|
+
| `network` | `"stellar:testnet" \| "stellar:pubnet"` | — | CAIP-2 network |
|
|
115
|
+
| `rpcUrl` | `string` | testnet default; **required** on pubnet | Soroban RPC endpoint |
|
|
116
|
+
| `precheck` | `boolean` | `true` | run the local policy mirror before signing |
|
|
117
|
+
| `onDenial` | `(d: CardPolicyDenied) => void` | — | called at every denial stage |
|
|
118
|
+
| `onPaymentError` | `(e: PaymentError) => void` | — | called for every non-policy failure |
|
|
119
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | transport (`createMooringFetch` only) |
|
|
120
|
+
|
|
121
|
+
## API
|
|
122
|
+
|
|
123
|
+
| Export | What it does |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `createMooringFetch(opts)` | a `fetch` that pays from the card and rejects with `CardPolicyDenied` or `PaymentError`, never a bare `402`; builds a fresh client per call, so it is safe to share |
|
|
126
|
+
| `createMooringClient(opts)` | the underlying `x402Client`. **Single-flight per instance** — x402's hooks carry no request identity, so build one per payment. Used directly it is stock x402: it throws generic errors and returns bare `402`s, and only `onDenial` / `onPaymentError` carry the typed reason |
|
|
127
|
+
| `getSettlement(res)` | decodes `PAYMENT-RESPONSE`; `null` when absent or undecodable (a settlement you cannot read is not a failed payment) |
|
|
128
|
+
| `CardPolicyDenied`, `DenialReason`, `DenialStage`, `CARD_ERROR_CODES`, `classifyContractError`, `cardDenialFromEvents` | typed denials (payment-path codes 1-8) |
|
|
129
|
+
| `CARD_ERRORS` | the full contract error table (codes 1-13, including owner-operation errors like `InvalidLabel`) mapped to short user-facing sentences |
|
|
130
|
+
| `PaymentError` | every failure that is *not* the card's policy (`rejected` / `unconfirmed`) |
|
|
131
|
+
| `reconcileSettlement(rpcUrl, network, hash)` | asks the ledger what became of a settlement transaction |
|
|
132
|
+
| `precheck(info, merchants, terms, now)` | the local policy mirror, on its own |
|
|
133
|
+
| `readCardInfo(rpcUrl, passphrase, card)`, `readMerchants(…)` | read-only card views (`info()`, `merchants()`); `CardInfo.label` is the owner-chosen display name |
|
|
134
|
+
| `CardExactStellarScheme` | the x402 `exact` scheme client whose payer is the card |
|
|
135
|
+
| `signCardAuthEntries(tx, card, agent, expirationLedger)` | signs a card auth entry with the agent key |
|
|
136
|
+
|
|
137
|
+
## Notes
|
|
138
|
+
|
|
139
|
+
- The pre-check is **advisory**. The card contract is the authority and runs again at settlement;
|
|
140
|
+
the pre-check only avoids spending a signature and a round-trip on an obvious refusal.
|
|
141
|
+
- Muxed (`M…`) destinations are not payable — a Soroban `Address` does not exist for them.
|
|
142
|
+
- The client records legacy (v1) auth credentials (`useUpgradedAuth: false`) because the deployed
|
|
143
|
+
OZ Channels stack rejects CAP-71 `ADDRESS_V2` as malformed. It also **asserts** what the RPC
|
|
144
|
+
actually recorded and refuses to sign a v2 card entry, and `@stellar/stellar-sdk` is pinned
|
|
145
|
+
exactly to `17.0.1` while the workaround stands. All three go away together; see
|
|
146
|
+
`docs/x402-integration.md`.
|
|
147
|
+
- The auth entry expires at `latestLedger + max(2, ceil(maxTimeoutSeconds / 6) - 2)` — a
|
|
148
|
+
deliberately conservative ledger estimate, so the expiry stays inside the window the facilitator
|
|
149
|
+
computes from its own.
|
|
150
|
+
|
|
151
|
+
Full guide: [`docs/x402-integration.md`](../../docs/x402-integration.md). Testnet evidence:
|
|
152
|
+
[`docs/testnet.md`](../../docs/testnet.md).
|
package/dist/card.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type CardState = 0 | 1 | 2;
|
|
2
|
+
export interface CardPolicy {
|
|
3
|
+
period_amount: bigint;
|
|
4
|
+
period_duration: bigint;
|
|
5
|
+
max_per_tx: bigint;
|
|
6
|
+
expiry: bigint;
|
|
7
|
+
}
|
|
8
|
+
export interface CardInfo {
|
|
9
|
+
owner: string;
|
|
10
|
+
signer: Uint8Array;
|
|
11
|
+
token: string;
|
|
12
|
+
label: string;
|
|
13
|
+
policy: CardPolicy;
|
|
14
|
+
state: CardState;
|
|
15
|
+
period: {
|
|
16
|
+
start: bigint;
|
|
17
|
+
spent: bigint;
|
|
18
|
+
};
|
|
19
|
+
remaining: bigint;
|
|
20
|
+
balance: bigint;
|
|
21
|
+
allow_count: number;
|
|
22
|
+
}
|
|
23
|
+
/** `CardInfo` as `scValToNative` hands it back, before normalization. */
|
|
24
|
+
export type RawCardInfo = {
|
|
25
|
+
owner: string;
|
|
26
|
+
signer: Uint8Array;
|
|
27
|
+
token: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
policy: CardPolicy;
|
|
30
|
+
state: number;
|
|
31
|
+
period: {
|
|
32
|
+
start: bigint;
|
|
33
|
+
spent: bigint;
|
|
34
|
+
};
|
|
35
|
+
remaining: bigint;
|
|
36
|
+
balance: bigint;
|
|
37
|
+
allow_count: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Normalizes a raw `info()` result into `CardInfo`: `signer` becomes a
|
|
41
|
+
* plain `Uint8Array` (`scValToNative` hands back a node `Buffer`, which is one
|
|
42
|
+
* — copying it keeps node's type out of the published API), `allow_count` a
|
|
43
|
+
* `number`, and
|
|
44
|
+
* `state` is range-checked before it is narrowed. Every other field is already
|
|
45
|
+
* the `bigint` the contract declared, and is passed through untouched.
|
|
46
|
+
*
|
|
47
|
+
* @throws if `state` is not one of the contract's three variants.
|
|
48
|
+
* @throws if `label` is missing (a card deployed from a pre-label factory).
|
|
49
|
+
*/
|
|
50
|
+
export declare function normalizeInfo(raw: RawCardInfo): CardInfo;
|
|
51
|
+
/**
|
|
52
|
+
* Normalizes a raw `merchants()` result into strkeys. `scValToNative` already
|
|
53
|
+
* renders an `ScAddress` as a strkey, so the guard is a no-op today; it keeps
|
|
54
|
+
* the reader honest if a future SDK hands back the raw XDR instead.
|
|
55
|
+
*/
|
|
56
|
+
export declare function normalizeMerchants(list: readonly unknown[]): string[];
|
|
57
|
+
/** Reads the card's `info()` view via simulation (no signing, no fees). */
|
|
58
|
+
export declare function readCardInfo(rpcUrl: string, networkPassphrase: string, card: string): Promise<CardInfo>;
|
|
59
|
+
/** Reads the card's `merchants()` view via simulation. */
|
|
60
|
+
export declare function readMerchants(rpcUrl: string, networkPassphrase: string, card: string): Promise<string[]>;
|
package/dist/card.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Address, contract, scValToNative } from "@stellar/stellar-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Simulates a read-only contract method and returns its parsed result.
|
|
4
|
+
*
|
|
5
|
+
* `AssembledTransaction.build` without a `publicKey` simulates from a null
|
|
6
|
+
* account, so nothing is signed, submitted or charged.
|
|
7
|
+
*/
|
|
8
|
+
async function simulateView(rpcUrl, networkPassphrase, card, method) {
|
|
9
|
+
const tx = await contract.AssembledTransaction.build({
|
|
10
|
+
contractId: card,
|
|
11
|
+
method,
|
|
12
|
+
args: [],
|
|
13
|
+
networkPassphrase,
|
|
14
|
+
rpcUrl,
|
|
15
|
+
parseResultXdr: (v) => scValToNative(v),
|
|
16
|
+
});
|
|
17
|
+
return tx.result;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Normalizes a raw `info()` result into `CardInfo`: `signer` becomes a
|
|
21
|
+
* plain `Uint8Array` (`scValToNative` hands back a node `Buffer`, which is one
|
|
22
|
+
* — copying it keeps node's type out of the published API), `allow_count` a
|
|
23
|
+
* `number`, and
|
|
24
|
+
* `state` is range-checked before it is narrowed. Every other field is already
|
|
25
|
+
* the `bigint` the contract declared, and is passed through untouched.
|
|
26
|
+
*
|
|
27
|
+
* @throws if `state` is not one of the contract's three variants.
|
|
28
|
+
* @throws if `label` is missing (a card deployed from a pre-label factory).
|
|
29
|
+
*/
|
|
30
|
+
export function normalizeInfo(raw) {
|
|
31
|
+
const state = Number(raw.state);
|
|
32
|
+
if (state !== 0 && state !== 1 && state !== 2) {
|
|
33
|
+
throw new Error(`Unknown card state: ${raw.state}`);
|
|
34
|
+
}
|
|
35
|
+
if (typeof raw.label !== "string") {
|
|
36
|
+
throw new Error("card info has no label (card deployed from a pre-label factory?)");
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
owner: raw.owner,
|
|
40
|
+
signer: new Uint8Array(raw.signer),
|
|
41
|
+
token: raw.token,
|
|
42
|
+
label: raw.label,
|
|
43
|
+
policy: raw.policy,
|
|
44
|
+
state: state,
|
|
45
|
+
period: raw.period,
|
|
46
|
+
remaining: raw.remaining,
|
|
47
|
+
balance: raw.balance,
|
|
48
|
+
allow_count: Number(raw.allow_count),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Normalizes a raw `merchants()` result into strkeys. `scValToNative` already
|
|
53
|
+
* renders an `ScAddress` as a strkey, so the guard is a no-op today; it keeps
|
|
54
|
+
* the reader honest if a future SDK hands back the raw XDR instead.
|
|
55
|
+
*/
|
|
56
|
+
export function normalizeMerchants(list) {
|
|
57
|
+
return list.map((a) => typeof a === "string" ? a : Address.fromScAddress(a).toString());
|
|
58
|
+
}
|
|
59
|
+
/** Reads the card's `info()` view via simulation (no signing, no fees). */
|
|
60
|
+
export async function readCardInfo(rpcUrl, networkPassphrase, card) {
|
|
61
|
+
return normalizeInfo(await simulateView(rpcUrl, networkPassphrase, card, "info"));
|
|
62
|
+
}
|
|
63
|
+
/** Reads the card's `merchants()` view via simulation. */
|
|
64
|
+
export async function readMerchants(rpcUrl, networkPassphrase, card) {
|
|
65
|
+
return normalizeMerchants(await simulateView(rpcUrl, networkPassphrase, card, "merchants"));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=card.js.map
|
package/dist/card.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card.js","sourceRoot":"","sources":["../src/card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAO,MAAM,sBAAsB,CAAC;AAsC7E;;;;;GAKG;AACH,KAAK,UAAU,YAAY,CACzB,MAAc,EACd,iBAAyB,EACzB,IAAY,EACZ,MAAc;IAEd,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,oBAAoB,CAAC,KAAK,CAAI;QACtD,UAAU,EAAE,IAAI;QAChB,MAAM;QACN,IAAI,EAAE,EAAE;QACR,iBAAiB;QACjB,MAAM;QACN,cAAc,EAAE,CAAC,CAAY,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAM;KACxD,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,MAAM,CAAC;AACnB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,GAAgB;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;QAClC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,KAAkB;QACzB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;KACrC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAwB;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpB,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAkB,CAAC,CAAC,QAAQ,EAAE,CACjF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,iBAAyB,EACzB,IAAY;IAEZ,OAAO,aAAa,CAAC,MAAM,YAAY,CAAc,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AACjG,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,iBAAyB,EACzB,IAAY;IAEZ,OAAO,kBAAkB,CAAC,MAAM,YAAY,CAAY,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;AACzG,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Keypair } from "@stellar/stellar-sdk";
|
|
2
|
+
import { x402Client } from "@x402/fetch";
|
|
3
|
+
import { CardPolicyDenied, PaymentError } from "./denial.js";
|
|
4
|
+
import { type StellarNetwork } from "./scheme.js";
|
|
5
|
+
export interface MooringClientOptions {
|
|
6
|
+
card: string;
|
|
7
|
+
agent: Keypair;
|
|
8
|
+
network: StellarNetwork;
|
|
9
|
+
rpcUrl?: string;
|
|
10
|
+
/** Run the local policy pre-check before signing (default true). */
|
|
11
|
+
precheck?: boolean;
|
|
12
|
+
/** Called whenever the card's policy denies a payment, at any stage. */
|
|
13
|
+
onDenial?: (denial: CardPolicyDenied) => void;
|
|
14
|
+
/** Called whenever a payment fails for a reason that is *not* the card's policy. */
|
|
15
|
+
onPaymentError?: (error: PaymentError) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface Settlement {
|
|
18
|
+
success: boolean;
|
|
19
|
+
transaction: string;
|
|
20
|
+
network: string;
|
|
21
|
+
payer?: string;
|
|
22
|
+
errorReason?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Everything `createMooringFetch` can reject with. */
|
|
25
|
+
type PaymentFailure = CardPolicyDenied | PaymentError;
|
|
26
|
+
/**
|
|
27
|
+
* Internal: the last failure recorded by the hooks, so the fetch wrapper can
|
|
28
|
+
* rethrow it typed. `@x402/fetch` turns an aborted payment into a generic
|
|
29
|
+
* `Error` and returns verify/settle failures as a plain 402 response, so the
|
|
30
|
+
* hooks are the only place where the reason survives.
|
|
31
|
+
*/
|
|
32
|
+
declare class FailureBox {
|
|
33
|
+
last?: PaymentFailure;
|
|
34
|
+
set(failure: PaymentFailure, opts: MooringClientOptions): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds an x402 client that pays from a Mooring card.
|
|
38
|
+
*
|
|
39
|
+
* The card enforces the budget, allowlist and expiry on-chain; this client
|
|
40
|
+
* mirrors that policy locally so an over-limit payment is refused before the
|
|
41
|
+
* agent key signs anything, and classifies every other outcome into
|
|
42
|
+
* `CardPolicyDenied` (the card's policy refused) or `PaymentError` (anything
|
|
43
|
+
* else).
|
|
44
|
+
*
|
|
45
|
+
* **One instance is single-flight.** x402's hook contexts carry no request
|
|
46
|
+
* identity, so the failure recorded by one payment cannot be told apart from
|
|
47
|
+
* another's: do not run concurrent payments through the same client. Build one
|
|
48
|
+
* per payment, or use `createMooringFetch`, which does that for you.
|
|
49
|
+
*
|
|
50
|
+
* Used directly, the returned `x402Client` still throws x402's own generic
|
|
51
|
+
* errors and hands back bare 402 responses: only `onDenial` / `onPaymentError`
|
|
52
|
+
* surface the typed reason. `createMooringFetch` is what turns them into
|
|
53
|
+
* thrown `CardPolicyDenied` / `PaymentError`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createMooringClient(opts: MooringClientOptions, box?: FailureBox): x402Client;
|
|
56
|
+
/**
|
|
57
|
+
* A `fetch` that pays x402-protected requests from the card, and rejects with
|
|
58
|
+
* `CardPolicyDenied` (the card's policy refused) or `PaymentError` (anything
|
|
59
|
+
* else) — never a bare 402 — whenever the payment does not deliver the
|
|
60
|
+
* resource. Failed payments are never retried.
|
|
61
|
+
*
|
|
62
|
+
* Each call builds its own client and failure scope — `x402Client.fromConfig`
|
|
63
|
+
* does no I/O — so concurrent requests through the same returned `fetch` cannot
|
|
64
|
+
* observe each other's failures.
|
|
65
|
+
*/
|
|
66
|
+
export declare function createMooringFetch(opts: MooringClientOptions & {
|
|
67
|
+
fetch?: typeof globalThis.fetch;
|
|
68
|
+
}): typeof globalThis.fetch;
|
|
69
|
+
/**
|
|
70
|
+
* Decodes the PAYMENT-RESPONSE header of a paid response. Returns `null` when
|
|
71
|
+
* the header is absent or undecodable — a settlement we cannot read is not a
|
|
72
|
+
* failed payment, and must never turn a 200 into an error.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getSettlement(res: Response): Settlement | null;
|
|
75
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { decodePaymentResponseHeader, wrapFetchWithPayment, x402Client } from "@x402/fetch";
|
|
2
|
+
import { getNetworkPassphrase, getRpcUrl } from "@x402/stellar";
|
|
3
|
+
import { readCardInfo, readMerchants } from "./card.js";
|
|
4
|
+
import { CARD_ERROR_CODES, CardPolicyDenied, PaymentError, cardDenialFromEvents, classifyContractError, } from "./denial.js";
|
|
5
|
+
import { precheck } from "./precheck.js";
|
|
6
|
+
import { reconcileSettlement } from "./reconcile.js";
|
|
7
|
+
import { CardExactStellarScheme } from "./scheme.js";
|
|
8
|
+
/**
|
|
9
|
+
* Internal: the last failure recorded by the hooks, so the fetch wrapper can
|
|
10
|
+
* rethrow it typed. `@x402/fetch` turns an aborted payment into a generic
|
|
11
|
+
* `Error` and returns verify/settle failures as a plain 402 response, so the
|
|
12
|
+
* hooks are the only place where the reason survives.
|
|
13
|
+
*/
|
|
14
|
+
class FailureBox {
|
|
15
|
+
last;
|
|
16
|
+
set(failure, opts) {
|
|
17
|
+
this.last = failure;
|
|
18
|
+
if (failure instanceof CardPolicyDenied)
|
|
19
|
+
opts.onDenial?.(failure);
|
|
20
|
+
else
|
|
21
|
+
opts.onPaymentError?.(failure);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds an x402 client that pays from a Mooring card.
|
|
26
|
+
*
|
|
27
|
+
* The card enforces the budget, allowlist and expiry on-chain; this client
|
|
28
|
+
* mirrors that policy locally so an over-limit payment is refused before the
|
|
29
|
+
* agent key signs anything, and classifies every other outcome into
|
|
30
|
+
* `CardPolicyDenied` (the card's policy refused) or `PaymentError` (anything
|
|
31
|
+
* else).
|
|
32
|
+
*
|
|
33
|
+
* **One instance is single-flight.** x402's hook contexts carry no request
|
|
34
|
+
* identity, so the failure recorded by one payment cannot be told apart from
|
|
35
|
+
* another's: do not run concurrent payments through the same client. Build one
|
|
36
|
+
* per payment, or use `createMooringFetch`, which does that for you.
|
|
37
|
+
*
|
|
38
|
+
* Used directly, the returned `x402Client` still throws x402's own generic
|
|
39
|
+
* errors and hands back bare 402 responses: only `onDenial` / `onPaymentError`
|
|
40
|
+
* surface the typed reason. `createMooringFetch` is what turns them into
|
|
41
|
+
* thrown `CardPolicyDenied` / `PaymentError`.
|
|
42
|
+
*/
|
|
43
|
+
export function createMooringClient(opts, box = new FailureBox()) {
|
|
44
|
+
const rpcUrl = getRpcUrl(opts.network, opts.rpcUrl ? { url: opts.rpcUrl } : undefined);
|
|
45
|
+
const passphrase = getNetworkPassphrase(opts.network);
|
|
46
|
+
const scheme = new CardExactStellarScheme({
|
|
47
|
+
card: opts.card,
|
|
48
|
+
agent: opts.agent,
|
|
49
|
+
network: opts.network,
|
|
50
|
+
rpcUrl: opts.rpcUrl,
|
|
51
|
+
// A failure raised inside the scheme (the card refusing the enforcing
|
|
52
|
+
// simulation, or the CAP-71 credential guard) travels out as a thrown
|
|
53
|
+
// error, but it must reach the hooks and the box like every other stage.
|
|
54
|
+
onDenial: (denial) => box.set(denial, opts),
|
|
55
|
+
onPaymentError: (error) => box.set(error, opts),
|
|
56
|
+
});
|
|
57
|
+
const evaluate = async (req) => {
|
|
58
|
+
const [info, merchants] = await Promise.all([
|
|
59
|
+
readCardInfo(rpcUrl, passphrase, opts.card),
|
|
60
|
+
readMerchants(rpcUrl, passphrase, opts.card),
|
|
61
|
+
]);
|
|
62
|
+
return precheck(info, merchants, { payTo: req.payTo, amount: req.amount, asset: req.asset }, Math.floor(Date.now() / 1000));
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Classifies a refusal that happened *before* anything was submitted.
|
|
66
|
+
*
|
|
67
|
+
* The facilitator does not say whose rule it applied, so the card is asked
|
|
68
|
+
* instead: if the card's own state explains the refusal it is a policy
|
|
69
|
+
* denial, and `simulation_failed` is policy-shaped too (a card denial is a
|
|
70
|
+
* simulation failure — that is the whole of finding the reason on-chain, and
|
|
71
|
+
* a race can produce one the re-run pre-check no longer sees). Every other
|
|
72
|
+
* reason the facilitator can give — a malformed payload, a fee above its
|
|
73
|
+
* ceiling, an expiration too far out — is not the card's verdict and must
|
|
74
|
+
* not be reported as one.
|
|
75
|
+
*/
|
|
76
|
+
const classifyRefusal = async (stage, text, req) => {
|
|
77
|
+
const parsed = classifyContractError(text);
|
|
78
|
+
if (parsed && parsed.code in CARD_ERROR_CODES) {
|
|
79
|
+
return new CardPolicyDenied(parsed.reason, stage, {
|
|
80
|
+
contractError: parsed.code,
|
|
81
|
+
detail: text || undefined,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
let reason = null;
|
|
85
|
+
try {
|
|
86
|
+
reason = await evaluate(req);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* the card could not be read; fall through to the reason string */
|
|
90
|
+
}
|
|
91
|
+
if (reason)
|
|
92
|
+
return new CardPolicyDenied(reason, stage, { detail: text || undefined });
|
|
93
|
+
if (text.includes("simulation_failed")) {
|
|
94
|
+
return new CardPolicyDenied("unknown", stage, { detail: text || undefined });
|
|
95
|
+
}
|
|
96
|
+
return new PaymentError({ kind: "rejected", detail: text || undefined });
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Decides what a failed settlement means, by asking the ledger.
|
|
100
|
+
*
|
|
101
|
+
* A settle failure that carries a transaction hash is *not* evidence the
|
|
102
|
+
* payment did not happen: the facilitator returns the same
|
|
103
|
+
* `settle_exact_stellar_transaction_failed` when its poll times out, and
|
|
104
|
+
* inclusion can follow that answer. So the hash is reconciled before the
|
|
105
|
+
* failure is named — a transaction the ledger accepted is never reported as
|
|
106
|
+
* a policy denial, however the server described it.
|
|
107
|
+
*/
|
|
108
|
+
const classifySettleFailure = async (settle, req) => {
|
|
109
|
+
const reason = settle.errorReason ?? "settle failed";
|
|
110
|
+
const hash = settle.transaction;
|
|
111
|
+
if (!hash)
|
|
112
|
+
return classifyRefusal("settle", reason, req);
|
|
113
|
+
let outcome;
|
|
114
|
+
try {
|
|
115
|
+
outcome = await reconcileSettlement(rpcUrl, opts.network, hash);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
return new PaymentError({
|
|
119
|
+
kind: "unconfirmed",
|
|
120
|
+
transaction: hash,
|
|
121
|
+
detail: `${reason}; reconciliation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (outcome.status === "SUCCESS") {
|
|
125
|
+
return new PaymentError({
|
|
126
|
+
kind: "unconfirmed",
|
|
127
|
+
transaction: hash,
|
|
128
|
+
detail: "settled on-chain but the server reported failure; reconcile",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (outcome.status === "NOT_FOUND") {
|
|
132
|
+
return new PaymentError({
|
|
133
|
+
kind: "unconfirmed",
|
|
134
|
+
transaction: hash,
|
|
135
|
+
detail: `${reason}; not observed on-chain within the reconciliation window`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const parsed = cardDenialFromEvents(outcome.events, opts.card);
|
|
139
|
+
return new CardPolicyDenied(parsed?.reason ?? "unknown", "settle", {
|
|
140
|
+
contractError: parsed?.code,
|
|
141
|
+
detail: `${reason} tx=${hash}`,
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
const client = x402Client.fromConfig({
|
|
145
|
+
schemes: [{ network: opts.network, client: scheme }],
|
|
146
|
+
// The card enforces limits on-chain; x402's USD-based client caps would only get in the way.
|
|
147
|
+
spendControls: false,
|
|
148
|
+
});
|
|
149
|
+
client.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
150
|
+
if (opts.precheck === false)
|
|
151
|
+
return;
|
|
152
|
+
let reason;
|
|
153
|
+
try {
|
|
154
|
+
reason = await evaluate(selectedRequirements);
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
// Fail closed: a card we cannot read is a card we cannot spend from.
|
|
158
|
+
const detail = `pre-check unavailable: ${err instanceof Error ? err.message : String(err)}`;
|
|
159
|
+
box.set(new CardPolicyDenied("unknown", "precheck", { detail }), opts);
|
|
160
|
+
return { abort: true, reason: detail };
|
|
161
|
+
}
|
|
162
|
+
if (reason) {
|
|
163
|
+
box.set(new CardPolicyDenied(reason, "precheck"), opts);
|
|
164
|
+
return { abort: true, reason };
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
client.onPaymentResponse(async ({ requirements, settleResponse, paymentRequired, error }) => {
|
|
168
|
+
if (settleResponse && !settleResponse.success) {
|
|
169
|
+
box.set(await classifySettleFailure(settleResponse, requirements), opts);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (paymentRequired && !settleResponse) {
|
|
173
|
+
// Verify failed. The facilitator does not say why; ask the card.
|
|
174
|
+
const text = paymentRequired.error ?? error?.message ?? "";
|
|
175
|
+
box.set(await classifyRefusal("verify", text, requirements), opts);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return client;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* A `fetch` that pays x402-protected requests from the card, and rejects with
|
|
182
|
+
* `CardPolicyDenied` (the card's policy refused) or `PaymentError` (anything
|
|
183
|
+
* else) — never a bare 402 — whenever the payment does not deliver the
|
|
184
|
+
* resource. Failed payments are never retried.
|
|
185
|
+
*
|
|
186
|
+
* Each call builds its own client and failure scope — `x402Client.fromConfig`
|
|
187
|
+
* does no I/O — so concurrent requests through the same returned `fetch` cannot
|
|
188
|
+
* observe each other's failures.
|
|
189
|
+
*/
|
|
190
|
+
export function createMooringFetch(opts) {
|
|
191
|
+
const transport = opts.fetch ?? globalThis.fetch;
|
|
192
|
+
return (async (input, init) => {
|
|
193
|
+
const box = new FailureBox();
|
|
194
|
+
const wrapped = wrapFetchWithPayment(transport, createMooringClient(opts, box));
|
|
195
|
+
try {
|
|
196
|
+
const res = await wrapped(input, init);
|
|
197
|
+
// Verify and settle failures come back as a non-2xx response, not a
|
|
198
|
+
// throw; the hooks have recorded why, so surface it typed.
|
|
199
|
+
if (box.last)
|
|
200
|
+
throw box.last;
|
|
201
|
+
const settlement = getSettlement(res);
|
|
202
|
+
if (settlement && !settlement.success) {
|
|
203
|
+
// The hooks classify every settlement they see; reaching here means
|
|
204
|
+
// the header said "failed" without the hook firing, so the settlement
|
|
205
|
+
// state is exactly as unknown as the header is unexplained.
|
|
206
|
+
box.set(new PaymentError({
|
|
207
|
+
kind: "unconfirmed",
|
|
208
|
+
transaction: settlement.transaction || undefined,
|
|
209
|
+
detail: settlement.errorReason ?? "settle failed",
|
|
210
|
+
}), opts);
|
|
211
|
+
throw box.last;
|
|
212
|
+
}
|
|
213
|
+
if (res.status === 402) {
|
|
214
|
+
// The payment was made and the resource still withheld, with nothing
|
|
215
|
+
// in the response to say what happened to it. `@x402/express` answers
|
|
216
|
+
// exactly this from its settlement path, *after* the facilitator may
|
|
217
|
+
// have submitted — so this is an unknown settlement, not a denial.
|
|
218
|
+
box.set(new PaymentError({
|
|
219
|
+
kind: "unconfirmed",
|
|
220
|
+
detail: "402 without PAYMENT-REQUIRED or PAYMENT-RESPONSE; settlement state unknown",
|
|
221
|
+
}), opts);
|
|
222
|
+
throw box.last;
|
|
223
|
+
}
|
|
224
|
+
return res;
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
// A pre-check abort reaches us as a generic Error from @x402/fetch.
|
|
228
|
+
if (err instanceof CardPolicyDenied || err instanceof PaymentError)
|
|
229
|
+
throw err;
|
|
230
|
+
if (box.last)
|
|
231
|
+
throw box.last;
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Decodes the PAYMENT-RESPONSE header of a paid response. Returns `null` when
|
|
238
|
+
* the header is absent or undecodable — a settlement we cannot read is not a
|
|
239
|
+
* failed payment, and must never turn a 200 into an error.
|
|
240
|
+
*/
|
|
241
|
+
export function getSettlement(res) {
|
|
242
|
+
const header = res.headers.get("PAYMENT-RESPONSE") ?? res.headers.get("X-PAYMENT-RESPONSE");
|
|
243
|
+
if (!header)
|
|
244
|
+
return null;
|
|
245
|
+
try {
|
|
246
|
+
return decodePaymentResponseHeader(header);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|