@agentcash/router 1.2.0 → 1.2.2

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/dist/index.cjs CHANGED
@@ -626,6 +626,8 @@ function resolveMaxPrice(pricing) {
626
626
 
627
627
  // src/orchestrate.ts
628
628
  var import_mppx2 = require("mppx");
629
+ var import_actions = require("viem/actions");
630
+ var import_tempo = require("viem/tempo");
629
631
  var import_viem2 = require("viem");
630
632
 
631
633
  // src/protocols/x402.ts
@@ -739,30 +741,53 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
739
741
  function needsFacilitatorEnrichment(accepts) {
740
742
  return accepts.some((accept) => accept.scheme !== "exact") || hasSolanaAccepts(accepts);
741
743
  }
744
+ async function enrichGroup(group, resource) {
745
+ const accepted = await enrichRequirementsWithFacilitatorAccepts(
746
+ group.facilitator,
747
+ resource,
748
+ group.items.map(({ requirement }) => requirement)
749
+ );
750
+ if (accepted.length !== group.items.length) {
751
+ throw new Error(
752
+ `Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
753
+ );
754
+ }
755
+ return accepted;
756
+ }
742
757
  async function enrichChallengeRequirements(requirements, resource, facilitatorsByNetwork) {
743
758
  const groups = collectEnrichmentGroups(requirements, facilitatorsByNetwork);
744
759
  if (groups.length === 0) return requirements;
745
- const enrichedRequirements = [...requirements];
746
- await Promise.all(
760
+ const results = await Promise.all(
747
761
  groups.map(async (group) => {
748
- const enriched = await enrichRequirementsWithFacilitatorAccepts(
749
- group.facilitator,
750
- resource,
751
- group.items.map(({ requirement }) => requirement)
752
- );
753
- if (enriched.length !== group.items.length) {
754
- throw new Error(
755
- `Facilitator /accepts returned ${enriched.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
762
+ try {
763
+ return { success: true, group, accepted: await enrichGroup(group, resource) };
764
+ } catch (err) {
765
+ const label = group.facilitator.url ?? group.facilitator.network;
766
+ const reason = err instanceof Error ? err.message : String(err);
767
+ console.warn(
768
+ `[router] ${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
756
769
  );
770
+ return { success: false, group };
757
771
  }
758
- enriched.forEach((requirement, offset) => {
759
- const index = group.items[offset]?.index;
760
- if (index === void 0) return;
761
- enrichedRequirements[index] = requirement;
762
- });
763
772
  })
764
773
  );
765
- return enrichedRequirements;
774
+ const enriched = [...requirements];
775
+ results.filter((r) => r.success).forEach(({ group, accepted }) => {
776
+ accepted.forEach((req, offset) => {
777
+ const index = group.items[offset]?.index;
778
+ if (index !== void 0) enriched[index] = req;
779
+ });
780
+ });
781
+ const failedIndices = new Set(
782
+ results.filter((r) => !r.success).flatMap(({ group }) => group.items.map(({ index }) => index))
783
+ );
784
+ const remaining = enriched.filter((_, i) => !failedIndices.has(i));
785
+ if (remaining.length === 0) {
786
+ throw new Error(
787
+ "All facilitator enrichments failed; no payment requirements remain for challenge"
788
+ );
789
+ }
790
+ return remaining;
766
791
  }
767
792
  function collectEnrichmentGroups(requirements, facilitatorsByNetwork) {
768
793
  const groups = [];
@@ -1364,6 +1389,122 @@ function createRequestHandler(routeEntry, handler, deps) {
1364
1389
  console.error(`[router] ${routeEntry.key}: ${reason}`);
1365
1390
  return fail(500, reason, meta, pluginCtx, body.data);
1366
1391
  }
1392
+ const mppCredential = import_mppx2.Credential.fromRequest(request);
1393
+ const rawSource = mppCredential?.source ?? "";
1394
+ const didParts = rawSource.split(":");
1395
+ const lastPart = didParts[didParts.length - 1];
1396
+ const wallet = normalizeWalletAddress((0, import_viem2.isAddress)(lastPart) ? (0, import_viem2.getAddress)(lastPart) : rawSource);
1397
+ const payloadType = mppCredential?.payload?.type;
1398
+ if (payloadType === "transaction" && deps.tempoClient) {
1399
+ try {
1400
+ const serializedTx = mppCredential.payload.signature;
1401
+ const transaction = import_tempo.Transaction.deserialize(serializedTx);
1402
+ await (0, import_actions.call)(deps.tempoClient, {
1403
+ ...transaction,
1404
+ account: transaction.from,
1405
+ calls: transaction.calls ?? []
1406
+ });
1407
+ } catch (err) {
1408
+ const message = err instanceof Error ? err.message : String(err);
1409
+ console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
1410
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1411
+ level: "warn",
1412
+ message: `MPP simulation failed: ${message}`,
1413
+ route: routeEntry.key
1414
+ });
1415
+ return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1416
+ }
1417
+ pluginCtx.setVerifiedWallet(wallet);
1418
+ firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1419
+ protocol: "mpp",
1420
+ payer: wallet,
1421
+ amount: price,
1422
+ network: "tempo:4217"
1423
+ });
1424
+ const { response: response2, rawResult: rawResult2 } = await invoke(
1425
+ request,
1426
+ meta,
1427
+ pluginCtx,
1428
+ wallet,
1429
+ account,
1430
+ body.data
1431
+ );
1432
+ if (response2.status < 400) {
1433
+ let mppResult2;
1434
+ try {
1435
+ mppResult2 = await deps.mppx.charge({ amount: price })(request);
1436
+ } catch (err) {
1437
+ const message = err instanceof Error ? err.message : String(err);
1438
+ console.error(
1439
+ `[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
1440
+ );
1441
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1442
+ level: "critical",
1443
+ message: `MPP broadcast failed after handler: ${message}`,
1444
+ route: routeEntry.key
1445
+ });
1446
+ return fail(
1447
+ 500,
1448
+ `MPP payment processing failed: ${message}`,
1449
+ meta,
1450
+ pluginCtx,
1451
+ body.data
1452
+ );
1453
+ }
1454
+ if (mppResult2.status === 402) {
1455
+ let rejectReason = "";
1456
+ try {
1457
+ const problemBody = await mppResult2.challenge.clone().text();
1458
+ if (problemBody) {
1459
+ const problem = JSON.parse(problemBody);
1460
+ rejectReason = problem.detail || problem.title || "";
1461
+ }
1462
+ } catch {
1463
+ }
1464
+ const detail = rejectReason || "transaction reverted on-chain after handler execution";
1465
+ console.error(
1466
+ `[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
1467
+ );
1468
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1469
+ level: "critical",
1470
+ message: `MPP payment failed after handler: ${detail}`,
1471
+ route: routeEntry.key
1472
+ });
1473
+ return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
1474
+ }
1475
+ const receiptResponse = mppResult2.withReceipt(response2);
1476
+ receiptResponse.headers.set("Cache-Control", "private");
1477
+ let txHash2 = "";
1478
+ const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
1479
+ if (receiptHeader2) {
1480
+ try {
1481
+ txHash2 = import_mppx2.Receipt.deserialize(receiptHeader2).reference;
1482
+ } catch {
1483
+ }
1484
+ }
1485
+ if (routeEntry.siwxEnabled) {
1486
+ try {
1487
+ await deps.entitlementStore.grant(routeEntry.key, wallet);
1488
+ } catch (error) {
1489
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1490
+ level: "warn",
1491
+ message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
1492
+ route: routeEntry.key
1493
+ });
1494
+ }
1495
+ }
1496
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1497
+ protocol: "mpp",
1498
+ payer: wallet,
1499
+ transaction: txHash2,
1500
+ network: "tempo:4217"
1501
+ });
1502
+ finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
1503
+ return receiptResponse;
1504
+ }
1505
+ finalize(response2, rawResult2, meta, pluginCtx, body.data);
1506
+ return response2;
1507
+ }
1367
1508
  let mppResult;
1368
1509
  try {
1369
1510
  mppResult = await deps.mppx.charge({ amount: price })(request);
@@ -1396,11 +1537,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1396
1537
  });
1397
1538
  return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1398
1539
  }
1399
- const credential = import_mppx2.Credential.fromRequest(request);
1400
- const rawSource = credential?.source ?? "";
1401
- const didParts = rawSource.split(":");
1402
- const lastPart = didParts[didParts.length - 1];
1403
- const wallet = normalizeWalletAddress((0, import_viem2.isAddress)(lastPart) ? (0, import_viem2.getAddress)(lastPart) : rawSource);
1540
+ let txHash = "";
1541
+ const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
1542
+ "Payment-Receipt"
1543
+ );
1544
+ if (receiptHeader) {
1545
+ try {
1546
+ txHash = import_mppx2.Receipt.deserialize(receiptHeader).reference;
1547
+ } catch {
1548
+ }
1549
+ }
1404
1550
  pluginCtx.setVerifiedWallet(wallet);
1405
1551
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1406
1552
  protocol: "mpp",
@@ -1430,6 +1576,12 @@ function createRequestHandler(routeEntry, handler, deps) {
1430
1576
  }
1431
1577
  const receiptResponse = mppResult.withReceipt(response);
1432
1578
  receiptResponse.headers.set("Cache-Control", "private");
1579
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1580
+ protocol: "mpp",
1581
+ payer: wallet,
1582
+ transaction: txHash,
1583
+ network: "tempo:4217"
1584
+ });
1433
1585
  finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
1434
1586
  return receiptResponse;
1435
1587
  }
@@ -2291,7 +2443,8 @@ function createRouter(config) {
2291
2443
  network,
2292
2444
  x402FacilitatorsByNetwork: void 0,
2293
2445
  x402Accepts,
2294
- mppx: null
2446
+ mppx: null,
2447
+ tempoClient: null
2295
2448
  };
2296
2449
  deps.initPromise = (async () => {
2297
2450
  if (x402ConfigError) {
@@ -2314,11 +2467,10 @@ function createRouter(config) {
2314
2467
  try {
2315
2468
  const { Mppx, tempo } = await import("mppx/server");
2316
2469
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
2317
- const getClient = async () => {
2318
- const { createClient, http } = await import("viem");
2319
- const { tempo: tempoChain } = await import("viem/chains");
2320
- return createClient({ chain: tempoChain, transport: http(rpcUrl) });
2321
- };
2470
+ const { createClient, http } = await import("viem");
2471
+ const { tempo: tempoChain } = await import("viem/chains");
2472
+ deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
2473
+ const getClient = async () => deps.tempoClient;
2322
2474
  let feePayerAccount;
2323
2475
  if (config.mpp.feePayerKey) {
2324
2476
  const { Account } = await import("viem/tempo");
package/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import { FacilitatorConfig } from '@x402/core/http';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
  import { ZodType } from 'zod';
4
4
  import { PaymentRequirements, PaymentRequired, SettleResponse, Network } from '@x402/core/types';
5
+ import * as viem from 'viem';
5
6
  export { S as SIWX_ERROR_MESSAGES, a as SiwxErrorCode } from './siwx-BMlja_nt.cjs';
6
7
 
7
8
  interface EntitlementStore {
@@ -423,6 +424,7 @@ interface OrchestrateDeps {
423
424
  withReceipt: (response: Response) => Response;
424
425
  }>;
425
426
  } | null;
427
+ tempoClient?: viem.Client | null;
426
428
  }
427
429
 
428
430
  type True = true;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { FacilitatorConfig } from '@x402/core/http';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
  import { ZodType } from 'zod';
4
4
  import { PaymentRequirements, PaymentRequired, SettleResponse, Network } from '@x402/core/types';
5
+ import * as viem from 'viem';
5
6
  export { S as SIWX_ERROR_MESSAGES, a as SiwxErrorCode } from './siwx-BMlja_nt.js';
6
7
 
7
8
  interface EntitlementStore {
@@ -423,6 +424,7 @@ interface OrchestrateDeps {
423
424
  withReceipt: (response: Response) => Response;
424
425
  }>;
425
426
  } | null;
427
+ tempoClient?: viem.Client | null;
426
428
  }
427
429
 
428
430
  type True = true;
package/dist/index.js CHANGED
@@ -586,7 +586,9 @@ function resolveMaxPrice(pricing) {
586
586
  }
587
587
 
588
588
  // src/orchestrate.ts
589
- import { Credential as Credential2 } from "mppx";
589
+ import { Credential as Credential2, Receipt } from "mppx";
590
+ import { call as viemCall } from "viem/actions";
591
+ import { Transaction as TempoTransaction } from "viem/tempo";
590
592
  import { isAddress as isAddress2, getAddress as getAddress2 } from "viem";
591
593
 
592
594
  // src/protocols/x402.ts
@@ -700,30 +702,53 @@ async function buildChallengeRequirements(server, request, price, accepts, resou
700
702
  function needsFacilitatorEnrichment(accepts) {
701
703
  return accepts.some((accept) => accept.scheme !== "exact") || hasSolanaAccepts(accepts);
702
704
  }
705
+ async function enrichGroup(group, resource) {
706
+ const accepted = await enrichRequirementsWithFacilitatorAccepts(
707
+ group.facilitator,
708
+ resource,
709
+ group.items.map(({ requirement }) => requirement)
710
+ );
711
+ if (accepted.length !== group.items.length) {
712
+ throw new Error(
713
+ `Facilitator /accepts returned ${accepted.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
714
+ );
715
+ }
716
+ return accepted;
717
+ }
703
718
  async function enrichChallengeRequirements(requirements, resource, facilitatorsByNetwork) {
704
719
  const groups = collectEnrichmentGroups(requirements, facilitatorsByNetwork);
705
720
  if (groups.length === 0) return requirements;
706
- const enrichedRequirements = [...requirements];
707
- await Promise.all(
721
+ const results = await Promise.all(
708
722
  groups.map(async (group) => {
709
- const enriched = await enrichRequirementsWithFacilitatorAccepts(
710
- group.facilitator,
711
- resource,
712
- group.items.map(({ requirement }) => requirement)
713
- );
714
- if (enriched.length !== group.items.length) {
715
- throw new Error(
716
- `Facilitator /accepts returned ${enriched.length} requirements for ${group.items.length} inputs on ${group.facilitator.url ?? group.facilitator.network}`
723
+ try {
724
+ return { success: true, group, accepted: await enrichGroup(group, resource) };
725
+ } catch (err) {
726
+ const label = group.facilitator.url ?? group.facilitator.network;
727
+ const reason = err instanceof Error ? err.message : String(err);
728
+ console.warn(
729
+ `[router] ${label} /accepts failed, dropping ${group.items.length} requirement(s): ${reason}`
717
730
  );
731
+ return { success: false, group };
718
732
  }
719
- enriched.forEach((requirement, offset) => {
720
- const index = group.items[offset]?.index;
721
- if (index === void 0) return;
722
- enrichedRequirements[index] = requirement;
723
- });
724
733
  })
725
734
  );
726
- return enrichedRequirements;
735
+ const enriched = [...requirements];
736
+ results.filter((r) => r.success).forEach(({ group, accepted }) => {
737
+ accepted.forEach((req, offset) => {
738
+ const index = group.items[offset]?.index;
739
+ if (index !== void 0) enriched[index] = req;
740
+ });
741
+ });
742
+ const failedIndices = new Set(
743
+ results.filter((r) => !r.success).flatMap(({ group }) => group.items.map(({ index }) => index))
744
+ );
745
+ const remaining = enriched.filter((_, i) => !failedIndices.has(i));
746
+ if (remaining.length === 0) {
747
+ throw new Error(
748
+ "All facilitator enrichments failed; no payment requirements remain for challenge"
749
+ );
750
+ }
751
+ return remaining;
727
752
  }
728
753
  function collectEnrichmentGroups(requirements, facilitatorsByNetwork) {
729
754
  const groups = [];
@@ -1325,6 +1350,122 @@ function createRequestHandler(routeEntry, handler, deps) {
1325
1350
  console.error(`[router] ${routeEntry.key}: ${reason}`);
1326
1351
  return fail(500, reason, meta, pluginCtx, body.data);
1327
1352
  }
1353
+ const mppCredential = Credential2.fromRequest(request);
1354
+ const rawSource = mppCredential?.source ?? "";
1355
+ const didParts = rawSource.split(":");
1356
+ const lastPart = didParts[didParts.length - 1];
1357
+ const wallet = normalizeWalletAddress(isAddress2(lastPart) ? getAddress2(lastPart) : rawSource);
1358
+ const payloadType = mppCredential?.payload?.type;
1359
+ if (payloadType === "transaction" && deps.tempoClient) {
1360
+ try {
1361
+ const serializedTx = mppCredential.payload.signature;
1362
+ const transaction = TempoTransaction.deserialize(serializedTx);
1363
+ await viemCall(deps.tempoClient, {
1364
+ ...transaction,
1365
+ account: transaction.from,
1366
+ calls: transaction.calls ?? []
1367
+ });
1368
+ } catch (err) {
1369
+ const message = err instanceof Error ? err.message : String(err);
1370
+ console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
1371
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1372
+ level: "warn",
1373
+ message: `MPP simulation failed: ${message}`,
1374
+ route: routeEntry.key
1375
+ });
1376
+ return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1377
+ }
1378
+ pluginCtx.setVerifiedWallet(wallet);
1379
+ firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1380
+ protocol: "mpp",
1381
+ payer: wallet,
1382
+ amount: price,
1383
+ network: "tempo:4217"
1384
+ });
1385
+ const { response: response2, rawResult: rawResult2 } = await invoke(
1386
+ request,
1387
+ meta,
1388
+ pluginCtx,
1389
+ wallet,
1390
+ account,
1391
+ body.data
1392
+ );
1393
+ if (response2.status < 400) {
1394
+ let mppResult2;
1395
+ try {
1396
+ mppResult2 = await deps.mppx.charge({ amount: price })(request);
1397
+ } catch (err) {
1398
+ const message = err instanceof Error ? err.message : String(err);
1399
+ console.error(
1400
+ `[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
1401
+ );
1402
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1403
+ level: "critical",
1404
+ message: `MPP broadcast failed after handler: ${message}`,
1405
+ route: routeEntry.key
1406
+ });
1407
+ return fail(
1408
+ 500,
1409
+ `MPP payment processing failed: ${message}`,
1410
+ meta,
1411
+ pluginCtx,
1412
+ body.data
1413
+ );
1414
+ }
1415
+ if (mppResult2.status === 402) {
1416
+ let rejectReason = "";
1417
+ try {
1418
+ const problemBody = await mppResult2.challenge.clone().text();
1419
+ if (problemBody) {
1420
+ const problem = JSON.parse(problemBody);
1421
+ rejectReason = problem.detail || problem.title || "";
1422
+ }
1423
+ } catch {
1424
+ }
1425
+ const detail = rejectReason || "transaction reverted on-chain after handler execution";
1426
+ console.error(
1427
+ `[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
1428
+ );
1429
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1430
+ level: "critical",
1431
+ message: `MPP payment failed after handler: ${detail}`,
1432
+ route: routeEntry.key
1433
+ });
1434
+ return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
1435
+ }
1436
+ const receiptResponse = mppResult2.withReceipt(response2);
1437
+ receiptResponse.headers.set("Cache-Control", "private");
1438
+ let txHash2 = "";
1439
+ const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
1440
+ if (receiptHeader2) {
1441
+ try {
1442
+ txHash2 = Receipt.deserialize(receiptHeader2).reference;
1443
+ } catch {
1444
+ }
1445
+ }
1446
+ if (routeEntry.siwxEnabled) {
1447
+ try {
1448
+ await deps.entitlementStore.grant(routeEntry.key, wallet);
1449
+ } catch (error) {
1450
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1451
+ level: "warn",
1452
+ message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
1453
+ route: routeEntry.key
1454
+ });
1455
+ }
1456
+ }
1457
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1458
+ protocol: "mpp",
1459
+ payer: wallet,
1460
+ transaction: txHash2,
1461
+ network: "tempo:4217"
1462
+ });
1463
+ finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
1464
+ return receiptResponse;
1465
+ }
1466
+ finalize(response2, rawResult2, meta, pluginCtx, body.data);
1467
+ return response2;
1468
+ }
1328
1469
  let mppResult;
