Skip to content

Commit e61de93

Browse files
authored
Add onProtectedRequest hook (x402-foundation#1010)
* add new onRequest hook * add unit tests
1 parent 3b7772c commit e61de93

11 files changed

Lines changed: 1021 additions & 426 deletions

File tree

typescript/packages/core/src/http/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export {
9999
ProcessSettleFailureResponse,
100100
RouteValidationError,
101101
RouteConfigurationError,
102+
ProtectedRequestHook,
102103
} from "./x402HTTPResourceServer";
103104
export {
104105
HTTPFacilitatorClient,

typescript/packages/core/src/http/x402HTTPResourceServer.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ export interface RouteConfig {
155155
*/
156156
export type RoutesConfig = Record<string, RouteConfig> | RouteConfig;
157157

158+
/**
159+
* Hook that runs on every request to a protected route, before payment processing.
160+
* Can grant access without payment, deny the request, or continue to payment flow.
161+
*
162+
* @returns
163+
* - `void` - Continue to payment processing (default behavior)
164+
* - `{ grantAccess: true }` - Grant access without requiring payment
165+
* - `{ abort: true; reason: string }` - Deny the request (returns 403)
166+
*/
167+
export type ProtectedRequestHook = (
168+
context: HTTPRequestContext,
169+
routeConfig: RouteConfig,
170+
) => Promise<void | { grantAccess: true } | { abort: true; reason: string }>;
171+
158172
/**
159173
* Compiled route for efficient matching
160174
*/
@@ -260,6 +274,7 @@ export class x402HTTPResourceServer {
260274
private compiledRoutes: CompiledRoute[] = [];
261275
private routesConfig: RoutesConfig;
262276
private paywallProvider?: PaywallProvider;
277+
private protectedRequestHooks: ProtectedRequestHook[] = [];
263278

264279
/**
265280
* Creates a new x402HTTPResourceServer instance.
@@ -287,6 +302,24 @@ export class x402HTTPResourceServer {
287302
}
288303
}
289304

305+
/**
306+
* Get the underlying x402ResourceServer instance.
307+
*
308+
* @returns The underlying x402ResourceServer instance
309+
*/
310+
get server(): x402ResourceServer {
311+
return this.ResourceServer;
312+
}
313+
314+
/**
315+
* Get the routes configuration.
316+
*
317+
* @returns The routes configuration
318+
*/
319+
get routes(): RoutesConfig {
320+
return this.routesConfig;
321+
}
322+
290323
/**
291324
* Initialize the HTTP resource server.
292325
*
@@ -325,6 +358,18 @@ export class x402HTTPResourceServer {
325358
return this;
326359
}
327360

361+
/**
362+
* Register a hook that runs on every request to a protected route, before payment processing.
363+
* Hooks are executed in order of registration. The first hook to return a non-void result wins.
364+
*
365+
* @param hook - The request hook function
366+
* @returns The x402HTTPResourceServer instance for chaining
367+
*/
368+
onProtectedRequest(hook: ProtectedRequestHook): this {
369+
this.protectedRequestHooks.push(hook);
370+
return this;
371+
}
372+
328373
/**
329374
* Process HTTP request and return response instructions
330375
* This is the main entry point for framework middleware
@@ -345,6 +390,24 @@ export class x402HTTPResourceServer {
345390
return { type: "no-payment-required" }; // No payment required for this route
346391
}
347392

393+
// Execute request hooks before any payment processing
394+
for (const hook of this.protectedRequestHooks) {
395+
const result = await hook(context, routeConfig);
396+
if (result && "grantAccess" in result) {
397+
return { type: "no-payment-required" };
398+
}
399+
if (result && "abort" in result) {
400+
return {
401+
type: "payment-error",
402+
response: {
403+
status: 403,
404+
headers: { "Content-Type": "application/json" },
405+
body: { error: result.reason },
406+
},
407+
};
408+
}
409+
}
410+
348411
// Normalize accepts field to array of payment options
349412
const paymentOptions = this.normalizePaymentOptions(routeConfig);
350413

typescript/packages/core/src/server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { x402ResourceServer } from "./x402ResourceServer";
2-
export type { ResourceConfig, ResourceInfo } from "./x402ResourceServer";
2+
export type { ResourceConfig, ResourceInfo, SettleResultContext } from "./x402ResourceServer";
33

44
export { HTTPFacilitatorClient } from "../http/httpFacilitatorClient";
55
export type { FacilitatorClient, FacilitatorConfig } from "../http/httpFacilitatorClient";

0 commit comments

Comments
 (0)