mppx 0.6.31 → 0.8.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 (616) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +20 -11
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +27 -13
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/Constants.d.ts +46 -0
  7. package/dist/Constants.d.ts.map +1 -0
  8. package/dist/Constants.js +46 -0
  9. package/dist/Constants.js.map +1 -0
  10. package/dist/Credential.d.ts.map +1 -1
  11. package/dist/Credential.js +5 -4
  12. package/dist/Credential.js.map +1 -1
  13. package/dist/Mcp.d.ts +3 -0
  14. package/dist/Mcp.d.ts.map +1 -1
  15. package/dist/Mcp.js +2 -0
  16. package/dist/Mcp.js.map +1 -1
  17. package/dist/Method.d.ts +32 -4
  18. package/dist/Method.d.ts.map +1 -1
  19. package/dist/Method.js +5 -2
  20. package/dist/Method.js.map +1 -1
  21. package/dist/PaymentRequest.d.ts +10 -10
  22. package/dist/PaymentRequest.js +8 -8
  23. package/dist/Receipt.d.ts.map +1 -1
  24. package/dist/Receipt.js +3 -2
  25. package/dist/Receipt.js.map +1 -1
  26. package/dist/cli/cli.d.ts.map +1 -1
  27. package/dist/cli/cli.js +19 -11
  28. package/dist/cli/cli.js.map +1 -1
  29. package/dist/cli/plugins/tempo.d.ts.map +1 -1
  30. package/dist/cli/plugins/tempo.js +17 -6
  31. package/dist/cli/plugins/tempo.js.map +1 -1
  32. package/dist/cli/utils.d.ts +5 -0
  33. package/dist/cli/utils.d.ts.map +1 -1
  34. package/dist/cli/utils.js +10 -0
  35. package/dist/cli/utils.js.map +1 -1
  36. package/dist/client/Methods.d.ts +5 -2
  37. package/dist/client/Methods.d.ts.map +1 -1
  38. package/dist/client/Methods.js +5 -2
  39. package/dist/client/Methods.js.map +1 -1
  40. package/dist/client/Mppx.js +2 -2
  41. package/dist/client/Mppx.js.map +1 -1
  42. package/dist/client/Transport.d.ts +11 -16
  43. package/dist/client/Transport.d.ts.map +1 -1
  44. package/dist/client/Transport.js +55 -76
  45. package/dist/client/Transport.js.map +1 -1
  46. package/dist/client/index.d.ts +5 -1
  47. package/dist/client/index.d.ts.map +1 -1
  48. package/dist/client/index.js +3 -1
  49. package/dist/client/index.js.map +1 -1
  50. package/dist/client/internal/Fetch.d.ts.map +1 -1
  51. package/dist/client/internal/Fetch.js +60 -13
  52. package/dist/client/internal/Fetch.js.map +1 -1
  53. package/dist/client/internal/protocols/Mcp.d.ts +7 -0
  54. package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
  55. package/dist/client/internal/protocols/Mcp.js +159 -0
  56. package/dist/client/internal/protocols/Mcp.js.map +1 -0
  57. package/dist/client/internal/protocols/Mpp.d.ts +4 -0
  58. package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
  59. package/dist/client/internal/protocols/Mpp.js +18 -0
  60. package/dist/client/internal/protocols/Mpp.js.map +1 -0
  61. package/dist/client/internal/protocols/Protocol.d.ts +10 -0
  62. package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
  63. package/dist/client/internal/protocols/Protocol.js +2 -0
  64. package/dist/client/internal/protocols/Protocol.js.map +1 -0
  65. package/dist/client/internal/protocols/Shared.d.ts +5 -0
  66. package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
  67. package/dist/client/internal/protocols/Shared.js +20 -0
  68. package/dist/client/internal/protocols/Shared.js.map +1 -0
  69. package/dist/client/internal/protocols/X402.d.ts +8 -0
  70. package/dist/client/internal/protocols/X402.d.ts.map +1 -0
  71. package/dist/client/internal/protocols/X402.js +39 -0
  72. package/dist/client/internal/protocols/X402.js.map +1 -0
  73. package/dist/evm/client/index.d.ts +1 -0
  74. package/dist/evm/client/index.d.ts.map +1 -1
  75. package/dist/evm/client/index.js +1 -0
  76. package/dist/evm/client/index.js.map +1 -1
  77. package/dist/evm/index.d.ts +2 -0
  78. package/dist/evm/index.d.ts.map +1 -1
  79. package/dist/evm/index.js +2 -0
  80. package/dist/evm/index.js.map +1 -1
  81. package/dist/evm/server/Methods.d.ts +1 -1
  82. package/dist/evm/server/Methods.d.ts.map +1 -1
  83. package/dist/evm/server/index.d.ts +1 -0
  84. package/dist/evm/server/index.d.ts.map +1 -1
  85. package/dist/evm/server/index.js +1 -0
  86. package/dist/evm/server/index.js.map +1 -1
  87. package/dist/index.d.ts +1 -0
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +1 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/internal/AcceptPayment.d.ts +3 -0
  92. package/dist/internal/AcceptPayment.d.ts.map +1 -1
  93. package/dist/internal/AcceptPayment.js +15 -11
  94. package/dist/internal/AcceptPayment.js.map +1 -1
  95. package/dist/mcp/client/McpClient.d.ts +101 -0
  96. package/dist/mcp/client/McpClient.d.ts.map +1 -0
  97. package/dist/mcp/client/McpClient.js +162 -0
  98. package/dist/mcp/client/McpClient.js.map +1 -0
  99. package/dist/mcp/client/index.d.ts.map +1 -0
  100. package/dist/mcp/client/index.js.map +1 -0
  101. package/dist/mcp/server/Transport.d.ts.map +1 -0
  102. package/dist/mcp/server/Transport.js.map +1 -0
  103. package/dist/mcp/server/index.d.ts.map +1 -0
  104. package/dist/mcp/server/index.js.map +1 -0
  105. package/dist/server/Mppx.d.ts +12 -4
  106. package/dist/server/Mppx.d.ts.map +1 -1
  107. package/dist/server/Mppx.js +85 -27
  108. package/dist/server/Mppx.js.map +1 -1
  109. package/dist/server/Response.d.ts.map +1 -1
  110. package/dist/server/Response.js +2 -1
  111. package/dist/server/Response.js.map +1 -1
  112. package/dist/server/Transport.d.ts +1 -1
  113. package/dist/server/Transport.d.ts.map +1 -1
  114. package/dist/server/Transport.js +5 -4
  115. package/dist/server/Transport.js.map +1 -1
  116. package/dist/server/index.d.ts +1 -0
  117. package/dist/server/index.d.ts.map +1 -1
  118. package/dist/server/index.js +1 -0
  119. package/dist/server/index.js.map +1 -1
  120. package/dist/stripe/client/Charge.d.ts +1 -1
  121. package/dist/stripe/client/Charge.d.ts.map +1 -1
  122. package/dist/stripe/client/Charge.js +3 -1
  123. package/dist/stripe/client/Charge.js.map +1 -1
  124. package/dist/stripe/server/Charge.d.ts +1 -1
  125. package/dist/stripe/server/Charge.d.ts.map +1 -1
  126. package/dist/stripe/server/Charge.js +9 -2
  127. package/dist/stripe/server/Charge.js.map +1 -1
  128. package/dist/stripe/server/Methods.d.ts +1 -1
  129. package/dist/stripe/server/Methods.d.ts.map +1 -1
  130. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  131. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  132. package/dist/stripe/server/internal/html.gen.js +1 -1
  133. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  134. package/dist/tempo/Methods.d.ts +18 -0
  135. package/dist/tempo/Methods.d.ts.map +1 -1
  136. package/dist/tempo/Methods.js +16 -1
  137. package/dist/tempo/Methods.js.map +1 -1
  138. package/dist/tempo/Proof.d.ts +85 -1
  139. package/dist/tempo/Proof.d.ts.map +1 -1
  140. package/dist/tempo/Proof.js +35 -0
  141. package/dist/tempo/Proof.js.map +1 -1
  142. package/dist/tempo/client/Charge.d.ts +19 -1
  143. package/dist/tempo/client/Charge.d.ts.map +1 -1
  144. package/dist/tempo/client/Charge.js +47 -27
  145. package/dist/tempo/client/Charge.js.map +1 -1
  146. package/dist/tempo/client/Methods.d.ts +41 -10
  147. package/dist/tempo/client/Methods.d.ts.map +1 -1
  148. package/dist/tempo/client/Methods.js +16 -7
  149. package/dist/tempo/client/Methods.js.map +1 -1
  150. package/dist/tempo/client/ResolveAccount.d.ts +40 -0
  151. package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
  152. package/dist/tempo/client/ResolveAccount.js +2 -0
  153. package/dist/tempo/client/ResolveAccount.js.map +1 -0
  154. package/dist/tempo/client/index.d.ts +7 -4
  155. package/dist/tempo/client/index.d.ts.map +1 -1
  156. package/dist/tempo/client/index.js +5 -3
  157. package/dist/tempo/client/index.js.map +1 -1
  158. package/dist/tempo/index.d.ts +1 -0
  159. package/dist/tempo/index.d.ts.map +1 -1
  160. package/dist/tempo/index.js +1 -0
  161. package/dist/tempo/index.js.map +1 -1
  162. package/dist/tempo/internal/fee-payer.d.ts +29 -1
  163. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  164. package/dist/tempo/internal/fee-payer.js +138 -4
  165. package/dist/tempo/internal/fee-payer.js.map +1 -1
  166. package/dist/tempo/internal/proof.d.ts +71 -5
  167. package/dist/tempo/internal/proof.d.ts.map +1 -1
  168. package/dist/tempo/internal/proof.js +42 -6
  169. package/dist/tempo/internal/proof.js.map +1 -1
  170. package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
  171. package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
  172. package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
  173. package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
  174. package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
  175. package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
  176. package/dist/tempo/{client → legacy/client}/Session.js +14 -7
  177. package/dist/tempo/legacy/client/Session.js.map +1 -0
  178. package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
  179. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
  180. package/dist/tempo/{client → legacy/client}/SessionManager.js +30 -19
  181. package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
  182. package/dist/tempo/legacy/client/index.d.ts +7 -0
  183. package/dist/tempo/legacy/client/index.d.ts.map +1 -0
  184. package/dist/tempo/legacy/client/index.js +5 -0
  185. package/dist/tempo/legacy/client/index.js.map +1 -0
  186. package/dist/tempo/legacy/index.d.ts +7 -0
  187. package/dist/tempo/legacy/index.d.ts.map +1 -0
  188. package/dist/tempo/legacy/index.js +7 -0
  189. package/dist/tempo/legacy/index.js.map +1 -0
  190. package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
  191. package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
  192. package/dist/tempo/{server → legacy/server}/Session.js +12 -10
  193. package/dist/tempo/legacy/server/Session.js.map +1 -0
  194. package/dist/tempo/legacy/server/index.d.ts +5 -0
  195. package/dist/tempo/legacy/server/index.d.ts.map +1 -0
  196. package/dist/tempo/legacy/server/index.js +5 -0
  197. package/dist/tempo/legacy/server/index.js.map +1 -0
  198. package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
  199. package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
  200. package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
  201. package/dist/tempo/legacy/session/Chain.js.map +1 -0
  202. package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
  203. package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
  204. package/dist/tempo/legacy/session/Channel.js.map +1 -0
  205. package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
  206. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
  207. package/dist/tempo/legacy/session/ChannelStore.js +6 -0
  208. package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
  209. package/dist/tempo/legacy/session/Types.d.ts +73 -0
  210. package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
  211. package/dist/tempo/legacy/session/Types.js.map +1 -0
  212. package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
  213. package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
  214. package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
  215. package/dist/tempo/legacy/session/Voucher.js.map +1 -0
  216. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
  217. package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
  218. package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
  219. package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
  220. package/dist/tempo/legacy/session/index.d.ts +9 -0
  221. package/dist/tempo/legacy/session/index.d.ts.map +1 -0
  222. package/dist/tempo/legacy/session/index.js +9 -0
  223. package/dist/tempo/legacy/session/index.js.map +1 -0
  224. package/dist/tempo/server/Charge.d.ts +1 -1
  225. package/dist/tempo/server/Charge.d.ts.map +1 -1
  226. package/dist/tempo/server/Charge.js +51 -30
  227. package/dist/tempo/server/Charge.js.map +1 -1
  228. package/dist/tempo/server/Methods.d.ts +67 -8
  229. package/dist/tempo/server/Methods.d.ts.map +1 -1
  230. package/dist/tempo/server/Methods.js +40 -10
  231. package/dist/tempo/server/Methods.js.map +1 -1
  232. package/dist/tempo/server/Subscription.d.ts +11 -1
  233. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  234. package/dist/tempo/server/Subscription.js +135 -23
  235. package/dist/tempo/server/Subscription.js.map +1 -1
  236. package/dist/tempo/server/index.d.ts +6 -5
  237. package/dist/tempo/server/index.d.ts.map +1 -1
  238. package/dist/tempo/server/index.js +5 -5
  239. package/dist/tempo/server/index.js.map +1 -1
  240. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  241. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  242. package/dist/tempo/server/internal/html.gen.js +1 -1
  243. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  244. package/dist/tempo/server/internal/request-body.d.ts +7 -2
  245. package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
  246. package/dist/tempo/server/internal/request-body.js +20 -3
  247. package/dist/tempo/server/internal/request-body.js.map +1 -1
  248. package/dist/tempo/server/internal/transport.d.ts +8 -4
  249. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  250. package/dist/tempo/server/internal/transport.js +8 -7
  251. package/dist/tempo/server/internal/transport.js.map +1 -1
  252. package/dist/tempo/session/Snapshot.d.ts +32 -0
  253. package/dist/tempo/session/Snapshot.d.ts.map +1 -0
  254. package/dist/tempo/session/Snapshot.js +37 -0
  255. package/dist/tempo/session/Snapshot.js.map +1 -0
  256. package/dist/tempo/session/client/ChannelOps.d.ts +81 -0
  257. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
  258. package/dist/tempo/session/client/ChannelOps.js +201 -0
  259. package/dist/tempo/session/client/ChannelOps.js.map +1 -0
  260. package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
  261. package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
  262. package/dist/tempo/session/client/ChannelStore.js +63 -0
  263. package/dist/tempo/session/client/ChannelStore.js.map +1 -0
  264. package/dist/tempo/session/client/CredentialState.d.ts +245 -0
  265. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
  266. package/dist/tempo/session/client/CredentialState.js +419 -0
  267. package/dist/tempo/session/client/CredentialState.js.map +1 -0
  268. package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
  269. package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
  270. package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
  271. package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
  272. package/dist/tempo/session/client/Runtime.d.ts +464 -0
  273. package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
  274. package/dist/tempo/session/client/Runtime.js +499 -0
  275. package/dist/tempo/session/client/Runtime.js.map +1 -0
  276. package/dist/tempo/session/client/Session.d.ts +138 -0
  277. package/dist/tempo/session/client/Session.d.ts.map +1 -0
  278. package/dist/tempo/session/client/Session.js +69 -0
  279. package/dist/tempo/session/client/Session.js.map +1 -0
  280. package/dist/tempo/session/client/SessionManager.d.ts +84 -0
  281. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
  282. package/dist/tempo/session/client/SessionManager.js +577 -0
  283. package/dist/tempo/session/client/SessionManager.js.map +1 -0
  284. package/dist/tempo/session/client/Transports.d.ts +449 -0
  285. package/dist/tempo/session/client/Transports.d.ts.map +1 -0
  286. package/dist/tempo/session/client/Transports.js +721 -0
  287. package/dist/tempo/session/client/Transports.js.map +1 -0
  288. package/dist/tempo/session/client/index.d.ts +11 -0
  289. package/dist/tempo/session/client/index.d.ts.map +1 -0
  290. package/dist/tempo/session/client/index.js +6 -0
  291. package/dist/tempo/session/client/index.js.map +1 -0
  292. package/dist/tempo/session/index.d.ts +7 -8
  293. package/dist/tempo/session/index.d.ts.map +1 -1
  294. package/dist/tempo/session/index.js +7 -8
  295. package/dist/tempo/session/index.js.map +1 -1
  296. package/dist/tempo/session/precompile/Chain.d.ts +319 -0
  297. package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
  298. package/dist/tempo/session/precompile/Chain.js +492 -0
  299. package/dist/tempo/session/precompile/Chain.js.map +1 -0
  300. package/dist/tempo/session/precompile/Channel.d.ts +46 -0
  301. package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
  302. package/dist/tempo/session/precompile/Channel.js +56 -0
  303. package/dist/tempo/session/precompile/Channel.js.map +1 -0
  304. package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
  305. package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
  306. package/dist/tempo/session/precompile/Protocol.js +264 -0
  307. package/dist/tempo/session/precompile/Protocol.js.map +1 -0
  308. package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
  309. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
  310. package/dist/tempo/session/precompile/Voucher.js +125 -0
  311. package/dist/tempo/session/precompile/Voucher.js.map +1 -0
  312. package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
  313. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
  314. package/dist/tempo/session/precompile/escrow.abi.js +224 -0
  315. package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
  316. package/dist/tempo/session/precompile/index.d.ts +24 -0
  317. package/dist/tempo/session/precompile/index.d.ts.map +1 -0
  318. package/dist/tempo/session/precompile/index.js +22 -0
  319. package/dist/tempo/session/precompile/index.js.map +1 -0
  320. package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
  321. package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
  322. package/dist/tempo/session/server/ChannelOps.js +91 -0
  323. package/dist/tempo/session/server/ChannelOps.js.map +1 -0
  324. package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
  325. package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
  326. package/dist/tempo/session/server/ChannelStore.js +404 -0
  327. package/dist/tempo/session/server/ChannelStore.js.map +1 -0
  328. package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
  329. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
  330. package/dist/tempo/session/server/CredentialVerification.js +494 -0
  331. package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
  332. package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
  333. package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
  334. package/dist/tempo/session/server/MeteredStream.js +42 -0
  335. package/dist/tempo/session/server/MeteredStream.js.map +1 -0
  336. package/dist/tempo/session/server/RequestState.d.ts +208 -0
  337. package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
  338. package/dist/tempo/session/server/RequestState.js +252 -0
  339. package/dist/tempo/session/server/RequestState.js.map +1 -0
  340. package/dist/tempo/session/server/Session.d.ts +169 -0
  341. package/dist/tempo/session/server/Session.d.ts.map +1 -0
  342. package/dist/tempo/session/server/Session.js +351 -0
  343. package/dist/tempo/session/server/Session.js.map +1 -0
  344. package/dist/tempo/session/server/Settlement.d.ts +185 -0
  345. package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
  346. package/dist/tempo/session/server/Settlement.js +252 -0
  347. package/dist/tempo/session/server/Settlement.js.map +1 -0
  348. package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
  349. package/dist/tempo/session/server/Sse.d.ts.map +1 -0
  350. package/dist/tempo/session/server/Sse.js +184 -0
  351. package/dist/tempo/session/server/Sse.js.map +1 -0
  352. package/dist/tempo/session/server/Transports.d.ts +89 -0
  353. package/dist/tempo/session/server/Transports.d.ts.map +1 -0
  354. package/dist/tempo/session/server/Transports.js +149 -0
  355. package/dist/tempo/session/server/Transports.js.map +1 -0
  356. package/dist/tempo/session/server/Ws.d.ts +48 -0
  357. package/dist/tempo/session/server/Ws.d.ts.map +1 -0
  358. package/dist/tempo/session/server/Ws.js +244 -0
  359. package/dist/tempo/session/server/Ws.js.map +1 -0
  360. package/dist/tempo/session/server/index.d.ts +4 -0
  361. package/dist/tempo/session/server/index.d.ts.map +1 -0
  362. package/dist/tempo/session/server/index.js +2 -0
  363. package/dist/tempo/session/server/index.js.map +1 -0
  364. package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
  365. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  366. package/dist/tempo/subscription/Store.d.ts +2 -0
  367. package/dist/tempo/subscription/Store.d.ts.map +1 -1
  368. package/dist/tempo/subscription/Store.js +16 -1
  369. package/dist/tempo/subscription/Store.js.map +1 -1
  370. package/dist/x402/index.d.ts +1 -0
  371. package/dist/x402/index.d.ts.map +1 -1
  372. package/dist/x402/index.js +1 -0
  373. package/dist/x402/index.js.map +1 -1
  374. package/package.json +25 -9
  375. package/src/Challenge.test.ts +40 -0
  376. package/src/Challenge.ts +28 -13
  377. package/src/Constants.ts +58 -0
  378. package/src/Credential.ts +5 -4
  379. package/src/Mcp.ts +4 -0
  380. package/src/Method.ts +46 -5
  381. package/src/PaymentRequest.ts +10 -10
  382. package/src/Receipt.ts +3 -2
  383. package/src/cli/cli.test.ts +38 -43
  384. package/src/cli/cli.ts +23 -10
  385. package/src/cli/mcp.test.ts +21 -7
  386. package/src/cli/plugins/tempo.ts +21 -8
  387. package/src/cli/utils.test.ts +25 -1
  388. package/src/cli/utils.ts +10 -0
  389. package/src/client/Methods.ts +5 -2
  390. package/src/client/Mppx.test-d.ts +31 -1
  391. package/src/client/Mppx.test.ts +76 -1
  392. package/src/client/Mppx.ts +2 -2
  393. package/src/client/Transport.test.ts +225 -178
  394. package/src/client/Transport.ts +77 -84
  395. package/src/client/index.ts +25 -1
  396. package/src/client/internal/Fetch.test.ts +236 -6
  397. package/src/client/internal/Fetch.ts +69 -11
  398. package/src/client/internal/protocols/Mcp.test.ts +220 -0
  399. package/src/client/internal/protocols/Mcp.ts +162 -0
  400. package/src/client/internal/protocols/Mpp.ts +21 -0
  401. package/src/client/internal/protocols/Protocol.ts +10 -0
  402. package/src/client/internal/protocols/Shared.ts +25 -0
  403. package/src/client/internal/protocols/X402.ts +42 -0
  404. package/src/discovery/OpenApi.test.ts +1 -1
  405. package/src/env.d.ts +1 -1
  406. package/src/evm/PublicInterface.test-d.ts +1 -1
  407. package/src/evm/client/index.ts +1 -0
  408. package/src/evm/index.ts +2 -0
  409. package/src/evm/server/Charge.test.ts +1 -1
  410. package/src/evm/server/index.ts +1 -0
  411. package/src/index.ts +1 -0
  412. package/src/internal/AcceptPayment.test.ts +61 -0
  413. package/src/internal/AcceptPayment.ts +21 -14
  414. package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +18 -11
  415. package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -11
  416. package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
  417. package/src/mcp/client/McpClient.ts +307 -0
  418. package/src/mcp/client/McpClient.unit.test.ts +135 -0
  419. package/src/middlewares/elysia.test.ts +9 -5
  420. package/src/middlewares/express.test.ts +9 -5
  421. package/src/middlewares/hono.test.ts +5 -5
  422. package/src/middlewares/internal/mppx.test.ts +1 -1
  423. package/src/middlewares/nextjs.test.ts +9 -5
  424. package/src/proxy/Proxy.test.ts +9 -9
  425. package/src/proxy/services/anthropic.test.ts +1 -1
  426. package/src/proxy/services/openai.test.ts +1 -1
  427. package/src/proxy/services/stripe.test.ts +1 -1
  428. package/src/server/Mppx.authorize.test.ts +1 -1
  429. package/src/server/Mppx.test-d.ts +55 -1
  430. package/src/server/Mppx.test.ts +220 -9
  431. package/src/server/Mppx.ts +501 -407
  432. package/src/server/Response.ts +2 -1
  433. package/src/server/Transport.test.ts +6 -6
  434. package/src/server/Transport.ts +5 -4
  435. package/src/server/index.ts +1 -0
  436. package/src/stripe/Charge.integration.test.ts +1 -1
  437. package/src/stripe/client/Charge.test.ts +21 -6
  438. package/src/stripe/client/Charge.ts +6 -2
  439. package/src/stripe/server/Charge.test.ts +115 -2
  440. package/src/stripe/server/Charge.ts +13 -2
  441. package/src/stripe/server/internal/html/package.json +1 -1
  442. package/src/stripe/server/internal/html.gen.ts +1 -1
  443. package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
  444. package/src/tempo/Methods.test.ts +45 -17
  445. package/src/tempo/Methods.ts +22 -0
  446. package/src/tempo/Proof.conformance.test.ts +146 -0
  447. package/src/tempo/Proof.test-d.ts +15 -0
  448. package/src/tempo/Proof.ts +52 -1
  449. package/src/tempo/PublicExports.test-d.ts +105 -0
  450. package/src/tempo/Subscription.integration.test.ts +1 -1
  451. package/src/tempo/client/Charge.test.ts +258 -0
  452. package/src/tempo/client/Charge.ts +84 -38
  453. package/src/tempo/client/Methods.ts +22 -8
  454. package/src/tempo/client/ResolveAccount.ts +46 -0
  455. package/src/tempo/client/index.ts +15 -4
  456. package/src/tempo/index.ts +1 -0
  457. package/src/tempo/internal/fee-payer.test.ts +296 -17
  458. package/src/tempo/internal/fee-payer.ts +186 -4
  459. package/src/tempo/internal/fee-token.test.ts +14 -9
  460. package/src/tempo/internal/proof.test.ts +12 -4
  461. package/src/tempo/internal/proof.ts +55 -6
  462. package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
  463. package/src/tempo/legacy/README.md +9 -0
  464. package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
  465. package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
  466. package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
  467. package/src/tempo/{client → legacy/client}/Session.ts +25 -11
  468. package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
  469. package/src/tempo/{client → legacy/client}/SessionManager.ts +52 -23
  470. package/src/tempo/legacy/client/index.ts +6 -0
  471. package/src/tempo/legacy/index.ts +6 -0
  472. package/src/tempo/{server → legacy/server}/Session.test.ts +136 -71
  473. package/src/tempo/{server → legacy/server}/Session.ts +32 -23
  474. package/src/tempo/legacy/server/index.ts +4 -0
  475. package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
  476. package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
  477. package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
  478. package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
  479. package/src/tempo/legacy/session/ChannelStore.ts +39 -0
  480. package/src/tempo/legacy/session/Types.ts +91 -0
  481. package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
  482. package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
  483. package/src/tempo/legacy/session/index.ts +8 -0
  484. package/src/tempo/server/AtomicStore.test-d.ts +16 -11
  485. package/src/tempo/server/Charge.test.ts +480 -31
  486. package/src/tempo/server/Charge.ts +54 -30
  487. package/src/tempo/server/Methods.ts +58 -10
  488. package/src/tempo/server/Sse.test.ts +2 -2
  489. package/src/tempo/server/Subscription.test.ts +465 -3
  490. package/src/tempo/server/Subscription.ts +174 -19
  491. package/src/tempo/server/index.ts +6 -5
  492. package/src/tempo/server/internal/html/package.json +2 -2
  493. package/src/tempo/server/internal/html.gen.ts +1 -1
  494. package/src/tempo/server/internal/request-body.test.ts +37 -4
  495. package/src/tempo/server/internal/request-body.ts +25 -6
  496. package/src/tempo/server/internal/transport.test.ts +4 -4
  497. package/src/tempo/server/internal/transport.ts +19 -10
  498. package/src/tempo/session/Snapshot.test.ts +41 -0
  499. package/src/tempo/session/Snapshot.ts +74 -0
  500. package/src/tempo/session/client/ChannelOps.test.ts +163 -0
  501. package/src/tempo/session/client/ChannelOps.ts +330 -0
  502. package/src/tempo/session/client/ChannelStore.ts +111 -0
  503. package/src/tempo/session/client/CredentialState.test.ts +789 -0
  504. package/src/tempo/session/client/CredentialState.ts +799 -0
  505. package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
  506. package/src/tempo/session/client/Runtime.test.ts +1092 -0
  507. package/src/tempo/session/client/Runtime.ts +986 -0
  508. package/src/tempo/session/client/Session.test.ts +774 -0
  509. package/src/tempo/session/client/Session.ts +123 -0
  510. package/src/tempo/session/client/SessionManager.test.ts +1397 -0
  511. package/src/tempo/session/client/SessionManager.ts +751 -0
  512. package/src/tempo/session/client/Transports.test.ts +837 -0
  513. package/src/tempo/session/client/Transports.ts +1292 -0
  514. package/src/tempo/session/client/index.ts +40 -0
  515. package/src/tempo/session/index.ts +7 -8
  516. package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
  517. package/src/tempo/session/precompile/Chain.test.ts +1258 -0
  518. package/src/tempo/session/precompile/Chain.ts +979 -0
  519. package/src/tempo/session/precompile/Channel.test.ts +138 -0
  520. package/src/tempo/session/precompile/Channel.ts +103 -0
  521. package/src/tempo/session/precompile/Protocol.test.ts +358 -0
  522. package/src/tempo/session/precompile/Protocol.ts +520 -0
  523. package/src/tempo/session/precompile/Voucher.test.ts +354 -0
  524. package/src/tempo/session/precompile/Voucher.ts +162 -0
  525. package/src/tempo/session/precompile/escrow.abi.ts +226 -0
  526. package/src/tempo/session/precompile/index.ts +33 -0
  527. package/src/tempo/session/server/ChannelOps.test.ts +129 -0
  528. package/src/tempo/session/server/ChannelOps.ts +157 -0
  529. package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
  530. package/src/tempo/session/server/ChannelStore.ts +835 -0
  531. package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
  532. package/src/tempo/session/server/CredentialVerification.ts +710 -0
  533. package/src/tempo/session/server/MeteredStream.ts +88 -0
  534. package/src/tempo/session/server/RequestState.test.ts +531 -0
  535. package/src/tempo/session/server/RequestState.ts +499 -0
  536. package/src/tempo/session/server/Session.integration.test.ts +444 -0
  537. package/src/tempo/session/server/Session.test.ts +3253 -0
  538. package/src/tempo/session/server/Session.ts +543 -0
  539. package/src/tempo/session/server/Settlement.test.ts +329 -0
  540. package/src/tempo/session/server/Settlement.ts +471 -0
  541. package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
  542. package/src/tempo/session/server/Sse.ts +254 -0
  543. package/src/tempo/session/server/Transports.test.ts +346 -0
  544. package/src/tempo/session/server/Transports.ts +255 -0
  545. package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
  546. package/src/tempo/session/server/Ws.ts +380 -0
  547. package/src/tempo/session/server/index.ts +8 -0
  548. package/src/tempo/subscription/Store.ts +27 -9
  549. package/src/x402/Exact.e2e.test.ts +1 -1
  550. package/src/x402/PublicInterface.test-d.ts +1 -1
  551. package/src/x402/index.ts +1 -0
  552. package/dist/mcp-sdk/client/McpClient.d.ts +0 -78
  553. package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
  554. package/dist/mcp-sdk/client/McpClient.js +0 -105
  555. package/dist/mcp-sdk/client/McpClient.js.map +0 -1
  556. package/dist/mcp-sdk/client/index.d.ts.map +0 -1
  557. package/dist/mcp-sdk/client/index.js.map +0 -1
  558. package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
  559. package/dist/mcp-sdk/server/Transport.js.map +0 -1
  560. package/dist/mcp-sdk/server/index.d.ts.map +0 -1
  561. package/dist/mcp-sdk/server/index.js.map +0 -1
  562. package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
  563. package/dist/tempo/client/ChannelOps.js.map +0 -1
  564. package/dist/tempo/client/Session.d.ts.map +0 -1
  565. package/dist/tempo/client/Session.js.map +0 -1
  566. package/dist/tempo/client/SessionManager.d.ts.map +0 -1
  567. package/dist/tempo/client/SessionManager.js.map +0 -1
  568. package/dist/tempo/server/Session.d.ts.map +0 -1
  569. package/dist/tempo/server/Session.js.map +0 -1
  570. package/dist/tempo/session/Chain.d.ts.map +0 -1
  571. package/dist/tempo/session/Chain.js.map +0 -1
  572. package/dist/tempo/session/Channel.d.ts.map +0 -1
  573. package/dist/tempo/session/Channel.js.map +0 -1
  574. package/dist/tempo/session/ChannelStore.d.ts +0 -117
  575. package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
  576. package/dist/tempo/session/ChannelStore.js +0 -172
  577. package/dist/tempo/session/ChannelStore.js.map +0 -1
  578. package/dist/tempo/session/Receipt.d.ts +0 -22
  579. package/dist/tempo/session/Receipt.d.ts.map +0 -1
  580. package/dist/tempo/session/Receipt.js +0 -34
  581. package/dist/tempo/session/Receipt.js.map +0 -1
  582. package/dist/tempo/session/Sse.d.ts.map +0 -1
  583. package/dist/tempo/session/Sse.js +0 -363
  584. package/dist/tempo/session/Sse.js.map +0 -1
  585. package/dist/tempo/session/Types.d.ts +0 -78
  586. package/dist/tempo/session/Types.d.ts.map +0 -1
  587. package/dist/tempo/session/Types.js.map +0 -1
  588. package/dist/tempo/session/Voucher.d.ts.map +0 -1
  589. package/dist/tempo/session/Voucher.js.map +0 -1
  590. package/dist/tempo/session/Ws.d.ts +0 -87
  591. package/dist/tempo/session/Ws.d.ts.map +0 -1
  592. package/dist/tempo/session/Ws.js +0 -443
  593. package/dist/tempo/session/Ws.js.map +0 -1
  594. package/dist/tempo/session/escrow.abi.js.map +0 -1
  595. package/src/mcp-sdk/client/McpClient.ts +0 -196
  596. package/src/tempo/session/ChannelStore.ts +0 -308
  597. package/src/tempo/session/Receipt.test.ts +0 -89
  598. package/src/tempo/session/Receipt.ts +0 -46
  599. package/src/tempo/session/Sse.ts +0 -462
  600. package/src/tempo/session/Types.ts +0 -86
  601. package/src/tempo/session/Ws.ts +0 -576
  602. /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
  603. /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
  604. /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
  605. /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
  606. /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
  607. /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
  608. /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
  609. /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
  610. /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
  611. /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
  612. /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
  613. /package/src/{mcp-sdk → mcp}/server/index.ts +0 -0
  614. /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
  615. /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
  616. /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