1329
1470
  try {
1330
1471
  mppResult = await deps.mppx.charge({ amount: price })(request);
@@ -1357,11 +1498,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1357
1498
  });
1358
1499
  return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1359
1500
  }
1360
- const credential = Credential2.fromRequest(request);
1361
- const rawSource = credential?.source ?? "";
1362
- const didParts = rawSource.split(":");
1363
- const lastPart = didParts[didParts.length - 1];
1364
- const wallet = normalizeWalletAddress(isAddress2(lastPart) ? getAddress2(lastPart) : rawSource);
1501
+ let txHash = "";
1502
+ const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
1503
+ "Payment-Receipt"
1504
+ );
1505
+ if (receiptHeader) {
1506
+ try {
1507
+ txHash = Receipt.deserialize(receiptHeader).reference;
1508
+ } catch {
1509
+ }
1510
+ }
1365
1511
  pluginCtx.setVerifiedWallet(wallet);
1366
1512
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1367
1513
  protocol: "mpp",
@@ -1391,6 +1537,12 @@ function createRequestHandler(routeEntry, handler, deps) {
1391
1537
  }
1392
1538
  const receiptResponse = mppResult.withReceipt(response);
1393
1539
  receiptResponse.headers.set("Cache-Control", "private");
1540
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1541
+ protocol: "mpp",
1542
+ payer: wallet,
1543
+ transaction: txHash,
1544
+ network: "tempo:4217"
1545
+ });
1394
1546
  finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
