@agentcash/router 1.1.10 → 1.2.1

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
@@ -625,8 +625,10 @@ function resolveMaxPrice(pricing) {
625
625
  }
626
626
 
627
627
  // src/orchestrate.ts
628
- var import_mppx = require("mppx");
629
- var import_viem = require("viem");
628
+ var import_mppx2 = require("mppx");
629
+ var import_actions = require("viem/actions");
630
+ var import_tempo = require("viem/tempo");
631
+ var import_viem2 = require("viem");
630
632
 
631
633
  // src/protocols/x402.ts
632
634
  init_x402_facilitators();
@@ -898,6 +900,22 @@ async function buildSIWXExtension() {
898
900
  return declareSIWxExtension();
899
901
  }
900
902
 
903
+ // src/auth/mpp-siwx.ts
904
+ var import_mppx = require("mppx");
905
+ var import_viem = require("viem");
906
+ async function verifyMppSiwx(request, mppx) {
907
+ const result = await mppx.charge({ amount: "0" })(request);
908
+ if (result.status === 402) {
909
+ return { valid: false, challenge: result.challenge };
910
+ }
911
+ const credential = import_mppx.Credential.fromRequest(request);
912
+ const rawSource = credential?.source ?? "";
913
+ const didParts = rawSource.split(":");
914
+ const lastPart = didParts[didParts.length - 1];
915
+ const wallet = normalizeWalletAddress((0, import_viem.isAddress)(lastPart) ? (0, import_viem.getAddress)(lastPart) : rawSource);
916
+ return { valid: true, wallet, withReceipt: result.withReceipt };
917
+ }
918
+
901
919
  // src/auth/api-key.ts
902
920
  async function verifyApiKey(request, resolver) {
903
921
  const apiKey = request.headers.get("X-API-Key") ?? extractBearerToken(request.headers.get("Authorization"));
@@ -1063,6 +1081,33 @@ function createRequestHandler(routeEntry, handler, deps) {
1063
1081
  }
1064
1082
  }
1065
1083
  const siwxHeader = request.headers.get("SIGN-IN-WITH-X");
1084
+ if (!siwxHeader && protocol === "mpp" && routeEntry.authMode === "siwx" && deps.mppx) {
1085
+ let mppSiwxResult;
1086
+ try {
1087
+ mppSiwxResult = await verifyMppSiwx(request, deps.mppx);
1088
+ } catch (err) {
1089
+ const message = err instanceof Error ? err.message : String(err);
1090
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1091
+ level: "critical",
1092
+ message: `MPP SIWX verification failed: ${message}`,
1093
+ route: routeEntry.key
1094
+ });
1095
+ return fail(500, `MPP SIWX verification failed: ${message}`, meta, pluginCtx);
1096
+ }
1097
+ if (mppSiwxResult.valid) {
1098
+ pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
1099
+ firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
1100
+ authMode: "siwx",
1101
+ wallet: mppSiwxResult.wallet,
1102
+ route: routeEntry.key
1103
+ });
1104
+ const authResponse = await handleAuth(mppSiwxResult.wallet, void 0);
1105
+ if (authResponse.status < 400) {
1106
+ return mppSiwxResult.withReceipt(authResponse);
1107
+ }
1108
+ return authResponse;
1109
+ }
1110
+ }
1066
1111
  if (!siwxHeader && routeEntry.authMode === "siwx") {
1067
1112
  const url = new URL(request.url);
1068
1113
  const nonce = crypto.randomUUID().replace(/-/g, "");
@@ -1118,6 +1163,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1118
1163
  headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
1119
1164
  });
1120
1165
  if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
1166
+ if (deps.mppx) {
1167
+ try {
1168
+ const mppChallenge = await deps.mppx.charge({ amount: "0" })(request);
1169
+ if (mppChallenge.status === 402) {
1170
+ const wwwAuth = mppChallenge.challenge.headers.get("WWW-Authenticate");
1171
+ if (wwwAuth) response.headers.set("WWW-Authenticate", wwwAuth);
1172
+ }
1173
+ } catch {
1174
+ }
1175
+ }
1121
1176
  firePluginResponse(deps, pluginCtx, meta, response);
1122
1177
  return response;
1123
1178
  }
