xytara 3.1.13 → 3.1.15

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.
Files changed (68) hide show
  1. package/.env.example +38 -4
  2. package/BSV_TERANODE_PLATFORM_DISCONTINUITY_RUNBOOK.md +126 -126
  3. package/REAL_PAYMENT_SETUP.md +14 -7
  4. package/RELEASE_NOTES.md +9 -4
  5. package/fixtures/catalog.js +22 -0
  6. package/lib/a2a_rpc.js +287 -280
  7. package/lib/account_auth.js +585 -585
  8. package/lib/agent_identity.js +162 -162
  9. package/lib/asyncapi_contract.js +3 -3
  10. package/lib/bolt_adapter.js +187 -187
  11. package/lib/bolt_payment_front.js +224 -224
  12. package/lib/buyer_event_log.js +215 -0
  13. package/lib/buyer_safety_policy.js +132 -0
  14. package/lib/checkout_processor_adapter.js +217 -217
  15. package/lib/command_flow.js +911 -722
  16. package/lib/commerce_artifacts.js +591 -591
  17. package/lib/commerce_bridge.js +274 -274
  18. package/lib/commerce_cases.js +399 -399
  19. package/lib/commerce_client.js +7694 -7694
  20. package/lib/commerce_quotes.js +29 -0
  21. package/lib/commerce_runtime.js +322 -322
  22. package/lib/execution_dedupe.js +86 -86
  23. package/lib/external_credit_bridge.js +142 -142
  24. package/lib/http_transport.js +99 -81
  25. package/lib/l402_adapter.js +241 -241
  26. package/lib/l402_payment_front.js +265 -265
  27. package/lib/mpp_adapter.js +237 -237
  28. package/lib/openapi_contract.js +4 -0
  29. package/lib/payment_config.js +257 -253
  30. package/lib/payment_verification.js +357 -357
  31. package/lib/provider_payout_execution.js +155 -155
  32. package/lib/provider_registry.js +852 -842
  33. package/lib/rate_limiter.js +81 -81
  34. package/lib/runtime_state_store.js +495 -369
  35. package/lib/settlement_bsv_live.js +600 -600
  36. package/lib/settlement_bsv_teranode.js +117 -117
  37. package/lib/stripe_mpp_payment_front.js +258 -258
  38. package/lib/x402_facilitator.js +198 -191
  39. package/lib/x402_lane_contract.js +5 -3
  40. package/lib/x402_settlement_recovery.js +175 -0
  41. package/package.json +9 -3
  42. package/scripts/buyer_events_daily_report.js +79 -0
  43. package/scripts/state_backup_offsite.js +196 -0
  44. package/scripts/verify_a2a_rpc.js +131 -105
  45. package/scripts/verify_agent_identity.js +153 -153
  46. package/scripts/verify_all.js +1 -1
  47. package/scripts/verify_bolt_adapter.js +128 -128
  48. package/scripts/verify_buyer_event_log.js +123 -0
  49. package/scripts/verify_buyer_safety.js +141 -0
  50. package/scripts/verify_cdp_facilitator.js +110 -110
  51. package/scripts/verify_checkout_processor.js +109 -109
  52. package/scripts/verify_commerce_bridge.js +177 -177
  53. package/scripts/verify_execution_dedupe.js +189 -189
  54. package/scripts/verify_execution_surfacing.js +76 -76
  55. package/scripts/verify_final_activation.js +191 -191
  56. package/scripts/verify_l402_adapter.js +86 -86
  57. package/scripts/verify_lifecycle_spend_binding.js +88 -0
  58. package/scripts/verify_mpp_adapter.js +128 -128
  59. package/scripts/verify_production_readiness.js +268 -268
  60. package/scripts/verify_rate_limiter.js +52 -52
  61. package/scripts/verify_refund_credit.js +106 -106
  62. package/scripts/verify_release_candidate.js +81 -81
  63. package/scripts/verify_runtime_state_guard.js +117 -0
  64. package/scripts/verify_service.js +15421 -15421
  65. package/scripts/verify_verify_execute_settle.js +209 -208
  66. package/scripts/verify_x402_facilitator.js +307 -286
  67. package/scripts/verify_x402_settlement_recovery.js +153 -0
  68. package/server.js +12242 -11974
