@devlab.group/agent-commerce 2.4.1 → 2.5.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.
Files changed (108) hide show
  1. package/README.md +115 -15
  2. package/dist/ap2.d.ts +24 -12
  3. package/dist/ap2.js +31 -13
  4. package/dist/ap2.js.map +1 -1
  5. package/dist/{chunk-DTKU3Z2M.js → chunk-3OTCFFFT.js} +37 -4
  6. package/dist/chunk-3OTCFFFT.js.map +1 -0
  7. package/dist/{chunk-QPV3EGJO.js → chunk-4K7FIBJM.js} +25 -4
  8. package/dist/chunk-4K7FIBJM.js.map +1 -0
  9. package/dist/{chunk-CXWC5LWM.js → chunk-FKQK5XZA.js} +652 -68
  10. package/dist/chunk-FKQK5XZA.js.map +1 -0
  11. package/dist/chunk-GA3O2A7R.js +92 -0
  12. package/dist/chunk-GA3O2A7R.js.map +1 -0
  13. package/dist/{chunk-WZW5ORZH.js → chunk-VUCMBGUR.js} +3 -3
  14. package/dist/{chunk-WZW5ORZH.js.map → chunk-VUCMBGUR.js.map} +1 -1
  15. package/dist/cli/{ap2-GWSP4LYY.js → ap2-YAPLERSC.js} +34 -15
  16. package/dist/cli/ap2-YAPLERSC.js.map +1 -0
  17. package/dist/cli/{chunk-G2JEY7MB.js → chunk-2J2IQQ4X.js} +8 -7
  18. package/dist/cli/chunk-2J2IQQ4X.js.map +1 -0
  19. package/dist/cli/chunk-2UPPX3FJ.js +311 -0
  20. package/dist/cli/chunk-2UPPX3FJ.js.map +1 -0
  21. package/dist/cli/{chunk-WYOGIANL.js → chunk-4H2QLHQI.js} +4 -4
  22. package/dist/cli/{chunk-WYOGIANL.js.map → chunk-4H2QLHQI.js.map} +1 -1
  23. package/dist/cli/{chunk-OZ4LAWBK.js → chunk-5R7DM5LM.js} +25 -4
  24. package/dist/cli/chunk-5R7DM5LM.js.map +1 -0
  25. package/dist/cli/chunk-663ALDZA.js +92 -0
  26. package/dist/cli/chunk-663ALDZA.js.map +1 -0
  27. package/dist/cli/{chunk-QQ3WY5L3.js → chunk-IDY2735M.js} +36 -104
  28. package/dist/cli/chunk-IDY2735M.js.map +1 -0
  29. package/dist/cli/chunk-JFVRG2HH.js +107 -0
  30. package/dist/cli/chunk-JFVRG2HH.js.map +1 -0
  31. package/dist/cli/chunk-M7NXVRU5.js +8 -0
  32. package/dist/cli/{chunk-ITWNSS2B.js.map → chunk-M7NXVRU5.js.map} +1 -1
  33. package/dist/cli/chunk-NIU5EMSB.js +119 -0
  34. package/dist/cli/chunk-NIU5EMSB.js.map +1 -0
  35. package/dist/cli/{chunk-TNMLDPUO.js → chunk-QA2OPHBF.js} +655 -70
  36. package/dist/cli/chunk-QA2OPHBF.js.map +1 -0
  37. package/dist/cli/{chunk-GJIMDMYT.js → chunk-QLUCNPRT.js} +404 -161
  38. package/dist/cli/chunk-QLUCNPRT.js.map +1 -0
  39. package/dist/cli/chunk-SYQLJXIU.js +105 -0
  40. package/dist/cli/chunk-SYQLJXIU.js.map +1 -0
  41. package/dist/cli/{chunk-3MGAJ5FX.js → chunk-VJ4EO4LE.js} +3 -3
  42. package/dist/cli/{chunk-3MGAJ5FX.js.map → chunk-VJ4EO4LE.js.map} +1 -1
  43. package/dist/cli/{chunk-PIMFPCNZ.js → chunk-Z34UMZBB.js} +234 -85
  44. package/dist/cli/chunk-Z34UMZBB.js.map +1 -0
  45. package/dist/cli/config-BWFMN2ZB.js +12 -0
  46. package/dist/cli/{config-UI4EDG4L.js.map → config-BWFMN2ZB.js.map} +1 -1
  47. package/dist/cli/index.js +258 -11
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/{mcp-PGBRWZVA.js → mcp-KZEMXLHR.js} +5 -4
  50. package/dist/cli/mcp-KZEMXLHR.js.map +1 -0
  51. package/dist/cli/{near-5AY3HWYF.js → near-U6365VFX.js} +15 -4
  52. package/dist/cli/near-U6365VFX.js.map +1 -0
  53. package/dist/cli/payment-providers-SZL36H3Y.js +6 -0
  54. package/dist/cli/payment-providers-SZL36H3Y.js.map +1 -0
  55. package/dist/cli/{provider-I2ESL6RS.js → provider-6TKB5ADJ.js} +26 -15
  56. package/dist/cli/provider-6TKB5ADJ.js.map +1 -0
  57. package/dist/cli/receipts-ZGLJWMQG.js +8 -0
  58. package/dist/cli/receipts-ZGLJWMQG.js.map +1 -0
  59. package/dist/cli/reconcile-IC22MJD5.js +4 -0
  60. package/dist/cli/reconcile-IC22MJD5.js.map +1 -0
  61. package/dist/cli/{run-JK4ERPJV.js → run-ZTYPQI4D.js} +151 -115
  62. package/dist/cli/run-ZTYPQI4D.js.map +1 -0
  63. package/dist/cli/svm-KDSPDFJG.js +48 -0
  64. package/dist/cli/svm-KDSPDFJG.js.map +1 -0
  65. package/dist/cli/{x402-7WBFZQXV.js → x402-WLYBLODI.js} +10 -9
  66. package/dist/cli/x402-WLYBLODI.js.map +1 -0
  67. package/dist/index.d.ts +112 -12
  68. package/dist/index.js +948 -157
  69. package/dist/index.js.map +1 -1
  70. package/dist/mcp.d.ts +1 -2
  71. package/dist/mcp.js +1 -1
  72. package/dist/mpp.js +20 -10
  73. package/dist/mpp.js.map +1 -1
  74. package/dist/{near-D6C42VPE.js → near-FH7HPZV3.js} +15 -4
  75. package/dist/near-FH7HPZV3.js.map +1 -0
  76. package/dist/{protocol-adapter-C9odYl6l.d.ts → protocol-adapter-DEXpdp8m.d.ts} +102 -48
  77. package/dist/{runtime-Cl_BLVZV.d.ts → runtime-Dz2fSO-P.d.ts} +220 -2
  78. package/dist/svm-KNC3PHMS.js +48 -0
  79. package/dist/svm-KNC3PHMS.js.map +1 -0
  80. package/dist/x402.js +5 -5
  81. package/package.json +1 -1
  82. package/dist/authorization-DjAecwuW.d.ts +0 -115
  83. package/dist/chunk-CXWC5LWM.js.map +0 -1
  84. package/dist/chunk-DTKU3Z2M.js.map +0 -1
  85. package/dist/chunk-KA2AX5FH.js +0 -44
  86. package/dist/chunk-KA2AX5FH.js.map +0 -1
  87. package/dist/chunk-QPV3EGJO.js.map +0 -1
  88. package/dist/cli/ap2-GWSP4LYY.js.map +0 -1
  89. package/dist/cli/chunk-G2JEY7MB.js.map +0 -1
  90. package/dist/cli/chunk-GJIMDMYT.js.map +0 -1
  91. package/dist/cli/chunk-ITWNSS2B.js +0 -8
  92. package/dist/cli/chunk-OSTCU6ZK.js +0 -44
  93. package/dist/cli/chunk-OSTCU6ZK.js.map +0 -1
  94. package/dist/cli/chunk-OZ4LAWBK.js.map +0 -1
  95. package/dist/cli/chunk-PIMFPCNZ.js.map +0 -1
  96. package/dist/cli/chunk-QQ3WY5L3.js.map +0 -1
  97. package/dist/cli/chunk-TNMLDPUO.js.map +0 -1
  98. package/dist/cli/config-UI4EDG4L.js +0 -11
  99. package/dist/cli/mcp-PGBRWZVA.js.map +0 -1
  100. package/dist/cli/near-5AY3HWYF.js.map +0 -1
  101. package/dist/cli/provider-I2ESL6RS.js.map +0 -1
  102. package/dist/cli/run-JK4ERPJV.js.map +0 -1
  103. package/dist/cli/svm-3MMYFPC7.js +0 -33
  104. package/dist/cli/svm-3MMYFPC7.js.map +0 -1
  105. package/dist/cli/x402-7WBFZQXV.js.map +0 -1
  106. package/dist/near-D6C42VPE.js.map +0 -1
  107. package/dist/svm-QEUAUNPQ.js +0 -33
  108. package/dist/svm-QEUAUNPQ.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { ACP_CHECKOUT_OPERATIONS, ACP_SPEC_VERSION } from './chunk-PIMFPCNZ.js';
