mppx 0.8.15 → 0.8.18

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 (966) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +15 -0
  3. package/dist/Challenge.js +61 -39
  4. package/dist/Constants.d.ts +17 -17
  5. package/dist/Errors.d.ts +3 -3
  6. package/dist/Method.d.ts +35 -5
  7. package/dist/Method.js +7 -22
  8. package/dist/Store.d.ts +19 -0
  9. package/dist/Store.js +43 -0
  10. package/dist/attestation/Client.d.ts +17 -0
  11. package/dist/attestation/Client.js +28 -0
  12. package/dist/attestation/Constants.d.ts +15 -0
  13. package/dist/attestation/Constants.js +15 -0
  14. package/dist/attestation/Server.d.ts +4 -0
  15. package/dist/attestation/Server.js +8 -0
  16. package/dist/attestation/Types.d.ts +76 -0
  17. package/dist/attestation/Types.js +2 -0
  18. package/dist/attestation/index.d.ts +6 -0
  19. package/dist/attestation/index.js +6 -0
  20. package/dist/attestation/internal/HttpMessageSignature.d.ts +73 -0
  21. package/dist/attestation/internal/HttpMessageSignature.js +320 -0
  22. package/dist/attestation/internal/SigningContext.d.ts +4 -0
  23. package/dist/attestation/internal/SigningContext.js +11 -0
  24. package/dist/bin.js +3 -3
  25. package/dist/cli/THIRD-PARTY-LICENSES.txt +328 -0
  26. package/dist/cli/account.d.ts +14 -14
  27. package/dist/cli/cli.d.ts +6 -6
  28. package/dist/cli/cli.js +253 -1504
  29. package/dist/cli/plugins/index.js +19 -5
  30. package/dist/cli/plugins/tempo.js +7 -3
  31. package/dist/cli/sessions/Manager.js +7 -4
  32. package/dist/cli/sessions/commands.d.ts +4 -4
  33. package/dist/cli/sessions/request.d.ts +2 -0
  34. package/dist/cli/sessions/request.js +18 -0
  35. package/dist/cli/sessions/store.js +28 -14
  36. package/dist/cli/utils.d.ts +1 -1
  37. package/dist/cli/validate/index.d.ts +3 -3
  38. package/dist/client/Mppx.d.ts +3 -0
  39. package/dist/client/Mppx.js +13 -5
  40. package/dist/client/Transport.d.ts +3 -5
  41. package/dist/client/Transport.js +1 -17
  42. package/dist/client/internal/Fetch.js +2 -6
  43. package/dist/discovery/Discovery.js +2 -1
  44. package/dist/discovery/OpenApi.d.ts +3 -5
  45. package/dist/discovery/OpenApi.js +13 -34
  46. package/dist/discovery/internal/Metadata.d.ts +20 -0
  47. package/dist/discovery/internal/Metadata.js +36 -0
  48. package/dist/evm/Methods.d.ts +11 -11
  49. package/dist/evm/Types.d.ts +17 -17
  50. package/dist/evm/client/Charge.d.ts +11 -11
  51. package/dist/evm/client/Charge.js +8 -0
  52. package/dist/evm/client/Methods.d.ts +11 -11
  53. package/dist/evm/server/Charge.d.ts +1 -1
  54. package/dist/evm/server/Charge.js +2 -0
  55. package/dist/evm/server/Methods.d.ts +11 -11
  56. package/dist/internal/HeaderCodec.d.ts +2 -2
  57. package/dist/internal/types.d.ts +6 -0
  58. package/dist/proxy/Proxy.d.ts +2 -0
  59. package/dist/proxy/Proxy.js +117 -9
  60. package/dist/proxy/Service.d.ts +36 -1
  61. package/dist/proxy/Service.js +9 -8
  62. package/dist/proxy/services/anthropic.js +1 -0
  63. package/dist/proxy/services/openai.js +1 -0
  64. package/dist/proxy/services/stripe.js +1 -0
  65. package/dist/server/Mppx.d.ts +75 -13
  66. package/dist/server/Mppx.js +201 -23
  67. package/dist/server/core.d.ts +9 -0
  68. package/dist/server/core.js +9 -0
  69. package/dist/server/internal/html/config.d.ts +9 -9
  70. package/dist/server/internal/html/constants.d.ts +17 -17
  71. package/dist/stripe/Methods.d.ts +10 -10
  72. package/dist/stripe/client/Charge.d.ts +10 -10
  73. package/dist/stripe/client/Methods.d.ts +10 -10
  74. package/dist/stripe/internal/constants.d.ts +1 -1
  75. package/dist/stripe/internal/constants.js +1 -1
  76. package/dist/stripe/internal/types.d.ts +1 -0
  77. package/dist/stripe/server/Charge.d.ts +11 -11
  78. package/dist/stripe/server/Charge.js +51 -5
  79. package/dist/stripe/server/Methods.d.ts +137 -48
  80. package/dist/stripe/server/Methods.js +265 -4
  81. package/dist/stripe/server/internal/deposit-address.d.ts +13 -0
  82. package/dist/stripe/server/internal/deposit-address.js +28 -0
  83. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  84. package/dist/stripe/server/internal/html.gen.js +1 -1
  85. package/dist/stripe/server/internal/record-payment.d.ts +15 -0
  86. package/dist/stripe/server/internal/record-payment.js +42 -0
  87. package/dist/stripe/server/internal/request.d.ts +25 -0
  88. package/dist/stripe/server/internal/request.js +29 -0
  89. package/dist/stripe/server/spt.d.ts +8 -0
  90. package/dist/stripe/server/spt.js +8 -0
  91. package/dist/tap/Constants.d.ts +28 -0
  92. package/dist/tap/Constants.js +32 -0
  93. package/dist/tap/Types.d.ts +11 -0
  94. package/dist/tap/Types.js +2 -0
  95. package/dist/tap/client/index.d.ts +26 -0
  96. package/dist/tap/client/index.js +33 -0
  97. package/dist/tap/index.d.ts +5 -0
  98. package/dist/tap/index.js +5 -0
  99. package/dist/tap/server/index.d.ts +20 -0
  100. package/dist/tap/server/index.js +36 -0
  101. package/dist/tempo/Attribution.d.ts +1 -1
  102. package/dist/tempo/Methods.d.ts +73 -60
  103. package/dist/tempo/Methods.js +14 -3
  104. package/dist/tempo/Proof.d.ts +16 -16
  105. package/dist/tempo/client/Charge.d.ts +22 -19
  106. package/dist/tempo/client/Charge.js +32 -16
  107. package/dist/tempo/client/Methods.d.ts +51 -38
  108. package/dist/tempo/client/Subscription.d.ts +23 -23
  109. package/dist/tempo/internal/address.d.ts +2 -1
  110. package/dist/tempo/internal/address.js +3 -1
  111. package/dist/tempo/internal/auto-swap.js +4 -19
  112. package/dist/tempo/internal/defaults.d.ts +17 -6
  113. package/dist/tempo/internal/defaults.js +11 -0
  114. package/dist/tempo/internal/fee-payer.d.ts +12 -12
  115. package/dist/tempo/internal/machine-token-charge.d.ts +51 -0
  116. package/dist/tempo/internal/machine-token-charge.js +113 -0
  117. package/dist/tempo/internal/machine-token-session.d.ts +88 -0
  118. package/dist/tempo/internal/machine-token-session.js +173 -0
  119. package/dist/tempo/internal/proof.d.ts +17 -17
  120. package/dist/tempo/internal/signature-envelope.d.ts +14 -0
  121. package/dist/tempo/internal/signature-envelope.js +28 -0
  122. package/dist/tempo/legacy/client/ChannelOps.d.ts +2 -2
  123. package/dist/tempo/legacy/client/ChannelOps.js +11 -5
  124. package/dist/tempo/legacy/client/Session.d.ts +33 -21
  125. package/dist/tempo/legacy/client/Session.js +3 -3
  126. package/dist/tempo/legacy/client/SessionManager.d.ts +3 -1
  127. package/dist/tempo/legacy/client/SessionManager.js +2 -0
  128. package/dist/tempo/legacy/index.d.ts +1 -3
  129. package/dist/tempo/legacy/index.js +1 -3
  130. package/dist/tempo/legacy/session/escrow.abi.d.ts +374 -374
  131. package/dist/tempo/server/Charge.d.ts +28 -11
  132. package/dist/tempo/server/Charge.js +101 -55
  133. package/dist/tempo/server/Methods.d.ts +58 -56
  134. package/dist/tempo/server/Methods.js +8 -18
  135. package/dist/tempo/server/Relay.d.ts +1 -1
  136. package/dist/tempo/server/Relay.js +2 -2
  137. package/dist/tempo/server/Subscription.d.ts +25 -25
  138. package/dist/tempo/server/Subscription.js +2 -0
  139. package/dist/tempo/server/index.d.ts +1 -1
  140. package/dist/tempo/server/index.js +1 -1
  141. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  142. package/dist/tempo/server/internal/html.gen.js +1 -1
  143. package/dist/tempo/server/internal/transport.d.ts +2 -0
  144. package/dist/tempo/server/internal/transport.js +9 -2
  145. package/dist/tempo/session/client/ChannelOps.d.ts +18 -3
  146. package/dist/tempo/session/client/ChannelOps.js +35 -6
  147. package/dist/tempo/session/client/ChannelStore.d.ts +0 -5
  148. package/dist/tempo/session/client/ChannelStore.js +2 -2
  149. package/dist/tempo/session/client/CredentialState.d.ts +18 -3
  150. package/dist/tempo/session/client/CredentialState.js +77 -76
  151. package/dist/tempo/session/client/Runtime.d.ts +4 -2
  152. package/dist/tempo/session/client/Runtime.js +21 -5
  153. package/dist/tempo/session/client/Session.d.ts +34 -22
  154. package/dist/tempo/session/client/Session.js +186 -63
  155. package/dist/tempo/session/client/SessionManager.d.ts +3 -1
  156. package/dist/tempo/session/client/SessionManager.js +35 -11
  157. package/dist/tempo/session/client/Transports.d.ts +2 -0
  158. package/dist/tempo/session/client/Transports.js +12 -2
  159. package/dist/tempo/session/precompile/Chain.d.ts +37 -2
  160. package/dist/tempo/session/precompile/Chain.js +116 -15
  161. package/dist/tempo/session/precompile/Protocol.d.ts +15 -0
  162. package/dist/tempo/session/precompile/Protocol.js +7 -0
  163. package/dist/tempo/session/precompile/Voucher.d.ts +7 -5
  164. package/dist/tempo/session/precompile/Voucher.js +6 -12
  165. package/dist/tempo/session/precompile/escrow.abi.d.ts +306 -306
  166. package/dist/tempo/session/server/ChannelStore.d.ts +15 -0
  167. package/dist/tempo/session/server/ChannelStore.js +27 -12
  168. package/dist/tempo/session/server/CredentialVerification.d.ts +39 -20
  169. package/dist/tempo/session/server/CredentialVerification.js +359 -89
  170. package/dist/tempo/session/server/MeteredStream.d.ts +4 -1
  171. package/dist/tempo/session/server/MeteredStream.js +8 -5
  172. package/dist/tempo/session/server/RequestState.d.ts +19 -0
  173. package/dist/tempo/session/server/RequestState.js +61 -15
  174. package/dist/tempo/session/server/Session.d.ts +46 -23
  175. package/dist/tempo/session/server/Session.js +150 -56
  176. package/dist/tempo/session/server/Settlement.d.ts +11 -4
  177. package/dist/tempo/session/server/Settlement.js +53 -21
  178. package/dist/tempo/session/server/Sse.d.ts +3 -1
  179. package/dist/tempo/session/server/Sse.js +2 -1
  180. package/dist/tempo/session/server/Transports.d.ts +1 -1
  181. package/dist/tempo/session/server/Transports.js +1 -0
  182. package/dist/tempo/session/server/Ws.d.ts +6 -1
  183. package/dist/tempo/session/server/Ws.js +1 -0
  184. package/dist/tempo/subscription/KeyAuthorization.d.ts +39 -39
  185. package/dist/web-bot-auth/Constants.d.ts +20 -0
  186. package/dist/web-bot-auth/Constants.js +24 -0
  187. package/dist/web-bot-auth/Types.d.ts +10 -0
  188. package/dist/web-bot-auth/Types.js +2 -0
  189. package/dist/web-bot-auth/client/index.d.ts +29 -0
  190. package/dist/web-bot-auth/client/index.js +64 -0
  191. package/dist/web-bot-auth/index.d.ts +7 -0
  192. package/dist/web-bot-auth/index.js +7 -0
  193. package/dist/web-bot-auth/internal/JwkThumbprint.d.ts +5 -0
  194. package/dist/web-bot-auth/internal/JwkThumbprint.js +20 -0
  195. package/dist/web-bot-auth/internal/SignatureAgent.d.ts +3 -0
  196. package/dist/web-bot-auth/internal/SignatureAgent.js +18 -0
  197. package/dist/web-bot-auth/server/index.d.ts +35 -0
  198. package/dist/web-bot-auth/server/index.js +88 -0
  199. package/dist/x402/Types.d.ts +6 -6
  200. package/dist/x402/client/Exact.d.ts +8 -0
  201. package/dist/x402/client/Exact.js +40 -10
  202. package/dist/x402/server/EvmCharge.d.ts +22 -0
  203. package/dist/x402/server/EvmCharge.js +54 -20
  204. package/package.json +28 -34
  205. package/dist/BodyDigest.d.ts.map +0 -1
  206. package/dist/BodyDigest.js.map +0 -1
  207. package/dist/Challenge.d.ts.map +0 -1
  208. package/dist/Challenge.js.map +0 -1
  209. package/dist/Constants.d.ts.map +0 -1
  210. package/dist/Constants.js.map +0 -1
  211. package/dist/Credential.d.ts.map +0 -1
  212. package/dist/Credential.js.map +0 -1
  213. package/dist/Errors.d.ts.map +0 -1
  214. package/dist/Errors.js.map +0 -1
  215. package/dist/Expires.d.ts.map +0 -1
  216. package/dist/Expires.js.map +0 -1
  217. package/dist/Html.d.ts.map +0 -1
  218. package/dist/Html.js.map +0 -1
  219. package/dist/Mcp.d.ts.map +0 -1
  220. package/dist/Mcp.js.map +0 -1
  221. package/dist/Method.d.ts.map +0 -1
  222. package/dist/Method.js.map +0 -1
  223. package/dist/PaymentRequest.d.ts.map +0 -1
  224. package/dist/PaymentRequest.js.map +0 -1
  225. package/dist/Receipt.d.ts.map +0 -1
  226. package/dist/Receipt.js.map +0 -1
  227. package/dist/Store.d.ts.map +0 -1
  228. package/dist/Store.js.map +0 -1
  229. package/dist/bin.d.ts.map +0 -1
  230. package/dist/bin.js.map +0 -1
  231. package/dist/cli/account.d.ts.map +0 -1
  232. package/dist/cli/account.js.map +0 -1
  233. package/dist/cli/cli.d.ts.map +0 -1
  234. package/dist/cli/cli.js.map +0 -1
  235. package/dist/cli/config.d.ts.map +0 -1
  236. package/dist/cli/config.js.map +0 -1
  237. package/dist/cli/internal.d.ts.map +0 -1
  238. package/dist/cli/internal.js.map +0 -1
  239. package/dist/cli/plugins/evm.d.ts.map +0 -1
  240. package/dist/cli/plugins/evm.js.map +0 -1
  241. package/dist/cli/plugins/index.d.ts.map +0 -1
  242. package/dist/cli/plugins/index.js.map +0 -1
  243. package/dist/cli/plugins/plugin.d.ts.map +0 -1
  244. package/dist/cli/plugins/plugin.js.map +0 -1
  245. package/dist/cli/plugins/stripe.d.ts.map +0 -1
  246. package/dist/cli/plugins/stripe.js.map +0 -1
  247. package/dist/cli/plugins/tempo.d.ts.map +0 -1
  248. package/dist/cli/plugins/tempo.js.map +0 -1
  249. package/dist/cli/sessions/Manager.d.ts.map +0 -1
  250. package/dist/cli/sessions/Manager.js.map +0 -1
  251. package/dist/cli/sessions/commands.d.ts.map +0 -1
  252. package/dist/cli/sessions/commands.js.map +0 -1
  253. package/dist/cli/sessions/request.d.ts.map +0 -1
  254. package/dist/cli/sessions/request.js.map +0 -1
  255. package/dist/cli/sessions/store.d.ts.map +0 -1
  256. package/dist/cli/sessions/store.js.map +0 -1
  257. package/dist/cli/utils.d.ts.map +0 -1
  258. package/dist/cli/utils.js.map +0 -1
  259. package/dist/cli/validate/challenge.d.ts.map +0 -1
  260. package/dist/cli/validate/challenge.js.map +0 -1
  261. package/dist/cli/validate/discovery.d.ts.map +0 -1
  262. package/dist/cli/validate/discovery.js.map +0 -1
  263. package/dist/cli/validate/helpers.d.ts.map +0 -1
  264. package/dist/cli/validate/helpers.js.map +0 -1
  265. package/dist/cli/validate/index.d.ts.map +0 -1
  266. package/dist/cli/validate/index.js.map +0 -1
  267. package/dist/cli/validate/messages.d.ts.map +0 -1
  268. package/dist/cli/validate/messages.js.map +0 -1
  269. package/dist/cli/validate/payment.d.ts.map +0 -1
  270. package/dist/cli/validate/payment.js.map +0 -1
  271. package/dist/client/Methods.d.ts.map +0 -1
  272. package/dist/client/Methods.js.map +0 -1
  273. package/dist/client/Mppx.d.ts.map +0 -1
  274. package/dist/client/Mppx.js.map +0 -1
  275. package/dist/client/Transport.d.ts.map +0 -1
  276. package/dist/client/Transport.js.map +0 -1
  277. package/dist/client/index.d.ts.map +0 -1
  278. package/dist/client/index.js.map +0 -1
  279. package/dist/client/internal/Fetch.d.ts.map +0 -1
  280. package/dist/client/internal/Fetch.js.map +0 -1
  281. package/dist/client/internal/MethodChallenge.d.ts.map +0 -1
  282. package/dist/client/internal/MethodChallenge.js.map +0 -1
  283. package/dist/client/internal/MethodResponse.d.ts.map +0 -1
  284. package/dist/client/internal/MethodResponse.js.map +0 -1
  285. package/dist/client/internal/protocols/Mcp.d.ts.map +0 -1
  286. package/dist/client/internal/protocols/Mcp.js.map +0 -1
  287. package/dist/client/internal/protocols/Mpp.d.ts.map +0 -1
  288. package/dist/client/internal/protocols/Mpp.js.map +0 -1
  289. package/dist/client/internal/protocols/Protocol.d.ts.map +0 -1
  290. package/dist/client/internal/protocols/Protocol.js.map +0 -1
  291. package/dist/client/internal/protocols/Shared.d.ts.map +0 -1
  292. package/dist/client/internal/protocols/Shared.js.map +0 -1
  293. package/dist/client/internal/protocols/X402.d.ts.map +0 -1
  294. package/dist/client/internal/protocols/X402.js.map +0 -1
  295. package/dist/client/node.d.ts.map +0 -1
  296. package/dist/client/node.js.map +0 -1
  297. package/dist/discovery/Discovery.d.ts.map +0 -1
  298. package/dist/discovery/Discovery.js.map +0 -1
  299. package/dist/discovery/OpenApi.d.ts.map +0 -1
  300. package/dist/discovery/OpenApi.js.map +0 -1
  301. package/dist/discovery/Validate.d.ts.map +0 -1
  302. package/dist/discovery/Validate.js.map +0 -1
  303. package/dist/discovery/index.d.ts.map +0 -1
  304. package/dist/discovery/index.js.map +0 -1
  305. package/dist/evm/Assets.d.ts.map +0 -1
  306. package/dist/evm/Assets.js.map +0 -1
  307. package/dist/evm/Chains.d.ts.map +0 -1
  308. package/dist/evm/Chains.js.map +0 -1
  309. package/dist/evm/Methods.d.ts.map +0 -1
  310. package/dist/evm/Methods.js.map +0 -1
  311. package/dist/evm/Types.d.ts.map +0 -1
  312. package/dist/evm/Types.js.map +0 -1
  313. package/dist/evm/client/Charge.d.ts.map +0 -1
  314. package/dist/evm/client/Charge.js.map +0 -1
  315. package/dist/evm/client/Methods.d.ts.map +0 -1
  316. package/dist/evm/client/Methods.js.map +0 -1
  317. package/dist/evm/client/index.d.ts.map +0 -1
  318. package/dist/evm/client/index.js.map +0 -1
  319. package/dist/evm/index.d.ts.map +0 -1
  320. package/dist/evm/index.js.map +0 -1
  321. package/dist/evm/server/Charge.d.ts.map +0 -1
  322. package/dist/evm/server/Charge.js.map +0 -1
  323. package/dist/evm/server/Methods.d.ts.map +0 -1
  324. package/dist/evm/server/Methods.js.map +0 -1
  325. package/dist/evm/server/index.d.ts.map +0 -1
  326. package/dist/evm/server/index.js.map +0 -1
  327. package/dist/index.d.ts.map +0 -1
  328. package/dist/index.js.map +0 -1
  329. package/dist/internal/AcceptPayment.d.ts.map +0 -1
  330. package/dist/internal/AcceptPayment.js.map +0 -1
  331. package/dist/internal/HeaderCodec.d.ts.map +0 -1
  332. package/dist/internal/HeaderCodec.js.map +0 -1
  333. package/dist/internal/constantTimeEqual.d.ts.map +0 -1
  334. package/dist/internal/constantTimeEqual.js.map +0 -1
  335. package/dist/internal/env.d.ts.map +0 -1
  336. package/dist/internal/env.js.map +0 -1
  337. package/dist/internal/types.d.ts.map +0 -1
  338. package/dist/internal/types.js.map +0 -1
  339. package/dist/mcp/client/McpClient.d.ts.map +0 -1
  340. package/dist/mcp/client/McpClient.js.map +0 -1
  341. package/dist/mcp/client/index.d.ts.map +0 -1
  342. package/dist/mcp/client/index.js.map +0 -1
  343. package/dist/mcp/server/Transport.d.ts.map +0 -1
  344. package/dist/mcp/server/Transport.js.map +0 -1
  345. package/dist/mcp/server/index.d.ts.map +0 -1
  346. package/dist/mcp/server/index.js.map +0 -1
  347. package/dist/middlewares/elysia.d.ts.map +0 -1
  348. package/dist/middlewares/elysia.js.map +0 -1
  349. package/dist/middlewares/express.d.ts.map +0 -1
  350. package/dist/middlewares/express.js.map +0 -1
  351. package/dist/middlewares/hono.d.ts.map +0 -1
  352. package/dist/middlewares/hono.js.map +0 -1
  353. package/dist/middlewares/internal/mppx.d.ts.map +0 -1
  354. package/dist/middlewares/internal/mppx.js.map +0 -1
  355. package/dist/middlewares/nextjs.d.ts.map +0 -1
  356. package/dist/middlewares/nextjs.js.map +0 -1
  357. package/dist/proxy/Proxy.d.ts.map +0 -1
  358. package/dist/proxy/Proxy.js.map +0 -1
  359. package/dist/proxy/Service.d.ts.map +0 -1
  360. package/dist/proxy/Service.js.map +0 -1
  361. package/dist/proxy/index.d.ts.map +0 -1
  362. package/dist/proxy/index.js.map +0 -1
  363. package/dist/proxy/internal/Headers.d.ts.map +0 -1
  364. package/dist/proxy/internal/Headers.js.map +0 -1
  365. package/dist/proxy/internal/Route.d.ts.map +0 -1
  366. package/dist/proxy/internal/Route.js.map +0 -1
  367. package/dist/proxy/services/anthropic.d.ts.map +0 -1
  368. package/dist/proxy/services/anthropic.js.map +0 -1
  369. package/dist/proxy/services/openai.d.ts.map +0 -1
  370. package/dist/proxy/services/openai.js.map +0 -1
  371. package/dist/proxy/services/stripe.d.ts.map +0 -1
  372. package/dist/proxy/services/stripe.js.map +0 -1
  373. package/dist/server/Methods.d.ts.map +0 -1
  374. package/dist/server/Methods.js.map +0 -1
  375. package/dist/server/Mppx.d.ts.map +0 -1
  376. package/dist/server/Mppx.js.map +0 -1
  377. package/dist/server/NodeListener.d.ts.map +0 -1
  378. package/dist/server/NodeListener.js.map +0 -1
  379. package/dist/server/Request.d.ts.map +0 -1
  380. package/dist/server/Request.js.map +0 -1
  381. package/dist/server/Response.d.ts.map +0 -1
  382. package/dist/server/Response.js.map +0 -1
  383. package/dist/server/Transport.d.ts.map +0 -1
  384. package/dist/server/Transport.js.map +0 -1
  385. package/dist/server/index.d.ts.map +0 -1
  386. package/dist/server/index.js.map +0 -1
  387. package/dist/server/internal/html/compose.main.gen.d.ts.map +0 -1
  388. package/dist/server/internal/html/compose.main.gen.js.map +0 -1
  389. package/dist/server/internal/html/config.d.ts.map +0 -1
  390. package/dist/server/internal/html/config.js.map +0 -1
  391. package/dist/server/internal/html/constants.d.ts.map +0 -1
  392. package/dist/server/internal/html/constants.js.map +0 -1
  393. package/dist/server/internal/html/serviceWorker.client.d.ts.map +0 -1
  394. package/dist/server/internal/html/serviceWorker.client.js.map +0 -1
  395. package/dist/server/internal/html/serviceWorker.gen.d.ts.map +0 -1
  396. package/dist/server/internal/html/serviceWorker.gen.js.map +0 -1
  397. package/dist/server/internal/scope.d.ts.map +0 -1
  398. package/dist/server/internal/scope.js.map +0 -1
  399. package/dist/stripe/Methods.d.ts.map +0 -1
  400. package/dist/stripe/Methods.js.map +0 -1
  401. package/dist/stripe/client/Charge.d.ts.map +0 -1
  402. package/dist/stripe/client/Charge.js.map +0 -1
  403. package/dist/stripe/client/Methods.d.ts.map +0 -1
  404. package/dist/stripe/client/Methods.js.map +0 -1
  405. package/dist/stripe/client/index.d.ts.map +0 -1
  406. package/dist/stripe/client/index.js.map +0 -1
  407. package/dist/stripe/index.d.ts.map +0 -1
  408. package/dist/stripe/index.js.map +0 -1
  409. package/dist/stripe/internal/constants.d.ts.map +0 -1
  410. package/dist/stripe/internal/constants.js.map +0 -1
  411. package/dist/stripe/internal/types.d.ts.map +0 -1
  412. package/dist/stripe/internal/types.js.map +0 -1
  413. package/dist/stripe/server/Charge.d.ts.map +0 -1
  414. package/dist/stripe/server/Charge.js.map +0 -1
  415. package/dist/stripe/server/Methods.d.ts.map +0 -1
  416. package/dist/stripe/server/Methods.js.map +0 -1
  417. package/dist/stripe/server/index.d.ts.map +0 -1
  418. package/dist/stripe/server/index.js.map +0 -1
  419. package/dist/stripe/server/internal/html/types.d.ts.map +0 -1
  420. package/dist/stripe/server/internal/html/types.js.map +0 -1
  421. package/dist/stripe/server/internal/html.gen.d.ts.map +0 -1
  422. package/dist/stripe/server/internal/html.gen.js.map +0 -1
  423. package/dist/tempo/Attribution.d.ts.map +0 -1
  424. package/dist/tempo/Attribution.js.map +0 -1
  425. package/dist/tempo/Methods.d.ts.map +0 -1
  426. package/dist/tempo/Methods.js.map +0 -1
  427. package/dist/tempo/Proof.d.ts.map +0 -1
  428. package/dist/tempo/Proof.js.map +0 -1
  429. package/dist/tempo/client/Charge.d.ts.map +0 -1
  430. package/dist/tempo/client/Charge.js.map +0 -1
  431. package/dist/tempo/client/Methods.d.ts.map +0 -1
  432. package/dist/tempo/client/Methods.js.map +0 -1
  433. package/dist/tempo/client/ResolveAccount.d.ts.map +0 -1
  434. package/dist/tempo/client/ResolveAccount.js.map +0 -1
  435. package/dist/tempo/client/Subscription.d.ts.map +0 -1
  436. package/dist/tempo/client/Subscription.js.map +0 -1
  437. package/dist/tempo/client/index.d.ts.map +0 -1
  438. package/dist/tempo/client/index.js.map +0 -1
  439. package/dist/tempo/index.d.ts.map +0 -1
  440. package/dist/tempo/index.js.map +0 -1
  441. package/dist/tempo/internal/account.d.ts.map +0 -1
  442. package/dist/tempo/internal/account.js.map +0 -1
  443. package/dist/tempo/internal/address.d.ts.map +0 -1
  444. package/dist/tempo/internal/address.js.map +0 -1
  445. package/dist/tempo/internal/auto-swap.d.ts.map +0 -1
  446. package/dist/tempo/internal/auto-swap.js.map +0 -1
  447. package/dist/tempo/internal/charge.d.ts.map +0 -1
  448. package/dist/tempo/internal/charge.js.map +0 -1
  449. package/dist/tempo/internal/defaults.d.ts.map +0 -1
  450. package/dist/tempo/internal/defaults.js.map +0 -1
  451. package/dist/tempo/internal/fee-payer.d.ts.map +0 -1
  452. package/dist/tempo/internal/fee-payer.js.map +0 -1
  453. package/dist/tempo/internal/fee-token.d.ts.map +0 -1
  454. package/dist/tempo/internal/fee-token.js.map +0 -1
  455. package/dist/tempo/internal/proof.d.ts.map +0 -1
  456. package/dist/tempo/internal/proof.js.map +0 -1
  457. package/dist/tempo/internal/selectors.d.ts.map +0 -1
  458. package/dist/tempo/internal/selectors.js.map +0 -1
  459. package/dist/tempo/internal/types.d.ts.map +0 -1
  460. package/dist/tempo/internal/types.js.map +0 -1
  461. package/dist/tempo/legacy/client/ChannelOps.d.ts.map +0 -1
  462. package/dist/tempo/legacy/client/ChannelOps.js.map +0 -1
  463. package/dist/tempo/legacy/client/Session.d.ts.map +0 -1
  464. package/dist/tempo/legacy/client/Session.js.map +0 -1
  465. package/dist/tempo/legacy/client/SessionManager.d.ts.map +0 -1
  466. package/dist/tempo/legacy/client/SessionManager.js.map +0 -1
  467. package/dist/tempo/legacy/client/index.d.ts.map +0 -1
  468. package/dist/tempo/legacy/client/index.js.map +0 -1
  469. package/dist/tempo/legacy/index.d.ts.map +0 -1
  470. package/dist/tempo/legacy/index.js.map +0 -1
  471. package/dist/tempo/legacy/server/Session.d.ts +0 -208
  472. package/dist/tempo/legacy/server/Session.d.ts.map +0 -1
  473. package/dist/tempo/legacy/server/Session.js +0 -698
  474. package/dist/tempo/legacy/server/Session.js.map +0 -1
  475. package/dist/tempo/legacy/server/index.d.ts +0 -5
  476. package/dist/tempo/legacy/server/index.d.ts.map +0 -1
  477. package/dist/tempo/legacy/server/index.js +0 -5
  478. package/dist/tempo/legacy/server/index.js.map +0 -1
  479. package/dist/tempo/legacy/session/Chain.d.ts.map +0 -1
  480. package/dist/tempo/legacy/session/Chain.js.map +0 -1
  481. package/dist/tempo/legacy/session/Channel.d.ts.map +0 -1
  482. package/dist/tempo/legacy/session/Channel.js.map +0 -1
  483. package/dist/tempo/legacy/session/ChannelStore.d.ts +0 -22
  484. package/dist/tempo/legacy/session/ChannelStore.d.ts.map +0 -1
  485. package/dist/tempo/legacy/session/ChannelStore.js +0 -6
  486. package/dist/tempo/legacy/session/ChannelStore.js.map +0 -1
  487. package/dist/tempo/legacy/session/Types.d.ts.map +0 -1
  488. package/dist/tempo/legacy/session/Types.js.map +0 -1
  489. package/dist/tempo/legacy/session/Voucher.d.ts.map +0 -1
  490. package/dist/tempo/legacy/session/Voucher.js.map +0 -1
  491. package/dist/tempo/legacy/session/escrow.abi.d.ts.map +0 -1
  492. package/dist/tempo/legacy/session/escrow.abi.js.map +0 -1
  493. package/dist/tempo/legacy/session/index.d.ts.map +0 -1
  494. package/dist/tempo/legacy/session/index.js.map +0 -1
  495. package/dist/tempo/server/Charge.d.ts.map +0 -1
  496. package/dist/tempo/server/Charge.js.map +0 -1
  497. package/dist/tempo/server/Methods.d.ts.map +0 -1
  498. package/dist/tempo/server/Methods.js.map +0 -1
  499. package/dist/tempo/server/Relay.d.ts.map +0 -1
  500. package/dist/tempo/server/Relay.js.map +0 -1
  501. package/dist/tempo/server/SponsorBudget.d.ts.map +0 -1
  502. package/dist/tempo/server/SponsorBudget.js.map +0 -1
  503. package/dist/tempo/server/Subscription.d.ts.map +0 -1
  504. package/dist/tempo/server/Subscription.js.map +0 -1
  505. package/dist/tempo/server/index.d.ts.map +0 -1
  506. package/dist/tempo/server/index.js.map +0 -1
  507. package/dist/tempo/server/internal/html.gen.d.ts.map +0 -1
  508. package/dist/tempo/server/internal/html.gen.js.map +0 -1
  509. package/dist/tempo/server/internal/request-body.d.ts.map +0 -1
  510. package/dist/tempo/server/internal/request-body.js.map +0 -1
  511. package/dist/tempo/server/internal/transport.d.ts.map +0 -1
  512. package/dist/tempo/server/internal/transport.js.map +0 -1
  513. package/dist/tempo/session/Snapshot.d.ts.map +0 -1
  514. package/dist/tempo/session/Snapshot.js.map +0 -1
  515. package/dist/tempo/session/client/ChannelOps.d.ts.map +0 -1
  516. package/dist/tempo/session/client/ChannelOps.js.map +0 -1
  517. package/dist/tempo/session/client/ChannelStore.d.ts.map +0 -1
  518. package/dist/tempo/session/client/ChannelStore.js.map +0 -1
  519. package/dist/tempo/session/client/CredentialState.d.ts.map +0 -1
  520. package/dist/tempo/session/client/CredentialState.js.map +0 -1
  521. package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +0 -1
  522. package/dist/tempo/session/client/ReceiptCoordinator.js.map +0 -1
  523. package/dist/tempo/session/client/Runtime.d.ts.map +0 -1
  524. package/dist/tempo/session/client/Runtime.js.map +0 -1
  525. package/dist/tempo/session/client/Session.d.ts.map +0 -1
  526. package/dist/tempo/session/client/Session.js.map +0 -1
  527. package/dist/tempo/session/client/SessionManager.d.ts.map +0 -1
  528. package/dist/tempo/session/client/SessionManager.js.map +0 -1
  529. package/dist/tempo/session/client/Transports.d.ts.map +0 -1
  530. package/dist/tempo/session/client/Transports.js.map +0 -1
  531. package/dist/tempo/session/client/index.d.ts.map +0 -1
  532. package/dist/tempo/session/client/index.js.map +0 -1
  533. package/dist/tempo/session/client/internal/SessionManager.d.ts.map +0 -1
  534. package/dist/tempo/session/client/internal/SessionManager.js.map +0 -1
  535. package/dist/tempo/session/index.d.ts.map +0 -1
  536. package/dist/tempo/session/index.js.map +0 -1
  537. package/dist/tempo/session/precompile/Chain.d.ts.map +0 -1
  538. package/dist/tempo/session/precompile/Chain.js.map +0 -1
  539. package/dist/tempo/session/precompile/Channel.d.ts.map +0 -1
  540. package/dist/tempo/session/precompile/Channel.js.map +0 -1
  541. package/dist/tempo/session/precompile/Protocol.d.ts.map +0 -1
  542. package/dist/tempo/session/precompile/Protocol.js.map +0 -1
  543. package/dist/tempo/session/precompile/Voucher.d.ts.map +0 -1
  544. package/dist/tempo/session/precompile/Voucher.js.map +0 -1
  545. package/dist/tempo/session/precompile/escrow.abi.d.ts.map +0 -1
  546. package/dist/tempo/session/precompile/escrow.abi.js.map +0 -1
  547. package/dist/tempo/session/precompile/index.d.ts.map +0 -1
  548. package/dist/tempo/session/precompile/index.js.map +0 -1
  549. package/dist/tempo/session/server/ChannelOps.d.ts.map +0 -1
  550. package/dist/tempo/session/server/ChannelOps.js.map +0 -1
  551. package/dist/tempo/session/server/ChannelStore.d.ts.map +0 -1
  552. package/dist/tempo/session/server/ChannelStore.js.map +0 -1
  553. package/dist/tempo/session/server/CredentialVerification.d.ts.map +0 -1
  554. package/dist/tempo/session/server/CredentialVerification.js.map +0 -1
  555. package/dist/tempo/session/server/MeteredStream.d.ts.map +0 -1
  556. package/dist/tempo/session/server/MeteredStream.js.map +0 -1
  557. package/dist/tempo/session/server/RequestState.d.ts.map +0 -1
  558. package/dist/tempo/session/server/RequestState.js.map +0 -1
  559. package/dist/tempo/session/server/Session.d.ts.map +0 -1
  560. package/dist/tempo/session/server/Session.js.map +0 -1
  561. package/dist/tempo/session/server/Settlement.d.ts.map +0 -1
  562. package/dist/tempo/session/server/Settlement.js.map +0 -1
  563. package/dist/tempo/session/server/Sse.d.ts.map +0 -1
  564. package/dist/tempo/session/server/Sse.js.map +0 -1
  565. package/dist/tempo/session/server/Transports.d.ts.map +0 -1
  566. package/dist/tempo/session/server/Transports.js.map +0 -1
  567. package/dist/tempo/session/server/Ws.d.ts.map +0 -1
  568. package/dist/tempo/session/server/Ws.js.map +0 -1
  569. package/dist/tempo/session/server/index.d.ts.map +0 -1
  570. package/dist/tempo/session/server/index.js.map +0 -1
  571. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +0 -1
  572. package/dist/tempo/subscription/KeyAuthorization.js.map +0 -1
  573. package/dist/tempo/subscription/Receipt.d.ts.map +0 -1
  574. package/dist/tempo/subscription/Receipt.js.map +0 -1
  575. package/dist/tempo/subscription/Store.d.ts.map +0 -1
  576. package/dist/tempo/subscription/Store.js.map +0 -1
  577. package/dist/tempo/subscription/Types.d.ts.map +0 -1
  578. package/dist/tempo/subscription/Types.js.map +0 -1
  579. package/dist/tempo/subscription/index.d.ts.map +0 -1
  580. package/dist/tempo/subscription/index.js.map +0 -1
  581. package/dist/validation/core.d.ts.map +0 -1
  582. package/dist/validation/core.js.map +0 -1
  583. package/dist/validation/index.d.ts.map +0 -1
  584. package/dist/validation/index.js.map +0 -1
  585. package/dist/viem/Account.d.ts.map +0 -1
  586. package/dist/viem/Account.js.map +0 -1
  587. package/dist/viem/Client.d.ts.map +0 -1
  588. package/dist/viem/Client.js.map +0 -1
  589. package/dist/x402/Assets.d.ts.map +0 -1
  590. package/dist/x402/Assets.js.map +0 -1
  591. package/dist/x402/Header.d.ts.map +0 -1
  592. package/dist/x402/Header.js.map +0 -1
  593. package/dist/x402/Types.d.ts.map +0 -1
  594. package/dist/x402/Types.js.map +0 -1
  595. package/dist/x402/client/Exact.d.ts.map +0 -1
  596. package/dist/x402/client/Exact.js.map +0 -1
  597. package/dist/x402/index.d.ts.map +0 -1
  598. package/dist/x402/index.js.map +0 -1
  599. package/dist/x402/internal/ChallengeBrand.d.ts.map +0 -1
  600. package/dist/x402/internal/ChallengeBrand.js.map +0 -1
  601. package/dist/x402/internal/RouteBinding.d.ts.map +0 -1
  602. package/dist/x402/internal/RouteBinding.js.map +0 -1
  603. package/dist/x402/server/EvmCharge.d.ts.map +0 -1
  604. package/dist/x402/server/EvmCharge.js.map +0 -1
  605. package/dist/x402/server/Facilitator.d.ts.map +0 -1
  606. package/dist/x402/server/Facilitator.js.map +0 -1
  607. package/dist/zod.d.ts.map +0 -1
  608. package/dist/zod.js.map +0 -1
  609. package/src/BodyDigest.test.ts +0 -43
  610. package/src/BodyDigest.ts +0 -55
  611. package/src/Challenge.fuzz.test.ts +0 -121
  612. package/src/Challenge.test-d.ts +0 -82
  613. package/src/Challenge.test.ts +0 -995
  614. package/src/Challenge.ts +0 -683
  615. package/src/Constants.ts +0 -58
  616. package/src/Credential.fuzz.test.ts +0 -62
  617. package/src/Credential.test.ts +0 -378
  618. package/src/Credential.ts +0 -217
  619. package/src/Errors.test.ts +0 -520
  620. package/src/Errors.ts +0 -448
  621. package/src/Expires.test.ts +0 -112
  622. package/src/Expires.ts +0 -59
  623. package/src/Html.ts +0 -67
  624. package/src/Mcp.ts +0 -85
  625. package/src/Method.test.ts +0 -177
  626. package/src/Method.ts +0 -617
  627. package/src/PaymentRequest.test.ts +0 -147
  628. package/src/PaymentRequest.ts +0 -108
  629. package/src/Receipt.test.ts +0 -151
  630. package/src/Receipt.ts +0 -146
  631. package/src/Store.test-d.ts +0 -195
  632. package/src/Store.test.ts +0 -357
  633. package/src/Store.ts +0 -329
  634. package/src/bin.ts +0 -4
  635. package/src/cli/account.ts +0 -269
  636. package/src/cli/cli.test.ts +0 -2221
  637. package/src/cli/cli.ts +0 -1649
  638. package/src/cli/config.test.ts +0 -89
  639. package/src/cli/config.ts +0 -50
  640. package/src/cli/internal.ts +0 -114
  641. package/src/cli/mcp.test.ts +0 -262
  642. package/src/cli/plugins/evm.ts +0 -55
  643. package/src/cli/plugins/index.ts +0 -4
  644. package/src/cli/plugins/plugin.ts +0 -84
  645. package/src/cli/plugins/stripe.ts +0 -174
  646. package/src/cli/plugins/tempo.ts +0 -1102
  647. package/src/cli/sessions/Manager.test.ts +0 -249
  648. package/src/cli/sessions/Manager.ts +0 -93
  649. package/src/cli/sessions/commands.ts +0 -444
  650. package/src/cli/sessions/request.test.ts +0 -51
  651. package/src/cli/sessions/request.ts +0 -353
  652. package/src/cli/sessions/store.test.ts +0 -581
  653. package/src/cli/sessions/store.ts +0 -940
  654. package/src/cli/utils.test.ts +0 -156
  655. package/src/cli/utils.ts +0 -394
  656. package/src/cli/validate/challenge.ts +0 -565
  657. package/src/cli/validate/discovery.test.ts +0 -435
  658. package/src/cli/validate/discovery.ts +0 -221
  659. package/src/cli/validate/helpers.ts +0 -166
  660. package/src/cli/validate/index.ts +0 -229
  661. package/src/cli/validate/messages.ts +0 -7
  662. package/src/cli/validate/payment.ts +0 -774
  663. package/src/cli/validate.test.ts +0 -843
  664. package/src/client/Methods.ts +0 -8
  665. package/src/client/Mppx.test-d.ts +0 -270
  666. package/src/client/Mppx.test.ts +0 -1065
  667. package/src/client/Mppx.ts +0 -466
  668. package/src/client/Transport.test.ts +0 -619
  669. package/src/client/Transport.ts +0 -191
  670. package/src/client/index.ts +0 -29
  671. package/src/client/internal/Fetch.browser.test.ts +0 -197
  672. package/src/client/internal/Fetch.test-d.ts +0 -109
  673. package/src/client/internal/Fetch.test.ts +0 -2336
  674. package/src/client/internal/Fetch.ts +0 -1079
  675. package/src/client/internal/MethodChallenge.ts +0 -35
  676. package/src/client/internal/MethodResponse.ts +0 -67
  677. package/src/client/internal/protocols/Mcp.test.ts +0 -220
  678. package/src/client/internal/protocols/Mcp.ts +0 -186
  679. package/src/client/internal/protocols/Mpp.ts +0 -21
  680. package/src/client/internal/protocols/Protocol.ts +0 -10
  681. package/src/client/internal/protocols/Shared.ts +0 -25
  682. package/src/client/internal/protocols/X402.ts +0 -46
  683. package/src/client/node.test.ts +0 -115
  684. package/src/client/node.ts +0 -247
  685. package/src/discovery/Discovery.test.ts +0 -214
  686. package/src/discovery/Discovery.ts +0 -105
  687. package/src/discovery/OpenApi.test.ts +0 -453
  688. package/src/discovery/OpenApi.ts +0 -224
  689. package/src/discovery/Validate.test.ts +0 -305
  690. package/src/discovery/Validate.ts +0 -77
  691. package/src/discovery/index.ts +0 -3
  692. package/src/env.d.ts +0 -12
  693. package/src/evm/Assets.ts +0 -1
  694. package/src/evm/Chains.ts +0 -11
  695. package/src/evm/Methods.ts +0 -44
  696. package/src/evm/PublicInterface.test-d.ts +0 -134
  697. package/src/evm/Types.ts +0 -140
  698. package/src/evm/client/Charge.test.ts +0 -347
  699. package/src/evm/client/Charge.ts +0 -187
  700. package/src/evm/client/Methods.ts +0 -19
  701. package/src/evm/client/index.ts +0 -6
  702. package/src/evm/index.ts +0 -16
  703. package/src/evm/server/Charge.test.ts +0 -339
  704. package/src/evm/server/Charge.ts +0 -290
  705. package/src/evm/server/Methods.ts +0 -22
  706. package/src/evm/server/index.ts +0 -6
  707. package/src/index.ts +0 -14
  708. package/src/internal/AcceptPayment.test.ts +0 -343
  709. package/src/internal/AcceptPayment.ts +0 -366
  710. package/src/internal/HeaderCodec.ts +0 -36
  711. package/src/internal/changeset.test.ts +0 -106
  712. package/src/internal/constantTimeEqual.test.ts +0 -47
  713. package/src/internal/constantTimeEqual.ts +0 -10
  714. package/src/internal/env.test.ts +0 -42
  715. package/src/internal/env.ts +0 -49
  716. package/src/internal/types.test-d.ts +0 -21
  717. package/src/internal/types.ts +0 -470
  718. package/src/mcp/client/McpClient.integration.test.ts +0 -652
  719. package/src/mcp/client/McpClient.test-d.ts +0 -144
  720. package/src/mcp/client/McpClient.test.ts +0 -463
  721. package/src/mcp/client/McpClient.ts +0 -307
  722. package/src/mcp/client/McpClient.unit.test.ts +0 -135
  723. package/src/mcp/client/index.ts +0 -2
  724. package/src/mcp/server/Transport.test.ts +0 -191
  725. package/src/mcp/server/Transport.ts +0 -111
  726. package/src/mcp/server/index.ts +0 -1
  727. package/src/middlewares/elysia.test.ts +0 -295
  728. package/src/middlewares/elysia.ts +0 -116
  729. package/src/middlewares/express.test.ts +0 -371
  730. package/src/middlewares/express.ts +0 -140
  731. package/src/middlewares/hono.test.ts +0 -476
  732. package/src/middlewares/hono.ts +0 -153
  733. package/src/middlewares/internal/mppx.test.ts +0 -177
  734. package/src/middlewares/internal/mppx.ts +0 -67
  735. package/src/middlewares/nextjs.test.ts +0 -369
  736. package/src/middlewares/nextjs.ts +0 -107
  737. package/src/proxy/Proxy.test.ts +0 -1031
  738. package/src/proxy/Proxy.ts +0 -345
  739. package/src/proxy/Service.test.ts +0 -182
  740. package/src/proxy/Service.ts +0 -253
  741. package/src/proxy/index.ts +0 -6
  742. package/src/proxy/internal/Headers.test.ts +0 -139
  743. package/src/proxy/internal/Headers.ts +0 -66
  744. package/src/proxy/internal/Route.test.ts +0 -217
  745. package/src/proxy/internal/Route.ts +0 -64
  746. package/src/proxy/services/anthropic.test.ts +0 -132
  747. package/src/proxy/services/anthropic.ts +0 -52
  748. package/src/proxy/services/openai.test.ts +0 -157
  749. package/src/proxy/services/openai.ts +0 -59
  750. package/src/proxy/services/stripe.test.ts +0 -175
  751. package/src/proxy/services/stripe.ts +0 -58
  752. package/src/server/Methods.ts +0 -3
  753. package/src/server/Mppx.authorize.test.ts +0 -210
  754. package/src/server/Mppx.test-d.ts +0 -360
  755. package/src/server/Mppx.test.ts +0 -5813
  756. package/src/server/Mppx.ts +0 -2623
  757. package/src/server/NodeListener.test.ts +0 -293
  758. package/src/server/NodeListener.ts +0 -45
  759. package/src/server/Request.test.ts +0 -255
  760. package/src/server/Request.ts +0 -155
  761. package/src/server/Response.test.ts +0 -32
  762. package/src/server/Response.ts +0 -45
  763. package/src/server/Transport.test.ts +0 -598
  764. package/src/server/Transport.ts +0 -320
  765. package/src/server/index.ts +0 -9
  766. package/src/server/internal/html/compose.main.gen.ts +0 -2
  767. package/src/server/internal/html/compose.main.ts +0 -88
  768. package/src/server/internal/html/config.ts +0 -672
  769. package/src/server/internal/html/constants.ts +0 -28
  770. package/src/server/internal/html/serviceWorker.client.ts +0 -28
  771. package/src/server/internal/html/serviceWorker.gen.ts +0 -2
  772. package/src/server/internal/html/serviceWorker.ts +0 -27
  773. package/src/server/internal/html/tsconfig.compose.json +0 -8
  774. package/src/server/internal/html/tsconfig.worker.client.json +0 -8
  775. package/src/server/internal/html/tsconfig.worker.json +0 -8
  776. package/src/server/internal/scope.ts +0 -43
  777. package/src/stripe/Charge.integration.test.ts +0 -329
  778. package/src/stripe/Methods.test.ts +0 -62
  779. package/src/stripe/Methods.ts +0 -44
  780. package/src/stripe/client/Charge.test.ts +0 -205
  781. package/src/stripe/client/Charge.ts +0 -132
  782. package/src/stripe/client/Methods.ts +0 -37
  783. package/src/stripe/client/index.ts +0 -2
  784. package/src/stripe/index.ts +0 -1
  785. package/src/stripe/internal/constants.ts +0 -7
  786. package/src/stripe/internal/types.ts +0 -46
  787. package/src/stripe/server/Charge.test-d.ts +0 -66
  788. package/src/stripe/server/Charge.test.ts +0 -621
  789. package/src/stripe/server/Charge.ts +0 -434
  790. package/src/stripe/server/Methods.ts +0 -24
  791. package/src/stripe/server/index.ts +0 -2
  792. package/src/stripe/server/internal/html/main.ts +0 -165
  793. package/src/stripe/server/internal/html/node_modules/.bin/mppx +0 -22
  794. package/src/stripe/server/internal/html/node_modules/.bin/mppx.src +0 -22
  795. package/src/stripe/server/internal/html/package.json +0 -9
  796. package/src/stripe/server/internal/html/stripe-js-pure.d.ts +0 -7
  797. package/src/stripe/server/internal/html/tsconfig.json +0 -8
  798. package/src/stripe/server/internal/html/types.ts +0 -5
  799. package/src/stripe/server/internal/html.gen.ts +0 -2
  800. package/src/tempo/AccessKeyAuthorization.test.ts +0 -141
  801. package/src/tempo/Attribution.test.ts +0 -294
  802. package/src/tempo/Attribution.ts +0 -185
  803. package/src/tempo/Methods.test.ts +0 -425
  804. package/src/tempo/Methods.ts +0 -367
  805. package/src/tempo/Proof.conformance.test.ts +0 -146
  806. package/src/tempo/Proof.test-d.ts +0 -32
  807. package/src/tempo/Proof.test.ts +0 -40
  808. package/src/tempo/Proof.ts +0 -69
  809. package/src/tempo/PublicExports.test-d.ts +0 -130
  810. package/src/tempo/Subscription.integration.test.ts +0 -591
  811. package/src/tempo/client/Charge.test.ts +0 -501
  812. package/src/tempo/client/Charge.ts +0 -289
  813. package/src/tempo/client/Methods.ts +0 -45
  814. package/src/tempo/client/ResolveAccount.ts +0 -46
  815. package/src/tempo/client/Subscription.test.ts +0 -180
  816. package/src/tempo/client/Subscription.ts +0 -156
  817. package/src/tempo/client/index.ts +0 -17
  818. package/src/tempo/index.ts +0 -5
  819. package/src/tempo/internal/account.ts +0 -56
  820. package/src/tempo/internal/address.ts +0 -6
  821. package/src/tempo/internal/auto-swap.test.ts +0 -198
  822. package/src/tempo/internal/auto-swap.ts +0 -188
  823. package/src/tempo/internal/charge.test.ts +0 -66
  824. package/src/tempo/internal/charge.ts +0 -43
  825. package/src/tempo/internal/defaults.test.ts +0 -95
  826. package/src/tempo/internal/defaults.ts +0 -53
  827. package/src/tempo/internal/fee-payer.test.ts +0 -1177
  828. package/src/tempo/internal/fee-payer.ts +0 -808
  829. package/src/tempo/internal/fee-token.test.ts +0 -165
  830. package/src/tempo/internal/fee-token.ts +0 -72
  831. package/src/tempo/internal/proof.test.ts +0 -97
  832. package/src/tempo/internal/proof.ts +0 -87
  833. package/src/tempo/internal/selectors.ts +0 -10
  834. package/src/tempo/internal/types.ts +0 -19
  835. package/src/tempo/legacy/AccessKeyAuthorization.test.ts +0 -162
  836. package/src/tempo/legacy/README.md +0 -9
  837. package/src/tempo/legacy/client/ChannelOps.test.ts +0 -379
  838. package/src/tempo/legacy/client/ChannelOps.ts +0 -271
  839. package/src/tempo/legacy/client/Session.test.ts +0 -800
  840. package/src/tempo/legacy/client/Session.ts +0 -397
  841. package/src/tempo/legacy/client/SessionManager.test.ts +0 -468
  842. package/src/tempo/legacy/client/SessionManager.ts +0 -909
  843. package/src/tempo/legacy/client/index.ts +0 -6
  844. package/src/tempo/legacy/index.ts +0 -6
  845. package/src/tempo/legacy/server/Defaults.test-d.ts +0 -9
  846. package/src/tempo/legacy/server/Session.test.ts +0 -6370
  847. package/src/tempo/legacy/server/Session.ts +0 -1024
  848. package/src/tempo/legacy/server/index.ts +0 -4
  849. package/src/tempo/legacy/session/Chain.test.ts +0 -1349
  850. package/src/tempo/legacy/session/Chain.ts +0 -830
  851. package/src/tempo/legacy/session/Channel.test.ts +0 -109
  852. package/src/tempo/legacy/session/Channel.ts +0 -51
  853. package/src/tempo/legacy/session/ChannelStore.test.ts +0 -58
  854. package/src/tempo/legacy/session/ChannelStore.ts +0 -39
  855. package/src/tempo/legacy/session/Types.ts +0 -91
  856. package/src/tempo/legacy/session/Voucher.test.ts +0 -410
  857. package/src/tempo/legacy/session/Voucher.ts +0 -183
  858. package/src/tempo/legacy/session/escrow.abi.ts +0 -760
  859. package/src/tempo/legacy/session/index.ts +0 -8
  860. package/src/tempo/server/AtomicStore.test-d.ts +0 -50
  861. package/src/tempo/server/Charge.test.ts +0 -6121
  862. package/src/tempo/server/Charge.ts +0 -1351
  863. package/src/tempo/server/Methods.ts +0 -106
  864. package/src/tempo/server/Relay.test.ts +0 -677
  865. package/src/tempo/server/Relay.ts +0 -297
  866. package/src/tempo/server/SponsorBudget.test.ts +0 -154
  867. package/src/tempo/server/SponsorBudget.ts +0 -219
  868. package/src/tempo/server/Sse.test.ts +0 -321
  869. package/src/tempo/server/Subscription.test.ts +0 -1944
  870. package/src/tempo/server/Subscription.ts +0 -1333
  871. package/src/tempo/server/index.ts +0 -12
  872. package/src/tempo/server/internal/html/main.ts +0 -115
  873. package/src/tempo/server/internal/html/node_modules/.bin/mppx +0 -22
  874. package/src/tempo/server/internal/html/node_modules/.bin/mppx.src +0 -22
  875. package/src/tempo/server/internal/html/package.json +0 -10
  876. package/src/tempo/server/internal/html/tsconfig.json +0 -8
  877. package/src/tempo/server/internal/html.gen.ts +0 -2
  878. package/src/tempo/server/internal/request-body.test.ts +0 -210
  879. package/src/tempo/server/internal/request-body.ts +0 -61
  880. package/src/tempo/server/internal/transport.test.ts +0 -903
  881. package/src/tempo/server/internal/transport.ts +0 -310
  882. package/src/tempo/session/README.md +0 -208
  883. package/src/tempo/session/Snapshot.test.ts +0 -41
  884. package/src/tempo/session/Snapshot.ts +0 -95
  885. package/src/tempo/session/client/ChannelOps.test.ts +0 -218
  886. package/src/tempo/session/client/ChannelOps.ts +0 -348
  887. package/src/tempo/session/client/ChannelStore.ts +0 -111
  888. package/src/tempo/session/client/CredentialState.test.ts +0 -789
  889. package/src/tempo/session/client/CredentialState.ts +0 -942
  890. package/src/tempo/session/client/ReceiptCoordinator.ts +0 -95
  891. package/src/tempo/session/client/Runtime.test.ts +0 -1104
  892. package/src/tempo/session/client/Runtime.ts +0 -1008
  893. package/src/tempo/session/client/Session.test.ts +0 -1355
  894. package/src/tempo/session/client/Session.ts +0 -420
  895. package/src/tempo/session/client/SessionManager.test.ts +0 -1899
  896. package/src/tempo/session/client/SessionManager.ts +0 -996
  897. package/src/tempo/session/client/Transports.test.ts +0 -1141
  898. package/src/tempo/session/client/Transports.ts +0 -1534
  899. package/src/tempo/session/client/index.ts +0 -40
  900. package/src/tempo/session/client/internal/SessionManager.ts +0 -36
  901. package/src/tempo/session/index.ts +0 -7
  902. package/src/tempo/session/precompile/Chain.integration.test.ts +0 -339
  903. package/src/tempo/session/precompile/Chain.test.ts +0 -1641
  904. package/src/tempo/session/precompile/Chain.ts +0 -1149
  905. package/src/tempo/session/precompile/Channel.test.ts +0 -147
  906. package/src/tempo/session/precompile/Channel.ts +0 -108
  907. package/src/tempo/session/precompile/Protocol.test.ts +0 -358
  908. package/src/tempo/session/precompile/Protocol.ts +0 -520
  909. package/src/tempo/session/precompile/Voucher.test.ts +0 -354
  910. package/src/tempo/session/precompile/Voucher.ts +0 -162
  911. package/src/tempo/session/precompile/escrow.abi.ts +0 -226
  912. package/src/tempo/session/precompile/index.ts +0 -33
  913. package/src/tempo/session/server/ChannelOps.test.ts +0 -129
  914. package/src/tempo/session/server/ChannelOps.ts +0 -157
  915. package/src/tempo/session/server/ChannelStore.test.ts +0 -1093
  916. package/src/tempo/session/server/ChannelStore.ts +0 -835
  917. package/src/tempo/session/server/CredentialVerification.test.ts +0 -299
  918. package/src/tempo/session/server/CredentialVerification.ts +0 -775
  919. package/src/tempo/session/server/MeteredStream.ts +0 -95
  920. package/src/tempo/session/server/RequestState.test.ts +0 -574
  921. package/src/tempo/session/server/RequestState.ts +0 -507
  922. package/src/tempo/session/server/Session.integration.test.ts +0 -449
  923. package/src/tempo/session/server/Session.test.ts +0 -3862
  924. package/src/tempo/session/server/Session.ts +0 -570
  925. package/src/tempo/session/server/Settlement.test.ts +0 -345
  926. package/src/tempo/session/server/Settlement.ts +0 -527
  927. package/src/tempo/session/server/Sse.fuzz.test.ts +0 -138
  928. package/src/tempo/session/server/Sse.test.ts +0 -557
  929. package/src/tempo/session/server/Sse.ts +0 -254
  930. package/src/tempo/session/server/Transports.test.ts +0 -346
  931. package/src/tempo/session/server/Transports.ts +0 -255
  932. package/src/tempo/session/server/Ws.test.ts +0 -594
  933. package/src/tempo/session/server/Ws.ts +0 -385
  934. package/src/tempo/session/server/index.ts +0 -12
  935. package/src/tempo/subscription/KeyAuthorization.test.ts +0 -282
  936. package/src/tempo/subscription/KeyAuthorization.ts +0 -396
  937. package/src/tempo/subscription/Receipt.ts +0 -28
  938. package/src/tempo/subscription/Store.test.ts +0 -609
  939. package/src/tempo/subscription/Store.ts +0 -449
  940. package/src/tempo/subscription/Types.ts +0 -68
  941. package/src/tempo/subscription/index.ts +0 -23
  942. package/src/tsconfig.json +0 -10
  943. package/src/validation/core.ts +0 -386
  944. package/src/validation/index.ts +0 -11
  945. package/src/viem/Account.test.ts +0 -72
  946. package/src/viem/Account.ts +0 -30
  947. package/src/viem/Client.test.ts +0 -366
  948. package/src/viem/Client.ts +0 -93
  949. package/src/x402/Assets.test.ts +0 -253
  950. package/src/x402/Assets.ts +0 -221
  951. package/src/x402/Exact.e2e.test.ts +0 -448
  952. package/src/x402/Exact.localnet.test.ts +0 -475
  953. package/src/x402/Header.test.ts +0 -73
  954. package/src/x402/Header.ts +0 -79
  955. package/src/x402/PublicInterface.test-d.ts +0 -39
  956. package/src/x402/Types.ts +0 -248
  957. package/src/x402/client/Exact.test.ts +0 -309
  958. package/src/x402/client/Exact.ts +0 -186
  959. package/src/x402/index.ts +0 -6
  960. package/src/x402/internal/ChallengeBrand.ts +0 -14
  961. package/src/x402/internal/RouteBinding.ts +0 -18
  962. package/src/x402/server/EvmCharge.ts +0 -394
  963. package/src/x402/server/Facilitator.test.ts +0 -111
  964. package/src/x402/server/Facilitator.ts +0 -56
  965. package/src/zod.test.ts +0 -169
  966. package/src/zod.ts +0 -71