@@ -1311,6 +1366,122 @@ function createRequestHandler(routeEntry, handler, deps) {
1311
1366
  console.error(`[router] ${routeEntry.key}: ${reason}`);
1312
1367
  return fail(500, reason, meta, pluginCtx, body.data);
1313
1368
  }
1369
+ const mppCredential = import_mppx2.Credential.fromRequest(request);
1370
+ const rawSource = mppCredential?.source ?? "";
1371
+ const didParts = rawSource.split(":");
1372
+ const lastPart = didParts[didParts.length - 1];
1373
+ const wallet = normalizeWalletAddress((0, import_viem2.isAddress)(lastPart) ? (0, import_viem2.getAddress)(lastPart) : rawSource);
1374
+ const payloadType = mppCredential?.payload?.type;
1375
+ if (payloadType === "transaction" && deps.tempoClient) {
1376
+ try {
1377
+ const serializedTx = mppCredential.payload.signature;
1378
+ const transaction = import_tempo.Transaction.deserialize(serializedTx);
1379
+ await (0, import_actions.call)(deps.tempoClient, {
1380
+ ...transaction,
1381
+ account: transaction.from,
1382
+ calls: transaction.calls ?? []
1383
+ });
1384
+ } catch (err) {
1385
+ const message = err instanceof Error ? err.message : String(err);
1386
+ console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
1387
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1388
+ level: "warn",
1389
+ message: `MPP simulation failed: ${message}`,
1390
+ route: routeEntry.key
1391
+ });
1392
+ return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1393
+ }
1394
+ pluginCtx.setVerifiedWallet(wallet);
1395
+ firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1396
+ protocol: "mpp",
1397
+ payer: wallet,
1398
+ amount: price,
1399
+ network: "tempo:4217"
1400
+ });
1401
+ const { response: response2, rawResult: rawResult2 } = await invoke(
1402
+ request,
1403
+ meta,
1404
+ pluginCtx,
1405
+ wallet,
1406
+ account,
1407
+ body.data
1408
+ );
1409
+ if (response2.status < 400) {
1410
+ let mppResult2;
1411
+ try {
1412
+ mppResult2 = await deps.mppx.charge({ amount: price })(request);
1413
+ } catch (err) {
1414
+ const message = err instanceof Error ? err.message : String(err);
1415
+ console.error(
1416
+ `[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
1417
+ );
1418
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1419
+ level: "critical",
1420
+ message: `MPP broadcast failed after handler: ${message}`,
1421
+ route: routeEntry.key
1422
+ });
1423
+ return fail(
1424
+ 500,
1425
+ `MPP payment processing failed: ${message}`,
1426
+ meta,
1427
+ pluginCtx,
1428
+ body.data
1429
+ );
1430
+ }
1431
+ if (mppResult2.status === 402) {
1432
+ let rejectReason = "";
1433
+ try {
1434
+ const problemBody = await mppResult2.challenge.clone().text();
1435
+ if (problemBody) {
1436
+ const problem = JSON.parse(problemBody);
1437
+ rejectReason = problem.detail || problem.title || "";
1438
+ }
1439
+ } catch {
1440
+ }
1441
+ const detail = rejectReason || "transaction reverted on-chain after handler execution";
1442
+ console.error(
1443
+ `[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
1444
+ );
1445
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1446
+ level: "critical",
1447
+ message: `MPP payment failed after handler: ${detail}`,
1448
+ route: routeEntry.key
1449
+ });
1450
+ return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
1451
+ }
1452
+ const receiptResponse = mppResult2.withReceipt(response2);
1453
+ receiptResponse.headers.set("Cache-Control", "private");
1454
+ let txHash2 = "";
1455
+ const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
1456
+ if (receiptHeader2) {
1457
+ try {
1458
+ txHash2 = import_mppx2.Receipt.deserialize(receiptHeader2).reference;
1459
+ } catch {
1460
+ }
1461
+ }
1462
+ if (routeEntry.siwxEnabled) {
1463
+ try {
1464
+ await deps.entitlementStore.grant(routeEntry.key, wallet);
1465
+ } catch (error) {
1466
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1467
+ level: "warn",
1468
+ message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
1469
+ route: routeEntry.key
1470
+ });
1471
+ }
1472
+ }
1473
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1474
+ protocol: "mpp",
1475
+ payer: wallet,
1476
+ transaction: txHash2,
1477
+ network: "tempo:4217"
1478
+ });
1479
+ finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
1480
+ return receiptResponse;
1481
+ }
1482
+ finalize(response2, rawResult2, meta, pluginCtx, body.data);
1483
+ return response2;
1484
+ }
1314
1485
  let mppResult;