2
- import { openSqliteDatabase } from './chunk-3MGAJ5FX.js';
3
- import { PACKAGE_VERSION } from './chunk-ITWNSS2B.js';
4
- import { NOOP_LOGGER, systemClock, toCommerceError, CommerceError } from './chunk-QQ3WY5L3.js';
1
+ import { openSqliteDatabase } from './chunk-VJ4EO4LE.js';
2
+ import { PACKAGE_VERSION } from './chunk-M7NXVRU5.js';
3
+ import { NOOP_LOGGER, systemClock, transactionState } from './chunk-IDY2735M.js';
4
+ import { toCommerceError, CommerceError } from './chunk-SYQLJXIU.js';
5
5
  import { randomUUID } from 'crypto';
6
6
  import { accessSync, constants } from 'fs';
7
7
 
@@ -40,7 +40,8 @@ function receiptToRow(receipt) {
40
40
  duration_ms: receipt.durationMs ?? null,
41
41
  protocol: receipt.protocol ?? null,
42
42
  metadata_json: receipt.metadata !== void 0 ? JSON.stringify(redact(receipt.metadata)) : null,
43
- authorization_json: receipt.authorization !== void 0 ? JSON.stringify(redact(receipt.authorization)) : null
43
+ authorization_json: receipt.authorization !== void 0 ? JSON.stringify(redact(receipt.authorization)) : null,
44
+ transaction_id: receipt.transactionId ?? null
44
45
  };
45
46
  }
46
47
  function rowToReceipt(row) {
@@ -57,7 +58,8 @@ function rowToReceipt(row) {
57
58
  ...row.duration_ms !== null ? { durationMs: row.duration_ms } : {},
58
59
  ...row.protocol !== null ? { protocol: row.protocol } : {},
59
60
  ...metadata !== void 0 ? { metadata } : {},
60
- ...authorization !== void 0 ? { authorization } : {}
61
+ ...authorization !== void 0 ? { authorization } : {},
62
+ ...row.transaction_id !== null ? { transactionId: row.transaction_id } : {}
61
63
  };
62
64
  }
63
65
  function eventToRow(event) {
@@ -71,7 +73,8 @@ function eventToRow(event) {
71
73
  payment_provider: event.paymentProvider ?? null,
72
74
  duration_ms: event.durationMs ?? null,
73
75
  status: event.status ?? null,
74
- data_json: event.data !== void 0 ? JSON.stringify(redact(event.data)) : null
76
+ data_json: event.data !== void 0 ? JSON.stringify(redact(event.data)) : null,
77
+ transaction_id: event.transactionId ?? null
75
78
  };
76
79
  }