1395
1547
  return receiptResponse;
1396
1548
  }
@@ -2252,7 +2404,8 @@ function createRouter(config) {
2252
2404
  network,
2253
2405
  x402FacilitatorsByNetwork: void 0,
2254
2406
  x402Accepts,
2255
- mppx: null
2407
+ mppx: null,
2408
+ tempoClient: null
2256
2409
  };
2257
2410
  deps.initPromise = (async () => {
2258
2411
  if (x402ConfigError) {
@@ -2275,11 +2428,10 @@ function createRouter(config) {
2275
2428
  try {
2276
2429
  const { Mppx, tempo } = await import("mppx/server");
2277
2430
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
2278
- const getClient = async () => {
2279
- const { createClient, http } = await import("viem");
2280
- const { tempo: tempoChain } = await import("viem/chains");
2281
- return createClient({ chain: tempoChain, transport: http(rpcUrl) });
2282
- };
2431
+ const { createClient, http } = await import("viem");
2432
+ const { tempo: tempoChain } = await import("viem/chains");
2433
+ deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
2434
+ const getClient = async () => deps.tempoClient;
2283
2435
  let feePayerAccount;
2284
2436
  if (config.mpp.feePayerKey) {
2285
2437
  const { Account } = await import("viem/tempo");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Unified route builder for Next.js App Router APIs with x402, MPP, SIWX, and API key auth",
5
5
  "type": "module",
6
6
  "exports": {
@@ -61,7 +61,7 @@
61
61
  "@x402/extensions": "^2.3.0",
62
62
  "@x402/svm": "2.3.0",
63
63
  "eslint": "^10.0.0",
64
- "mppx": "^0.4.2",
64
+ "mppx": "^0.4.8",
65
65
  "next": "^15.0.0",
66
66
  "prettier": "^3.8.1",
67
67
  "react": "^19.0.0",