1315
1486
  try {
1316
1487
  mppResult = await deps.mppx.charge({ amount: price })(request);
@@ -1343,11 +1514,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1343
1514
  });
1344
1515
  return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1345
1516
  }
1346
- const credential = import_mppx.Credential.fromRequest(request);
1347
- const rawSource = credential?.source ?? "";
1348
- const didParts = rawSource.split(":");
1349
- const lastPart = didParts[didParts.length - 1];
1350
- const wallet = normalizeWalletAddress((0, import_viem.isAddress)(lastPart) ? (0, import_viem.getAddress)(lastPart) : rawSource);
1517
+ let txHash = "";
1518
+ const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
1519
+ "Payment-Receipt"
1520
+ );
1521
+ if (receiptHeader) {
1522
+ try {
1523
+ txHash = import_mppx2.Receipt.deserialize(receiptHeader).reference;
1524
+ } catch {
1525
+ }
1526
+ }
1351
1527
  pluginCtx.setVerifiedWallet(wallet);
1352
1528
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1353
1529
  protocol: "mpp",
@@ -1377,6 +1553,12 @@ function createRequestHandler(routeEntry, handler, deps) {
1377
1553
  }
1378
1554
  const receiptResponse = mppResult.withReceipt(response);
1379
1555
  receiptResponse.headers.set("Cache-Control", "private");
1556
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1557
+ protocol: "mpp",
1558
+ payer: wallet,
1559
+ transaction: txHash,
1560
+ network: "tempo:4217"
1561
+ });
1380
1562
  finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
1381
1563
  return receiptResponse;
1382
1564
  }