77
80
  function rowToEvent(row) {
@@ -86,7 +89,8 @@ function rowToEvent(row) {
86
89
  ...row.payment_provider !== null ? { paymentProvider: row.payment_provider } : {},
87
90
  ...row.duration_ms !== null ? { durationMs: row.duration_ms } : {},
88
91
  ...row.status !== null ? { status: row.status } : {},
89
- ...data !== void 0 ? { data } : {}
92
+ ...data !== void 0 ? { data } : {},
93
+ ...row.transaction_id !== null ? { transactionId: row.transaction_id } : {}
90
94
  };
91
95
  }
92
96
  function rowToPaymentAttempt(row) {
@@ -104,7 +108,61 @@ function rowToPaymentAttempt(row) {
104
108
  ...row.external_reference !== null ? { externalReference: row.external_reference } : {},
105
109
  ...row.rejection_reason !== null ? { rejectionReason: row.rejection_reason } : {},
106
110
  createdAt: row.created_at,
107
- updatedAt: row.updated_at
111
+ updatedAt: row.updated_at,
112
+ ...row.transaction_id !== null ? { transactionId: row.transaction_id } : {},
113
+ ...row.resolution_json !== null ? { resolution: JSON.parse(row.resolution_json) } : {}
114
+ };
115
+ }
116
+ function rowToTransaction(row, now) {
117
+ const status = row.payment_status;
118
+ const backend = row.backend_dispatched_at !== null && row.backend_deadline_at !== null ? {
119
+ dispatchedAt: row.backend_dispatched_at,
120
+ deadlineAt: row.backend_deadline_at,
121
+ ...row.backend_status !== null ? { status: row.backend_status } : {},
122
+ ...row.backend_error_code !== null ? { errorCode: row.backend_error_code } : {}
123
+ } : void 0;
124
+ const completedAt = row.completed_at ?? void 0;
125
+ const authorization = row.authorization_json !== null && row.reservation_id !== null ? {
126
+ ...JSON.parse(row.authorization_json),
127
+ reservationId: row.reservation_id
128
+ } : void 0;
129
+ return {
130
+ id: row.id,
131
+ requestId: row.request_id,
132
+ resourceId: row.resource_id,
133
+ protocol: row.protocol,
134
+ state: transactionState(
135
+ {
136
+ payment: { status },
137
+ ...backend !== void 0 ? { backend } : {},
138
+ ...completedAt !== void 0 ? { completedAt } : {}
139
+ },
140
+ now
141
+ ),
142
+ payment: {
143
+ provider: row.provider,
144
+ attemptId: row.attempt_id,
145
+ replayKey: row.replay_key,
146
+ status,
147
+ amount: row.payment_amount,
148
+ currency: row.payment_currency,
149
+ ...row.network !== null ? { network: row.network } : {},
150
+ ...row.asset !== null ? { asset: row.asset } : {},
151
+ ...row.payment_payee !== null ? { payee: row.payment_payee } : {},
152
+ ...row.payment_external_reference !== null ? { externalReference: row.payment_external_reference } : {},
153
+ ...row.locator_json !== null ? { locator: JSON.parse(row.locator_json) } : {},
154
+ ...row.settling_at !== null ? { settlingAt: row.settling_at } : {},
155
+ ...row.expires_at !== null ? { expiresAt: row.expires_at } : {}
156
+ },
157
+ ...authorization !== void 0 ? { authorization } : {},
158
+ ...backend !== void 0 ? { backend } : {},
159
+ ...row.receipt_id !== null ? { receiptId: row.receipt_id } : {},
160
+ ...row.input_digest !== null ? { inputDigest: row.input_digest } : {},
161
+ configDigest: row.config_digest,
162
+ ...row.persistence_failures_json !== null ? { persistenceFailures: JSON.parse(row.persistence_failures_json) } : {},
163
+ createdAt: row.created_at,
164
+ updatedAt: row.updated_at,
165
+ ...completedAt !== void 0 ? { completedAt } : {}
108
166
  };
109
167
  }
110
168
 