@@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
2
2
  import { isDeepStrictEqual } from 'node:util'
3
3
 
4
4
  import * as Challenge from '../Challenge.js'
5
+ import * as Constants from '../Constants.js'
5
6
  import * as Credential from '../Credential.js'
6
7
  import * as Errors from '../Errors.js'
7
8
  import * as Expires from '../Expires.js'
@@ -21,6 +22,9 @@ import * as NodeListener from './NodeListener.js'
21
22
  import * as Request from './Request.js'
22
23
  import * as Transport from './Transport.js'
23
24
 
25
+ const minimumSecretKeyBytes = 32
26
+ const secretKeyGenerationCommand = 'openssl rand -base64 32'
27
+
24
28
  export type Methods = readonly (Method.AnyServer | readonly Method.AnyServer[])[]
25
29
 
26
30
  export type ServerEventMap<
@@ -326,12 +330,30 @@ type UniqueIntentHandlers<
326
330
  }
327
331
 
328
332
  /** Nested handlers: `mppx.tempo.charge(...)`, grouped by method name then intent. */
333
+ type PublicAlias<method extends Method.AnyServer> = method extends {
334
+ alias?: infer alias
335
+ }
336
+ ? Exclude<alias, undefined> extends infer definedAlias
337
+ ? definedAlias extends string
338
+ ? string extends definedAlias
339
+ ? never
340
+ : definedAlias
341
+ : never
342
+ : never
343
+ : never
344
+
345
+ type PublicIntent<method extends Method.AnyServer> = [PublicAlias<method>] extends [never]
346
+ ? method extends { intent: infer intent extends string }
347
+ ? intent
348
+ : never
349
+ : PublicAlias<method>
350
+
329
351
  type NestedHandlers<