@@ -1426,7 +1608,7 @@ async function resolveDynamicPrice(bodyData, routeEntry, deps, pluginCtx, meta)
1426
1608
  if (routeEntry.maxPrice) {
1427
1609
  const calculated = parseFloat(price);
1428
1610
  const max = parseFloat(routeEntry.maxPrice);
1429
- if (calculated > max) {
1611
+ if (!Number.isFinite(calculated) || calculated > max) {
1430
1612
  firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1431
1613
  level: "warn",
1432
1614
  message: `Price ${price} exceeds maxPrice ${routeEntry.maxPrice}, capping`,
@@ -2238,7 +2420,8 @@ function createRouter(config) {
2238
2420
  network,
2239
2421
  x402FacilitatorsByNetwork: void 0,
2240
2422
  x402Accepts,
2241
- mppx: null
2423
+ mppx: null,
2424
+ tempoClient: null
2242
2425
  };
2243
2426
  deps.initPromise = (async () => {
2244
2427
  if (x402ConfigError) {
@@ -2261,11 +2444,10 @@ function createRouter(config) {
2261
2444
  try {
2262
2445
  const { Mppx, tempo } = await import("mppx/server");
2263
2446
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
2264
- const getClient = async () => {
2265
- const { createClient, http } = await import("viem");
2266
- const { tempo: tempoChain } = await import("viem/chains");
2267
- return createClient({ chain: tempoChain, transport: http(rpcUrl) });
2268
- };
2447
+ const { createClient, http } = await import("viem");
2448
+ const { tempo: tempoChain } = await import("viem/chains");
2449
+ deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
2450
+ const getClient = async () => deps.tempoClient;
2269
2451
  let feePayerAccount;
2270
2452
  if (config.mpp.feePayerKey) {
2271
2453
  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,8 +586,10 @@ function resolveMaxPrice(pricing) {
586
586
  }
587
587
 
588
588
  // src/orchestrate.ts
589
- import { Credential } from "mppx";
590
- import { isAddress, getAddress } from "viem";
589
+ import { Credential as Credential2, Receipt } from "mppx";
590
+ import { call as viemCall } from "viem/actions";
591
+ import { Transaction as TempoTransaction } from "viem/tempo";
592
+ import { isAddress as isAddress2, getAddress as getAddress2 } from "viem";
591
593
 
592
594
  // src/protocols/x402.ts
593
595
  init_x402_facilitators();
@@ -859,6 +861,22 @@ async function buildSIWXExtension() {
859
861
  return declareSIWxExtension();
860
862
  }
861
863
 
864
+ // src/auth/mpp-siwx.ts
865
+ import { Credential } from "mppx";
866
+ import { isAddress, getAddress } from "viem";
867
+ async function verifyMppSiwx(request, mppx) {
868
+ const result = await mppx.charge({ amount: "0" })(request);
869
+ if (result.status === 402) {
870
+ return { valid: false, challenge: result.challenge };
871
+ }
872
+ const credential = Credential.fromRequest(request);
873
+ const rawSource = credential?.source ?? "";
874
+ const didParts = rawSource.split(":");
875
+ const lastPart = didParts[didParts.length - 1];
876
+ const wallet = normalizeWalletAddress(isAddress(lastPart) ? getAddress(lastPart) : rawSource);
877
+ return { valid: true, wallet, withReceipt: result.withReceipt };
878
+ }
879
+
862
880
  // src/auth/api-key.ts
863
881
  async function verifyApiKey(request, resolver) {
864
882
  const apiKey = request.headers.get("X-API-Key") ?? extractBearerToken(request.headers.get("Authorization"));
@@ -1024,6 +1042,33 @@ function createRequestHandler(routeEntry, handler, deps) {
1024
1042
  }
1025
1043
  }
1026
1044
  const siwxHeader = request.headers.get("SIGN-IN-WITH-X");
1045
+ if (!siwxHeader && protocol === "mpp" && routeEntry.authMode === "siwx" && deps.mppx) {
1046
+ let mppSiwxResult;
1047
+ try {
1048
+ mppSiwxResult = await verifyMppSiwx(request, deps.mppx);
1049
+ } catch (err) {
1050
+ const message = err instanceof Error ? err.message : String(err);
1051
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1052
+ level: "critical",
1053
+ message: `MPP SIWX verification failed: ${message}`,
1054
+ route: routeEntry.key
1055
+ });
1056
+ return fail(500, `MPP SIWX verification failed: ${message}`, meta, pluginCtx);
1057
+ }
1058
+ if (mppSiwxResult.valid) {
1059
+ pluginCtx.setVerifiedWallet(mppSiwxResult.wallet);
1060
+ firePluginHook(deps.plugin, "onAuthVerified", pluginCtx, {
1061
+ authMode: "siwx",
1062
+ wallet: mppSiwxResult.wallet,
1063
+ route: routeEntry.key
1064
+ });
1065
+ const authResponse = await handleAuth(mppSiwxResult.wallet, void 0);
1066
+ if (authResponse.status < 400) {
1067
+ return mppSiwxResult.withReceipt(authResponse);
1068
+ }
1069
+ return authResponse;
1070
+ }
1071
+ }
1027
1072
  if (!siwxHeader && routeEntry.authMode === "siwx") {
1028
1073
  const url = new URL(request.url);
1029
1074
  const nonce = crypto.randomUUID().replace(/-/g, "");
@@ -1079,6 +1124,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1079
1124
  headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
1080
1125
  });
1081
1126
  if (encoded) response.headers.set("PAYMENT-REQUIRED", encoded);
1127
+ if (deps.mppx) {
1128
+ try {
1129
+ const mppChallenge = await deps.mppx.charge({ amount: "0" })(request);
1130
+ if (mppChallenge.status === 402) {
1131
+ const wwwAuth = mppChallenge.challenge.headers.get("WWW-Authenticate");
1132
+ if (wwwAuth) response.headers.set("WWW-Authenticate", wwwAuth);
1133
+ }
1134
+ } catch {
1135
+ }
1136
+ }
1082
1137
  firePluginResponse(deps, pluginCtx, meta, response);
1083
1138
  return response;
1084
1139
  }
@@ -1272,6 +1327,122 @@ function createRequestHandler(routeEntry, handler, deps) {
1272
1327
  console.error(`[router] ${routeEntry.key}: ${reason}`);
1273
1328
  return fail(500, reason, meta, pluginCtx, body.data);
1274
1329
  }
1330
+ const mppCredential = Credential2.fromRequest(request);
1331
+ const rawSource = mppCredential?.source ?? "";
1332
+ const didParts = rawSource.split(":");
1333
+ const lastPart = didParts[didParts.length - 1];
1334
+ const wallet = normalizeWalletAddress(isAddress2(lastPart) ? getAddress2(lastPart) : rawSource);
1335
+ const payloadType = mppCredential?.payload?.type;
1336
+ if (payloadType === "transaction" && deps.tempoClient) {
1337
+ try {
1338
+ const serializedTx = mppCredential.payload.signature;
1339
+ const transaction = TempoTransaction.deserialize(serializedTx);
1340
+ await viemCall(deps.tempoClient, {
1341
+ ...transaction,
1342
+ account: transaction.from,
1343
+ calls: transaction.calls ?? []
1344
+ });
1345
+ } catch (err) {
1346
+ const message = err instanceof Error ? err.message : String(err);
1347
+ console.warn(`[router] ${routeEntry.key}: MPP simulation failed \u2014 ${message}`);
1348
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1349
+ level: "warn",
1350
+ message: `MPP simulation failed: ${message}`,
1351
+ route: routeEntry.key
1352
+ });
1353
+ return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1354
+ }
1355
+ pluginCtx.setVerifiedWallet(wallet);
1356
+ firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1357
+ protocol: "mpp",
1358
+ payer: wallet,
1359
+ amount: price,
1360
+ network: "tempo:4217"
1361
+ });
1362
+ const { response: response2, rawResult: rawResult2 } = await invoke(
1363
+ request,
1364
+ meta,
1365
+ pluginCtx,
1366
+ wallet,
1367
+ account,
1368
+ body.data
1369
+ );
1370
+ if (response2.status < 400) {
1371
+ let mppResult2;
1372
+ try {
1373
+ mppResult2 = await deps.mppx.charge({ amount: price })(request);
1374
+ } catch (err) {
1375
+ const message = err instanceof Error ? err.message : String(err);
1376
+ console.error(
1377
+ `[router] ${routeEntry.key}: MPP broadcast failed after handler: ${message}`
1378
+ );
1379
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1380
+ level: "critical",
1381
+ message: `MPP broadcast failed after handler: ${message}`,
1382
+ route: routeEntry.key
1383
+ });
1384
+ return fail(
1385
+ 500,
1386
+ `MPP payment processing failed: ${message}`,
1387
+ meta,
1388
+ pluginCtx,
1389
+ body.data
1390
+ );
1391
+ }
1392
+ if (mppResult2.status === 402) {
1393
+ let rejectReason = "";
1394
+ try {
1395
+ const problemBody = await mppResult2.challenge.clone().text();
1396
+ if (problemBody) {
1397
+ const problem = JSON.parse(problemBody);
1398
+ rejectReason = problem.detail || problem.title || "";
1399
+ }
1400
+ } catch {
1401
+ }
1402
+ const detail = rejectReason || "transaction reverted on-chain after handler execution";
1403
+ console.error(
1404
+ `[router] ${routeEntry.key}: MPP payment failed after handler \u2014 ${detail}`
1405
+ );
1406
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1407
+ level: "critical",
1408
+ message: `MPP payment failed after handler: ${detail}`,
1409
+ route: routeEntry.key
1410
+ });
1411
+ return fail(500, `MPP payment failed: ${detail}`, meta, pluginCtx, body.data);
1412
+ }
1413
+ const receiptResponse = mppResult2.withReceipt(response2);
1414
+ receiptResponse.headers.set("Cache-Control", "private");
1415
+ let txHash2 = "";
1416
+ const receiptHeader2 = receiptResponse.headers.get("Payment-Receipt");
1417
+ if (receiptHeader2) {
1418
+ try {
1419
+ txHash2 = Receipt.deserialize(receiptHeader2).reference;
1420
+ } catch {
1421
+ }
1422
+ }
1423
+ if (routeEntry.siwxEnabled) {
1424
+ try {
1425
+ await deps.entitlementStore.grant(routeEntry.key, wallet);
1426
+ } catch (error) {
1427
+ firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1428
+ level: "warn",
1429
+ message: `Entitlement grant failed: ${error instanceof Error ? error.message : String(error)}`,
1430
+ route: routeEntry.key
1431
+ });
1432
+ }
1433
+ }
1434
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1435
+ protocol: "mpp",
1436
+ payer: wallet,
1437
+ transaction: txHash2,
1438
+ network: "tempo:4217"
1439
+ });
1440
+ finalize(receiptResponse, rawResult2, meta, pluginCtx, body.data);
1441
+ return receiptResponse;
1442
+ }
1443
+ finalize(response2, rawResult2, meta, pluginCtx, body.data);
1444
+ return response2;
1445
+ }
1275
1446
  let mppResult;