@@ -1,208 +1,209 @@
1
- "use strict";
2
-
3
- // Verifies the don't-charge-on-failure guarantee for the x402 command path:
4
- // - when external execution FAILS, the deferred x402 settlement is NEVER called
5
- // (the facilitator /settle is not hit), the response is a non-2xx carrying
6
- // payment_settled:false and a "you were not charged" remediation, and no
7
- // PAYMENT-RESPONSE receipt is emitted;
8
- // - when external execution SUCCEEDS, settlement runs exactly once AFTER the work,
9
- // the response is 200 with the real tool output surfaced and a PAYMENT-RESPONSE
10
- // receipt attached.
11
- //
12
- // This is the verify -> execute -> settle reorder: an x402 authorization is verified
13
- // up front but only settled once the deliverable is produced, so a failed call costs
14
- // the paying agent nothing. No real network or chain is used.
15
-
16
- const assert = require("assert");
17
- const http = require("http");
18
-
19
- const ACCEPTS = [
20
- {
21
- scheme: "exact",
22
- network: "eip155:84532",
23
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
24
- amount: "1000",
25
- payTo: "0x7cd2E0Ed2721319B20FAac4109774d030833F84D",
26
- extra: { name: "USDC", version: "2" },
27
- maxTimeoutSeconds: 60
28
- }
29
- ];
30
-
31
- // One mock server stands in for BOTH the x402 facilitator (/verify, /settle) and the
32
- // external execution target (/exec-ok succeeds, /exec-fail rejects with a 502).
33
- function startMockServer() {
34
- const calls = { verify: 0, settle: 0, execOk: 0, execFail: 0 };
35
- const server = http.createServer((req, res) => {
36
- const chunks = [];
37
- req.on("data", (c) => chunks.push(c));
38
- req.on("end", () => {
39
- let body = {};
40
- try { body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch (_) { body = {}; }
41
- const reply = (payload, status) => {
42
- const text = JSON.stringify(payload);
43
- res.writeHead(status || 200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
44
- res.end(text);
45
- };
46
- if (req.method === "POST" && req.url === "/verify") {
47
- calls.verify += 1;
48
- return reply({ isValid: true, payer: "0x1111111111111111111111111111111111111111" });
49
- }
50
- if (req.method === "POST" && req.url === "/settle") {
51
- calls.settle += 1;
52
- const sig = body && body.paymentPayload && body.paymentPayload.payload ? String(body.paymentPayload.payload.signature || "") : "";
53
- if (sig === "settle-fails") return reply({ success: false, errorReason: "insufficient_funds" });
54
- return reply({ success: true, transaction: "0xmocktx", network: "eip155:84532", payer: "0x1111111111111111111111111111111111111111" });
55
- }
56
- if (req.method === "POST" && req.url === "/exec-ok") {
57
- calls.execOk += 1;
58
- return reply({ ok: true, status: "completed", output: { value: "0xdeadbeef" } });
59
- }
60
- if (req.method === "POST" && req.url === "/exec-fail") {
61
- calls.execFail += 1;
62
- return reply({ ok: false, reason: "data_required" }, 502);
63
- }
64
- res.writeHead(404); res.end();
65
- });
66
- });
67
- return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve({ server, calls, port: server.address().port })));
68
- }
69
-
70
- function paymentHeader(signature) {
71
- const accepted = ACCEPTS[0];
72
- return Buffer.from(JSON.stringify({
73
- x402Version: 2,
74
- accepted,
75
- payload: {
76
- signature,
77
- authorization: {
78
- from: "0x1111111111111111111111111111111111111111",
79
- to: accepted.payTo,
80
- value: accepted.amount,
81
- validAfter: "0",
82
- validBefore: String(Math.floor(Date.now() / 1000) + 600),
83
- nonce: `0x${"ab".repeat(32)}`
84
- }
85
- }
86
- }), "utf8").toString("base64");
87
- }
88
-
89
- async function post(baseUrl, path, body, headers = {}) {
90
- const res = await fetch(`${baseUrl}${path}`, {
91
- method: "POST",
92
- headers: { "content-type": "application/json", ...headers },
93
- body: JSON.stringify(body)
94
- });
95
- let json = null;
96
- try { json = await res.json(); } catch (_) { json = null; }
97
- return { status: res.status, json, headers: res.headers };
98
- }
99
-
100
- async function main() {
101
- const mock = await startMockServer();
102
- const mockBase = `http://127.0.0.1:${mock.port}`;
103
- const previous = {
104
- mode: process.env.XYTARA_PAYMENT_VERIFICATION_MODE,
105
- accepts: process.env.XYTARA_X402_ACCEPTS_JSON,
106
- facUrl: process.env.XYTARA_X402_FACILITATOR_URL,
107
- targets: process.env.XYTARA_EXECUTION_TARGETS_JSON
108
- };
109
- process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "x402_facilitator";
110
- process.env.XYTARA_X402_ACCEPTS_JSON = JSON.stringify(ACCEPTS);
111
- process.env.XYTARA_X402_FACILITATOR_URL = mockBase;
112
- process.env.XYTARA_EXECUTION_TARGETS_JSON = JSON.stringify([
113
- { target_id: "exec_ok", task_ref: "exec.ok", endpoint_url: `${mockBase}/exec-ok` },
114
- { target_id: "exec_fail", task_ref: "exec.fail", endpoint_url: `${mockBase}/exec-fail` }
115
- ]);
116
-
117
- delete require.cache[require.resolve("../server")];
118
- const { createServer } = require("../server");
119
- const server = createServer();
120
- await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
121
- const baseUrl = `http://127.0.0.1:${server.address().port}`;
122
-
123
- try {
124
- // 1. Execution FAILS -> deferred settlement is never reached; caller is not charged.
125
- const settleBeforeFail = mock.calls.settle;
126
- const verifyBeforeFail = mock.calls.verify;
127
- const failBody = { account_id: "acct_ves", command: "failing execution", task_ref: "exec.fail", body: { subject_id: "s-1" } };
128
- const failed = await post(baseUrl, "/x402/commands/execute", failBody, { "payment-signature": paymentHeader("ok") });
129
- assert.strictEqual(failed.status, 502, "failed execution must return 502");
130
- assert.strictEqual(failed.json.ok, false, "failed execution must report ok:false");
131
- assert.strictEqual(failed.json.payment_settled, false, "a failed x402 call must NOT be settled");
132
- assert.ok(/not charged/i.test(String(failed.json.remediation || "")), "failed x402 call must tell the agent it was not charged");
133
- assert.ok(!failed.json.settlement, "failed x402 call must carry no settlement");
134
- assert.strictEqual(failed.headers.get("payment-response"), null, "failed x402 call must NOT emit a PAYMENT-RESPONSE receipt");
135
- assert.strictEqual(mock.calls.settle, settleBeforeFail, "facilitator /settle must NOT be called when execution fails");
136
- assert.strictEqual(mock.calls.verify, verifyBeforeFail + 1, "the payment must still have been verified up front");
137
- assert.strictEqual(mock.calls.execFail, 1, "the failing execution target must have been attempted");
138
-
139
- // 2. Execution SUCCEEDS -> settle runs exactly once, after the work, with a receipt.
140
- const settleBeforeOk = mock.calls.settle;
141
- const okBody = { account_id: "acct_ves", command: "successful execution", task_ref: "exec.ok", body: { subject_id: "s-2" } };
142
- const ok = await post(baseUrl, "/x402/commands/execute", okBody, { "payment-signature": paymentHeader("ok") });
143
- assert.strictEqual(ok.status, 200, "successful execution must return 200");
144
- assert.strictEqual(ok.json.transaction.status, "completed", "successful transaction must be completed");
145
- assert.strictEqual(ok.json.transaction.payment.verification_mode, "x402_facilitator", "verification mode must be recorded");
146
- assert.strictEqual(mock.calls.settle, settleBeforeOk + 1, "facilitator /settle must be called exactly once on success");
147
- assert.strictEqual(mock.calls.execOk, 1, "the successful execution target must have been invoked");
148
- const receiptHeader = ok.headers.get("payment-response");
149
- assert.strictEqual(typeof receiptHeader, "string", "a settled success must carry a PAYMENT-RESPONSE receipt");
150
- const receipt = JSON.parse(Buffer.from(receiptHeader, "base64").toString("utf8"));
151
- assert.strictEqual(receipt.success, true, "settlement receipt must report success");
152
- assert.strictEqual(receipt.transaction, "0xmocktx", "settlement receipt transaction mismatch");
153
-
154
- // 3. Settle FAILS after a SUCCESSFUL execution -> deliverable withheld, not charged.
155
- const settleBeforeSf = mock.calls.settle;
156
- const sfBody = { account_id: "acct_ves", command: "settle fails after success", task_ref: "exec.ok", body: { subject_id: "s-3" } };
157
- const settleFail = await post(baseUrl, "/x402/commands/execute", sfBody, { "payment-signature": paymentHeader("settle-fails") });
158
- assert.strictEqual(settleFail.status, 402, "a failed post-execution settlement must 402");
159
- assert.strictEqual(settleFail.json.ok, false, "a failed settlement must report ok:false");
160
- assert.strictEqual(settleFail.json.payment_settled, false, "a failed settlement must report payment_settled:false");
161
- assert.ok(/not charged/i.test(String(settleFail.json.remediation || "")), "settle-fail must tell the agent it was not charged");
162
- assert.ok(!settleFail.json.transaction, "settle-fail must NOT return the deliverable");
163
- assert.strictEqual(settleFail.headers.get("payment-response"), null, "settle-fail must emit no receipt");
164
- assert.strictEqual(mock.calls.settle, settleBeforeSf + 1, "settle must have been attempted once");
165
- assert.strictEqual(mock.calls.execOk, 2, "the work must have executed before the settle attempt");
166
-
167
- // 4. Non-x402 lane (presence mode, no on-chain settlement) that fails execution must
168
- // NOT claim the caller was charged and must NOT point them at a refund flow — the
169
- // failure flag is keyed on an actual receipt, not merely on "not deferred".
170
- await new Promise((resolve) => server.close(resolve));
171
- const prevMode = process.env.XYTARA_PAYMENT_VERIFICATION_MODE;
172
- process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "presence";
173
- delete require.cache[require.resolve("../server")];
174
- const presenceServerFactory = require("../server").createServer;
175
- const presenceServer = presenceServerFactory();
176
- await new Promise((resolve) => presenceServer.listen(0, "127.0.0.1", resolve));
177
- const presenceBase = `http://127.0.0.1:${presenceServer.address().port}`;
178
- try {
179
- const presFail = await post(presenceBase, "/v1/commands/execute", { account_id: "acct_ves", command: "presence failing exec", task_ref: "exec.fail", body: { subject_id: "s-4" } }, {
180
- "x-wallet-id": "wallet-presence",
181
- "x-payment": "encoded",
182
- "x-payment-signature": "sig"
183
- });
184
- assert.strictEqual(presFail.json.ok, false, "presence failing execution must report ok:false");
185
- assert.strictEqual(presFail.json.payment_settled, false, "presence lane must NOT claim a charge on failure");
186
- assert.ok(!/refund/i.test(String(presFail.json.remediation || "")), "presence lane must NOT point to a refund flow on failure");
187
- assert.strictEqual(presFail.headers.get("payment-response"), null, "presence failure must emit no receipt");
188
- } finally {
189
- await new Promise((resolve) => presenceServer.close(resolve));
190
- if (prevMode === undefined) delete process.env.XYTARA_PAYMENT_VERIFICATION_MODE; else process.env.XYTARA_PAYMENT_VERIFICATION_MODE = prevMode;
191
- }
192
-
193
- console.log("xytara verify:verify-execute-settle passed");
194
- } finally {
195
- await new Promise((resolve) => server.close(resolve));
196
- await new Promise((resolve) => mock.server.close(resolve));
197
- const restore = (key, value) => { if (value === undefined) delete process.env[key]; else process.env[key] = value; };
198
- restore("XYTARA_PAYMENT_VERIFICATION_MODE", previous.mode);
199
- restore("XYTARA_X402_ACCEPTS_JSON", previous.accepts);
200
- restore("XYTARA_X402_FACILITATOR_URL", previous.facUrl);
201
- restore("XYTARA_EXECUTION_TARGETS_JSON", previous.targets);
202
- }
203
- }
204
-
205
- main().catch((error) => {
206
- console.error(error && error.message ? error.message : error);
207
- process.exitCode = 1;
208
- });
1
+ "use strict";
2
+
3
+ // Verifies the don't-charge-on-failure guarantee for the x402 command path:
4
+ // - when external execution FAILS, the deferred x402 settlement is NEVER called
5
+ // (the facilitator /settle is not hit), the response is a non-2xx carrying
6
+ // payment_settled:false and a "you were not charged" remediation, and no
7
+ // PAYMENT-RESPONSE receipt is emitted;
8
+ // - when external execution SUCCEEDS, settlement runs exactly once AFTER the work,
9
+ // the response is 200 with the real tool output surfaced and a PAYMENT-RESPONSE
10
+ // receipt attached.
11
+ //
12
+ // This is the verify -> execute -> settle reorder: an x402 authorization is verified
13
+ // up front but only settled once the deliverable is produced, so a failed call costs
14
+ // the paying agent nothing. No real network or chain is used.
15
+
16
+ const assert = require("assert");
17
+ const http = require("http");
18
+
19
+ const ACCEPTS = [
20
+ {
21
+ scheme: "exact",
22
+ network: "eip155:84532",
23
+ asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
24
+ amount: "1000",
25
+ payTo: "0x7cd2E0Ed2721319B20FAac4109774d030833F84D",
26
+ extra: { name: "USDC", version: "2" },
27
+ maxTimeoutSeconds: 60
28
+ }
29
+ ];
30
+
31
+ // One mock server stands in for BOTH the x402 facilitator (/verify, /settle) and the
32
+ // external execution target (/exec-ok succeeds, /exec-fail rejects with a 502).
33
+ function startMockServer() {
34
+ const calls = { verify: 0, settle: 0, execOk: 0, execFail: 0 };
35
+ const server = http.createServer((req, res) => {
36
+ const chunks = [];
37
+ req.on("data", (c) => chunks.push(c));
38
+ req.on("end", () => {
39
+ let body = {};
40
+ try { body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}"); } catch (_) { body = {}; }
41
+ const reply = (payload, status) => {
42
+ const text = JSON.stringify(payload);
43
+ res.writeHead(status || 200, { "content-type": "application/json", "content-length": Buffer.byteLength(text) });
44
+ res.end(text);
45
+ };
46
+ if (req.method === "POST" && req.url === "/verify") {
47
+ calls.verify += 1;
48
+ return reply({ isValid: true, payer: "0x1111111111111111111111111111111111111111" });
49
+ }
50
+ if (req.method === "POST" && req.url === "/settle") {
51
+ calls.settle += 1;
52
+ const sig = body && body.paymentPayload && body.paymentPayload.payload ? String(body.paymentPayload.payload.signature || "") : "";
53
+ if (sig === "settle-fails") return reply({ success: false, errorReason: "insufficient_funds" });
54
+ return reply({ success: true, transaction: "0xmocktx", network: "eip155:84532", payer: "0x1111111111111111111111111111111111111111" });
55
+ }
56
+ if (req.method === "POST" && req.url === "/exec-ok") {
57
+ calls.execOk += 1;
58
+ return reply({ ok: true, status: "completed", output: { value: "0xdeadbeef" } });
59
+ }
60
+ if (req.method === "POST" && req.url === "/exec-fail") {
61
+ calls.execFail += 1;
62
+ return reply({ ok: false, reason: "data_required" }, 502);
63
+ }
64
+ res.writeHead(404); res.end();
65
+ });
66
+ });
67
+ return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve({ server, calls, port: server.address().port })));
68
+ }
69
+
70
+ function paymentHeader(signature) {
71
+ const accepted = ACCEPTS[0];
72
+ return Buffer.from(JSON.stringify({
73
+ x402Version: 2,
74
+ accepted,
75
+ payload: {
76
+ signature,
77
+ authorization: {
78
+ from: "0x1111111111111111111111111111111111111111",
79
+ to: accepted.payTo,
80
+ value: accepted.amount,
81
+ validAfter: "0",
82
+ validBefore: String(Math.floor(Date.now() / 1000) + 600),
83
+ // A fresh nonce per payment, as a real wallet signs; a reused nonce is a replay.
84
+ nonce: `0x${require("crypto").randomBytes(32).toString("hex")}`
85
+ }
86
+ }
87
+ }), "utf8").toString("base64");
88
+ }
89
+
90
+ async function post(baseUrl, path, body, headers = {}) {
91
+ const res = await fetch(`${baseUrl}${path}`, {
92
+ method: "POST",
93
+ headers: { "content-type": "application/json", ...headers },
94
+ body: JSON.stringify(body)
95
+ });
96
+ let json = null;
97
+ try { json = await res.json(); } catch (_) { json = null; }
98
+ return { status: res.status, json, headers: res.headers };
99
+ }
100
+
101
+ async function main() {
102
+ const mock = await startMockServer();
103
+ const mockBase = `http://127.0.0.1:${mock.port}`;
104
+ const previous = {
105
+ mode: process.env.XYTARA_PAYMENT_VERIFICATION_MODE,
106
+ accepts: process.env.XYTARA_X402_ACCEPTS_JSON,
107
+ facUrl: process.env.XYTARA_X402_FACILITATOR_URL,
108
+ targets: process.env.XYTARA_EXECUTION_TARGETS_JSON
109
+ };
110
+ process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "x402_facilitator";
111
+ process.env.XYTARA_X402_ACCEPTS_JSON = JSON.stringify(ACCEPTS);
112
+ process.env.XYTARA_X402_FACILITATOR_URL = mockBase;
113
+ process.env.XYTARA_EXECUTION_TARGETS_JSON = JSON.stringify([
114
+ { target_id: "exec_ok", task_ref: "exec.ok", endpoint_url: `${mockBase}/exec-ok` },
115
+ { target_id: "exec_fail", task_ref: "exec.fail", endpoint_url: `${mockBase}/exec-fail` }
116
+ ]);
117
+
118
+ delete require.cache[require.resolve("../server")];
119
+ const { createServer } = require("../server");
120
+ const server = createServer();
121
+ await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
122
+ const baseUrl = `http://127.0.0.1:${server.address().port}`;
123
+
124
+ try {
125
+ // 1. Execution FAILS -> deferred settlement is never reached; caller is not charged.
126
+ const settleBeforeFail = mock.calls.settle;
127
+ const verifyBeforeFail = mock.calls.verify;
128
+ const failBody = { account_id: "acct_ves", command: "failing execution", task_ref: "exec.fail", body: { subject_id: "s-1" } };
129
+ const failed = await post(baseUrl, "/x402/commands/execute", failBody, { "payment-signature": paymentHeader("ok") });
130
+ assert.strictEqual(failed.status, 502, "failed execution must return 502");
131
+ assert.strictEqual(failed.json.ok, false, "failed execution must report ok:false");
132
+ assert.strictEqual(failed.json.payment_settled, false, "a failed x402 call must NOT be settled");
133
+ assert.ok(/not charged/i.test(String(failed.json.remediation || "")), "failed x402 call must tell the agent it was not charged");
134
+ assert.ok(!failed.json.settlement, "failed x402 call must carry no settlement");
135
+ assert.strictEqual(failed.headers.get("payment-response"), null, "failed x402 call must NOT emit a PAYMENT-RESPONSE receipt");
136
+ assert.strictEqual(mock.calls.settle, settleBeforeFail, "facilitator /settle must NOT be called when execution fails");
137
+ assert.strictEqual(mock.calls.verify, verifyBeforeFail + 1, "the payment must still have been verified up front");
138
+ assert.strictEqual(mock.calls.execFail, 1, "the failing execution target must have been attempted");
139
+
140
+ // 2. Execution SUCCEEDS -> settle runs exactly once, after the work, with a receipt.
141
+ const settleBeforeOk = mock.calls.settle;
142
+ const okBody = { account_id: "acct_ves", command: "successful execution", task_ref: "exec.ok", body: { subject_id: "s-2" } };
143
+ const ok = await post(baseUrl, "/x402/commands/execute", okBody, { "payment-signature": paymentHeader("ok") });
144
+ assert.strictEqual(ok.status, 200, "successful execution must return 200");
145
+ assert.strictEqual(ok.json.transaction.status, "completed", "successful transaction must be completed");
146
+ assert.strictEqual(ok.json.transaction.payment.verification_mode, "x402_facilitator", "verification mode must be recorded");
147
+ assert.strictEqual(mock.calls.settle, settleBeforeOk + 1, "facilitator /settle must be called exactly once on success");
148
+ assert.strictEqual(mock.calls.execOk, 1, "the successful execution target must have been invoked");
149
+ const receiptHeader = ok.headers.get("payment-response");
150
+ assert.strictEqual(typeof receiptHeader, "string", "a settled success must carry a PAYMENT-RESPONSE receipt");
151
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64").toString("utf8"));
152
+ assert.strictEqual(receipt.success, true, "settlement receipt must report success");
153
+ assert.strictEqual(receipt.transaction, "0xmocktx", "settlement receipt transaction mismatch");
154
+
155
+ // 3. Settle FAILS after a SUCCESSFUL execution -> deliverable withheld, not charged.
156
+ const settleBeforeSf = mock.calls.settle;
157
+ const sfBody = { account_id: "acct_ves", command: "settle fails after success", task_ref: "exec.ok", body: { subject_id: "s-3" } };
158
+ const settleFail = await post(baseUrl, "/x402/commands/execute", sfBody, { "payment-signature": paymentHeader("settle-fails") });
159
+ assert.strictEqual(settleFail.status, 402, "a failed post-execution settlement must 402");
160
+ assert.strictEqual(settleFail.json.ok, false, "a failed settlement must report ok:false");
161
+ assert.strictEqual(settleFail.json.payment_settled, false, "a failed settlement must report payment_settled:false");
162
+ assert.ok(/not charged/i.test(String(settleFail.json.remediation || "")), "settle-fail must tell the agent it was not charged");
163
+ assert.ok(!settleFail.json.transaction, "settle-fail must NOT return the deliverable");
164
+ assert.strictEqual(settleFail.headers.get("payment-response"), null, "settle-fail must emit no receipt");
165
+ assert.strictEqual(mock.calls.settle, settleBeforeSf + 1, "settle must have been attempted once");
166
+ assert.strictEqual(mock.calls.execOk, 2, "the work must have executed before the settle attempt");
167
+
168
+ // 4. Non-x402 lane (presence mode, no on-chain settlement) that fails execution must
169
+ // NOT claim the caller was charged and must NOT point them at a refund flow — the
170
+ // failure flag is keyed on an actual receipt, not merely on "not deferred".
171
+ await new Promise((resolve) => server.close(resolve));
172
+ const prevMode = process.env.XYTARA_PAYMENT_VERIFICATION_MODE;
173
+ process.env.XYTARA_PAYMENT_VERIFICATION_MODE = "presence";
174
+ delete require.cache[require.resolve("../server")];
175
+ const presenceServerFactory = require("../server").createServer;
176
+ const presenceServer = presenceServerFactory();
177
+ await new Promise((resolve) => presenceServer.listen(0, "127.0.0.1", resolve));
178
+ const presenceBase = `http://127.0.0.1:${presenceServer.address().port}`;
179
+ try {
180
+ const presFail = await post(presenceBase, "/v1/commands/execute", { account_id: "acct_ves", command: "presence failing exec", task_ref: "exec.fail", body: { subject_id: "s-4" } }, {
181
+ "x-wallet-id": "wallet-presence",
182
+ "x-payment": "encoded",
183
+ "x-payment-signature": "sig"
184
+ });
185
+ assert.strictEqual(presFail.json.ok, false, "presence failing execution must report ok:false");
186
+ assert.strictEqual(presFail.json.payment_settled, false, "presence lane must NOT claim a charge on failure");
187
+ assert.ok(!/refund/i.test(String(presFail.json.remediation || "")), "presence lane must NOT point to a refund flow on failure");
188
+ assert.strictEqual(presFail.headers.get("payment-response"), null, "presence failure must emit no receipt");
189
+ } finally {
190
+ await new Promise((resolve) => presenceServer.close(resolve));
191
+ if (prevMode === undefined) delete process.env.XYTARA_PAYMENT_VERIFICATION_MODE; else process.env.XYTARA_PAYMENT_VERIFICATION_MODE = prevMode;
192
+ }
193
+
194
+ console.log("xytara verify:verify-execute-settle passed");
195
+ } finally {
196
+ await new Promise((resolve) => server.close(resolve));
197
+ await new Promise((resolve) => mock.server.close(resolve));
198
+ const restore = (key, value) => { if (value === undefined) delete process.env[key]; else process.env[key] = value; };
199
+ restore("XYTARA_PAYMENT_VERIFICATION_MODE", previous.mode);
200
+ restore("XYTARA_X402_ACCEPTS_JSON", previous.accepts);
201
+ restore("XYTARA_X402_FACILITATOR_URL", previous.facUrl);
202
+ restore("XYTARA_EXECUTION_TARGETS_JSON", previous.targets);
203
+ }
204
+ }
205
+
206
+ main().catch((error) => {
207
+ console.error(error && error.message ? error.message : error);
208
+ process.exitCode = 1;
209
+ });