330
352
  methods extends readonly Method.AnyServer[],
331
353
  transport extends Transport.AnyTransport,
332
354
  > = {
333
355
  [name in methods[number]['name'] as name extends ReservedKey ? never : name]: {
334
- [mi in Extract<methods[number], { name: name }> as mi['intent']]: MethodFn<
356
+ [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: MethodFn<
335
357
  mi,
336
358
  EffectiveTransportOf<mi, transport>,
337
359
  NonNullable<mi['defaults']>
@@ -351,7 +373,12 @@ type Handlers<
351
373
  > &
352
374
  MethodExtensions<mi>
353
375
  } & UniqueIntentHandlers<methods, transport> &
354
- NestedHandlers<methods, transport>
376
+ NestedHandlers<methods, transport> & {
377
+ [mi in methods[number] as PublicAlias<mi> extends string
378
+ ? `${mi['name']}/${PublicAlias<mi>}`
379
+ : never]: MethodFn<mi, EffectiveTransportOf<mi, transport>, NonNullable<mi['defaults']>> &
380
+ MethodExtensions<mi>
381
+ }
355
382
 
356
383
  type MethodExtensions<method extends Method.AnyServer> = method extends {
357
384
  extensions?: (infer extensions) | undefined
@@ -364,7 +391,7 @@ type MethodExtensions<method extends Method.AnyServer> = method extends {
364
391
  /** Nested challenge generators: `mppx.challenge.tempo.charge(...)`. */
365
392
  type ChallengeHandlers<methods extends readonly Method.AnyServer[]> = {
366
393
  [name in methods[number]['name']]: {
367
- [mi in Extract<methods[number], { name: name }> as mi['intent']]: ChallengeFn<
394
+ [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: ChallengeFn<
368
395
  mi,
369
396
  NonNullable<mi['defaults']>
370
397
  >
@@ -407,6 +434,7 @@ export function create<
407
434
  'Missing secret key. Set the MPP_SECRET_KEY environment variable or pass `secretKey` to Mppx.create().',
408
435
  )
409
436
  }
437
+ assertSecretKey(secretKey)
410
438
 
411
439
  const methods = config.methods.flat() as unknown as FlattenMethods<methods>
412
440
  const serverEvents = createServerEventDispatcher<FlattenMethods<methods>, transport>()
@@ -426,6 +454,7 @@ export function create<
426
454
  method: mi,
427
455
  realm,
428
456
  events: serverEvents as never,
457
+ preflight: mi.preflight as never,
429
458
  request: mi.request as never,
430
459
  respond: mi.respond as never,
431
460
  secretKey,
@@ -433,8 +462,11 @@ export function create<
433
462
  transport: (mi.transport ?? transport) as never,
434
463
  verify: mi.verify as never,
435
464
  })
465
+ const wireKey = `${mi.name}/${mi.intent}`
466
+ const aliasKey = mi.alias ? `${mi.name}/${mi.alias}` : undefined
436
467
  if (mi.extensions) Object.assign(fn, mi.extensions)
437
- handlers[`${mi.name}/${mi.intent}`] = fn
468
+ if (!aliasKey || !handlers[wireKey]) handlers[wireKey] = fn
469
+ if (aliasKey) handlers[aliasKey] = fn
438
470
  }
439
471
 
440
472
  // Also set shorthand intent key when there's no collision
@@ -445,16 +477,17 @@ export function create<
445
477
  // Build nested handlers: mppx.tempo.charge(...)
446
478
  for (const mi of methods) {
447
479
  if (!handlers[mi.name]) handlers[mi.name] = {}
448
- const fn = handlers[`${mi.name}/${mi.intent}`] as AnyMethodFn & { _method?: Method.AnyServer }
480
+ const key = mi.alias ? `${mi.name}/${mi.alias}` : `${mi.name}/${mi.intent}`
481
+ const fn = handlers[key] as AnyMethodFn & { _method?: Method.AnyServer }
449
482
  fn._method = mi
450
- ;(handlers[mi.name] as Record<string, unknown>)[mi.intent] = fn
483
+ ;(handlers[mi.name] as Record<string, unknown>)[mi.alias ?? mi.intent] = fn
451
484
  }
452
485
 
453
486
  // Build challenge generators: mppx.challenge.tempo.charge(...)
454
487
  const challengeHandlers: Record<string, Record<string, unknown>> = {}
455
488
  for (const mi of methods) {
456
489
  if (!challengeHandlers[mi.name]) challengeHandlers[mi.name] = {}
457
- challengeHandlers[mi.name]![mi.intent] = createChallengeFn({
490
+ challengeHandlers[mi.name]![mi.alias ?? mi.intent] = createChallengeFn({
458
491
  defaults: mi.defaults,
459
492
  method: mi,
460
493
  realm,
@@ -472,11 +505,13 @@ export function create<
472
505
  typeof input === 'string' ? Credential.deserialize(input) : input,
473
506
  )
474
507
 
475
- // Find matching method by name + intent
508
+ // Find matching method by name + intent, then use method-details markers
509
+ // when multiple handlers intentionally share the same wire identity.
476
510
  const { method: credMethod, intent: credIntent } = credential.challenge
477
- const mi = (methods as readonly Method.AnyServer[]).find(
511
+ const methodCandidates = (methods as readonly Method.AnyServer[]).filter(
478
512
  (m) => m.name === credMethod && m.intent === credIntent,
479
513
  )
514
+ const mi = selectVerificationMethod(methodCandidates, credential.challenge)
480
515
  const eventMethod =
481
516
  mi ?? ({ intent: credIntent, name: credMethod } satisfies ServerMethodDescriptor)
482
517
 
@@ -688,8 +723,8 @@ export function create<
688
723
  typeof methodOrKey === 'string'
689
724
  ? methodOrKey
690
725
  : typeof methodOrKey === 'function' && '_method' in methodOrKey
691
- ? `${(methodOrKey._method as Method.AnyServer).name}/${(methodOrKey._method as Method.AnyServer).intent}`
692
- : `${(methodOrKey as Method.AnyServer).name}/${(methodOrKey as Method.AnyServer).intent}`
726
+ ? `${(methodOrKey._method as Method.AnyServer).name}/${(methodOrKey._method as Method.AnyServer).alias ?? (methodOrKey._method as Method.AnyServer).intent}`
727
+ : `${(methodOrKey as Method.AnyServer).name}/${(methodOrKey as Method.AnyServer).alias ?? (methodOrKey as Method.AnyServer).intent}`
693
728
  const handlerFn = handlers[key] as AnyMethodFn | undefined
694
729
  if (!handlerFn)
695
730
  throw new Error(`No handler for "${key}". Is this method in your methods array?`)
@@ -731,6 +766,15 @@ export function create<
731
766
  } as never
732
767
  }
733
768
 
769
+ function assertSecretKey(secretKey: string) {
770
+ const byteLength = new TextEncoder().encode(secretKey).byteLength
771
+ if (byteLength >= minimumSecretKeyBytes) return
772
+
773
+ throw new Error(
774
+ `Secret key must be at least ${minimumSecretKeyBytes} bytes. Generate one with \`${secretKeyGenerationCommand}\` and set MPP_SECRET_KEY or pass it to Mppx.create().`,
775
+ )
776
+ }
777
+
734
778
  export declare namespace create {
735
779
  type Config<
736
780
  methods extends Methods = Methods,
@@ -740,7 +784,7 @@ export declare namespace create {
740
784
  methods: methods
741
785
  /** Server realm (e.g., hostname). Resolution order: explicit value > env vars (`MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, etc.) > request URL hostname > `"MPP Payment"`. */
742
786
  realm?: string | undefined
743
- /** Secret key for HMAC-bound challenge IDs for stateless verification. Auto-detected from `MPP_SECRET_KEY` environment variable. Throws if neither provided nor set. */
787
+ /** Secret key for HMAC-bound challenge IDs for stateless verification. Must be at least 32 bytes. Auto-detected from `MPP_SECRET_KEY` environment variable. */
744
788
  secretKey?: string | undefined
745
789
  /** Transport to use. @default Transport.http() */
746
790
  transport?: transport | undefined
@@ -761,6 +805,7 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
761
805
  defaults,
762
806
  events,
763
807
  method,
808
+ preflight,
764
809
  realm,
765
810
  respond,
766
811
  secretKey,
@@ -772,75 +817,83 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
772
817
  return (options) => {
773
818
  const { description, meta, scope, ...rest } = options
774
819
  const staticMeta = Scope.merge({ meta, scope })
820
+ const internal: ConfiguredHandler['_internal'] = {
821
+ ...method,
822
+ ...defaults,
823
+ ...options,
824
+ ...(staticMeta !== undefined ? { meta: staticMeta } : {}),
825
+ name: method.name,
826
+ intent: method.intent,
827
+ html: method.html,
828
+ _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
829
+ _stableBinding: stableBinding as never,
830
+ }
775
831
 
776
- return Object.assign(
777
- async (input: Transport.InputOf): Promise<MethodFn.Response> => {
778
- if (method.html && isServiceWorkerRequest(input))
779
- return {
780
- status: 402,
781
- challenge: createServiceWorkerResponse(),
782
- } as MethodFn.Response<Transport.Http>
783
-
784
- const expires =
785
- 'expires' in options
786
- ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
787
- : Expires.minutes(5)
788
- const capturedRequest = await captureRequest(transport, input)
789
- const effectiveMeta =
790
- scope === undefined && input instanceof globalThis.Request
791
- ? Scope.merge({ meta: staticMeta, scope: Scope.get(input) })
792
- : staticMeta
793
-
794
- // Extract credential once — getCredential may have side effects (e.g. SSE transports).
795
- let [credential, credentialError] = (() => {
796
- try {
797
- const credential = transport.getCredential(input) as Credential.Credential | null
798
- return [credential ? hydrateCredentialMeta(credential) : null, undefined] as const
799
- } catch (e) {
800
- return [null, e as Error] as const
801
- }
802
- })()
832
+ const handler = async (input: Transport.InputOf): Promise<MethodFn.Response> => {
833
+ if (method.html && isServiceWorkerRequest(input))
834
+ return {
835
+ status: 402,
836
+ challenge: createServiceWorkerResponse(),
837
+ } as MethodFn.Response<Transport.Http>
803
838
 
804
- const emitChallenge = async (parameters: {
805
- challenge: Challenge.Challenge
806
- credential?: Credential.Credential | null | undefined
807
- error?: Errors.PaymentError | undefined
808
- html?: Method.Method['html'] | undefined
809
- request: Record<string, unknown>
810
- }) => {
811
- const response = await transport.respondChallenge({
812
- challenge: parameters.challenge,
813
- input,
814
- ...(parameters.error && { error: parameters.error }),
815
- ...(parameters.html && { html: parameters.html }),
816
- })
817
- if (isIssuedChallengeResponse(response))
818
- await events.emit(
819
- 'challenge.created',
820
- createChallengeContext({
821
- capturedRequest,
822
- challenge: parameters.challenge,
823
- credential: parameters.credential,
824
- error: parameters.error,
825
- input,
826
- method,
827
- request: parameters.request,
828
- }) as never,
829
- )
830
- return response
839
+ const expires =
840
+ 'expires' in options
841
+ ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
842
+ : Expires.minutes(5)
843
+ const capturedRequest = await captureRequest(transport, input)
844
+ const effectiveMeta =
845
+ scope === undefined && input instanceof globalThis.Request
846
+ ? Scope.merge({ meta: staticMeta, scope: Scope.get(input) })
847
+ : staticMeta
848
+
849
+ // Extract credential once — getCredential may have side effects (e.g. SSE transports).
850
+ let [credential, credentialError] = (() => {
851
+ try {
852
+ const credential = transport.getCredential(input) as Credential.Credential | null
853
+ return [credential ? hydrateCredentialMeta(credential) : null, undefined] as const
854
+ } catch (e) {
855
+ return [null, e as Error] as const
856
+ }
857
+ })()
858
+
859
+ if (preflight && input instanceof globalThis.Request) {
860
+ const response = await preflight({
861
+ capturedRequest,
862
+ credential,
863
+ expires,
864
+ input,
865
+ options: { ...defaults, ...rest },
866
+ realm: realm ?? new URL(input.url).hostname ?? 'MPP Payment',
867
+ secretKey,
868
+ } as never)
869
+ if (response) {
870
+ if (response.status === 402) return { challenge: response, status: 402 }
871
+ return {
872
+ status: 200,
873
+ withReceipt() {
874
+ return response
875
+ },
876
+ } as MethodFn.Response
831
877
  }
878
+ }
832
879
 
833
- const emitPaymentFailed = async (parameters: {
834
- challenge: Challenge.Challenge
835
- credential: Credential.Credential | null
836
- error: Errors.PaymentError
837
- request: Record<string, unknown>
838
- retryChallenge?: Challenge.Challenge | undefined
839
- submittedChallenge?: Challenge.Challenge | undefined
840
- }) => {
880
+ const emitChallenge = async (parameters: {
881
+ challenge: Challenge.Challenge
882
+ credential?: Credential.Credential | null | undefined
883
+ error?: Errors.PaymentError | undefined
884
+ html?: Method.Method['html'] | undefined
885
+ request: Record<string, unknown>
886
+ }) => {
887
+ const response = await transport.respondChallenge({
888
+ challenge: parameters.challenge,
889
+ input,
890
+ ...(parameters.error && { error: parameters.error }),
891
+ ...(parameters.html && { html: parameters.html }),
892
+ })
893
+ if (isIssuedChallengeResponse(response))
841
894
  await events.emit(
842
- 'payment.failed',
843
- createPaymentFailedContext({
895
+ 'challenge.created',
896
+ createChallengeContext({
844
897
  capturedRequest,
845
898
  challenge: parameters.challenge,
846
899
  credential: parameters.credential,
@@ -848,272 +901,199 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
848
901
  input,
849
902
  method,
850
903
  request: parameters.request,
851
- retryChallenge: parameters.retryChallenge,
852
- submittedChallenge: parameters.submittedChallenge,
853
904
  }) as never,
854
905
  )
855
- }
906
+ return response
907
+ }
908
+
909
+ const emitPaymentFailed = async (parameters: {
910
+ challenge: Challenge.Challenge
911
+ credential: Credential.Credential | null
912
+ error: Errors.PaymentError
913
+ request: Record<string, unknown>
914
+ retryChallenge?: Challenge.Challenge | undefined
915
+ submittedChallenge?: Challenge.Challenge | undefined
916
+ }) => {
917
+ await events.emit(
918
+ 'payment.failed',
919
+ createPaymentFailedContext({
920
+ capturedRequest,
921
+ challenge: parameters.challenge,
922
+ credential: parameters.credential,
923
+ error: parameters.error,
924
+ input,
925
+ method,
926
+ request: parameters.request,
927
+ retryChallenge: parameters.retryChallenge,
928
+ submittedChallenge: parameters.submittedChallenge,
929
+ }) as never,
930
+ )
931
+ }
856
932
 
857
- const routeChallenge = await resolveRouteChallenge({
933
+ const routeChallenge = await resolveRouteChallenge({
934
+ capturedRequest,
935
+ credential,
936
+ defaults,
937
+ description,
938
+ expires,
939
+ meta: effectiveMeta,
940
+ method,
941
+ realm,
942
+ request: parameters.request,
943
+ routeRequest: rest,
944
+ secretKey,
945
+ }).catch(async (e) => {
946
+ if (!(e instanceof Errors.PaymentError)) throw e
947
+ const challenge = createFallbackChallenge({
858
948
  capturedRequest,
859
- credential,
860
- defaults,
949
+ defaults: defaults ?? {},
861
950
  description,
862
951
  expires,
863
952
  meta: effectiveMeta,
864
953
  method,
865
954
  realm,
866
- request: parameters.request,
867
955
  routeRequest: rest,
868
956
  secretKey,
869
- }).catch(async (e) => {
870
- if (!(e instanceof Errors.PaymentError)) throw e
871
- const challenge = createFallbackChallenge({
872
- capturedRequest,
873
- defaults: defaults ?? {},
874
- description,
875
- expires,
876
- meta: effectiveMeta,
877
- method,
878
- realm,
879
- routeRequest: rest,
880
- secretKey,
881
- })
882
- if (credential)
883
- await emitPaymentFailed({
884
- challenge,
885
- credential,
886
- error: e,
887
- request: challenge.request,
888
- retryChallenge: challenge,
889
- submittedChallenge: credential.challenge,
890
- })
891
- const response = await emitChallenge({
957
+ })
958
+ if (credential)
959
+ await emitPaymentFailed({
892
960
  challenge,
893
961
  credential,
894
- request: challenge.request,
895
962
  error: e,
896
- html: method.html,
963
+ request: challenge.request,
964
+ retryChallenge: challenge,
965
+ submittedChallenge: credential.challenge,
897
966
  })
898
- return { response }
967
+ const response = await emitChallenge({
968
+ challenge,
969
+ credential,
970
+ request: challenge.request,
971
+ error: e,
972
+ html: method.html,
899
973
  })
900
- if ('response' in routeChallenge) return { challenge: routeChallenge.response, status: 402 }
901
- const { challenge, parsedRequest, request } = routeChallenge
974
+ return { response }
975
+ })
976
+ if ('response' in routeChallenge) return { challenge: routeChallenge.response, status: 402 }
977
+ const { challenge, parsedRequest, request } = routeChallenge
978
+ internal._canonicalRequest = parsedRequest
902
979
 
903
- if (credential && transport.bindCredential) {
904
- try {
905
- credential = hydrateCredentialMeta(
906
- (await transport.bindCredential({
907
- challenge,
908
- credential,
909
- input,
910
- })) as Credential.Credential,
911
- )
912
- } catch (e) {
913
- credential = null
914
- credentialError = e as Error
915
- }
980
+ if (credential && transport.bindCredential) {
981
+ try {
982
+ credential = hydrateCredentialMeta(
983
+ (await transport.bindCredential({
984
+ challenge,
985
+ credential,
986
+ input,
987
+ })) as Credential.Credential,
988
+ )
989
+ } catch (e) {
990
+ credential = null
991
+ credentialError = e as Error
916
992
  }
993
+ }
917
994
 
918
- // Credential was provided but malformed
919
- if (credentialError) {
920
- const reason = getSafeCredentialReason(credentialError)
921
- const error = new Errors.MalformedCredentialError(reason ? { reason } : {})
922
- await emitPaymentFailed({
923
- challenge,
924
- credential: null,
925
- error,
926
- request: parsedRequest,
927
- retryChallenge: challenge,
928
- })
929
- const response = await emitChallenge({
930
- challenge,
931
- credential: null,
932
- request: parsedRequest,
933
- error,
934
- html: method.html,
935
- })
936
- return { challenge: response, status: 402 }
937
- }
995
+ // Credential was provided but malformed
996
+ if (credentialError) {
997
+ const reason = getSafeCredentialReason(credentialError)
998
+ const error = new Errors.MalformedCredentialError(reason ? { reason } : {})
999
+ await emitPaymentFailed({
1000
+ challenge,
1001
+ credential: null,
1002
+ error,
1003
+ request: parsedRequest,
1004
+ retryChallenge: challenge,
1005
+ })
1006
+ const response = await emitChallenge({
1007
+ challenge,
1008
+ credential: null,
1009
+ request: parsedRequest,
1010
+ error,
1011
+ html: method.html,
1012
+ })
1013
+ return { challenge: response, status: 402 }
1014
+ }
938
1015
 
939
- const success = (
940
- receiptData: Receipt.Receipt,
941
- options: {
942
- challengeId?: string | undefined
943
- credentialForReceipt?: Credential.Credential | undefined
944
- envelopeForReceipt?: Method.VerifiedChallengeEnvelope | undefined
945
- managementResponse?: globalThis.Response | undefined
946
- } = {},
947
- ): MethodFn.Response => {
948
- const {
949
- challengeId = challenge.id,
950
- credentialForReceipt = { challenge, payload: {} } as Credential.Credential,
951
- envelopeForReceipt,
952
- managementResponse,
953
- } = options
1016
+ const success = (
1017
+ receiptData: Receipt.Receipt,
1018
+ options: {
1019
+ challengeId?: string | undefined
1020
+ credentialForReceipt?: Credential.Credential | undefined
1021
+ envelopeForReceipt?: Method.VerifiedChallengeEnvelope | undefined
1022
+ managementResponse?: globalThis.Response | undefined
1023
+ } = {},
1024
+ ): MethodFn.Response => {
1025
+ const {
1026
+ challengeId = challenge.id,
1027
+ credentialForReceipt = { challenge, payload: {} } as Credential.Credential,
1028
+ envelopeForReceipt,
1029
+ managementResponse,
1030
+ } = options
954
1031
 
955
- return {
956
- status: 200,
957
- withReceipt<response>(response?: response) {
958
- if (managementResponse) {
959
- return transport.respondReceipt({
960
- challengeId,
961
- credential: credentialForReceipt,
962
- ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
963
- input,
964
- receipt: receiptData,
965
- response: managementResponse as never,
966
- }) as response
967
- }
968
- if (!response) throw new MissingReceiptResponseError()
1032
+ return {
1033
+ status: 200,
1034
+ withReceipt<response>(response?: response) {
1035
+ if (managementResponse) {
969
1036
  return transport.respondReceipt({
970
1037
  challengeId,
971
1038
  credential: credentialForReceipt,
972
1039
  ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
973
1040
  input,
974
1041
  receipt: receiptData,
975
- response: response as never,
1042
+ response: managementResponse as never,
976
1043
  }) as response
977
- },
978
- }
1044
+ }
1045
+ if (!response) throw new MissingReceiptResponseError()
1046
+ return transport.respondReceipt({
1047
+ challengeId,
1048
+ credential: credentialForReceipt,
1049
+ ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
1050
+ input,
1051
+ receipt: receiptData,
1052
+ response: response as never,
1053
+ }) as response
1054
+ },
979
1055
  }
1056
+ }
980
1057
 
981
- // No credential provided—issue challenge
982
- if (!credential) {
983
- if (authorize && input instanceof globalThis.Request) {
984
- try {
985
- const authorized = await authorize({
986
- challenge,
987
- input,
988
- request: challenge.request,
989
- } as never)
990
- if (authorized) {
991
- await events.emit(
992
- 'payment.success',
993
- createPaymentSuccessContext({
994
- capturedRequest,
995
- challenge,
996
- input,
997
- method,
998
- receipt: authorized.receipt,
999
- request: parsedRequest,
1000
- }) as never,
1001
- )
1002
- return success(authorized.receipt, {
1003
- managementResponse: authorized.response,
1004
- })
1005
- }
1006
- } catch (e) {
1007
- if (!(e instanceof Errors.PaymentError))
1008
- console.error('mppx: internal authorization error', e)
1009
- const error =
1010
- e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1011
- await emitPaymentFailed({
1012
- challenge,
1013
- credential: null,
1014
- error,
1015
- request: parsedRequest,
1016
- retryChallenge: challenge,
1017
- })
1018
- const response = await emitChallenge({
1019
- challenge,
1020
- request: parsedRequest,
1021
- error,
1022
- html: method.html,
1058
+ // No credential provided—issue challenge
1059
+ if (!credential) {
1060
+ if (authorize && input instanceof globalThis.Request) {
1061
+ try {
1062
+ const authorized = await authorize({
1063
+ challenge,
1064
+ input,
1065
+ request: challenge.request,
1066
+ } as never)
1067
+ if (authorized) {
1068
+ await events.emit(
1069
+ 'payment.success',
1070
+ createPaymentSuccessContext({
1071
+ capturedRequest,
1072
+ challenge,
1073
+ input,
1074
+ method,
1075
+ receipt: authorized.receipt,
1076
+ request: parsedRequest,
1077
+ }) as never,
1078
+ )
1079
+ return success(authorized.receipt, {
1080
+ managementResponse: authorized.response,
1023
1081
  })
1024
- return { challenge: response, status: 402 }
1025
1082
  }
1026
- }
1027
-
1028
- const error = new Errors.PaymentRequiredError({ description })
1029
- const response = await emitChallenge({
1030
- challenge,
1031
- credential: null,
1032
- request: parsedRequest,
1033
- error,
1034
- html: method.html,
1035
- })
1036
- return { challenge: response, status: 402 }
1037
- }
1038
-
1039
- // ── Tier 1: HMAC provenance check (primary gate) ──────────────────
1040
- //
1041
- // Recompute the HMAC-SHA256 over the credential's echoed challenge
1042
- // parameters (realm|method|intent|request|expires|digest|opaque) and
1043
- // compare to the echoed `id`. This proves the challenge was issued by
1044
- // this server with these exact parameters — including opaque/meta,
1045
- // expires, and the full serialized request blob.
1046
- //
1047
- // This is the authoritative binding per §5.1.2.1.1 of the spec
1048
- // (https://paymentauth.org/draft-httpauth-payment-00.html#section-5.1.2.1.1).
1049
- // No database lookup is needed; the HMAC is stateless verification.
1050
- if (!Challenge.verify(credential.challenge, { secretKey })) {
1051
- const error = new Errors.InvalidChallengeError({
1052
- id: credential.challenge.id,
1053
- reason: 'challenge was not issued by this server',
1054
- })
1055
- await emitPaymentFailed({
1056
- challenge,
1057
- credential,
1058
- error,
1059
- request: parsedRequest,
1060
- retryChallenge: challenge,
1061
- submittedChallenge: credential.challenge,
1062
- })
1063
- const response = await emitChallenge({
1064
- challenge,
1065
- credential,
1066
- request: parsedRequest,
1067
- error,
1068
- html: method.html,
1069
- })
1070
- return { challenge: response, status: 402 }
1071
- }
1072
-
1073
- // ── Tier 2: Pinned field safety net ──────────────────────────────
1074
- //
1075
- // The HMAC check above (Tier 1) is the primary gate — it already
1076
- // covers ALL challenge fields including opaque, digest, and the full
1077
- // serialized request. So why this second check?
1078
- //
1079
- // The `request()` hook can produce credential-dependent output: for
1080
- // example, `feePayer` may differ between the 402 challenge call (no
1081
- // credential) and the credential-bearing call. This means the
1082
- // recomputed challenge here has a different `request` blob — and
1083
- // thus a different HMAC — than the original challenge the client
1084
- // echoes back. The HMAC check above verifies the *echoed* challenge
1085
- // was signed by us, but it cannot verify that the echoed challenge
1086
- // matches *this route's current configuration* when the request
1087
- // hook transforms fields between calls.
1088
- //
1089
- // This check compares the fields that MUST be stable across both
1090
- // calls. That includes the economically significant request fields
1091
- // plus `opaque`, which can carry route-scoping metadata (for example,
1092
- // sibling route identity) that must not be replayable across handlers.
1093
- // `expires` still is not pinned here because its default is generated
1094
- // per invocation, and `digest` is already bound by the echoed HMAC.
1095
- {
1096
- const mismatch = getChallengeBindingMismatch(
1097
- challenge,
1098
- credential.challenge,
1099
- stableBinding as never,
1100
- )
1101
- if (mismatch) {
1102
- const error = new Errors.InvalidChallengeError({
1103
- id: credential.challenge.id,
1104
- reason: `credential ${mismatch} does not match this route's requirements`,
1105
- })
1083
+ } catch (e) {
1084
+ if (!(e instanceof Errors.PaymentError))
1085
+ console.error('mppx: internal authorization error', e)
1086
+ const error =
1087
+ e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1106
1088
  await emitPaymentFailed({
1107
1089
  challenge,
1108
- credential,
1090
+ credential: null,
1109
1091
  error,
1110
1092
  request: parsedRequest,
1111
1093
  retryChallenge: challenge,
1112
- submittedChallenge: credential.challenge,
1113
1094
  })
1114
1095
  const response = await emitChallenge({
1115
1096
  challenge,
1116
- credential,
1117
1097
  request: parsedRequest,
1118
1098
  error,
1119
1099
  html: method.html,
@@ -1122,35 +1102,84 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
1122
1102
  }
1123
1103
  }
1124
1104
 
1125
- // Reject credentials without expires (fail-closed) or with expired timestamp
1126
- try {
1127
- Expires.assert(credential.challenge.expires, credential.challenge.id)
1128
- } catch (error) {
1129
- await emitPaymentFailed({
1130
- challenge,
1131
- credential,
1132
- error: error as Errors.PaymentError,
1133
- request: parsedRequest,
1134
- retryChallenge: challenge,
1135
- submittedChallenge: credential.challenge,
1136
- })
1137
- const response = await emitChallenge({
1138
- challenge,
1139
- credential,
1140
- request: parsedRequest,
1141
- error: error as Errors.PaymentError,
1105
+ const error = new Errors.PaymentRequiredError({ description })
1106
+ const response = await emitChallenge({
1107
+ challenge,
1108
+ credential: null,
1109
+ request: parsedRequest,
1110
+ error,
1111
+ html: method.html,
1112
+ })
1113
+ return { challenge: response, status: 402 }
1114
+ }
1115
+
1116
+ // ── Tier 1: HMAC provenance check (primary gate) ──────────────────
1117
+ //
1118
+ // Recompute the HMAC-SHA256 over the credential's echoed challenge
1119
+ // parameters (realm|method|intent|request|expires|digest|opaque) and
1120
+ // compare to the echoed `id`. This proves the challenge was issued by
1121
+ // this server with these exact parameters — including opaque/meta,
1122
+ // expires, and the full serialized request blob.
1123
+ //
1124
+ // This is the authoritative binding per §5.1.2.1.1 of the spec
1125
+ // (https://paymentauth.org/draft-httpauth-payment-00.html#section-5.1.2.1.1).
1126
+ // No database lookup is needed; the HMAC is stateless verification.
1127
+ if (!Challenge.verify(credential.challenge, { secretKey })) {
1128
+ const error = new Errors.InvalidChallengeError({
1129
+ id: credential.challenge.id,
1130
+ reason: 'challenge was not issued by this server',
1131
+ })
1132
+ await emitPaymentFailed({
1133
+ challenge,
1134
+ credential,
1135
+ error,
1136
+ request: parsedRequest,
1137
+ retryChallenge: challenge,
1138
+ submittedChallenge: credential.challenge,
1139
+ })
1140
+ const response = await emitChallenge({
1141
+ challenge,
1142
+ credential,
1143
+ request: parsedRequest,
1144
+ error,
1145
+ html: method.html,
1146
+ })
1147
+ return { challenge: response, status: 402 }
1148
+ }
1149
+
1150
+ // ── Tier 2: Pinned field safety net ──────────────────────────────
1151
+ //
1152
+ // The HMAC check above (Tier 1) is the primary gate — it already
1153
+ // covers ALL challenge fields including opaque, digest, and the full
1154
+ // serialized request. So why this second check?
1155
+ //
1156
+ // The `request()` hook can produce credential-dependent output: for
1157
+ // example, `feePayer` may differ between the 402 challenge call (no
1158
+ // credential) and the credential-bearing call. This means the
1159
+ // recomputed challenge here has a different `request` blob — and
1160
+ // thus a different HMAC — than the original challenge the client
1161
+ // echoes back. The HMAC check above verifies the *echoed* challenge
1162
+ // was signed by us, but it cannot verify that the echoed challenge
1163
+ // matches *this route's current configuration* when the request
1164
+ // hook transforms fields between calls.
1165
+ //
1166
+ // This check compares the fields that MUST be stable across both
1167
+ // calls. That includes the economically significant request fields
1168
+ // plus `opaque`, which can carry route-scoping metadata (for example,
1169
+ // sibling route identity) that must not be replayable across handlers.
1170
+ // `expires` still is not pinned here because its default is generated
1171
+ // per invocation, and `digest` is already bound by the echoed HMAC.
1172
+ {
1173
+ const mismatch = getChallengeBindingMismatch(
1174
+ challenge,
1175
+ credential.challenge,
1176
+ stableBinding as never,
1177
+ )
1178
+ if (mismatch) {
1179
+ const error = new Errors.InvalidChallengeError({
1180
+ id: credential.challenge.id,
1181
+ reason: `credential ${mismatch} does not match this route's requirements`,
1142
1182
  })
1143
- return { challenge: response, status: 402 }
1144
- }
1145
- // Validate payload structure against method schema
1146
- let parsedCredential: Credential.Credential
1147
- try {
1148
- parsedCredential = withParsedCredentialPayload(
1149
- credential,
1150
- method.schema.credential.payload.parse(credential.payload),
1151
- )
1152
- } catch {
1153
- const error = new Errors.InvalidPayloadError()
1154
1183
  await emitPaymentFailed({
1155
1184
  challenge,
1156
1185
  credential,
@@ -1164,92 +1193,129 @@ function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.R
1164
1193
  credential,
1165
1194
  request: parsedRequest,
1166
1195
  error,
1196
+ html: method.html,
1167
1197
  })
1168
1198
  return { challenge: response, status: 402 }
1169
1199
  }
1200
+ }
1170
1201
 
1171
- const envelope: Method.VerifiedChallengeEnvelope = Object.freeze({
1172
- capturedRequest,
1173
- challenge: credential.challenge,
1174
- credential: parsedCredential,
1202
+ // Reject credentials without expires (fail-closed) or with expired timestamp
1203
+ try {
1204
+ Expires.assert(credential.challenge.expires, credential.challenge.id)
1205
+ } catch (error) {
1206
+ await emitPaymentFailed({
1207
+ challenge,
1208
+ credential,
1209
+ error: error as Errors.PaymentError,
1210
+ request: parsedRequest,
1211
+ retryChallenge: challenge,
1212
+ submittedChallenge: credential.challenge,
1213
+ })
1214
+ const response = await emitChallenge({
1215
+ challenge,
1216
+ credential,
1175
1217
  request: parsedRequest,
1218
+ error: error as Errors.PaymentError,
1176
1219
  })
1220
+ return { challenge: response, status: 402 }
1221
+ }
1222
+ // Validate payload structure against method schema
1223
+ let parsedCredential: Credential.Credential
1224
+ try {
1225
+ parsedCredential = withParsedCredentialPayload(
1226
+ credential,
1227
+ method.schema.credential.payload.parse(credential.payload),
1228
+ )
1229
+ } catch {
1230
+ const error = new Errors.InvalidPayloadError()
1231
+ await emitPaymentFailed({
1232
+ challenge,
1233
+ credential,
1234
+ error,
1235
+ request: parsedRequest,
1236
+ retryChallenge: challenge,
1237
+ submittedChallenge: credential.challenge,
1238
+ })
1239
+ const response = await emitChallenge({
1240
+ challenge,
1241
+ credential,
1242
+ request: parsedRequest,
1243
+ error,
1244
+ })
1245
+ return { challenge: response, status: 402 }
1246
+ }
1177
1247
 
1178
- // User-provided verification (e.g., check signature, submit tx, verify payment).
1179
- // If verification fails, re-issue the challenge so the client can retry.
1180
- let receiptData: Receipt.Receipt
1181
- try {
1182
- receiptData = await verify({ credential: parsedCredential, envelope, request } as never)
1183
- } catch (e) {
1184
- if (!(e instanceof Errors.PaymentError))
1185
- console.error('mppx: internal verification error', e)
1186
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1187
- await emitPaymentFailed({
1188
- challenge,
1189
- credential: parsedCredential,
1190
- error,
1191
- request: parsedRequest,
1192
- retryChallenge: challenge,
1193
- submittedChallenge: credential.challenge,
1194
- })
1195
- const response = await emitChallenge({
1196
- challenge,
1197
- credential: parsedCredential,
1198
- request: parsedRequest,
1199
- error,
1200
- })
1201
- return { challenge: response, status: 402 }
1202
- }
1248
+ const envelope: Method.VerifiedChallengeEnvelope = Object.freeze({
1249
+ capturedRequest,
1250
+ challenge: credential.challenge,
1251
+ credential: parsedCredential,
1252
+ request: parsedRequest,
1253
+ })
1203
1254
 
1204
- // If the method's `respond` hook returns a Response, it means this
1205
- // request is a management action (e.g. channel open, voucher POST)
1206
- // and the user's route handler should NOT run. `withReceipt()` will
1207
- // return the management response directly. If undefined, `withReceipt()`
1208
- // expects the caller to pass the user handler's response instead.
1209
- const managementResponse = respond
1210
- ? await respond({
1211
- credential: parsedCredential,
1212
- envelope,
1213
- input,
1214
- receipt: receiptData,
1215
- request,
1216
- } as never)
1217
- : undefined
1255
+ // User-provided verification (e.g., check signature, submit tx, verify payment).
1256
+ // If verification fails, re-issue the challenge so the client can retry.
1257
+ let receiptData: Receipt.Receipt
1258
+ try {
1259
+ receiptData = await verify({ credential: parsedCredential, envelope, request } as never)
1260
+ } catch (e) {
1261
+ if (!(e instanceof Errors.PaymentError))
1262
+ console.error('mppx: internal verification error', e)
1263
+ const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1264
+ await emitPaymentFailed({
1265
+ challenge,
1266
+ credential: parsedCredential,
1267
+ error,
1268
+ request: parsedRequest,
1269
+ retryChallenge: challenge,
1270
+ submittedChallenge: credential.challenge,
1271
+ })
1272
+ const response = await emitChallenge({
1273
+ challenge,
1274
+ credential: parsedCredential,
1275
+ request: parsedRequest,
1276
+ error,
1277
+ })
1278
+ return { challenge: response, status: 402 }
1279
+ }
1218
1280
 
1219
- await events.emit(
1220
- 'payment.success',
1221
- createPaymentSuccessContext({
1222
- capturedRequest,
1223
- challenge: credential.challenge,
1281
+ // If the method's `respond` hook returns a Response, it means this
1282
+ // request is a management action (e.g. channel open, voucher POST)
1283
+ // and the user's route handler should NOT run. `withReceipt()` will
1284
+ // return the management response directly. If undefined, `withReceipt()`
1285
+ // expects the caller to pass the user handler's response instead.
1286
+ const managementResponse = respond
1287
+ ? await respond({
1224
1288
  credential: parsedCredential,
1225
1289
  envelope,
1226
1290
  input,
1227
- method,
1228
1291
  receipt: receiptData,
1229
- request: parsedRequest,
1230
- }) as never,
1231
- )
1292
+ request,
1293
+ } as never)
1294
+ : undefined
1232
1295
 
1233
- return success(receiptData, {
1234
- challengeId: credential.challenge.id,
1235
- credentialForReceipt: parsedCredential,
1236
- envelopeForReceipt: envelope,
1237
- managementResponse,
1238
- })
1239
- },
1240
- {
1241
- _internal: {
1242
- ...method,
1243
- ...defaults,
1244
- ...options,
1245
- ...(staticMeta !== undefined ? { meta: staticMeta } : {}),
1246
- name: method.name,
1247
- intent: method.intent,
1248
- _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
1249
- _stableBinding: stableBinding as never,
1250
- },
1251
- },
1252
- )
1296
+ await events.emit(
1297
+ 'payment.success',
1298
+ createPaymentSuccessContext({
1299
+ capturedRequest,
1300
+ challenge: credential.challenge,
1301
+ credential: parsedCredential,
1302
+ envelope,
1303
+ input,
1304
+ method,
1305
+ receipt: receiptData,
1306
+ request: parsedRequest,
1307
+ }) as never,
1308
+ )
1309
+
1310
+ return success(receiptData, {
1311
+ challengeId: credential.challenge.id,
1312
+ credentialForReceipt: parsedCredential,
1313
+ envelopeForReceipt: envelope,
1314
+ managementResponse,
1315
+ })
1316
+ }
1317
+
1318
+ return Object.assign(handler, { _internal: internal })
1253
1319
  }
1254
1320
  }
1255
1321
 
@@ -1305,6 +1371,7 @@ declare namespace createMethodFn {
1305
1371
  defaults?: defaults
1306
1372
  method: method
1307
1373
  events: ServerEventDispatcher<readonly [method], transport>
1374
+ preflight?: Method.PreflightFn<method>
1308
1375
  realm: string | undefined
1309
1376
  request?: Method.RequestFn<method>
1310
1377
  respond?: Method.RespondFn<method>
@@ -1767,7 +1834,7 @@ function captureRequestFromInput(input: unknown): Method.CapturedRequest {
1767
1834
  }
1768
1835
 
1769
1836
  const coreBindingFields = ['amount', 'currency', 'recipient'] as const
1770
- const methodBindingFields = ['chainId', 'memo', 'splits', 'unitType'] as const
1837
+ const methodBindingFields = ['chainId', 'memo', 'sessionProtocol', 'splits', 'unitType'] as const
1771
1838
  const pinnedRequestBindingFields = [...coreBindingFields, ...methodBindingFields] as const
1772
1839
 
1773
1840
  type CoreBindingField = (typeof coreBindingFields)[number]
@@ -1776,6 +1843,31 @@ type PinnedRequestBindingField = (typeof pinnedRequestBindingFields)[number]
1776
1843
  type PinnedChallengeField = 'method' | 'intent' | 'realm' | 'opaque' | PinnedRequestBindingField
1777
1844
  type StableBinding = Record<string, unknown>
1778
1845
 
1846
+ function selectVerificationMethod(
1847
+ methods: readonly Method.AnyServer[],
1848
+ challenge: Challenge.Challenge,
1849
+ ): Method.AnyServer | undefined {
1850
+ if (methods.length <= 1) return methods[0]
1851
+ if (
1852
+ challenge.method !== Constants.Methods.tempo ||
1853
+ challenge.intent !== Constants.Intents.session
1854
+ )
1855
+ return methods[0]
1856
+
1857
+ const sessionProtocolMarker = Constants.getMethodDetail(
1858
+ challenge.request.methodDetails,
1859
+ Constants.MethodDetailKeys.sessionProtocol,
1860
+ )
1861
+ if (
1862
+ sessionProtocolMarker === undefined ||
1863
+ sessionProtocolMarker === Constants.SessionProtocols.v1
1864
+ )
1865
+ return methods.find((method) => method.alias === 'sessionLegacy') ?? methods[0]
1866
+ if (sessionProtocolMarker === Constants.SessionProtocols.v2)
1867
+ return methods.find((method) => method.alias === undefined) ?? methods[0]
1868
+ return undefined
1869
+ }
1870
+
1779
1871
  function getChallengeBindingMismatch(
1780
1872
  expectedChallenge: Challenge.Challenge,
1781
1873
  actualChallenge: Challenge.Challenge,
@@ -1856,6 +1948,7 @@ function getPinnedRequestBinding(request: Record<string, unknown>): PinnedReques
1856
1948
  const currency = normalizeScalar(request.currency ?? methodDetails.currency)
1857
1949
  const memo = normalizeHex(methodDetails.memo)
1858
1950
  const recipient = normalizeScalar(request.recipient ?? methodDetails.recipient)
1951
+ const sessionProtocol = normalizeScalar(methodDetails.sessionProtocol)
1859
1952
  const splits = normalizeComparable(methodDetails.splits)
1860
1953
  const unitType = normalizeScalar(request.unitType ?? methodDetails.unitType)
1861
1954
 
@@ -1868,6 +1961,7 @@ function getPinnedRequestBinding(request: Record<string, unknown>): PinnedReques
1868
1961
  methodBinding: {
1869
1962
  ...(chainId !== undefined ? { chainId } : {}),
1870
1963
  ...(memo !== undefined ? { memo } : {}),
1964
+ ...(sessionProtocol !== undefined ? { sessionProtocol } : {}),
1871
1965
  ...(splits !== undefined ? { splits } : {}),
1872
1966
  ...(unitType !== undefined ? { unitType } : {}),
1873
1967
  },
@@ -2021,7 +2115,7 @@ type ConfiguredHandler = ((input: Request) => Promise<MethodFn.Response<Transpor
2021
2115
  }
2022
2116
  }
2023
2117
 
2024
- const paymentAuthChallengeHeader = 'WWW-Authenticate'
2118
+ const paymentAuthChallengeHeader = Constants.Headers.wwwAuthenticate
2025
2119
 
2026
2120
  const challengeHeaderMerges = [
2027
2121
  {
@@ -2141,7 +2235,7 @@ export function compose(
2141
2235
  // Try to extract a Payment credential to decide whether to dispatch or challenge.
2142
2236
  // Only gate on the Payment scheme — other auth schemes (Bearer, Basic, etc.)
2143
2237
  // should fall through to the merged-402 path so all offers are presented.
2144
- const header = input.headers.get('Authorization')
2238
+ const header = input.headers.get(Constants.Headers.authorization)
2145
2239
  const paymentHeader = header ? Credential.extractPaymentScheme(header) : null
2146
2240
 
2147
2241
  if (paymentHeader) {
@@ -2222,7 +2316,7 @@ export function compose(
2222
2316
  })
2223
2317
  }
2224
2318
 
2225
- const acceptPayment = input.headers.get('Accept-Payment')
2319
+ const acceptPayment = input.headers.get(Constants.Headers.acceptPayment)
2226
2320
  if (!acceptPayment) return entries
2227
2321
 
2228
2322
  try {
@@ -2243,7 +2337,7 @@ export function compose(
2243
2337
  result.status === 402 ? [result.challenge as Response] : [],
2244
2338
  )
2245
2339
  const unnegotiatedX402Responses =
2246
- input.headers.has('Accept-Payment') || challengeEntries.length === 0
2340
+ input.headers.has(Constants.Headers.acceptPayment) || challengeEntries.length === 0
2247
2341
  ? []
2248
2342
  : challengeResponses.filter(
2249
2343
  (response) =>