1276
1447
  try {
1277
1448
  mppResult = await deps.mppx.charge({ amount: price })(request);
@@ -1304,11 +1475,16 @@ function createRequestHandler(routeEntry, handler, deps) {
1304
1475
  });
1305
1476
  return await build402(request, routeEntry, deps, meta, pluginCtx, body.data);
1306
1477
  }
1307
- const credential = Credential.fromRequest(request);
1308
- const rawSource = credential?.source ?? "";
1309
- const didParts = rawSource.split(":");
1310
- const lastPart = didParts[didParts.length - 1];
1311
- const wallet = normalizeWalletAddress(isAddress(lastPart) ? getAddress(lastPart) : rawSource);
1478
+ let txHash = "";
1479
+ const receiptHeader = mppResult.withReceipt(new Response()).headers.get(
1480
+ "Payment-Receipt"
1481
+ );
1482
+ if (receiptHeader) {
1483
+ try {
1484
+ txHash = Receipt.deserialize(receiptHeader).reference;
1485
+ } catch {
1486
+ }
1487
+ }
1312
1488
  pluginCtx.setVerifiedWallet(wallet);
1313
1489
  firePluginHook(deps.plugin, "onPaymentVerified", pluginCtx, {
1314
1490
  protocol: "mpp",
@@ -1338,6 +1514,12 @@ function createRequestHandler(routeEntry, handler, deps) {
1338
1514
  }
1339
1515
  const receiptResponse = mppResult.withReceipt(response);
1340
1516
  receiptResponse.headers.set("Cache-Control", "private");
1517
+ firePluginHook(deps.plugin, "onPaymentSettled", pluginCtx, {
1518
+ protocol: "mpp",
1519
+ payer: wallet,
1520
+ transaction: txHash,
1521
+ network: "tempo:4217"
1522
+ });
1341
1523
  finalize(receiptResponse, rawResult, meta, pluginCtx, body.data);
1342
1524
  return receiptResponse;
1343
1525
  }
@@ -1387,7 +1569,7 @@ async function resolveDynamicPrice(bodyData, routeEntry, deps, pluginCtx, meta)
1387
1569
  if (routeEntry.maxPrice) {
1388
1570
  const calculated = parseFloat(price);
1389
1571
  const max = parseFloat(routeEntry.maxPrice);
1390
- if (calculated > max) {
1572
+ if (!Number.isFinite(calculated) || calculated > max) {
1391
1573
  firePluginHook(deps.plugin, "onAlert", pluginCtx, {
1392
1574
  level: "warn",
1393
1575
  message: `Price ${price} exceeds maxPrice ${routeEntry.maxPrice}, capping`,
@@ -2199,7 +2381,8 @@ function createRouter(config) {
2199
2381
  network,
2200
2382
  x402FacilitatorsByNetwork: void 0,
2201
2383
  x402Accepts,
2202
- mppx: null
2384
+ mppx: null,
2385
+ tempoClient: null
2203
2386
  };
2204
2387
  deps.initPromise = (async () => {
2205
2388
  if (x402ConfigError) {
@@ -2222,11 +2405,10 @@ function createRouter(config) {
2222
2405
  try {
2223
2406
  const { Mppx, tempo } = await import("mppx/server");
2224
2407
  const rpcUrl = config.mpp.rpcUrl ?? process.env.TEMPO_RPC_URL;
2225
- const getClient = async () => {
2226
- const { createClient, http } = await import("viem");
2227
- const { tempo: tempoChain } = await import("viem/chains");
2228
- return createClient({ chain: tempoChain, transport: http(rpcUrl) });
2229
- };
2408
+ const { createClient, http } = await import("viem");
2409
+ const { tempo: tempoChain } = await import("viem/chains");
2410
+ deps.tempoClient = createClient({ chain: tempoChain, transport: http(rpcUrl) });
2411
+ const getClient = async () => deps.tempoClient;
2230
2412
  let feePayerAccount;
2231
2413
  if (config.mpp.feePayerKey) {
2232
2414
  const { Account } = await import("viem/tempo");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcash/router",
3
- "version": "1.1.10",
3
+ "version": "1.2.1",
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": {