@@ -199,13 +257,60 @@ var MIGRATIONS = [
199
257
  CREATE INDEX IF NOT EXISTS idx_payment_attempt_history_request_id ON payment_attempt_history(request_id);
200
258
  `);
201
259
  }
260
+ },
261
+ {
262
+ version: 4,
263
+ up(db) {
264
+ db.exec(`
265
+ CREATE TABLE IF NOT EXISTS transactions (
266
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
267
+ id TEXT NOT NULL UNIQUE,
268
+ request_id TEXT NOT NULL,
269
+ resource_id TEXT NOT NULL,
270
+ protocol TEXT NOT NULL,
271
+ state TEXT NOT NULL,
272
+ provider TEXT NOT NULL,
273
+ attempt_id TEXT NOT NULL,
274
+ replay_key TEXT NOT NULL,
275
+ network TEXT,
276
+ asset TEXT,
277
+ authorization_json TEXT,
278
+ reservation_id TEXT,
279
+ locator_json TEXT,
280
+ expires_at TEXT,
281
+ input_digest TEXT,
282
+ config_digest TEXT NOT NULL,
283
+ settling_at TEXT,
284
+ backend_dispatched_at TEXT,
285
+ backend_deadline_at TEXT,
286
+ backend_status INTEGER,
287
+ backend_error_code TEXT,
288
+ receipt_id TEXT,
289
+ persistence_failures_json TEXT,
290
+ completed_at TEXT,
291
+ created_at TEXT NOT NULL,
292
+ updated_at TEXT NOT NULL
293
+ );
294
+ CREATE INDEX IF NOT EXISTS idx_transactions_state_seq ON transactions(state, seq);
295
+ CREATE INDEX IF NOT EXISTS idx_transactions_request_id ON transactions(request_id);
296
+ CREATE INDEX IF NOT EXISTS idx_transactions_replay_key ON transactions(replay_key);
297
+
298
+ ALTER TABLE receipts ADD COLUMN transaction_id TEXT;
299
+ ALTER TABLE events ADD COLUMN transaction_id TEXT;
300
+ ALTER TABLE payment_attempts ADD COLUMN transaction_id TEXT;
301
+ ALTER TABLE payment_attempts ADD COLUMN resolution_json TEXT;
302
+ ALTER TABLE payment_attempt_history ADD COLUMN transaction_id TEXT;
303
+ ALTER TABLE payment_attempt_history ADD COLUMN resolution_json TEXT;
304
+ CREATE INDEX IF NOT EXISTS idx_payment_attempt_history_id ON payment_attempt_history(id);
305
+ `);
306
+ }
202
307
  }
203
308
  ];
204
309
  var SCHEMA_VERSION = MIGRATIONS.at(-1)?.version ?? 0;
205
- function migrate(db) {
310
+ function migrate(db, target = SCHEMA_VERSION) {
206
311
  const current = db.pragma("user_version", { simple: true });
207
312
  for (const migration of MIGRATIONS) {
208
- if (migration.version <= current) continue;
313
+ if (migration.version <= current || migration.version > target) continue;
209
314
  const apply = db.transaction(() => {
210
315
  migration.up(db);
211
316
  db.pragma(`user_version = ${migration.version}`);
@@ -223,6 +328,22 @@ function clampListLimit(limit) {
223
328
  }
224
329
  var UNDELIVERED = "backend_status NOT BETWEEN 200 AND 299";
225
330
  var UNCERTAIN = "status IN ('settlement-uncertain', 'failed')";
331
+ var TRANSACTION_SELECT = `
332
+ SELECT t.*,
333
+ COALESCE(a.status, h.status) AS payment_status,
334
+ COALESCE(a.amount, h.amount) AS payment_amount,
335
+ COALESCE(a.currency, h.currency) AS payment_currency,
336
+ COALESCE(a.payee, h.payee) AS payment_payee,
337
+ COALESCE(a.external_reference, h.external_reference) AS payment_external_reference
338
+ FROM transactions t
339
+ LEFT JOIN payment_attempts a ON a.id = t.attempt_id
340
+ LEFT JOIN payment_attempt_history h ON a.id IS NULL AND h.seq = (
341
+ SELECT MAX(seq) FROM payment_attempt_history
342
+ WHERE replay_key = t.replay_key AND id = t.attempt_id
343
+ )`;
344
+ var TIME_DEPENDENT = "t.backend_deadline_at IS NOT NULL AND t.completed_at IS NULL";
345
+ var RESOLVED_STATES = ["delivered", "payment-failed"];
346
+ var CURSOR_PATTERN = /^[1-9][0-9]{0,15}$/;
226
347
  var defaultIds = {
227
348
  next: (prefix) => prefix !== void 0 ? `${prefix}_${randomUUID()}` : randomUUID()
228
349
  };
@@ -238,8 +359,8 @@ function createSqliteReceiptStore(options) {
238
359
  const db = openSqliteDatabase({ path, label: "Receipt database", logger });
239
360
  migrate(db);
240
361
  const insertReceiptStmt = db.prepare(
241
- `INSERT INTO receipts (id, request_id, resource_id, payment_json, delivered_at, backend_status, duration_ms, protocol, metadata_json, authorization_json)
242
- VALUES (@id, @request_id, @resource_id, @payment_json, @delivered_at, @backend_status, @duration_ms, @protocol, @metadata_json, @authorization_json)`
362
+ `INSERT INTO receipts (id, request_id, resource_id, payment_json, delivered_at, backend_status, duration_ms, protocol, metadata_json, authorization_json, transaction_id)
363
+ VALUES (@id, @request_id, @resource_id, @payment_json, @delivered_at, @backend_status, @duration_ms, @protocol, @metadata_json, @authorization_json, @transaction_id)`
243
364
  );
244
365
  const getReceiptStmt = db.prepare("SELECT * FROM receipts WHERE id = ?");
245
366
  const listReceiptsStmt = db.prepare(
@@ -261,8 +382,8 @@ function createSqliteReceiptStore(options) {
261
382
  `SELECT COUNT(*) AS count FROM receipts WHERE ${UNDELIVERED}`
262
383
  );
263
384
  const insertEventStmt = db.prepare(
264
- `INSERT INTO events (id, type, request_id, resource_id, at, adapter, payment_provider, duration_ms, status, data_json)
265
- VALUES (@id, @type, @request_id, @resource_id, @at, @adapter, @payment_provider, @duration_ms, @status, @data_json)`
385
+ `INSERT INTO events (id, type, request_id, resource_id, at, adapter, payment_provider, duration_ms, status, data_json, transaction_id)
386
+ VALUES (@id, @type, @request_id, @resource_id, @at, @adapter, @payment_provider, @duration_ms, @status, @data_json, @transaction_id)`
266
387
  );
267
388
  const listEventsStmt = db.prepare(
268
389
  "SELECT * FROM events ORDER BY at DESC, seq DESC LIMIT ?"
@@ -271,13 +392,13 @@ function createSqliteReceiptStore(options) {
271
392
  "SELECT * FROM events WHERE request_id = ? ORDER BY at DESC, seq DESC LIMIT ?"
272
393
  );
273
394
  const insertPaymentAttemptStmt = db.prepare(
274
- `INSERT INTO payment_attempts (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at)
275
- VALUES (@id, @request_id, @resource_id, @provider, @replay_key, @status, @amount, @currency, @payer, @payee, @external_reference, @rejection_reason, @created_at, @updated_at)
395
+ `INSERT INTO payment_attempts (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, transaction_id)
396
+ VALUES (@id, @request_id, @resource_id, @provider, @replay_key, @status, @amount, @currency, @payer, @payee, @external_reference, @rejection_reason, @created_at, @updated_at, @transaction_id)
276
397
  RETURNING *`
277
398
  );