@@ -1,2623 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http'
2
- import { isDeepStrictEqual } from 'node:util'
3
-
4
- import * as Challenge from '../Challenge.js'
5
- import * as Constants from '../Constants.js'
6
- import * as Credential from '../Credential.js'
7
- import * as Errors from '../Errors.js'
8
- import * as Expires from '../Expires.js'
9
- import * as AcceptPayment from '../internal/AcceptPayment.js'
10
- import * as Env from '../internal/env.js'
11
- import type { MaybePromise } from '../internal/types.js'
12
- import * as Method from '../Method.js'
13
- import * as PaymentRequest from '../PaymentRequest.js'
14
- import type * as Receipt from '../Receipt.js'
15
- import * as x402_Header from '../x402/Header.js'
16
- import * as x402_Types from '../x402/Types.js'
17
- import * as z from '../zod.js'
18
- import * as Html from './internal/html/config.js'
19
- import { serviceWorker } from './internal/html/serviceWorker.gen.js'
20
- import * as Scope from './internal/scope.js'
21
- import * as NodeListener from './NodeListener.js'
22
- import * as Request from './Request.js'
23
- import * as Transport from './Transport.js'
24
-
25
- const minimumSecretKeyBytes = 32
26
- const secretKeyGenerationCommand = 'openssl rand -base64 32'
27
-
28
- export type Methods = readonly (Method.AnyServer | readonly Method.AnyServer[])[]
29
-
30
- export type ServerEventMap<
31
- methods extends readonly Method.Method[] = readonly Method.Method[],
32
- transport extends Transport.AnyTransport = Transport.AnyTransport,
33
- > = {
34
- 'challenge.created': ChallengeContext<methods[number], transport>
35
- 'payment.failed': PaymentFailedContext<methods[number], transport>
36
- 'payment.success': PaymentSuccessContext<methods[number], transport>
37
- }
38
-
39
- export type ServerEventName<
40
- methods extends readonly Method.Method[] = readonly Method.Method[],
41
- transport extends Transport.AnyTransport = Transport.AnyTransport,
42
- > = keyof ServerEventMap<methods, transport> | '*'
43
-
44
- export type ServerEventEnvelope<
45
- methods extends readonly Method.Method[] = readonly Method.Method[],
46
- transport extends Transport.AnyTransport = Transport.AnyTransport,
47
- > = {
48
- [name in keyof ServerEventMap<methods, transport>]: Readonly<{
49
- name: name
50
- payload: ServerEventMap<methods, transport>[name]
51
- }>
52
- }[keyof ServerEventMap<methods, transport>]
53
-
54
- export type ServerEventPayload<
55
- methods extends readonly Method.Method[] = readonly Method.Method[],
56
- transport extends Transport.AnyTransport = Transport.AnyTransport,
57
- name extends ServerEventName<methods, transport> = ServerEventName<methods, transport>,
58
- > = name extends '*'
59
- ? ServerEventEnvelope<methods, transport>
60
- : name extends keyof ServerEventMap<methods, transport>
61
- ? ServerEventMap<methods, transport>[name]
62
- : never
63
-
64
- /**
65
- * Server event handler.
66
- *
67
- * Handlers are awaited inline and sequentially on the payment request path.
68
- * Errors are swallowed so observers cannot change payment control flow, but
69
- * slow handlers still delay the response.
70
- */
71
- export type ServerEventHandler<
72
- methods extends readonly Method.Method[] = readonly Method.Method[],
73
- transport extends Transport.AnyTransport = Transport.AnyTransport,
74
- name extends ServerEventName<methods, transport> = ServerEventName<methods, transport>,
75
- > = (context: ServerEventPayload<methods, transport, name>) => MaybePromise<void>
76
-
77
- /** Removes a registered server event handler. */
78
- export type Unsubscribe = () => void
79
-
80
- /** Inert method descriptor exposed to server event handlers. */
81
- export type ServerMethodDescriptor<
82
- method extends Pick<Method.Method, 'intent' | 'name'> = Method.Method,
83
- > = Readonly<{
84
- intent: method['intent']
85
- name: method['name']
86
- }>
87
-
88
- /** Context passed to `onChallengeCreated`. */
89
- export type ChallengeContext<
90
- method extends Method.Method = Method.Method,
91
- transport extends Transport.AnyTransport = Transport.AnyTransport,
92
- > = Readonly<{
93
- capturedRequest?: Method.CapturedRequest | undefined
94
- challenge: Challenge.Challenge
95
- credential?: Credential.Credential | null | undefined
96
- error?: Errors.PaymentError | undefined
97
- input?: Transport.InputOf<transport> | undefined
98
- method: ServerMethodDescriptor<method>
99
- request: z.output<method['schema']['request']>
100
- }>
101
-
102
- /** Context passed to `onPaymentFailed`. */
103
- export type PaymentFailedContext<
104
- method extends Method.Method = Method.Method,
105
- transport extends Transport.AnyTransport = Transport.AnyTransport,
106
- > = Readonly<{
107
- capturedRequest?: Method.CapturedRequest | undefined
108
- challenge: Challenge.Challenge
109
- credential: Credential.Credential | null
110
- error: Errors.PaymentError
111
- input?: Transport.InputOf<transport> | undefined
112
- method: ServerMethodDescriptor<method>
113
- request: z.output<method['schema']['request']>
114
- retryChallenge?: Challenge.Challenge | undefined
115
- submittedChallenge?: Challenge.Challenge | undefined
116
- }>
117
-
118
- /** Context passed to `onPaymentSuccess`. */
119
- export type PaymentSuccessContext<
120
- method extends Method.Method = Method.Method,
121
- transport extends Transport.AnyTransport = Transport.AnyTransport,
122
- > = Readonly<{
123
- capturedRequest?: Method.CapturedRequest | undefined
124
- challenge: Challenge.Challenge<
125
- z.output<method['schema']['request']>,
126
- method['intent'],
127
- method['name']
128
- >
129
- credential?:
130
- | Credential.Credential<
131
- z.output<method['schema']['credential']['payload']>,
132
- Challenge.Challenge<z.output<method['schema']['request']>, method['intent'], method['name']>
133
- >
134
- | undefined
135
- envelope?:
136
- | Method.VerifiedChallengeEnvelope<
137
- z.output<method['schema']['request']>,
138
- z.output<method['schema']['credential']['payload']>,
139
- method['intent'],
140
- method['name']
141
- >
142
- | undefined
143
- input?: Transport.InputOf<transport> | undefined
144
- method: ServerMethodDescriptor<method>
145
- receipt: Receipt.Receipt
146
- request: z.output<method['schema']['request']>
147
- }>
148
-
149
- /** Options for standalone credential verification. */
150
- export type VerifyCredentialOptions = {
151
- capturedRequest?: Method.CapturedRequest | undefined
152
- meta?: Record<string, string> | undefined
153
- realm?: string | undefined
154
- request?: Record<string, unknown> | undefined
155
- /** Optional expected route/resource scope bound via challenge `opaque`. */
156
- scope?: string | undefined
157
- }
158
-
159
- /**
160
- * Payment handler.
161
- */
162
- export type Mppx<
163
- methods extends Methods = Methods,
164
- transport extends Transport.AnyTransport = Transport.Http,
165
- > = {
166
- /** Methods to configure. */
167
- methods: FlattenMethods<methods>
168
- /** Server realm (e.g., hostname). */
169
- realm: string
170
- /** The transport used. */
171
- transport: transport
172
- /** Register a server event handler by canonical event name. */
173
- on<name extends ServerEventName<FlattenMethods<methods>, transport>>(
174
- name: name,
175
- handler: ServerEventHandler<FlattenMethods<methods>, transport, name>,
176
- ): Unsubscribe
177
- /** Register a handler for issued payment challenges. */
178
- onChallengeCreated(
179
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'challenge.created'>,
180
- ): Unsubscribe
181
- /** Register a handler for failed submitted payment credentials. */
182
- onPaymentFailed(
183
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'payment.failed'>,
184
- ): Unsubscribe
185
- /** Register a handler for successful verified payments. */
186
- onPaymentSuccess(
187
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'payment.success'>,
188
- ): Unsubscribe
189
- } & (transport extends Transport.Http
190
- ? {
191
- /**
192
- * Combines multiple method handlers into a single route handler that presents
193
- * all methods to the client via multiple `WWW-Authenticate` headers.
194
- *
195
- * Each entry is a `[method, options]` tuple where `method` is one of the
196
- * server methods passed to `Mppx.create()`, looked up by `name`+`intent`.
197
- *
198
- * Only available on HTTP transports.
199
- * No-credential authorize hooks run in entry order; the first 200 response
200
- * wins, and earlier hooks may have already run side effects.
201
- *
202
- * @example
203
- * ```ts
204
- * import { Mppx, tempo, stripe } from 'mppx/server'
205
- *
206
- * const mppx = Mppx.create({
207
- * methods: [
208
- * tempo.charge({ currency: USDC, recipient: '0x...' }),
209
- * stripe.charge({
210
- * client: stripeClient,
211
- * networkId: 'internal',
212
- * currency: 'usd',
213
- * decimals: 2,
214
- * paymentMethodTypes: ['card'],
215
- * }),
216
- * ],
217
- * secretKey,
218
- * })
219
- *
220
- * app.get('/api/resource', async (req) => {
221
- * const result = await mppx.compose(
222
- * ['tempo/charge', { amount: '100' }],
223
- * ['stripe/charge', { amount: '100' }],
224
- * )(req)
225
- * if (result.status === 402) return result.challenge
226
- * return result.withReceipt(new Response('OK'))
227
- * })
228
- * ```
229
- */
230
- compose(
231
- ...entries: ComposeEntry<FlattenMethods<methods>>[]
232
- ): (input: Request) => Promise<MethodFn.Response<Transport.Http>>
233
- }
234
- : {}) &
235
- Handlers<FlattenMethods<methods>, transport> & {
236
- /**
237
- * Generate Challenge objects for registered methods without going through
238
- * the HTTP 402 request lifecycle. Uses the same options, defaults, and
239
- * schema transforms as the corresponding intent handler.
240
- *
241
- * @example
242
- * ```ts
243
- * const challenge = await mppx.challenge.tempo.charge({ amount: '25.92' })
244
- * ```
245
- */
246
- challenge: ChallengeHandlers<FlattenMethods<methods>>
247
-
248
- /**
249
- * Verify a credential string or object end-to-end: deserialize,
250
- * HMAC-check, match to a registered method, validate payload schema,
251
- * check expiry, and call the method's broadcast or legacy verify function.
252
- *
253
- * Method verification can broadcast payments and persist state. For example,
254
- * subscription credentials may activate or renew a subscription. Failed
255
- * standalone verification emits `payment.failed` once a credential challenge
256
- * can be parsed; strings that cannot be deserialized have no challenge
257
- * context to report.
258
- *
259
- * @deprecated Use `broadcastCredential()` instead. This legacy name maps
260
- * to the same mutating operation and does not provide validation-only
261
- * semantics.
262
- *
263
- * @example
264
- * ```ts
265
- * const receipt = await mppx.verifyCredential('eyJjaGFsbGVuZ2...')
266
- * const receipt = await mppx.verifyCredential(credential)
267
- * const receipt = await mppx.verifyCredential(credential, { request: { amount: '1000' } })
268
- * ```
269
- */
270
- verifyCredential(
271
- credential: string | Credential.Credential,
272
- options?: VerifyCredentialOptions | undefined,
273
- ): Promise<Receipt.Receipt>
274
- /**
275
- * Validate a credential without consuming or broadcasting it.
276
- *
277
- * This is an advisory pre-check. Methods that support it must not write
278
- * replay state, sign fee-payer transactions, broadcast, or otherwise
279
- * mutate payment state. Use `broadcastCredential()` when accepting payment.
280
- */
281
- validateCredential(
282
- credential: string | Credential.Credential,
283
- options?: VerifyCredentialOptions | undefined,
284
- ): Promise<Method.Validation>
285
- /**
286
- * Re-validates and broadcasts a credential, returning a receipt.
287
- *
288
- * `verifyCredential()` is retained as a backwards-compatible alias for
289
- * this mutating path.
290
- */
291
- broadcastCredential(
292
- credential: string | Credential.Credential,
293
- options?: VerifyCredentialOptions | undefined,
294
- ): Promise<Receipt.Receipt>
295
- }
296
-
297
- /** Extracts the transport override from a method, if any. */
298
- type TransportOverrideOf<mi> = mi extends { transport?: infer transport }
299
- ? Exclude<transport, undefined> extends Transport.AnyTransport
300
- ? Exclude<transport, undefined>
301
- : never
302
- : never
303
-
304
- /** Resolves the effective transport for a method: override if present, else global default. */
305
- type EffectiveTransportOf<mi, defaultTransport extends Transport.AnyTransport> = [
306
- TransportOverrideOf<mi>,
307
- ] extends [never]
308
- ? defaultTransport
309
- : TransportOverrideOf<mi>
310
-
311
- const reservedMppxKeyValues = [
312
- 'challenge',
313
- 'compose',
314
- 'methods',
315
- 'on',
316
- 'onChallengeCreated',
317
- 'onPaymentFailed',
318
- 'onPaymentSuccess',
319
- 'realm',
320
- 'broadcastCredential',
321
- 'transport',
322
- 'validateCredential',
323
- 'verifyCredential',
324
- ] as const
325
-
326
- /** Public instance keys that payment method names and shorthand intents cannot shadow. */
327
- export type ReservedKey = (typeof reservedMppxKeyValues)[number]
328
-
329
- /** Public instance keys that payment method names and shorthand intents cannot shadow. */
330
- export const reservedMppxKeys: ReadonlySet<ReservedKey> = new Set(reservedMppxKeyValues)
331
-
332
- /** True when exactly one method has the given intent (no name collision). */
333
- type IsUniqueIntent<methods extends readonly Method.AnyServer[], intent extends string> =
334
- Extract<methods[number], { intent: intent }> extends infer M
335
- ? M extends M
336
- ? [Exclude<Extract<methods[number], { intent: intent }>, M>] extends [never]
337
- ? true
338
- : false
339
- : never
340
- : never
341
-
342
- /** Only includes shorthand intent keys when the intent is unique across methods. */
343
- type UniqueIntentHandlers<
344
- methods extends readonly Method.AnyServer[],
345
- transport extends Transport.AnyTransport,
346
- > = {
347
- [method_name in methods[number]['intent'] as IsUniqueIntent<methods, method_name> extends true
348
- ? method_name extends ReservedKey
349
- ? never
350
- : method_name
351
- : never]: MethodFn<
352
- Extract<methods[number], { intent: method_name }>,
353
- EffectiveTransportOf<Extract<methods[number], { intent: method_name }>, transport>,
354
- NonNullable<Extract<methods[number], { intent: method_name }>['defaults']>
355
- > &
356
- MethodExtensions<Extract<methods[number], { intent: method_name }>>
357
- }
358
-
359
- /** Nested handlers: `mppx.tempo.charge(...)`, grouped by method name then intent. */
360
- type PublicAlias<method extends Method.AnyServer> = method extends {
361
- alias?: infer alias
362
- }
363
- ? Exclude<alias, undefined> extends infer definedAlias
364
- ? definedAlias extends string
365
- ? string extends definedAlias
366
- ? never
367
- : definedAlias
368
- : never
369
- : never
370
- : never
371
-
372
- type PublicIntent<method extends Method.AnyServer> = [PublicAlias<method>] extends [never]
373
- ? method extends { intent: infer intent extends string }
374
- ? intent
375
- : never
376
- : PublicAlias<method>
377
-
378
- type NestedHandlers<
379
- methods extends readonly Method.AnyServer[],
380
- transport extends Transport.AnyTransport,
381
- > = {
382
- [name in methods[number]['name'] as name extends ReservedKey ? never : name]: {
383
- [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: MethodFn<
384
- mi,
385
- EffectiveTransportOf<mi, transport>,
386
- NonNullable<mi['defaults']>
387
- > &
388
- MethodExtensions<mi> & { _method: mi }
389
- }
390
- }
391
-
392
- type Handlers<
393
- methods extends readonly Method.AnyServer[],
394
- transport extends Transport.AnyTransport,
395
- > = {
396
- [mi in methods[number] as `${mi['name']}/${mi['intent']}`]: MethodFn<
397
- mi,
398
- EffectiveTransportOf<mi, transport>,
399
- NonNullable<mi['defaults']>
400
- > &
401
- MethodExtensions<mi>
402
- } & UniqueIntentHandlers<methods, transport> &
403
- NestedHandlers<methods, transport> & {
404
- [mi in methods[number] as PublicAlias<mi> extends string
405
- ? `${mi['name']}/${PublicAlias<mi>}`
406
- : never]: MethodFn<mi, EffectiveTransportOf<mi, transport>, NonNullable<mi['defaults']>> &
407
- MethodExtensions<mi>
408
- }
409
-
410
- type MethodExtensions<method extends Method.AnyServer> = method extends {
411
- extensions?: (infer extensions) | undefined
412
- }
413
- ? NonNullable<extensions> extends object
414
- ? NonNullable<extensions>
415
- : {}
416
- : {}
417
-
418
- /** Nested challenge generators: `mppx.challenge.tempo.charge(...)`. */
419
- type ChallengeHandlers<methods extends readonly Method.AnyServer[]> = {
420
- [name in methods[number]['name']]: {
421
- [mi in Extract<methods[number], { name: name }> as PublicIntent<mi>]: ChallengeFn<
422
- mi,
423
- NonNullable<mi['defaults']>
424
- >
425
- }
426
- }
427
-
428
- /** A function that generates a Challenge object from intent options. */
429
- type ChallengeFn<method extends Method.Method, defaults extends Record<string, unknown>> = (
430
- options: MethodFn.Options<method, defaults>,
431
- ) => Promise<Challenge.Challenge>
432
-
433
- /**
434
- * Creates a server-side payment handler from methods.
435
- *
436
- * It is highly recommended to set a `secretKey` to bind challenges to their contents,
437
- * and allow the server to verify that incoming credentials match challenges it issued.
438
- *
439
- * @example
440
- * ```ts
441
- * import { Mppx, tempo } from 'mppx/server'
442
- *
443
- * const payment = Mppx.create({
444
- * methods: [tempo()],
445
- * secretKey: process.env.PAYMENT_SECRET_KEY,
446
- * })
447
- * ```
448
- */
449
- export function create<
450
- const methods extends Methods,
451
- const transport extends Transport.AnyTransport = Transport.Http,
452
- >(config: create.Config<methods, transport>): Mppx<methods, transport> {
453
- const {
454
- realm = Env.get('realm'),
455
- secretKey = Env.get('secretKey'),
456
- transport = Transport.http() as transport,
457
- } = config
458
-
459
- if (!secretKey) {
460
- throw new Error(
461
- 'Missing secret key. Set the MPP_SECRET_KEY environment variable or pass `secretKey` to Mppx.create().',
462
- )
463
- }
464
- assertSecretKey(secretKey)
465
-
466
- const methods = config.methods.flat() as unknown as FlattenMethods<methods>
467
- const serverEvents = createServerEventDispatcher<FlattenMethods<methods>, transport>()
468
-
469
- const handlers: Record<string, unknown> = {}
470
- const intentCount: Record<string, number> = {}
471
-
472
- for (const mi of methods) {
473
- intentCount[mi.intent] = (intentCount[mi.intent] ?? 0) + 1
474
- }
475
- assertNoReservedMppxKeys(methods as readonly Method.AnyServer[])
476
-
477
- for (const mi of methods) {
478
- const fn = createMethodFn({
479
- authorize: mi.authorize as never,
480
- defaults: mi.defaults,
481
- method: mi,
482
- realm,
483
- events: serverEvents as never,
484
- preflight: mi.preflight as never,
485
- request: mi.request as never,
486
- respond: mi.respond as never,
487
- broadcast: mi.broadcast as never,
488
- secretKey,
489
- stableBinding: mi.stableBinding as never,
490
- transport: (mi.transport ?? transport) as never,
491
- validate: mi.validate as never,
492
- verify: mi.verify as never,
493
- })
494
- const wireKey = `${mi.name}/${mi.intent}`
495
- const aliasKey = mi.alias ? `${mi.name}/${mi.alias}` : undefined
496
- if (mi.extensions) Object.assign(fn, mi.extensions)
497
- if (!aliasKey || !handlers[wireKey]) handlers[wireKey] = fn
498
- if (aliasKey) handlers[aliasKey] = fn
499
- }
500
-
501
- // Also set shorthand intent key when there's no collision
502
- for (const mi of methods) {
503
- if (intentCount[mi.intent] === 1) handlers[mi.intent] = handlers[`${mi.name}/${mi.intent}`]
504
- }
505
-
506
- // Build nested handlers: mppx.tempo.charge(...)
507
- for (const mi of methods) {
508
- if (!handlers[mi.name]) handlers[mi.name] = {}
509
- const key = mi.alias ? `${mi.name}/${mi.alias}` : `${mi.name}/${mi.intent}`
510
- const fn = handlers[key] as AnyMethodFn & { _method?: Method.AnyServer }
511
- fn._method = mi
512
- ;(handlers[mi.name] as Record<string, unknown>)[mi.alias ?? mi.intent] = fn
513
- }
514
-
515
- // Build challenge generators: mppx.challenge.tempo.charge(...)
516
- const challengeHandlers: Record<string, Record<string, unknown>> = {}
517
- for (const mi of methods) {
518
- if (!challengeHandlers[mi.name]) challengeHandlers[mi.name] = {}
519
- challengeHandlers[mi.name]![mi.alias ?? mi.intent] = createChallengeFn({
520
- defaults: mi.defaults,
521
- method: mi,
522
- realm,
523
- request: mi.request as never,
524
- secretKey,
525
- })
526
- }
527
-
528
- async function prepareStandaloneCredential(
529
- input: string | Credential.Credential,
530
- options: VerifyCredentialOptions | undefined,
531
- parameters: { emitFailures?: boolean | undefined; requireValidate?: boolean | undefined } = {},
532
- ) {
533
- const credential = hydrateCredentialMeta(
534
- typeof input === 'string' ? Credential.deserialize(input) : input,
535
- )
536
-
537
- const emitFailures = parameters.emitFailures === true
538
- const { method: credMethod, intent: credIntent } = credential.challenge
539
- const methodCandidates = (methods as readonly Method.AnyServer[]).filter(
540
- (m) => m.name === credMethod && m.intent === credIntent,
541
- )
542
- const mi = Method.selectServerMethod(methodCandidates, credential.challenge)
543
- const eventMethod =
544
- mi ?? ({ intent: credIntent, name: credMethod } satisfies ServerMethodDescriptor)
545
-
546
- const emitStandalonePaymentFailed = async (failure: {
547
- challenge: Challenge.Challenge
548
- credential: Credential.Credential | null
549
- error: Errors.PaymentError
550
- request: Record<string, unknown>
551
- submittedChallenge?: Challenge.Challenge | undefined
552
- }) => {
553
- if (!emitFailures) return
554
- await serverEvents.emit(
555
- 'payment.failed',
556
- createPaymentFailedContext({
557
- capturedRequest: options?.capturedRequest,
558
- challenge: failure.challenge,
559
- credential: failure.credential,
560
- error: failure.error,
561
- method: eventMethod,
562
- request: failure.request,
563
- submittedChallenge: failure.submittedChallenge,
564
- }) as never,
565
- )
566
- }
567
-
568
- const fail = async (
569
- error: Errors.PaymentError,
570
- failure: {
571
- credential?: Credential.Credential | null | undefined
572
- request?: Record<string, unknown> | undefined
573
- submittedChallenge?: Challenge.Challenge | undefined
574
- } = {},
575
- thrown: unknown = error,
576
- ): Promise<never> => {
577
- await emitStandalonePaymentFailed({
578
- challenge: credential.challenge,
579
- credential: failure.credential ?? credential,
580
- error,
581
- request: failure.request ?? (credential.challenge.request as Record<string, unknown>),
582
- submittedChallenge: failure.submittedChallenge ?? credential.challenge,
583
- })
584
- throw thrown
585
- }
586
-
587
- if (!mi) {
588
- await fail(
589
- new Errors.InvalidChallengeError({
590
- id: credential.challenge.id,
591
- reason: `no registered method for ${credMethod}/${credIntent}`,
592
- }),
593
- )
594
- }
595
-
596
- if (parameters.requireValidate && !mi.validate)
597
- await fail(
598
- new Errors.VerificationFailedError({
599
- details: { intent: credIntent, method: credMethod },
600
- reason: `${credMethod}/${credIntent} does not support non-mutating credential validation`,
601
- }),
602
- )
603
-
604
- if (!Challenge.verify(credential.challenge, { secretKey: secretKey! })) {
605
- await fail(
606
- new Errors.InvalidChallengeError({
607
- id: credential.challenge.id,
608
- reason: 'challenge was not issued by this server',
609
- }),
610
- )
611
- }
612
-
613
- try {
614
- Expires.assert(credential.challenge.expires, credential.challenge.id)
615
- } catch (e) {
616
- if (e instanceof Errors.PaymentError) await fail(e)
617
- throw e
618
- }
619
-
620
- let parsedCredential!: Credential.Credential
621
- try {
622
- parsedCredential = withParsedCredentialPayload(
623
- credential,
624
- mi.schema.credential.payload.parse(credential.payload),
625
- )
626
- } catch (e) {
627
- await fail(new Errors.InvalidPayloadError(), {}, e)
628
- }
629
-
630
- const expectedMeta = Scope.merge({ meta: options?.meta, scope: options?.scope })
631
-
632
- if (options?.scope !== undefined && Scope.read(credential.challenge.meta) !== options.scope) {
633
- await fail(
634
- new Errors.InvalidChallengeError({
635
- id: credential.challenge.id,
636
- reason: "credential scope does not match this route's requirements",
637
- }),
638
- { credential: parsedCredential },
639
- )
640
- }
641
-
642
- const shouldValidateRoute =
643
- options?.capturedRequest !== undefined ||
644
- options?.meta !== undefined ||
645
- options?.realm !== undefined ||
646
- options?.request !== undefined
647
- const expectedRealm =
648
- options?.realm ??
649
- realm ??
650
- (options?.capturedRequest === undefined ? credential.challenge.realm : undefined)
651
-
652
- let parsedRequest = credential.challenge.request as Record<string, unknown>
653
- let request: z.input<typeof mi.schema.request>
654
- try {
655
- request = shouldValidateRoute
656
- ? await resolveRouteChallenge({
657
- capturedRequest: options?.capturedRequest,
658
- credential: parsedCredential,
659
- defaults: mi.defaults,
660
- expires: credential.challenge.expires,
661
- meta: expectedMeta,
662
- method: mi,
663
- realm: expectedRealm,
664
- request: mi.request as never,
665
- routeRequest: options?.request ?? {},
666
- secretKey: secretKey!,
667
- }).then((resolved) => {
668
- const mismatch = getChallengeBindingMismatch(
669
- resolved.challenge,
670
- credential.challenge,
671
- mi.stableBinding as never,
672
- )
673
- if (mismatch)
674
- throw new Errors.InvalidChallengeError({
675
- id: credential.challenge.id,
676
- reason: `credential ${mismatch} does not match this route's requirements`,
677
- })
678
-
679
- parsedRequest = resolved.parsedRequest
680
- return resolved.request as z.input<typeof mi.schema.request>
681
- })
682
- : (credential.challenge.request as z.input<typeof mi.schema.request>)
683
- } catch (e) {
684
- if (e instanceof Errors.PaymentError)
685
- await fail(e, {
686
- credential: parsedCredential,
687
- request: credential.challenge.request as Record<string, unknown>,
688
- })
689
- throw e
690
- }
691
-
692
- const envelope = options?.capturedRequest
693
- ? ({
694
- capturedRequest: options.capturedRequest,
695
- challenge: credential.challenge,
696
- credential: parsedCredential,
697
- request: parsedRequest,
698
- } as Method.VerifiedChallengeEnvelope)
699
- : undefined
700
-
701
- return {
702
- credential,
703
- envelope,
704
- eventMethod,
705
- method: mi,
706
- parsedCredential,
707
- parsedRequest,
708
- request,
709
- }
710
- }
711
-
712
- async function validateCredentialFn(
713
- input: string | Credential.Credential,
714
- options?: VerifyCredentialOptions,
715
- ): Promise<Method.Validation> {
716
- const prepared = await prepareStandaloneCredential(input, options, { requireValidate: true })
717
- return prepared.method.validate!({
718
- credential: prepared.parsedCredential,
719
- envelope: prepared.envelope,
720
- request: prepared.request,
721
- } as never)
722
- }
723
-
724
- // broadcastCredential: single-call end-to-end validation and broadcast
725
- async function broadcastCredentialFn(
726
- input: string | Credential.Credential,
727
- options?: VerifyCredentialOptions,
728
- ): Promise<Receipt.Receipt> {
729
- const prepared = await prepareStandaloneCredential(input, options, { emitFailures: true })
730
- const { method: mi, parsedCredential, parsedRequest, request, envelope } = prepared
731
-
732
- const emitStandalonePaymentFailed = async (parameters: {
733
- challenge: Challenge.Challenge
734
- credential: Credential.Credential | null
735
- error: Errors.PaymentError
736
- request: Record<string, unknown>
737
- submittedChallenge?: Challenge.Challenge | undefined
738
- }) => {
739
- await serverEvents.emit(
740
- 'payment.failed',
741
- createPaymentFailedContext({
742
- capturedRequest: options?.capturedRequest,
743
- challenge: parameters.challenge,
744
- credential: parameters.credential,
745
- error: parameters.error,
746
- method: prepared.eventMethod,
747
- request: parameters.request,
748
- submittedChallenge: parameters.submittedChallenge,
749
- }) as never,
750
- )
751
- }
752
-
753
- let receipt: Receipt.Receipt
754
- try {
755
- if (mi.broadcast && mi.validate)
756
- await mi.validate({ credential: parsedCredential, envelope, request } as never)
757
- const broadcast = mi.broadcast ?? mi.verify
758
- receipt = await broadcast({ credential: parsedCredential, envelope, request } as never)
759
- } catch (e) {
760
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
761
- await emitStandalonePaymentFailed({
762
- challenge: prepared.credential.challenge,
763
- credential: parsedCredential,
764
- error,
765
- request: parsedRequest,
766
- submittedChallenge: prepared.credential.challenge,
767
- })
768
- throw e
769
- }
770
-
771
- await serverEvents.emit(
772
- 'payment.success',
773
- createPaymentSuccessContext({
774
- capturedRequest: options?.capturedRequest,
775
- challenge: prepared.credential.challenge,
776
- credential: parsedCredential,
777
- envelope,
778
- method: mi,
779
- receipt,
780
- request: parsedRequest,
781
- }) as never,
782
- )
783
-
784
- return receipt
785
- }
786
-
787
- const verifyCredentialFn = broadcastCredentialFn
788
-
789
- function composeFn(
790
- ...entries: readonly [
791
- Method.AnyServer | AnyMethodFnWithMethod | string,
792
- Record<string, unknown>,
793
- ][]
794
- ) {
795
- if (transport.name !== 'http') throw new Error('compose() only supports HTTP transport')
796
- if (entries.length === 0) throw new Error('compose() requires at least one entry')
797
- const configured = entries.map(([methodOrKey, options]) => {
798
- const key =
799
- typeof methodOrKey === 'string'
800
- ? methodOrKey
801
- : typeof methodOrKey === 'function' && '_method' in methodOrKey
802
- ? `${(methodOrKey._method as Method.AnyServer).name}/${(methodOrKey._method as Method.AnyServer).alias ?? (methodOrKey._method as Method.AnyServer).intent}`
803
- : `${(methodOrKey as Method.AnyServer).name}/${(methodOrKey as Method.AnyServer).alias ?? (methodOrKey as Method.AnyServer).intent}`
804
- const handlerFn = handlers[key] as AnyMethodFn | undefined
805
- if (!handlerFn)
806
- throw new Error(`No handler for "${key}". Is this method in your methods array?`)
807
- return handlerFn(options)
808
- })
809
- return compose(...(configured as ConfiguredHandler[]))
810
- }
811
-
812
- function onChallengeCreated(
813
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'challenge.created'>,
814
- ) {
815
- return serverEvents.on('challenge.created', handler)
816
- }
817
-
818
- function onPaymentFailed(
819
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'payment.failed'>,
820
- ) {
821
- return serverEvents.on('payment.failed', handler)
822
- }
823
-
824
- function onPaymentSuccess(
825
- handler: ServerEventHandler<FlattenMethods<methods>, transport, 'payment.success'>,
826
- ) {
827
- return serverEvents.on('payment.success', handler)
828
- }
829
-
830
- return {
831
- methods,
832
- challenge: challengeHandlers,
833
- compose: composeFn,
834
- on: serverEvents.on,
835
- onChallengeCreated,
836
- onPaymentFailed,
837
- onPaymentSuccess,
838
- realm: realm as string | undefined,
839
- broadcastCredential: broadcastCredentialFn,
840
- transport,
841
- validateCredential: validateCredentialFn,
842
- verifyCredential: verifyCredentialFn,
843
- ...handlers,
844
- } as never
845
- }
846
-
847
- function assertSecretKey(secretKey: string) {
848
- const byteLength = new TextEncoder().encode(secretKey).byteLength
849
- if (byteLength >= minimumSecretKeyBytes) return
850
-
851
- throw new Error(
852
- `Secret key must be at least ${minimumSecretKeyBytes} bytes. Generate one with \`${secretKeyGenerationCommand}\` and set MPP_SECRET_KEY or pass it to Mppx.create().`,
853
- )
854
- }
855
-
856
- export declare namespace create {
857
- type Config<
858
- methods extends Methods = Methods,
859
- transport extends Transport.AnyTransport = Transport.Http,
860
- > = {
861
- /** Array of configured methods. @example [tempo()] */
862
- methods: methods
863
- /** Server realm (e.g., hostname). Resolution order: explicit value > env vars (`MPP_REALM`, `FLY_APP_NAME`, `VERCEL_URL`, etc.) > request URL hostname > `"MPP Payment"`. */
864
- realm?: string | undefined
865
- /** Secret key for HMAC-bound challenge IDs for stateless verification. Must be at least 32 bytes. Auto-detected from `MPP_SECRET_KEY` environment variable. */
866
- secretKey?: string | undefined
867
- /** Transport to use. @default Transport.http() */
868
- transport?: transport | undefined
869
- }
870
- }
871
-
872
- function createMethodFn<
873
- method extends Method.Method,
874
- transport extends Transport.AnyTransport,
875
- defaults extends Record<string, unknown>,
876
- >(
877
- parameters: createMethodFn.Parameters<method, transport, defaults>,
878
- ): createMethodFn.ReturnType<method, transport, defaults>
879
- // biome-ignore lint/correctness/noUnusedVariables: _
880
- function createMethodFn(parameters: createMethodFn.Parameters): createMethodFn.ReturnType {
881
- const {
882
- authorize,
883
- defaults,
884
- events,
885
- method,
886
- preflight,
887
- realm,
888
- respond,
889
- secretKey,
890
- broadcast,
891
- stableBinding,
892
- transport,
893
- validate,
894
- verify,
895
- } = parameters
896
-
897
- return (options) => {
898
- const { description, meta, scope, ...rest } = options
899
- const staticMeta = Scope.merge({ meta, scope })
900
- const internal: ConfiguredHandler['_internal'] = {
901
- ...method,
902
- ...defaults,
903
- ...options,
904
- ...(staticMeta !== undefined ? { meta: staticMeta } : {}),
905
- name: method.name,
906
- intent: method.intent,
907
- html: method.html,
908
- _canonicalRequest: PaymentRequest.fromMethod(method, { ...defaults, ...rest }),
909
- _stableBinding: stableBinding as never,
910
- }
911
-
912
- const handler = async (input: Transport.InputOf): Promise<MethodFn.Response> => {
913
- if (method.html && isServiceWorkerRequest(input))
914
- return {
915
- status: 402,
916
- challenge: createServiceWorkerResponse(),
917
- } as MethodFn.Response<Transport.Http>
918
-
919
- const expires =
920
- 'expires' in options
921
- ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
922
- : Expires.minutes(5)
923
- const capturedRequest = await captureRequest(transport, input)
924
- const effectiveMeta =
925
- scope === undefined && input instanceof globalThis.Request
926
- ? Scope.merge({ meta: staticMeta, scope: Scope.get(input) })
927
- : staticMeta
928
-
929
- // Extract credential once — getCredential may have side effects (e.g. SSE transports).
930
- let [credential, credentialError] = (() => {
931
- try {
932
- const credential = transport.getCredential(input) as Credential.Credential | null
933
- return [credential ? hydrateCredentialMeta(credential) : null, undefined] as const
934
- } catch (e) {
935
- return [null, e as Error] as const
936
- }
937
- })()
938
-
939
- if (preflight && input instanceof globalThis.Request) {
940
- const response = await preflight({
941
- capturedRequest,
942
- credential,
943
- expires,
944
- input,
945
- options: { ...defaults, ...rest },
946
- realm: realm ?? new URL(input.url).hostname ?? 'MPP Payment',
947
- secretKey,
948
- } as never)
949
- if (response) {
950
- if (response.status === 402) return { challenge: response, status: 402 }
951
- return {
952
- status: 200,
953
- withReceipt() {
954
- return response
955
- },
956
- } as MethodFn.Response
957
- }
958
- }
959
-
960
- const emitChallenge = async (parameters: {
961
- challenge: Challenge.Challenge
962
- credential?: Credential.Credential | null | undefined
963
- error?: Errors.PaymentError | undefined
964
- html?: Method.Method['html'] | undefined
965
- request: Record<string, unknown>
966
- }) => {
967
- const response = await transport.respondChallenge({
968
- challenge: parameters.challenge,
969
- input,
970
- ...(parameters.error && { error: parameters.error }),
971
- ...(parameters.html && { html: parameters.html }),
972
- })
973
- if (isIssuedChallengeResponse(response))
974
- await events.emit(
975
- 'challenge.created',
976
- createChallengeContext({
977
- capturedRequest,
978
- challenge: parameters.challenge,
979
- credential: parameters.credential,
980
- error: parameters.error,
981
- input,
982
- method,
983
- request: parameters.request,
984
- }) as never,
985
- )
986
- return response
987
- }
988
-
989
- const emitPaymentFailed = async (parameters: {
990
- challenge: Challenge.Challenge
991
- credential: Credential.Credential | null
992
- error: Errors.PaymentError
993
- request: Record<string, unknown>
994
- retryChallenge?: Challenge.Challenge | undefined
995
- submittedChallenge?: Challenge.Challenge | undefined
996
- }) => {
997
- await events.emit(
998
- 'payment.failed',
999
- createPaymentFailedContext({
1000
- capturedRequest,
1001
- challenge: parameters.challenge,
1002
- credential: parameters.credential,
1003
- error: parameters.error,
1004
- input,
1005
- method,
1006
- request: parameters.request,
1007
- retryChallenge: parameters.retryChallenge,
1008
- submittedChallenge: parameters.submittedChallenge,
1009
- }) as never,
1010
- )
1011
- }
1012
-
1013
- const routeChallenge = await resolveRouteChallenge({
1014
- capturedRequest,
1015
- credential,
1016
- defaults,
1017
- description,
1018
- expires,
1019
- meta: effectiveMeta,
1020
- method,
1021
- realm,
1022
- request: parameters.request,
1023
- routeRequest: rest,
1024
- secretKey,
1025
- }).catch(async (e) => {
1026
- if (!(e instanceof Errors.PaymentError)) throw e
1027
- const challenge = createFallbackChallenge({
1028
- capturedRequest,
1029
- defaults: defaults ?? {},
1030
- description,
1031
- expires,
1032
- meta: effectiveMeta,
1033
- method,
1034
- realm,
1035
- routeRequest: rest,
1036
- secretKey,
1037
- })
1038
- if (credential)
1039
- await emitPaymentFailed({
1040
- challenge,
1041
- credential,
1042
- error: e,
1043
- request: challenge.request,
1044
- retryChallenge: challenge,
1045
- submittedChallenge: credential.challenge,
1046
- })
1047
- const response = await emitChallenge({
1048
- challenge,
1049
- credential,
1050
- request: challenge.request,
1051
- error: e,
1052
- html: method.html,
1053
- })
1054
- return { response }
1055
- })
1056
- if ('response' in routeChallenge) return { challenge: routeChallenge.response, status: 402 }
1057
- const { challenge, parsedRequest, request } = routeChallenge
1058
- internal._canonicalRequest = parsedRequest
1059
-
1060
- if (credential && transport.bindCredential) {
1061
- try {
1062
- credential = hydrateCredentialMeta(
1063
- (await transport.bindCredential({
1064
- challenge,
1065
- credential,
1066
- input,
1067
- })) as Credential.Credential,
1068
- )
1069
- } catch (e) {
1070
- credential = null
1071
- credentialError = e as Error
1072
- }
1073
- }
1074
-
1075
- // Credential was provided but malformed
1076
- if (credentialError) {
1077
- const reason = getSafeCredentialReason(credentialError)
1078
- const error = new Errors.MalformedCredentialError(reason ? { reason } : {})
1079
- await emitPaymentFailed({
1080
- challenge,
1081
- credential: null,
1082
- error,
1083
- request: parsedRequest,
1084
- retryChallenge: challenge,
1085
- })
1086
- const response = await emitChallenge({
1087
- challenge,
1088
- credential: null,
1089
- request: parsedRequest,
1090
- error,
1091
- html: method.html,
1092
- })
1093
- return { challenge: response, status: 402 }
1094
- }
1095
-
1096
- const success = (
1097
- receiptData: Receipt.Receipt,
1098
- options: {
1099
- challengeId?: string | undefined
1100
- credentialForReceipt?: Credential.Credential | undefined
1101
- envelopeForReceipt?: Method.VerifiedChallengeEnvelope | undefined
1102
- managementResponse?: globalThis.Response | undefined
1103
- } = {},
1104
- ): MethodFn.Response => {
1105
- const {
1106
- challengeId = challenge.id,
1107
- credentialForReceipt = { challenge, payload: {} } as Credential.Credential,
1108
- envelopeForReceipt,
1109
- managementResponse,
1110
- } = options
1111
-
1112
- return {
1113
- status: 200,
1114
- withReceipt<response>(response?: response) {
1115
- if (managementResponse) {
1116
- return transport.respondReceipt({
1117
- challengeId,
1118
- credential: credentialForReceipt,
1119
- ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
1120
- input,
1121
- receipt: receiptData,
1122
- response: managementResponse as never,
1123
- }) as response
1124
- }
1125
- if (!response) throw new MissingReceiptResponseError()
1126
- return transport.respondReceipt({
1127
- challengeId,
1128
- credential: credentialForReceipt,
1129
- ...(envelopeForReceipt ? { envelope: envelopeForReceipt } : {}),
1130
- input,
1131
- receipt: receiptData,
1132
- response: response as never,
1133
- }) as response
1134
- },
1135
- }
1136
- }
1137
-
1138
- // No credential provided—issue challenge
1139
- if (!credential) {
1140
- if (authorize && input instanceof globalThis.Request) {
1141
- try {
1142
- const authorized = await authorize({
1143
- challenge,
1144
- input,
1145
- request: challenge.request,
1146
- } as never)
1147
- if (authorized) {
1148
- await events.emit(
1149
- 'payment.success',
1150
- createPaymentSuccessContext({
1151
- capturedRequest,
1152
- challenge,
1153
- input,
1154
- method,
1155
- receipt: authorized.receipt,
1156
- request: parsedRequest,
1157
- }) as never,
1158
- )
1159
- return success(authorized.receipt, {
1160
- managementResponse: authorized.response,
1161
- })
1162
- }
1163
- } catch (e) {
1164
- if (!(e instanceof Errors.PaymentError))
1165
- console.error('mppx: internal authorization error', e)
1166
- const error =
1167
- e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1168
- await emitPaymentFailed({
1169
- challenge,
1170
- credential: null,
1171
- error,
1172
- request: parsedRequest,
1173
- retryChallenge: challenge,
1174
- })
1175
- const response = await emitChallenge({
1176
- challenge,
1177
- request: parsedRequest,
1178
- error,
1179
- html: method.html,
1180
- })
1181
- return { challenge: response, status: 402 }
1182
- }
1183
- }
1184
-
1185
- const error = new Errors.PaymentRequiredError({ description })
1186
- const response = await emitChallenge({
1187
- challenge,
1188
- credential: null,
1189
- request: parsedRequest,
1190
- error,
1191
- html: method.html,
1192
- })
1193
- return { challenge: response, status: 402 }
1194
- }
1195
-
1196
- // ── Tier 1: HMAC provenance check (primary gate) ──────────────────
1197
- //
1198
- // Recompute the HMAC-SHA256 over the credential's echoed challenge
1199
- // parameters (realm|method|intent|request|expires|digest|opaque) and
1200
- // compare to the echoed `id`. This proves the challenge was issued by
1201
- // this server with these exact parameters — including opaque/meta,
1202
- // expires, and the full serialized request blob.
1203
- //
1204
- // This is the authoritative binding per §5.1.2.1.1 of the spec
1205
- // (https://paymentauth.org/draft-httpauth-payment-00.html#section-5.1.2.1.1).
1206
- // No database lookup is needed; the HMAC is stateless verification.
1207
- if (!Challenge.verify(credential.challenge, { secretKey })) {
1208
- const error = new Errors.InvalidChallengeError({
1209
- id: credential.challenge.id,
1210
- reason: 'challenge was not issued by this server',
1211
- })
1212
- await emitPaymentFailed({
1213
- challenge,
1214
- credential,
1215
- error,
1216
- request: parsedRequest,
1217
- retryChallenge: challenge,
1218
- submittedChallenge: credential.challenge,
1219
- })
1220
- const response = await emitChallenge({
1221
- challenge,
1222
- credential,
1223
- request: parsedRequest,
1224
- error,
1225
- html: method.html,
1226
- })
1227
- return { challenge: response, status: 402 }
1228
- }
1229
-
1230
- // ── Tier 2: Pinned field safety net ──────────────────────────────
1231
- //
1232
- // The HMAC check above (Tier 1) is the primary gate — it already
1233
- // covers ALL challenge fields including opaque, digest, and the full
1234
- // serialized request. So why this second check?
1235
- //
1236
- // The `request()` hook can produce credential-dependent output: for
1237
- // example, `feePayer` may differ between the 402 challenge call (no
1238
- // credential) and the credential-bearing call. This means the
1239
- // recomputed challenge here has a different `request` blob — and
1240
- // thus a different HMAC — than the original challenge the client
1241
- // echoes back. The HMAC check above verifies the *echoed* challenge
1242
- // was signed by us, but it cannot verify that the echoed challenge
1243
- // matches *this route's current configuration* when the request
1244
- // hook transforms fields between calls.
1245
- //
1246
- // This check compares the fields that MUST be stable across both
1247
- // calls. That includes the economically significant request fields
1248
- // plus `opaque`, which can carry route-scoping metadata (for example,
1249
- // sibling route identity) that must not be replayable across handlers.
1250
- // `expires` still is not pinned here because its default is generated
1251
- // per invocation, and `digest` is already bound by the echoed HMAC.
1252
- {
1253
- const mismatch = getChallengeBindingMismatch(
1254
- challenge,
1255
- credential.challenge,
1256
- stableBinding as never,
1257
- )
1258
- if (mismatch) {
1259
- const error = new Errors.InvalidChallengeError({
1260
- id: credential.challenge.id,
1261
- reason: `credential ${mismatch} does not match this route's requirements`,
1262
- })
1263
- await emitPaymentFailed({
1264
- challenge,
1265
- credential,
1266
- error,
1267
- request: parsedRequest,
1268
- retryChallenge: challenge,
1269
- submittedChallenge: credential.challenge,
1270
- })
1271
- const response = await emitChallenge({
1272
- challenge,
1273
- credential,
1274
- request: parsedRequest,
1275
- error,
1276
- html: method.html,
1277
- })
1278
- return { challenge: response, status: 402 }
1279
- }
1280
- }
1281
-
1282
- // Reject credentials without expires (fail-closed) or with expired timestamp
1283
- try {
1284
- Expires.assert(credential.challenge.expires, credential.challenge.id)
1285
- } catch (error) {
1286
- await emitPaymentFailed({
1287
- challenge,
1288
- credential,
1289
- error: error as Errors.PaymentError,
1290
- request: parsedRequest,
1291
- retryChallenge: challenge,
1292
- submittedChallenge: credential.challenge,
1293
- })
1294
- const response = await emitChallenge({
1295
- challenge,
1296
- credential,
1297
- request: parsedRequest,
1298
- error: error as Errors.PaymentError,
1299
- })
1300
- return { challenge: response, status: 402 }
1301
- }
1302
- // Validate payload structure against method schema
1303
- let parsedCredential: Credential.Credential
1304
- try {
1305
- parsedCredential = withParsedCredentialPayload(
1306
- credential,
1307
- method.schema.credential.payload.parse(credential.payload),
1308
- )
1309
- } catch {
1310
- const error = new Errors.InvalidPayloadError()
1311
- await emitPaymentFailed({
1312
- challenge,
1313
- credential,
1314
- error,
1315
- request: parsedRequest,
1316
- retryChallenge: challenge,
1317
- submittedChallenge: credential.challenge,
1318
- })
1319
- const response = await emitChallenge({
1320
- challenge,
1321
- credential,
1322
- request: parsedRequest,
1323
- error,
1324
- })
1325
- return { challenge: response, status: 402 }
1326
- }
1327
-
1328
- const envelope: Method.VerifiedChallengeEnvelope = Object.freeze({
1329
- capturedRequest,
1330
- challenge: credential.challenge,
1331
- credential: parsedCredential,
1332
- request: parsedRequest,
1333
- })
1334
-
1335
- // User-provided verification (e.g., check signature, submit tx, verify payment).
1336
- // If verification fails, re-issue the challenge so the client can retry.
1337
- let receiptData: Receipt.Receipt
1338
- try {
1339
- if (broadcast && validate)
1340
- await validate({ credential: parsedCredential, envelope, request } as never)
1341
- const broadcastCredential = broadcast ?? verify
1342
- receiptData = await broadcastCredential({
1343
- credential: parsedCredential,
1344
- envelope,
1345
- request,
1346
- } as never)
1347
- } catch (e) {
1348
- if (!(e instanceof Errors.PaymentError))
1349
- console.error('mppx: internal verification error', e)
1350
- const error = e instanceof Errors.PaymentError ? e : new Errors.VerificationFailedError()
1351
- await emitPaymentFailed({
1352
- challenge,
1353
- credential: parsedCredential,
1354
- error,
1355
- request: parsedRequest,
1356
- retryChallenge: challenge,
1357
- submittedChallenge: credential.challenge,
1358
- })
1359
- const response = await emitChallenge({
1360
- challenge,
1361
- credential: parsedCredential,
1362
- request: parsedRequest,
1363
- error,
1364
- })
1365
- return { challenge: response, status: 402 }
1366
- }
1367
-
1368
- // If the method's `respond` hook returns a Response, it means this
1369
- // request is a management action (e.g. channel open, voucher POST)
1370
- // and the user's route handler should NOT run. `withReceipt()` will
1371
- // return the management response directly. If undefined, `withReceipt()`
1372
- // expects the caller to pass the user handler's response instead.
1373
- const managementResponse = respond
1374
- ? await respond({
1375
- credential: parsedCredential,
1376
- envelope,
1377
- input,
1378
- receipt: receiptData,
1379
- request,
1380
- } as never)
1381
- : undefined
1382
-
1383
- await events.emit(
1384
- 'payment.success',
1385
- createPaymentSuccessContext({
1386
- capturedRequest,
1387
- challenge: credential.challenge,
1388
- credential: parsedCredential,
1389
- envelope,
1390
- input,
1391
- method,
1392
- receipt: receiptData,
1393
- request: parsedRequest,
1394
- }) as never,
1395
- )
1396
-
1397
- return success(receiptData, {
1398
- challengeId: credential.challenge.id,
1399
- credentialForReceipt: parsedCredential,
1400
- envelopeForReceipt: envelope,
1401
- managementResponse,
1402
- })
1403
- }
1404
-
1405
- return Object.assign(handler, { _internal: internal })
1406
- }
1407
- }
1408
-
1409
- /**
1410
- * Creates a challenge generator for a single method+intent.
1411
- * Applies the same defaults and request transform as createMethodFn,
1412
- * but returns a Challenge object directly instead of a request handler.
1413
- */
1414
- function createChallengeFn(parameters: {
1415
- defaults?: Record<string, unknown>
1416
- method: Method.Method
1417
- realm: string | undefined
1418
- request?: Method.RequestFn<Method.Method>
1419
- secretKey: string
1420
- }): (options: Record<string, unknown>) => Promise<Challenge.Challenge> {
1421
- const { defaults, method, realm, secretKey } = parameters
1422
-
1423
- return async (options) => {
1424
- const { description, meta, scope, ...rest } = options as {
1425
- description?: string
1426
- expires?: z.DatetimeInput
1427
- meta?: Record<string, string>
1428
- scope?: string
1429
- [key: string]: unknown
1430
- }
1431
- const effectiveMeta = Scope.merge({ meta, scope })
1432
- const expires =
1433
- 'expires' in options
1434
- ? normalizeExpires(options.expires as z.DatetimeInput | undefined)
1435
- : Expires.minutes(5)
1436
-
1437
- return resolveRouteChallenge({
1438
- defaults,
1439
- description,
1440
- expires,
1441
- meta: effectiveMeta,
1442
- method,
1443
- realm,
1444
- request: parameters.request,
1445
- routeRequest: rest,
1446
- secretKey,
1447
- }).then((resolved) => resolved.challenge)
1448
- }
1449
- }
1450
-
1451
- declare namespace createMethodFn {
1452
- type Parameters<
1453
- method extends Method.Method = Method.Method,
1454
- transport extends Transport.AnyTransport = Transport.Http,
1455
- defaults extends Record<string, unknown> = Record<string, unknown>,
1456
- > = {
1457
- authorize?: Method.AuthorizeFn<method>
1458
- defaults?: defaults
1459
- method: method
1460
- events: ServerEventDispatcher<readonly [method], transport>
1461
- preflight?: Method.PreflightFn<method>
1462
- realm: string | undefined
1463
- request?: Method.RequestFn<method>
1464
- respond?: Method.RespondFn<method>
1465
- broadcast?: Method.BroadcastFn<method>
1466
- secretKey: string
1467
- stableBinding?: Method.StableBindingFn<method>
1468
- transport: transport
1469
- validate?: Method.ValidateFn<method>
1470
- verify: Method.VerifyFn<method>
1471
- }
1472
-
1473
- type ReturnType<
1474
- method extends Method.Method = Method.Method,
1475
- transport extends Transport.AnyTransport = Transport.Http,
1476
- defaults extends Record<string, unknown> = Record<string, unknown>,
1477
- > = MethodFn<method, transport, defaults>
1478
- }
1479
-
1480
- type ServerEventDispatcher<
1481
- methods extends readonly Method.Method[],
1482
- transport extends Transport.AnyTransport,
1483
- > = {
1484
- emit<name extends keyof ServerEventMap<methods, transport>>(
1485
- name: name,
1486
- context: ServerEventMap<methods, transport>[name],
1487
- ): Promise<void>
1488
- on<name extends ServerEventName<methods, transport>>(
1489
- name: name,
1490
- handler: ServerEventHandler<methods, transport, name>,
1491
- ): Unsubscribe
1492
- }
1493
-
1494
- function createServerEventDispatcher<
1495
- methods extends readonly Method.Method[],
1496
- transport extends Transport.AnyTransport,
1497
- >(): ServerEventDispatcher<methods, transport> {
1498
- const handlers = {
1499
- '*': new Set<ServerEventHandler<methods, transport, '*'>>(),
1500
- 'challenge.created': new Set<ServerEventHandler<methods, transport, 'challenge.created'>>(),
1501
- 'payment.failed': new Set<ServerEventHandler<methods, transport, 'payment.failed'>>(),
1502
- 'payment.success': new Set<ServerEventHandler<methods, transport, 'payment.success'>>(),
1503
- }
1504
-
1505
- const on: ServerEventDispatcher<methods, transport>['on'] = (name, handler) => {
1506
- switch (name) {
1507
- case '*':
1508
- case 'challenge.created':
1509
- case 'payment.failed':
1510
- case 'payment.success':
1511
- handlers[name].add(handler as never)
1512
- return () => handlers[name].delete(handler as never)
1513
- default:
1514
- throw new Error(`Unknown server event "${String(name)}".`)
1515
- }
1516
- }
1517
-
1518
- return {
1519
- async emit(name, context) {
1520
- await emitServerEventHandlers(handlers[name], context)
1521
- await emitServerEventHandlers(handlers['*'], toServerEventEnvelope(name, context))
1522
- },
1523
- on,
1524
- }
1525
- }
1526
-
1527
- function toServerEventEnvelope<
1528
- methods extends readonly Method.Method[],
1529
- transport extends Transport.AnyTransport,
1530
- name extends keyof ServerEventMap<methods, transport>,
1531
- >(
1532
- name: name,
1533
- payload: ServerEventMap<methods, transport>[name],
1534
- ): ServerEventPayload<methods, transport, '*'> {
1535
- return Object.freeze({ name, payload }) as ServerEventPayload<methods, transport, '*'>
1536
- }
1537
-
1538
- async function emitServerEventHandlers(
1539
- handlers: ReadonlySet<(context: never) => MaybePromise<void>>,
1540
- context: unknown,
1541
- ): Promise<void> {
1542
- for (const handler of handlers) {
1543
- try {
1544
- await handler(context as never)
1545
- } catch {
1546
- // Errors are isolated, but handlers are still awaited inline.
1547
- }
1548
- }
1549
- }
1550
-
1551
- function assertNoReservedMppxKeys(methods: readonly Method.AnyServer[]) {
1552
- for (const method of methods) {
1553
- if (reservedMppxKeys.has(method.name as ReservedKey))
1554
- throw new Error(`Method name "${method.name}" conflicts with a reserved Mppx property.`)
1555
- if (reservedMppxKeys.has(method.intent as ReservedKey))
1556
- throw new Error(`Method intent "${method.intent}" conflicts with a reserved Mppx property.`)
1557
- }
1558
- }
1559
-
1560
- function createChallengeContext(parameters: {
1561
- capturedRequest?: Method.CapturedRequest | undefined
1562
- challenge: Challenge.Challenge
1563
- credential?: Credential.Credential | null | undefined
1564
- error?: Errors.PaymentError | undefined
1565
- input?: unknown
1566
- method: Method.Method | ServerMethodDescriptor
1567
- request: Record<string, unknown>
1568
- }): ChallengeContext {
1569
- return Object.freeze({
1570
- ...(parameters.capturedRequest
1571
- ? { capturedRequest: snapshotCapturedRequest(parameters.capturedRequest) }
1572
- : {}),
1573
- challenge: snapshotValue(parameters.challenge),
1574
- credential:
1575
- parameters.credential === undefined
1576
- ? undefined
1577
- : snapshotNullableValue(parameters.credential),
1578
- error: snapshotError(parameters.error),
1579
- ...snapshotInputProperty(parameters.input),
1580
- method: snapshotMethod(parameters.method),
1581
- request: snapshotValue(parameters.request),
1582
- }) as never
1583
- }
1584
-
1585
- function createPaymentFailedContext(parameters: {
1586
- capturedRequest?: Method.CapturedRequest | undefined
1587
- challenge: Challenge.Challenge
1588
- credential: Credential.Credential | null
1589
- error: Errors.PaymentError
1590
- input?: unknown
1591
- method: Method.Method | ServerMethodDescriptor
1592
- request: Record<string, unknown>
1593
- retryChallenge?: Challenge.Challenge | undefined
1594
- submittedChallenge?: Challenge.Challenge | undefined
1595
- }): PaymentFailedContext {
1596
- return Object.freeze({
1597
- ...(parameters.capturedRequest
1598
- ? { capturedRequest: snapshotCapturedRequest(parameters.capturedRequest) }
1599
- : {}),
1600
- challenge: snapshotValue(parameters.challenge),
1601
- credential: snapshotNullableValue(parameters.credential),
1602
- error: snapshotError(parameters.error),
1603
- ...snapshotInputProperty(parameters.input),
1604
- method: snapshotMethod(parameters.method),
1605
- request: snapshotValue(parameters.request),
1606
- ...(parameters.retryChallenge
1607
- ? { retryChallenge: snapshotValue(parameters.retryChallenge) }
1608
- : {}),
1609
- ...(parameters.submittedChallenge
1610
- ? { submittedChallenge: snapshotValue(parameters.submittedChallenge) }
1611
- : {}),
1612
- }) as never
1613
- }
1614
-
1615
- function createPaymentSuccessContext(parameters: {
1616
- capturedRequest?: Method.CapturedRequest | undefined
1617
- challenge: Challenge.Challenge
1618
- credential?: Credential.Credential | undefined
1619
- envelope?: Method.VerifiedChallengeEnvelope | undefined
1620
- input?: unknown
1621
- method: Method.Method | ServerMethodDescriptor
1622
- receipt: Receipt.Receipt
1623
- request: Record<string, unknown>
1624
- }): PaymentSuccessContext {
1625
- return Object.freeze({
1626
- ...(parameters.capturedRequest
1627
- ? { capturedRequest: snapshotCapturedRequest(parameters.capturedRequest) }
1628
- : {}),
1629
- challenge: snapshotValue(parameters.challenge),
1630
- ...(parameters.credential ? { credential: snapshotValue(parameters.credential) } : {}),
1631
- ...(parameters.envelope ? { envelope: snapshotVerifiedEnvelope(parameters.envelope) } : {}),
1632
- ...snapshotInputProperty(parameters.input),
1633
- method: snapshotMethod(parameters.method),
1634
- receipt: snapshotValue(parameters.receipt),
1635
- request: snapshotValue(parameters.request),
1636
- }) as never
1637
- }
1638
-
1639
- function snapshotMethod<method extends Pick<Method.Method, 'intent' | 'name'>>(
1640
- method: method,
1641
- ): ServerMethodDescriptor<method> {
1642
- return Object.freeze({
1643
- intent: method.intent,
1644
- name: method.name,
1645
- }) as ServerMethodDescriptor<method>
1646
- }
1647
-
1648
- function snapshotError<error extends Errors.PaymentError | undefined>(error: error): error {
1649
- if (!error) return error
1650
- const snapshot = Object.assign(Object.create(Object.getPrototypeOf(error)), error)
1651
- Object.defineProperties(snapshot, {
1652
- message: { value: error.message, enumerable: false },
1653
- name: { value: error.name, enumerable: false },
1654
- })
1655
- return Object.freeze(snapshot) as error
1656
- }
1657
-
1658
- function snapshotVerifiedEnvelope(
1659
- envelope: Method.VerifiedChallengeEnvelope,
1660
- ): Method.VerifiedChallengeEnvelope {
1661
- return Object.freeze({
1662
- capturedRequest: snapshotCapturedRequest(envelope.capturedRequest),
1663
- challenge: snapshotValue(envelope.challenge),
1664
- credential: snapshotValue(envelope.credential),
1665
- request: snapshotValue(envelope.request),
1666
- }) as Method.VerifiedChallengeEnvelope
1667
- }
1668
-
1669
- function snapshotCapturedRequest(capturedRequest: Method.CapturedRequest): Method.CapturedRequest {
1670
- return Object.freeze({
1671
- headers: new Headers(capturedRequest.headers),
1672
- hasBody: capturedRequest.hasBody,
1673
- method: capturedRequest.method,
1674
- url: new URL(capturedRequest.url),
1675
- })
1676
- }
1677
-
1678
- function snapshotNullableValue<value>(value: value | null): value | null {
1679
- if (value === null) return null
1680
- return snapshotValue(value)
1681
- }
1682
-
1683
- function snapshotValue<value>(value: value): value {
1684
- try {
1685
- return freezeSnapshot(structuredClone(value))
1686
- } catch {
1687
- return freezeSnapshot(value)
1688
- }
1689
- }
1690
-
1691
- function snapshotInputProperty(input: unknown): { input: unknown } | {} {
1692
- if (input === undefined) return {}
1693
- const snapshot = snapshotTransportInput(input)
1694
- return snapshot === undefined ? {} : { input: snapshot }
1695
- }
1696
-
1697
- function snapshotTransportInput<input>(input: input): input | undefined {
1698
- if (input instanceof globalThis.Request) {
1699
- try {
1700
- return new globalThis.Request(input.url, {
1701
- headers: new Headers(input.headers),
1702
- method: input.method,
1703
- }) as input
1704
- } catch {
1705
- return undefined
1706
- }
1707
- }
1708
- try {
1709
- return freezeSnapshot(structuredClone(input))
1710
- } catch {
1711
- warnOnce(
1712
- Warnings.transportInputSnapshot,
1713
- 'Could not clone server event input; omitting `context.input`. Use `capturedRequest` for request correlation.',
1714
- )
1715
- return undefined
1716
- }
1717
- }
1718
-
1719
- // Event payloads are cloned before listeners see them; shallow freezing keeps
1720
- // the guard simple while preventing top-level mutation of receipts/challenges.
1721
- function freezeSnapshot<value>(value: value): value {
1722
- if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value
1723
- Object.freeze(value)
1724
- return value
1725
- }
1726
-
1727
- function isServiceWorkerRequest(input: unknown): input is Request {
1728
- return (
1729
- input instanceof globalThis.Request &&
1730
- new URL(input.url).searchParams.has(Html.params.serviceWorker)
1731
- )
1732
- }
1733
-
1734
- function createServiceWorkerResponse() {
1735
- return new Response(serviceWorker, {
1736
- status: 200,
1737
- headers: {
1738
- 'Content-Type': 'application/javascript',
1739
- 'Cache-Control': 'no-store',
1740
- },
1741
- })
1742
- }
1743
-
1744
- function isIssuedChallengeResponse(response: unknown): boolean {
1745
- return !(response instanceof globalThis.Response) || response.status === 402
1746
- }
1747
-
1748
- function getSafeCredentialReason(error: unknown): string | undefined {
1749
- if (error instanceof Credential.InvalidCredentialEncodingError) return error.message
1750
- if (error instanceof Credential.MissingAuthorizationHeaderError) return error.message
1751
- if (error instanceof Credential.MissingPaymentSchemeError) return error.message
1752
- return undefined
1753
- }
1754
-
1755
- const defaultRealm = 'MPP Payment'
1756
- const Warnings = {
1757
- realmFallback: 'realm-fallback',
1758
- transportInputSnapshot: 'transport-input-snapshot',
1759
- } as const
1760
- const missingReceiptResponseErrorName = 'MissingReceiptResponseError'
1761
- const missingReceiptResponseErrorMessage = 'withReceipt() requires a response argument'
1762
-
1763
- /** Error thrown when `withReceipt()` needs a response but none was provided. */
1764
- export class MissingReceiptResponseError extends Error {
1765
- override name = missingReceiptResponseErrorName
1766
-
1767
- constructor() {
1768
- super(missingReceiptResponseErrorMessage)
1769
- }
1770
- }
1771
-
1772
- /** Returns true when an error is the typed `withReceipt()` no-response sentinel. */
1773
- export function isMissingReceiptResponseError(
1774
- error: unknown,
1775
- ): error is MissingReceiptResponseError {
1776
- if (error instanceof MissingReceiptResponseError) return true
1777
- if (!error || typeof error !== 'object') return false
1778
- const value = error as { message?: unknown; name?: unknown }
1779
- return (
1780
- value.name === missingReceiptResponseErrorName &&
1781
- value.message === missingReceiptResponseErrorMessage
1782
- )
1783
- }
1784
-
1785
- function normalizeExpires(expires: z.DatetimeInput | undefined): string | undefined {
1786
- return expires === undefined ? undefined : z.toDatetimeString(expires)
1787
- }
1788
-
1789
- const _warned = new Set<string>()
1790
- function warnOnce(key: string, message: string) {
1791
- if (_warned.has(key)) return
1792
- _warned.add(key)
1793
- console.warn(`[mppx] ${message}`)
1794
- }
1795
-
1796
- /** Extracts hostname from the captured request URL, falling back to a default. */
1797
- function resolveRealmFromCapturedRequest(capturedRequest: Method.CapturedRequest): string {
1798
- try {
1799
- const { protocol, hostname } = capturedRequest.url
1800
- if (/^https?:$/.test(protocol) && hostname) return hostname
1801
- } catch {}
1802
- warnOnce(
1803
- Warnings.realmFallback,
1804
- `Could not auto-detect realm from request. Falling back to "${defaultRealm}". Set \`realm\` in Mppx.create() or the MPP_REALM env var.`,
1805
- )
1806
- return defaultRealm
1807
- }
1808
-
1809
- async function resolveRouteChallenge(parameters: {
1810
- capturedRequest?: Method.CapturedRequest | undefined
1811
- credential?: Credential.Credential | null | undefined
1812
- defaults?: Record<string, unknown> | undefined
1813
- description?: string | undefined
1814
- expires?: string | undefined
1815
- meta?: Record<string, string> | undefined
1816
- method: Method.Method
1817
- realm?: string | undefined
1818
- request?: Method.RequestFn<Method.Method> | undefined
1819
- routeRequest: Record<string, unknown>
1820
- secretKey: string
1821
- }): Promise<{
1822
- challenge: Challenge.Challenge
1823
- parsedRequest: Record<string, unknown>
1824
- request: Record<string, unknown>
1825
- }> {
1826
- // Resolve the route's canonical request exactly as the handler path does:
1827
- const request = await (async () => {
1828
- // start from defaults + route options, then let the method request hook
1829
- const merged = { ...parameters.defaults, ...parameters.routeRequest }
1830
- // normalize or enrich it using the captured request and credential.
1831
- return parameters.request
1832
- ? ((await parameters.request({
1833
- capturedRequest: parameters.capturedRequest,
1834
- credential: parameters.credential,
1835
- request: merged,
1836
- } as never)) as Record<string, unknown>)
1837
- : merged
1838
- })()
1839
-
1840
- const effectiveRealm =
1841
- parameters.realm ??
1842
- (parameters.capturedRequest
1843
- ? resolveRealmFromCapturedRequest(parameters.capturedRequest)
1844
- : defaultRealm)
1845
-
1846
- const challenge = Challenge.fromMethod(parameters.method, {
1847
- description: parameters.description,
1848
- expires: parameters.expires,
1849
- meta: parameters.meta,
1850
- realm: effectiveRealm,
1851
- request: request as never,
1852
- secretKey: parameters.secretKey,
1853
- })
1854
-
1855
- return {
1856
- challenge,
1857
- parsedRequest: challenge.request as Record<string, unknown>,
1858
- request,
1859
- }
1860
- }
1861
-
1862
- function createFallbackChallenge(parameters: {
1863
- capturedRequest?: Method.CapturedRequest | undefined
1864
- defaults: Record<string, unknown>
1865
- description?: string | undefined
1866
- expires?: string | undefined
1867
- meta?: Record<string, string> | undefined
1868
- method: Method.Method
1869
- realm?: string | undefined
1870
- routeRequest: Record<string, unknown>
1871
- secretKey: string
1872
- }) {
1873
- return Challenge.fromMethod(parameters.method, {
1874
- description: parameters.description,
1875
- expires: parameters.expires,
1876
- meta: parameters.meta,
1877
- realm:
1878
- parameters.realm ??
1879
- (parameters.capturedRequest
1880
- ? resolveRealmFromCapturedRequest(parameters.capturedRequest)
1881
- : defaultRealm),
1882
- request: { ...parameters.defaults, ...parameters.routeRequest } as never,
1883
- secretKey: parameters.secretKey,
1884
- })
1885
- }
1886
-
1887
- /**
1888
- * Captures the transport request into a frozen snapshot at the start of the
1889
- * verification flow. This snapshot is threaded through request() → verify() →
1890
- * respond() → respondReceipt() so every hook sees the same authoritative
1891
- * request state — preventing the raw transport input from being re-read or
1892
- * mutated between verification steps.
1893
- *
1894
- * Note: Object.freeze is shallow — it prevents reassigning top-level properties
1895
- * but does not deep-freeze mutable class instances like Headers or URL. This is
1896
- * an accidental-mutation guard for trusted server events, not a security boundary.
1897
- */
1898
- async function captureRequest(
1899
- transport: Transport.AnyTransport,
1900
- input: unknown,
1901
- ): Promise<Method.CapturedRequest> {
1902
- const capturedRequest = transport.captureRequest
1903
- ? await transport.captureRequest(input)
1904
- : captureRequestFromInput(input)
1905
-
1906
- return Object.freeze(capturedRequest)
1907
- }
1908
-
1909
- function captureRequestFromInput(input: unknown): Method.CapturedRequest {
1910
- const source = input as {
1911
- body?: unknown
1912
- headers?: HeadersInit | undefined
1913
- method?: string | undefined
1914
- url?: string | URL | undefined
1915
- }
1916
-
1917
- return {
1918
- headers: new Headers(source.headers),
1919
- hasBody: source.body === undefined ? undefined : source.body !== null,
1920
- method: source.method ?? 'POST',
1921
- url: Transport.safeUrl(source.url),
1922
- }
1923
- }
1924
-
1925
- const coreBindingFields = ['amount', 'currency', 'recipient'] as const
1926
- const methodBindingFields = ['chainId', 'memo', 'sessionProtocol', 'splits', 'unitType'] as const
1927
- const pinnedRequestBindingFields = [...coreBindingFields, ...methodBindingFields] as const
1928
-
1929
- type CoreBindingField = (typeof coreBindingFields)[number]
1930
- type MethodBindingField = (typeof methodBindingFields)[number]
1931
- type PinnedRequestBindingField = (typeof pinnedRequestBindingFields)[number]
1932
- type PinnedChallengeField = 'method' | 'intent' | 'realm' | 'opaque' | PinnedRequestBindingField
1933
- type StableBinding = Record<string, unknown>
1934
-
1935
- function getChallengeBindingMismatch(
1936
- expectedChallenge: Challenge.Challenge,
1937
- actualChallenge: Challenge.Challenge,
1938
- stableBinding?: Method.StableBindingFn<Method.Method> | undefined,
1939
- ): string | undefined {
1940
- if (!stableBinding) return getPinnedChallengeMismatch(expectedChallenge, actualChallenge)
1941
-
1942
- for (const field of ['method', 'intent', 'realm'] as const) {
1943
- if (actualChallenge[field] !== expectedChallenge[field]) return field
1944
- }
1945
-
1946
- if (!opaqueValuesMatch(expectedChallenge.meta, actualChallenge.meta)) return 'opaque'
1947
-
1948
- return getRequestBindingMismatch(
1949
- getStableBinding(expectedChallenge.request as Record<string, unknown>, stableBinding),
1950
- getStableBinding(actualChallenge.request as Record<string, unknown>, stableBinding),
1951
- )
1952
- }
1953
-
1954
- /**
1955
- * Compares only the fields that MUST be stable across request-hook transforms.
1956
- *
1957
- * This is NOT the primary integrity check — the HMAC binding (Challenge.verify)
1958
- * already covers every challenge field including opaque, digest, and the full
1959
- * serialized request. This function exists as a secondary safety net for the
1960
- * case where the `request()` hook produces credential-dependent output, causing
1961
- * the recomputed challenge to differ from the original in non-economic fields
1962
- * (e.g. `feePayer`). We only need to verify that the economically significant
1963
- * subset hasn't drifted.
1964
- */
1965
- function getPinnedChallengeMismatch(
1966
- expectedChallenge: Challenge.Challenge,
1967
- actualChallenge: Challenge.Challenge,
1968
- ): PinnedChallengeField | undefined {
1969
- for (const field of ['method', 'intent', 'realm'] as const) {
1970
- if (actualChallenge[field] !== expectedChallenge[field]) return field
1971
- }
1972
-
1973
- if (!opaqueValuesMatch(expectedChallenge.meta, actualChallenge.meta)) return 'opaque'
1974
-
1975
- return getPinnedRequestBindingMismatch(
1976
- expectedChallenge.request as Record<string, unknown>,
1977
- actualChallenge.request as Record<string, unknown>,
1978
- )
1979
- }
1980
-
1981
- function getPinnedRequestBindingMismatch(
1982
- expectedRequest: Record<string, unknown>,
1983
- actualRequest: Record<string, unknown>,
1984
- ): PinnedRequestBindingField | undefined {
1985
- const expected = getPinnedRequestBinding(expectedRequest)
1986
- const actual = getPinnedRequestBinding(actualRequest)
1987
-
1988
- return (
1989
- getCoreBindingMismatch(expected.coreBinding, actual.coreBinding) ??
1990
- getMethodBindingMismatch(expected.methodBinding, actual.methodBinding)
1991
- )
1992
- }
1993
-
1994
- function getCoreBindingMismatch(
1995
- expected: CoreBinding,
1996
- actual: CoreBinding,
1997
- ): CoreBindingField | undefined {
1998
- return coreBindingFields.find((field) => !isDeepStrictEqual(expected[field], actual[field]))
1999
- }
2000
-
2001
- function getMethodBindingMismatch(
2002
- expected: MethodBinding,
2003
- actual: MethodBinding,
2004
- ): MethodBindingField | undefined {
2005
- return methodBindingFields.find((field) => !isDeepStrictEqual(expected[field], actual[field]))
2006
- }
2007
-
2008
- function getPinnedRequestBinding(request: Record<string, unknown>): PinnedRequestBinding {
2009
- const methodDetails = (request.methodDetails ?? {}) as Record<string, unknown>
2010
- const amount = normalizeScalar(request.amount ?? methodDetails.amount)
2011
- const chainId = normalizeScalar(request.chainId ?? methodDetails.chainId)
2012
- const currency = normalizeScalar(request.currency ?? methodDetails.currency)
2013
- const memo = normalizeHex(methodDetails.memo)
2014
- const recipient = normalizeScalar(request.recipient ?? methodDetails.recipient)
2015
- const sessionProtocol = normalizeScalar(methodDetails.sessionProtocol)
2016
- const splits = normalizeComparable(methodDetails.splits)
2017
- const unitType = normalizeScalar(request.unitType ?? methodDetails.unitType)
2018
-
2019
- return {
2020
- coreBinding: {
2021
- ...(amount !== undefined ? { amount } : {}),
2022
- ...(currency !== undefined ? { currency } : {}),
2023
- ...(recipient !== undefined ? { recipient } : {}),
2024
- },
2025
- methodBinding: {
2026
- ...(chainId !== undefined ? { chainId } : {}),
2027
- ...(memo !== undefined ? { memo } : {}),
2028
- ...(sessionProtocol !== undefined ? { sessionProtocol } : {}),
2029
- ...(splits !== undefined ? { splits } : {}),
2030
- ...(unitType !== undefined ? { unitType } : {}),
2031
- },
2032
- }
2033
- }
2034
-
2035
- function getRequestBindingMismatch(
2036
- expected: StableBinding,
2037
- actual: StableBinding,
2038
- ): string | undefined {
2039
- const fields = [
2040
- ...Object.keys(expected),
2041
- ...Object.keys(actual).filter((key) => !(key in expected)),
2042
- ]
2043
-
2044
- return fields.find(
2045
- (field) =>
2046
- !isDeepStrictEqual(normalizeComparable(expected[field]), normalizeComparable(actual[field])),
2047
- )
2048
- }
2049
-
2050
- function getStableBinding(
2051
- request: Record<string, unknown>,
2052
- stableBinding: Method.StableBindingFn<Method.Method>,
2053
- ): StableBinding {
2054
- return stableBinding(request as never)
2055
- }
2056
-
2057
- /** Top-level economic fields that should never drift after challenge issuance. */
2058
- type CoreBinding = {
2059
- [field in CoreBindingField]?: string
2060
- }
2061
-
2062
- /** Method-specific fields that are pinned by the fallback binding check. */
2063
- type MethodBinding = {
2064
- [field in MethodBindingField]?: unknown
2065
- }
2066
-
2067
- /** Normalized request subset used when a method does not provide a custom stable binding. */
2068
- type PinnedRequestBinding = {
2069
- coreBinding: CoreBinding
2070
- methodBinding: MethodBinding
2071
- }
2072
-
2073
- function normalizeScalar(value: unknown): string | undefined {
2074
- return value === undefined ? undefined : String(value)
2075
- }
2076
-
2077
- function normalizeHex(value: unknown): string | undefined {
2078
- if (value === undefined) return undefined
2079
-
2080
- const normalized = String(value)
2081
- return normalized.startsWith('0x') ? normalized.toLowerCase() : normalized
2082
- }
2083
-
2084
- function normalizeComparable(value: unknown): unknown {
2085
- if (value === undefined) return undefined
2086
- if (Array.isArray(value)) return value.map(normalizeComparable)
2087
-
2088
- if (value && typeof value === 'object') {
2089
- return Object.fromEntries(
2090
- Object.entries(value as Record<string, unknown>)
2091
- .sort(([left], [right]) => left.localeCompare(right))
2092
- .map(([key, nested]) => [key, normalizeComparable(nested)]),
2093
- )
2094
- }
2095
-
2096
- return typeof value === 'string' ? normalizeHex(value) : value
2097
- }
2098
-
2099
- function opaqueValuesMatch(
2100
- expected: Record<string, string> | undefined,
2101
- actual: Record<string, string> | undefined,
2102
- ): boolean {
2103
- return isDeepStrictEqual(expected, actual)
2104
- }
2105
-
2106
- function hydrateCredentialMeta<payload>(
2107
- credential: Credential.Credential<payload>,
2108
- ): Credential.Credential<payload> {
2109
- const { challenge } = credential
2110
- if (challenge.opaque === undefined) return credential
2111
- const hydratedChallenge = Challenge.Schema.parse({
2112
- ...challenge,
2113
- meta: PaymentRequest.deserialize(challenge.opaque),
2114
- })
2115
- return {
2116
- ...credential,
2117
- challenge: hydratedChallenge,
2118
- }
2119
- }
2120
-
2121
- function withParsedCredentialPayload<payload>(
2122
- credential: Credential.Credential,
2123
- payload: payload,
2124
- ): Credential.Credential<payload> {
2125
- return {
2126
- ...credential,
2127
- payload,
2128
- }
2129
- }
2130
- export type MethodFn<
2131
- method extends Method.Method,
2132
- transport extends Transport.AnyTransport,
2133
- defaults extends Record<string, unknown>,
2134
- > = (
2135
- options: MethodFn.Options<method, defaults>,
2136
- ) => (input: Transport.InputOf<transport>) => Promise<MethodFn.Response<transport>>
2137
- /** @internal */
2138
- export type AnyMethodFn = (options: any) => (input: any) => Promise<any>
2139
- /** A MethodFn tagged with its source Method (set by `create()`). @internal */
2140
- type AnyMethodFnWithMethod = AnyMethodFn & { _method: Method.AnyServer }
2141
-
2142
- /** @internal */
2143
- declare namespace MethodFn {
2144
- export type Options<
2145
- method extends Method.Method,
2146
- defaults extends Record<string, unknown> = Record<string, unknown>,
2147
- > = {
2148
- /** Optional human-readable description of the payment. */
2149
- description?: string | undefined
2150
- /** Optional challenge expiration timestamp (ISO 8601) or Date. */
2151
- expires?: z.DatetimeInput | undefined
2152
- /** Optional server-defined correlation data (serialized as `opaque` in the request). Flat string-to-string map; clients MUST NOT modify. */
2153
- meta?: Record<string, string> | undefined
2154
- /** Optional route/resource scope bound via reserved challenge metadata. */
2155
- scope?: string | undefined
2156
- } & Method.WithDefaults<z.input<method['schema']['request']>, defaults>
2157
-
2158
- export type Response<transport extends Transport.AnyTransport = Transport.Http> =
2159
- | {
2160
- challenge: Transport.ChallengeOutputOf<transport>
2161
- status: 402
2162
- }
2163
- | {
2164
- status: 200
2165
- withReceipt: Transport.WithReceipt<transport>
2166
- }
2167
- }
2168
-
2169
- /** A configured handler — the return value of e.g. `mppx.charge({ ... })`. @internal */
2170
- type ConfiguredHandler = ((input: Request) => Promise<MethodFn.Response<Transport.Http>>) & {
2171
- _internal: {
2172
- name: string
2173
- intent: string
2174
- html: Html.Options | undefined
2175
- meta?: Record<string, string> | undefined
2176
- scope?: string | undefined
2177
- _canonicalRequest: Record<string, unknown>
2178
- _stableBinding?: Method.StableBindingFn<Method.Method> | undefined
2179
- }
2180
- }
2181
-
2182
- const paymentAuthChallengeHeader = Constants.Headers.wwwAuthenticate
2183
-
2184
- const challengeHeaderMerges = [
2185
- {
2186
- name: paymentAuthChallengeHeader,
2187
- values: (context) =>
2188
- context.challengeEntries
2189
- .map((entry) =>
2190
- (entry.result.challenge as Response).headers.get(paymentAuthChallengeHeader),
2191
- )
2192
- .filter((value): value is string => value !== null),
2193
- merge: (values) => values,
2194
- },
2195
- {
2196
- name: x402_Types.paymentRequiredHeader,
2197
- values: (context) =>
2198
- context.negotiatedChallengeResponses
2199
- .map((response) => response.headers.get(x402_Types.paymentRequiredHeader))
2200
- .filter((value): value is string => value !== null),
2201
- merge: mergeX402PaymentRequiredHeaders,
2202
- },
2203
- ] satisfies readonly ChallengeHeaderMerge[]
2204
-
2205
- /** An entry for `compose()`: a method reference, handler function ref, or string key paired with its options. */
2206
- type ComposeEntry<methods extends readonly Method.AnyServer[]> =
2207
- | {
2208
- [i in keyof methods]: readonly [
2209
- methods[i],
2210
- MethodFn.Options<methods[i], NonNullable<methods[i]['defaults']>>,
2211
- ]
2212
- }[number]
2213
- | {
2214
- [i in keyof methods]: readonly [
2215
- `${methods[i]['name']}/${methods[i]['intent']}`,
2216
- MethodFn.Options<methods[i], NonNullable<methods[i]['defaults']>>,
2217
- ]
2218
- }[number]
2219
- | {
2220
- [i in keyof methods]: readonly [
2221
- MethodFn<methods[i], any, any> & { _method: methods[i] },
2222
- MethodFn.Options<methods[i], NonNullable<methods[i]['defaults']>>,
2223
- ]
2224
- }[number]
2225
-
2226
- /**
2227
- * Combines multiple configured payment handlers into a single route handler
2228
- * that presents all methods to the client via multiple `WWW-Authenticate` headers.
2229
- *
2230
- * When no credential is present, all handlers are called and their challenges
2231
- * are merged into a single 402 response. When a credential is present, it is
2232
- * dispatched to the handler matching the credential's `method`+`intent`.
2233
- *
2234
- * @example
2235
- * ```ts
2236
- * import { Mppx, tempo, stripe } from 'mppx/server'
2237
- *
2238
- * const mppx = Mppx.create({
2239
- * methods: [tempo(), stripe()],
2240
- * secretKey: process.env.PAYMENT_SECRET_KEY,
2241
- * })
2242
- *
2243
- * app.get('/api/resource', async (req) => {
2244
- * const result = await Mppx.compose(
2245
- * mppx['tempo/charge']({ amount: '100', currency: USDC, recipient: '0x...' }),
2246
- * mppx['stripe/charge']({ amount: '100', currency: 'usd' }),
2247
- * )(req)
2248
- * if (result.status === 402) return result.challenge
2249
- * return result.withReceipt(new Response('OK'))
2250
- * })
2251
- * ```
2252
- */
2253
- type ComposeHtmlOptions = Html.Config
2254
-
2255
- export function compose(
2256
- ...args: readonly unknown[]
2257
- ): (input: Request) => Promise<MethodFn.Response<Transport.Http>> {
2258
- // Extract optional html options from last argument
2259
- const last = args[args.length - 1]
2260
- const composeOptions: Html.Options | undefined =
2261
- typeof last === 'object' &&
2262
- last !== null &&
2263
- typeof last !== 'function' &&
2264
- !('_internal' in last)
2265
- ? (() => {
2266
- const opts = last as ComposeHtmlOptions
2267
- return {
2268
- config: {},
2269
- content: '',
2270
- formatAmount: () => '',
2271
- text: opts.text,
2272
- theme: opts.theme,
2273
- }
2274
- })()
2275
- : undefined
2276
- const handlers = (composeOptions ? args.slice(0, -1) : args) as readonly ((
2277
- input: Request,
2278
- ) => Promise<MethodFn.Response<Transport.Http>>)[]
2279
-
2280
- if (handlers.length === 0) throw new Error('compose() requires at least one handler')
2281
-
2282
- return async (input: Request) => {
2283
- // Serve service worker for html-enabled compose
2284
- if (new URL(input.url).searchParams.has(Html.params.serviceWorker)) {
2285
- const hasHtml = handlers.some((h) => (h as ConfiguredHandler)._internal?.html)
2286
- if (hasHtml)
2287
- return {
2288
- status: 402,
2289
- challenge: new Response(serviceWorker, {
2290
- status: 200,
2291
- headers: {
2292
- 'Content-Type': 'application/javascript',
2293
- 'Cache-Control': 'no-store',
2294
- },
2295
- }),
2296
- } as MethodFn.Response<Transport.Http>
2297
- }
2298
-
2299
- // Try to extract a Payment credential to decide whether to dispatch or challenge.
2300
- // Only gate on the Payment scheme — other auth schemes (Bearer, Basic, etc.)
2301
- // should fall through to the merged-402 path so all offers are presented.
2302
- const header = input.headers.get(Constants.Headers.authorization)
2303
- const paymentHeader = header ? Credential.extractPaymentScheme(header) : null
2304
-
2305
- if (paymentHeader) {
2306
- // Parse the credential to find method+intent for dispatch.
2307
- let credential: Credential.Credential | undefined
2308
- try {
2309
- credential = hydrateCredentialMeta(Credential.deserialize(paymentHeader))
2310
- } catch {}
2311
-
2312
- if (credential) {
2313
- const { method: credMethod, intent: credIntent } = credential.challenge
2314
- const credReq = credential.challenge.request as Record<string, unknown>
2315
-
2316
- // Filter by name+intent, then narrow by comparing stable request fields
2317
- // from the echoed challenge against each handler's canonical request.
2318
- // Uses the schema-parsed canonical form (not raw options) so that
2319
- // transformed fields (e.g. amount with decimals) match correctly.
2320
- // Also checks inside methodDetails for fields moved there by transforms.
2321
- const candidates = handlers.filter((h) => {
2322
- try {
2323
- const internal = (h as ConfiguredHandler)._internal
2324
- if (!internal || internal.name !== credMethod || internal.intent !== credIntent)
2325
- return false
2326
- const mismatch = internal._stableBinding
2327
- ? getRequestBindingMismatch(
2328
- getStableBinding(internal._canonicalRequest, internal._stableBinding),
2329
- getStableBinding(credReq, internal._stableBinding),
2330
- )
2331
- : getPinnedRequestBindingMismatch(internal._canonicalRequest, credReq)
2332
- return !mismatch && opaqueValuesMatch(internal.meta, credential.challenge.meta)
2333
- } catch {
2334
- return false
2335
- }
2336
- })
2337
-
2338
- const match =
2339
- candidates[0] ??
2340
- handlers.find((h) => {
2341
- const meta = (h as ConfiguredHandler)._internal
2342
- return meta?.name === credMethod && meta?.intent === credIntent
2343
- })
2344
- if (match) return match(input)
2345
- }
2346
-
2347
- // Payment credential present but no matching handler — dispatch to first
2348
- // handler which will reject with an appropriate error (invalid challenge, etc.).
2349
- return handlers[0]!(input)
2350
- }
2351
-
2352
- // No credential — evaluate handlers sequentially so authorize()/renewal hooks
2353
- // can safely claim the request without racing each other.
2354
- const results: MethodFn.Response<Transport.Http>[] = []
2355
- for (const handler of handlers) {
2356
- const result = await handler(input)
2357
- if (result.status === 200) return result
2358
- results.push(result)
2359
- }
2360
-
2361
- const challengeEntries = (() => {
2362
- const entries: {
2363
- handler: ConfiguredHandler
2364
- challenge: Challenge.Challenge
2365
- result: Extract<MethodFn.Response<Transport.Http>, { status: 402 }>
2366
- }[] = []
2367
-
2368
- for (let i = 0; i < handlers.length; i++) {
2369
- const result = results[i]
2370
- if (result?.status !== 402) continue
2371
-
2372
- const response = result.challenge as Response
2373
- const wwwAuth = response.headers.get(paymentAuthChallengeHeader)
2374
- if (!wwwAuth) continue
2375
-
2376
- entries.push({
2377
- handler: handlers[i] as ConfiguredHandler,
2378
- challenge: Challenge.deserialize(wwwAuth),
2379
- result,
2380
- })
2381
- }
2382
-
2383
- const acceptPayment = input.headers.get(Constants.Headers.acceptPayment)
2384
- if (!acceptPayment) return entries
2385
-
2386
- try {
2387
- const ranked = AcceptPayment.rank(
2388
- entries.map((entry) => entry.challenge),
2389
- AcceptPayment.parse(acceptPayment),
2390
- )
2391
- if (ranked.length === 0) return entries
2392
-
2393
- const entriesById = new Map(entries.map((entry) => [entry.challenge.id, entry] as const))
2394
- return ranked.map((challenge) => entriesById.get(challenge.id)!)
2395
- } catch {
2396
- return entries
2397
- }
2398
- })()
2399
-
2400
- const challengeResponses = results.flatMap((result) =>
2401
- result.status === 402 ? [result.challenge as Response] : [],
2402
- )
2403
- const unnegotiatedX402Responses =
2404
- input.headers.has(Constants.Headers.acceptPayment) || challengeEntries.length === 0
2405
- ? []
2406
- : challengeResponses.filter(
2407
- (response) =>
2408
- response.headers.has(x402_Types.paymentRequiredHeader) &&
2409
- !response.headers.has(paymentAuthChallengeHeader),
2410
- )
2411
- const negotiatedChallengeResponses =
2412
- challengeEntries.length > 0
2413
- ? [
2414
- ...challengeEntries.map((entry) => entry.result.challenge as Response),
2415
- ...unnegotiatedX402Responses,
2416
- ]
2417
- : challengeResponses
2418
-
2419
- // Merge challenge headers from the negotiated 402 responses.
2420
- const mergedHeaders = new Headers()
2421
- mergedHeaders.set('Cache-Control', 'no-store')
2422
-
2423
- for (const header of challengeHeaderMerges) {
2424
- for (const value of header.merge(
2425
- header.values({
2426
- challengeEntries,
2427
- challengeResponses,
2428
- negotiatedChallengeResponses,
2429
- }),
2430
- )) {
2431
- mergedHeaders.append(header.name, value)
2432
- }
2433
- }
2434
-
2435
- // Collect html-enabled handlers and their challenges
2436
- const htmlEntries = challengeEntries.filter((entry) => entry.handler._internal?.html)
2437
-
2438
- const wantsHtml = input.headers.get('Accept')?.includes('text/html')
2439
- if (wantsHtml && htmlEntries.length > 0) {
2440
- const { theme, text } = Html.resolveOptions(
2441
- // Use compose-level options or first html-enabled method's config for the page shell
2442
- composeOptions ?? htmlEntries[0]?.handler._internal.html ?? ({} as Html.Options),
2443
- )
2444
-
2445
- // Build data map keyed by challenge.id
2446
- const dataMap: Record<string, Html.Data> = {}
2447
- for (let i = 0; i < htmlEntries.length; i++) {
2448
- const entry = htmlEntries[i]!
2449
- dataMap[entry.challenge.id] = {
2450
- label: entry.handler._internal.name,
2451
- rootId: `${Html.ids.root}-${i}`,
2452
- formattedAmount: await entry.handler._internal.html!.formatAmount(
2453
- entry.challenge.request,
2454
- ),
2455
- config: entry.handler._internal.html!.config,
2456
- challenge: entry.challenge as never,
2457
- text,
2458
- theme,
2459
- }
2460
- }
2461
-
2462
- mergedHeaders.set('Content-Type', 'text/html; charset=utf-8')
2463
-
2464
- const firstData = Object.values(dataMap)[0]!
2465
- const body = Html.render({
2466
- entries: htmlEntries.map((entry) => ({
2467
- challenge: entry.challenge,
2468
- content: entry.handler._internal.html!.content,
2469
- })),
2470
- dataMap,
2471
- formattedAmount: firstData.formattedAmount,
2472
- panels: true,
2473
- text,
2474
- theme,
2475
- })
2476
-
2477
- return {
2478
- status: 402,
2479
- challenge: new Response(body, { status: 402, headers: mergedHeaders }),
2480
- }
2481
- }
2482
-
2483
- // Non-HTML fallback: prefer the first Payment-auth body, otherwise use
2484
- // the first transport-specific 402 body.
2485
- let body: string | null = null
2486
- const bodyResponses =
2487
- challengeEntries.length > 0
2488
- ? challengeEntries.map((entry) => entry.result.challenge as Response)
2489
- : challengeResponses
2490
- for (const response of bodyResponses) {
2491
- if (!body) {
2492
- const contentType = response.headers.get('Content-Type')
2493
- if (contentType) mergedHeaders.set('Content-Type', contentType)
2494
- body = await response.text()
2495
- break
2496
- }
2497
- }
2498
-
2499
- return {
2500
- status: 402,
2501
- challenge: new Response(body, { status: 402, headers: mergedHeaders }),
2502
- }
2503
- }
2504
- }
2505
-
2506
- type ChallengeHeaderMerge = {
2507
- name: string
2508
- values(context: {
2509
- challengeEntries: readonly {
2510
- handler: ConfiguredHandler
2511
- challenge: Challenge.Challenge
2512
- result: Extract<MethodFn.Response<Transport.Http>, { status: 402 }>
2513
- }[]
2514
- challengeResponses: readonly Response[]
2515
- negotiatedChallengeResponses: readonly Response[]
2516
- }): readonly string[]
2517
- merge(values: readonly string[]): readonly string[]
2518
- }
2519
-
2520
- function mergeX402PaymentRequiredHeaders(values: readonly string[]): readonly string[] {
2521
- if (values.length === 0) return []
2522
- const [first, ...rest] = values.map((value) => x402_Header.decodePaymentRequired(value))
2523
- if (!first) throw new Error('Expected at least one x402 payment-required header.')
2524
- const incompatible = rest.some(
2525
- (value) =>
2526
- !isDeepStrictEqual(value.resource, first.resource) ||
2527
- !isDeepStrictEqual(value.extensions, first.extensions),
2528
- )
2529
- if (incompatible)
2530
- return [
2531
- x402_Header.encodePaymentRequired({
2532
- ...first,
2533
- error: [first.error, 'Cannot merge x402 payment requirements with different resources.']
2534
- .filter((value): value is string => value !== undefined && value.length > 0)
2535
- .join('; '),
2536
- }),
2537
- ]
2538
- const error = [first.error, ...rest.map((value) => value.error)]
2539
- .filter((value): value is string => value !== undefined && value.length > 0)
2540
- .join('; ')
2541
- return [
2542
- x402_Header.encodePaymentRequired({
2543
- ...first,
2544
- accepts: [first.accepts, ...rest.map((value) => value.accepts)].flat(),
2545
- ...(error ? { error } : {}),
2546
- }),
2547
- ]
2548
- }
2549
-
2550
- /**
2551
- * Wraps a payment handler to create a Node.js HTTP listener.
2552
- *
2553
- * On 402: writes the challenge response and ends the connection.
2554
- * On 200: sets the Payment-Receipt header; caller should write response body.
2555
- *
2556
- * @example
2557
- * ```ts
2558
- * import * as http from 'node:http'
2559
- * import { Mppx } from 'mppx/server'
2560
- *
2561
- * const payment = Mppx.create({ ... })
2562
- *
2563
- * http.createServer(async (req, res) => {
2564
- * const result = await Mppx.toNodeListener(
2565
- * payment.charge({
2566
- * amount: '1', currency: '...', recipient: '0x...',
2567
- * }),
2568
- * )(req, res)
2569
- * if (result.status === 402) return
2570
- * res.end('OK')
2571
- * })
2572
- * ```
2573
- */
2574
- export function toNodeListener(
2575
- handler: (input: globalThis.Request) => Promise<MethodFn.Response<Transport.Http>>,
2576
- ): (req: IncomingMessage, res: ServerResponse) => Promise<MethodFn.Response<Transport.Http>> {
2577
- return async (req, res) => {
2578
- const result = await handler(Request.fromNodeListener(req, res))
2579
-
2580
- if (result.status === 402) {
2581
- await NodeListener.sendResponse(res, result.challenge as globalThis.Response)
2582
- } else {
2583
- const managementResponse = getManagementResponse(result)
2584
- if (managementResponse) {
2585
- await NodeListener.sendResponse(res, managementResponse)
2586
- return { challenge: managementResponse, status: 402 }
2587
- }
2588
-
2589
- const wrapped = result.withReceipt(new globalThis.Response()) as globalThis.Response
2590
- for (const [name, value] of wrapped.headers) res.setHeader(name, value)
2591
- }
2592
-
2593
- return result
2594
- }
2595
- }
2596
-
2597
- function getManagementResponse(
2598
- result: Extract<MethodFn.Response<Transport.Http>, { status: 200 }>,
2599
- ): globalThis.Response | null {
2600
- try {
2601
- return (result.withReceipt as () => globalThis.Response)()
2602
- } catch (error) {
2603
- if (isMissingReceiptResponseError(error)) {
2604
- return null
2605
- }
2606
- throw error
2607
- }
2608
- }
2609
-
2610
- /**
2611
- * Flattens a methods config tuple, preserving positional types.
2612
- * @internal
2613
- */
2614
- type FlattenMethods<methods extends Methods> = methods extends readonly [
2615
- infer head,
2616
- ...infer tail extends Methods,
2617
- ]
2618
- ? head extends readonly Method.AnyServer[]
2619
- ? readonly [...head, ...FlattenMethods<tail>]
2620
- : head extends Method.AnyServer
2621
- ? readonly [head, ...FlattenMethods<tail>]
2622
- : never
2623
- : readonly []