278
399
  const archiveRejectedAttemptStmt = db.prepare(
279
- `INSERT INTO payment_attempt_history (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, superseded_at)
280
- SELECT id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, @created_at
400
+ `INSERT INTO payment_attempt_history (id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, superseded_at, transaction_id, resolution_json)
401
+ SELECT id, request_id, resource_id, provider, replay_key, status, amount, currency, payer, payee, external_reference, rejection_reason, created_at, updated_at, @created_at, transaction_id, resolution_json
281
402
  FROM payment_attempts
282
403
  WHERE replay_key = @replay_key AND provider = @provider AND status = 'rejected'`
283
404
  );
@@ -285,7 +406,8 @@ function createSqliteReceiptStore(options) {
285
406
  `UPDATE payment_attempts SET id = @id, request_id = @request_id, resource_id = @resource_id,
286
407
  status = 'reserved', amount = @amount, currency = @currency,
287
408
  payer = @payer, payee = @payee, external_reference = NULL, rejection_reason = NULL,
288
- created_at = @created_at, updated_at = @updated_at
409
+ created_at = @created_at, updated_at = @updated_at,
410
+ transaction_id = @transaction_id, resolution_json = NULL
289
411
  WHERE replay_key = @replay_key AND provider = @provider AND status = 'rejected'
290
412
  RETURNING *`
291
413
  );
@@ -294,7 +416,7 @@ function createSqliteReceiptStore(options) {
294
416
  external_reference = COALESCE(@external_reference, external_reference),
295
417
  rejection_reason = COALESCE(@rejection_reason, rejection_reason),
296
418
  updated_at = @updated_at
297
- WHERE replay_key = @replay_key`
419
+ WHERE replay_key = @replay_key AND resolution_json IS NULL`
298
420
  );
299
421
  const listPaymentAttemptsStmt = db.prepare(
300
422
  "SELECT * FROM payment_attempts ORDER BY created_at DESC, seq DESC LIMIT ?"
@@ -311,13 +433,176 @@ function createSqliteReceiptStore(options) {
311
433
  const countUncertainPaymentAttemptsStmt = db.prepare(
312
434
  `SELECT COUNT(*) AS count FROM payment_attempts WHERE ${UNCERTAIN}`
313
435
  );
314
- const paymentAttemptStatusStmt = db.prepare(
315
- "SELECT status FROM payment_attempts WHERE replay_key = ?"
436
+ const paymentAttemptStatusStmt = db.prepare("SELECT id, status, resolution_json FROM payment_attempts WHERE replay_key = ?");
437
+ const insertTransactionStmt = db.prepare(
438
+ `INSERT INTO transactions (id, request_id, resource_id, protocol, state, provider, attempt_id, replay_key, network, asset, authorization_json, reservation_id, locator_json, expires_at, input_digest, config_digest, created_at, updated_at)
439
+ VALUES (@id, @request_id, @resource_id, @protocol, @state, @provider, @attempt_id, @replay_key, @network, @asset, @authorization_json, @reservation_id, @locator_json, @expires_at, @input_digest, @config_digest, @created_at, @created_at)`
440
+ );
441
+ const getTransactionStmt = db.prepare(
442
+ `${TRANSACTION_SELECT} WHERE t.id = ?`
443
+ );
444
+ const transactionsOfAttemptStmt = db.prepare(
445
+ `SELECT t.id FROM payment_attempts a
446
+ JOIN transactions t ON t.replay_key = a.replay_key AND t.attempt_id = a.id
447
+ WHERE a.replay_key = ?`
448
+ );
449
+ const transactionsOfAttemptIdStmt = db.prepare(
450
+ `SELECT t.id FROM payment_attempts a
451
+ JOIN transactions t ON t.replay_key = a.replay_key AND t.attempt_id = a.id
452
+ WHERE a.id = ?`
453
+ );
454
+ const setTransactionStateStmt = db.prepare("UPDATE transactions SET state = ? WHERE id = ?");
455
+ const stepStmts = {
456
+ settling: db.prepare(
457
+ "UPDATE transactions SET settling_at = @at, updated_at = @at WHERE id = @id"
458
+ ),
459
+ // A transaction without an authorization has no hold to record
460
+ hold: db.prepare(
461
+ `UPDATE transactions SET authorization_json = json_set(authorization_json, '$.hold', @hold), updated_at = @at
462
+ WHERE id = @id AND authorization_json IS NOT NULL`
463
+ ),
464
+ "backend-dispatched": db.prepare(
465
+ `UPDATE transactions SET backend_dispatched_at = @at, backend_deadline_at = @deadline_at, updated_at = @at
466
+ WHERE id = @id`
467
+ ),
468
+ // Preserve a backend result even when the dispatch step was not saved:
469
+ // use the result time for missing dispatch and deadline timestamps
470
+ "backend-result": db.prepare(
471
+ `UPDATE transactions SET backend_status = @status, backend_error_code = @error_code,
472
+ backend_dispatched_at = COALESCE(backend_dispatched_at, @at),
473
+ backend_deadline_at = COALESCE(backend_deadline_at, @at), updated_at = @at
474
+ WHERE id = @id`
475
+ ),
476
+ receipt: db.prepare(
477
+ "UPDATE transactions SET receipt_id = @receipt_id, updated_at = @at WHERE id = @id"
478
+ ),
479
+ completed: db.prepare(
480
+ `UPDATE transactions SET completed_at = @at, persistence_failures_json = @persistence_failures_json, updated_at = @at
481
+ WHERE id = @id`
482
+ )
483
+ };
484
+ const getPaymentAttemptStmt = db.prepare(
485
+ "SELECT * FROM payment_attempts WHERE id = ?"
486
+ );
487
+ const getArchivedPaymentAttemptStmt = db.prepare(
488
+ "SELECT * FROM payment_attempt_history WHERE id = ? ORDER BY seq DESC LIMIT 1"
489
+ );
490
+ const resolvePaymentAttemptStmt = db.prepare(
491
+ `UPDATE payment_attempts SET status = @status,
492
+ rejection_reason = COALESCE(@rejection_reason, rejection_reason),
493
+ external_reference = COALESCE(@external_reference, external_reference),
494
+ resolution_json = @resolution_json, updated_at = @updated_at
495
+ WHERE id = @id AND status = @expected_status AND resolution_json IS NULL`
496
+ );
497
+ function refreshTransactionState(id, now) {
498
+ const row = getTransactionStmt.get(id);
499
+ if (row !== void 0) setTransactionStateStmt.run(rowToTransaction(row, now).state, id);
500
+ }
501
+ const reserve = db.transaction(
502
+ (reservation, params) => {
503
+ let row;
504
+ if (reservation.reclaimRejected === true) {
505
+ archiveRejectedAttemptStmt.run(params);
506
+ row = reclaimPaymentAttemptStmt.get(params);
507
+ }
508
+ row ??= insertPaymentAttemptStmt.get({
509
+ ...params,
510
+ status: "reserved",
511
+ external_reference: null,
512
+ rejection_reason: null
513
+ });
514
+ const txn = reservation.transaction;
515
+ if (txn !== void 0) {
516
+ const { reservationId, ...authorization } = txn.authorization ?? {};
517
+ insertTransactionStmt.run({
518
+ id: txn.id,
519
+ request_id: reservation.requestId,
520
+ resource_id: reservation.resourceId,
521
+ protocol: txn.protocol,
522
+ state: transactionState({ payment: { status: "reserved" } }, row.created_at),
523
+ provider: reservation.provider,
524
+ attempt_id: row.id,
525
+ replay_key: reservation.replayKey,
526
+ network: txn.network ?? null,
527
+ asset: txn.asset ?? null,
528
+ authorization_json: reservationId !== void 0 ? JSON.stringify(redact({ ...authorization, hold: "reserved" })) : null,
529
+ reservation_id: reservationId ?? null,
530
+ locator_json: txn.locator !== void 0 ? JSON.stringify(redact(txn.locator)) : null,
531
+ expires_at: txn.expiresAt ?? null,
532
+ input_digest: txn.inputDigest ?? null,
533
+ config_digest: txn.configDigest,
534
+ created_at: row.created_at
535
+ });
536
+ }
537
+ return row;
538
+ }
316
539
  );
317
- const reclaimRejectedAttempt = db.transaction(
318
- (params) => {
319
- archiveRejectedAttemptStmt.run(params);
320
- return reclaimPaymentAttemptStmt.get(params);
540
+ const updateAttempt = db.transaction((update, now) => {
541
+ const { changes } = updatePaymentAttemptStmt.run({
542
+ replay_key: update.replayKey,
543
+ status: update.status,
544
+ external_reference: update.externalReference ?? null,
545
+ rejection_reason: update.rejectionReason ?? null,
546
+ updated_at: now
547
+ });
548
+ for (const { id } of transactionsOfAttemptStmt.all(update.replayKey)) {
549
+ refreshTransactionState(id, now);
550
+ }
551
+ return changes;
552
+ });
553
+ const recordStep = db.transaction((step) => {
554
+ const base = { id: step.transactionId, at: step.at };
555
+ let changes;
556
+ switch (step.kind) {
557
+ case "settling":
558
+ changes = stepStmts.settling.run(base).changes;
559
+ break;
560
+ case "hold":
561
+ changes = stepStmts.hold.run({ ...base, hold: step.hold }).changes;
562
+ break;
563
+ case "backend-dispatched":
564
+ changes = stepStmts["backend-dispatched"].run({
565
+ ...base,
566
+ deadline_at: step.deadlineAt
567
+ }).changes;
568
+ break;
569
+ case "backend-result":
570
+ changes = stepStmts["backend-result"].run({
571
+ ...base,
572
+ status: step.status,
573
+ error_code: step.errorCode ?? null
574
+ }).changes;
575
+ break;
576
+ case "receipt":
577
+ changes = stepStmts.receipt.run({ ...base, receipt_id: step.receiptId }).changes;
578
+ break;
579
+ case "completed":
580
+ changes = stepStmts.completed.run({
581
+ ...base,
582
+ persistence_failures_json: step.persistenceFailures !== void 0 && step.persistenceFailures.length > 0 ? JSON.stringify(step.persistenceFailures) : null
583
+ }).changes;
584
+ break;
585
+ }
586
+ if (changes > 0) refreshTransactionState(step.transactionId, step.at);
587
+ return changes;
588
+ });
589
+ const resolveAttempt = db.transaction(
590
+ (request, now) => {
591
+ const { changes } = resolvePaymentAttemptStmt.run({
592
+ id: request.attemptId,
593
+ expected_status: request.expectedStatus,
594
+ status: request.status,
595
+ rejection_reason: request.rejectionReason ?? null,
596
+ // An operator's reference is unverified; it stays in resolution_json only
597
+ external_reference: request.resolution.method === "reconcile" ? request.resolution.externalReference ?? null : null,
598
+ resolution_json: JSON.stringify(redact(request.resolution)),
599
+ updated_at: now
600
+ });
601
+ if (changes === 0) return false;
602
+ for (const { id } of transactionsOfAttemptIdStmt.all(request.attemptId)) {
603
+ refreshTransactionState(id, now);
604
+ }
605
+ return true;
321
606
  }
322
607
  );
323
608
  let closed = false;
@@ -326,7 +611,7 @@ function createSqliteReceiptStore(options) {
326
611
  kind: "storage",
327
612
  implementationVersion: PACKAGE_VERSION,
328
613
  supportedSpec: `sqlite-schema-v${SCHEMA_VERSION}`,
329
- capabilities: ["receipts", "events", "payment-attempts"],
614
+ capabilities: ["receipts", "events", "payment-attempts", "transactions"],
330
615
  status: "stable"
331
616
  };
332
617
  const store = {
@@ -366,17 +651,12 @@ function createSqliteReceiptStore(options) {
366
651
  payer: reservation.payer ?? null,
367
652
  payee: reservation.payee ?? null,
368
653
  created_at: now,
369
- updated_at: now
654
+ updated_at: now,
655
+ transaction_id: reservation.transaction?.id ?? null
370
656
  };
371
657
  let row;
372
658
  try {
373
- row = (reservation.reclaimRejected === true ? reclaimRejectedAttempt.immediate(params) : void 0) ?? // RETURNING on a successful insert always yields the inserted row
374
- insertPaymentAttemptStmt.get({
375
- ...params,
376
- status: "reserved",
377
- external_reference: null,
378
- rejection_reason: null
379
- });
659
+ row = reserve.immediate(reservation, params);
380
660
  } catch (err) {
381
661
  if (isUniqueConstraintOn(err, "payment_attempts.replay_key")) {
382
662
  const existing = paymentAttemptStatusStmt.get(reservation.replayKey);
@@ -396,14 +676,23 @@ function createSqliteReceiptStore(options) {
396
676
  },
397
677
  async updatePaymentAttempt(update) {
398
678
  try {
399
- const result = updatePaymentAttemptStmt.run({
400
- replay_key: update.replayKey,
401
- status: update.status,
402
- external_reference: update.externalReference ?? null,
403
- rejection_reason: update.rejectionReason ?? null,
404
- updated_at: clock.nowIso()
405
- });
406
- if (result.changes === 0) {
679
+ if (updateAttempt(update, clock.nowIso()) === 0) {
680
+ const existing = paymentAttemptStatusStmt.get(update.replayKey);
681
+ if (existing?.resolution_json != null) {
682
+ logger.warn(
683
+ {
684
+ attemptId: existing.id,
685
+ resolvedStatus: existing.status,
686
+ status: update.status,
687
+ ...update.externalReference !== void 0 ? { externalReference: update.externalReference } : {}
688
+ },
689
+ "receipt-store: payment attempt already resolved; update not applied"
690
+ );
691
+ throw new CommerceError(
692
+ "STORAGE_ERROR",
693
+ `Payment attempt ${existing.id} is already resolved; the update was not applied`
694
+ );
695
+ }
407
696
  throw new CommerceError(
408
697
  "STORAGE_ERROR",
409
698
  `No payment attempt found for replayKey=${update.replayKey}`
@@ -455,6 +744,74 @@ function createSqliteReceiptStore(options) {
455
744
  const rows = listOptions.requestId !== void 0 ? (uncertain ? listUncertainPaymentAttemptsByRequestStmt : listPaymentAttemptsByRequestStmt).all(listOptions.requestId, limit) : (uncertain ? listUncertainPaymentAttemptsStmt : listPaymentAttemptsStmt).all(limit);
456
745
  return rows.map(rowToPaymentAttempt);
457
746
  },
747
+ async recordTransactionStep(step) {
748
+ try {
749
+ if (recordStep(step) === 0) {
750
+ throw new CommerceError(
751
+ "STORAGE_ERROR",
752
+ step.kind === "hold" ? `No transaction with an authorization hold found for transactionId=${step.transactionId}` : `No transaction found for transactionId=${step.transactionId}`
753
+ );
754
+ }
755
+ } catch (err) {
756
+ throw toCommerceError(err, "STORAGE_ERROR", "Failed to record transaction step");
757
+ }
758
+ },
759
+ async getTransaction(id) {
760
+ const row = getTransactionStmt.get(id);
761
+ return row !== void 0 ? rowToTransaction(row, clock.nowIso()) : void 0;
762
+ },
763
+ async listTransactions(listOptions = {}) {
764
+ const { cursor, state, unresolved, pendingHold, requestId } = listOptions;
765
+ if (cursor !== void 0 && !CURSOR_PATTERN.test(cursor)) {
766
+ throw new CommerceError("INPUT_INVALID", "Malformed transaction list cursor");
767
+ }
768
+ const limit = clampListLimit(listOptions.limit);
769
+ const where = [];
770
+ const params = [];
771
+ if (cursor !== void 0) {
772
+ where.push("t.seq < ?");
773
+ params.push(Number(cursor));
774
+ }
775
+ if (requestId !== void 0) {
776
+ where.push("t.request_id = ?");
777
+ params.push(requestId);
778
+ }
779
+ if (state !== void 0) {
780
+ where.push(`(t.state = ? OR (${TIME_DEPENDENT}))`);
781
+ params.push(state);
782
+ }
783
+ if (pendingHold === true) {
784
+ where.push("json_extract(t.authorization_json, '$.hold') IN ('reserved', 'uncertain')");
785
+ }
786
+ if (unresolved === true) {
787
+ where.push(`t.state NOT IN (${RESOLVED_STATES.map(() => "?").join(", ")})`);
788
+ params.push(...RESOLVED_STATES);
789
+ }
790
+ const rows = db.prepare(
791
+ `${TRANSACTION_SELECT}${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY t.seq DESC LIMIT ?`
792
+ ).all(...params, limit);
793
+ const now = clock.nowIso();
794
+ const resolved = RESOLVED_STATES;
795
+ const transactions = rows.map((row) => rowToTransaction(row, now)).filter(
796
+ (t) => (state === void 0 || t.state === state) && (unresolved !== true || !resolved.includes(t.state))
797
+ );
798
+ const last = rows.at(-1);
799
+ return {
800
+ transactions,
801
+ ...rows.length === limit && last !== void 0 ? { nextCursor: String(last.seq) } : {}
802
+ };
803
+ },
804
+ async getPaymentAttempt(id) {
805
+ const row = getPaymentAttemptStmt.get(id) ?? getArchivedPaymentAttemptStmt.get(id);
806
+ return row !== void 0 ? rowToPaymentAttempt(row) : void 0;
807
+ },
808
+ async resolvePaymentAttempt(request) {
809
+ try {
810
+ return resolveAttempt(request, clock.nowIso());
811
+ } catch (err) {
812
+ throw toCommerceError(err, "STORAGE_ERROR", "Failed to resolve payment attempt");
813
+ }
814
+ },
458
815
  descriptor,
459
816
  async health() {
460
817
  const startedAt = clock.monotonicMs();
@@ -520,120 +877,6 @@ function createSqliteReceiptStore(options) {
520
877
  return store;
521
878
  }
522
879
 
523
- // src/protocols/a2a/constants.ts
524
- var A2A_SPEC_VERSION = "1.0.1";
525
- var A2A_PROTOCOL_VERSION = "1.0";
526
- var A2A_PROTOCOL_BINDING = "JSONRPC";
527
- var A2A_AGENT_CARD_PATH = "/.well-known/agent-card.json";
528
- var A2A_DEFAULT_MOUNT_PATH = "/a2a";
529
- var A2A_JSON_MEDIA_TYPE = "application/json";
530
- var A2A_DEFAULT_AGENT_NAME = "agent-commerce";
531
- var A2A_VERSION_HEADER = "a2a-version";
532
- var A2A_METHOD_SEND_MESSAGE = "SendMessage";
533
- var A2A_PUSH_CONFIG_METHODS = [
534
- "CreateTaskPushNotificationConfig",
535
- "GetTaskPushNotificationConfig",
536
- "ListTaskPushNotificationConfigs",
537
- "DeleteTaskPushNotificationConfig"
538
- ];
539
- var A2A_UNSUPPORTED_METHODS = [
540
- "SendStreamingMessage",
541
- "GetTask",
542
- "ListTasks",
543
- "CancelTask",
544
- "SubscribeToTask",
545
- ...A2A_PUSH_CONFIG_METHODS,
546
- "GetExtendedAgentCard"
547
- ];
548
- var A2A_TASK_LOOKUP_METHODS = ["GetTask", "CancelTask"];
549
- var A2A_TASK_STATE_COMPLETED = "TASK_STATE_COMPLETED";
550
- var A2A_TASK_STATE_FAILED = "TASK_STATE_FAILED";
551
- var A2A_TASK_STATE_INPUT_REQUIRED = "TASK_STATE_INPUT_REQUIRED";
552
- var A2A_EXTENSIONS_HEADER = "a2a-extensions";
553
-
554
- // src/protocols/a2a/descriptor.ts
555
- var A2A_CAPABILITIES = [
556
- "agent-card",
557
- "jsonrpc",
558
- "SendMessage",
559
- // The x402 A2A transport, opt-in per request
560
- "x402-extension"
561
- ];
562
- var A2A_UNSUPPORTED = [
563
- // Methods, as the protocol names them; the transport refuses the same list
564
- ...A2A_UNSUPPORTED_METHODS,
565
- // Transports other than the one binding served
566
- "HTTP+JSON/REST binding",
567
- "gRPC binding",
568
- // Behaviors
569
- "SSE",
570
- "long-running task persistence",
571
- "task resumption other than an x402 extension payment",
572
- "push notifications",
573
- "multi-turn conversational continuation",
574
- "authenticated extended agent cards",
575
- "A2A authentication schemes",
576
- "artifact types beyond Agent Commerce outcome data",
577
- // Pending x402 payments live in process memory
578
- "x402 extension payments across gateway restarts or instances"
579
- ];
580
- function buildDescriptor(implementationVersion) {
581
- return {
582
- name: "a2a",
583
- kind: "protocol",
584
- implementationVersion,
585
- supportedSpec: A2A_SPEC_VERSION,
586
- capabilities: A2A_CAPABILITIES,
587
- status: "experimental",
588
- unsupported: A2A_UNSUPPORTED
589
- };
590
- }
591
-
592
- // src/protocols/acp/descriptor.ts
593
- var ACP_CAPABILITIES = [
594
- "rest transport",
595
- "well-known discovery",
596
- "bearer authentication",
597
- "API-Version enforcement",
598
- // Forwarded as received: the gateway verifies neither signature header
599
- "request header forwarding to the merchant",
600
- "checkout service",
601
- ...ACP_CHECKOUT_OPERATIONS
602
- ];
603
- var ACP_UNSUPPORTED = [
604
- // Services this seller does not implement, named as ACP names them. The
605
- // discovery document advertises `checkout` alone, and these are the reason.
606
- "carts service",
607
- "feed service",
608
- "standalone orders service",
609
- "delegate_payment",
610
- "delegate_authentication",
611
- // Bindings and delivery directions
612
- "ACP MCP transport binding",
613
- "webhooks",
614
- "outbound merchant-to-agent delivery",
615
- "ACP client role",
616
- // Request authentication beyond the bearer token
617
- "Signature header verification",
618
- "Timestamp replay-window verification",
619
- // Extensions, versioning and payment handlers
620
- "discount extension",
621
- "general ACP extension framework",
622
- "multiple ACP API versions",
623
- "seller-backed payment handler integration"
624
- ];
625
- function buildDescriptor2(implementationVersion) {
626
- return {
627
- name: "acp",
628
- kind: "protocol",
629
- implementationVersion,
630
- supportedSpec: ACP_SPEC_VERSION,
631
- capabilities: ACP_CAPABILITIES,
632
- status: "experimental",
633
- unsupported: ACP_UNSUPPORTED
634
- };
635
- }
636
-
637
- export { A2A_AGENT_CARD_PATH, A2A_DEFAULT_AGENT_NAME, A2A_DEFAULT_MOUNT_PATH, A2A_EXTENSIONS_HEADER, A2A_JSON_MEDIA_TYPE, A2A_METHOD_SEND_MESSAGE, A2A_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION, A2A_PUSH_CONFIG_METHODS, A2A_SPEC_VERSION, A2A_TASK_LOOKUP_METHODS, A2A_TASK_STATE_COMPLETED, A2A_TASK_STATE_FAILED, A2A_TASK_STATE_INPUT_REQUIRED, A2A_UNSUPPORTED, A2A_UNSUPPORTED_METHODS, A2A_VERSION_HEADER, ACP_UNSUPPORTED, buildDescriptor, buildDescriptor2, createSqliteReceiptStore };
638
- //# sourceMappingURL=chunk-GJIMDMYT.js.map
639
- //# sourceMappingURL=chunk-GJIMDMYT.js.map
880
+ export { createSqliteReceiptStore };
881
+ //# sourceMappingURL=chunk-QLUCNPRT.js.map
882
+ //# sourceMappingURL=chunk-QLUCNPRT.js.map