@agentcash/router 1.10.5 → 1.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.cjs +83 -23
- package/dist/index.d.cts +22 -3
- package/dist/index.d.ts +22 -3
- package/dist/index.js +83 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -150,6 +150,21 @@ The barrel forces every route module to load before the discovery handler walks
|
|
|
150
150
|
|
|
151
151
|
The `openapi.json` should be hosted at `GET <origin>/openapi.json`.
|
|
152
152
|
|
|
153
|
+
### 4. Unmatched route fallback
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// app/api/[[...path]]/route.ts
|
|
157
|
+
import { router } from '@/lib/router';
|
|
158
|
+
|
|
159
|
+
export const GET = router.notFound();
|
|
160
|
+
export const POST = router.notFound();
|
|
161
|
+
export const DELETE = router.notFound();
|
|
162
|
+
export const PUT = router.notFound();
|
|
163
|
+
export const PATCH = router.notFound();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This catches stale agent calls to API paths that no longer exist and returns a JSON 404 telling the client to rediscover the origin.
|
|
167
|
+
|
|
153
168
|
## Auth modes
|
|
154
169
|
|
|
155
170
|
| Method | Purpose |
|
package/dist/index.cjs
CHANGED
|
@@ -1207,7 +1207,7 @@ async function trySiwxFastPath(ctx, account) {
|
|
|
1207
1207
|
function shouldParseBodyEarly(incomingStrategy, routeEntry, pricing) {
|
|
1208
1208
|
if (incomingStrategy) return false;
|
|
1209
1209
|
if (!routeEntry.bodySchema) return false;
|
|
1210
|
-
return (pricing?.needsBody ?? false) || !!routeEntry.validateFn;
|
|
1210
|
+
return (pricing?.needsBody ?? false) || !!routeEntry.validateFn || !!routeEntry.checkoutSession;
|
|
1211
1211
|
}
|
|
1212
1212
|
|
|
1213
1213
|
// src/pipeline/steps/resolve-early-body.ts
|
|
@@ -2549,30 +2549,37 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
|
|
|
2549
2549
|
return errorResponse;
|
|
2550
2550
|
}
|
|
2551
2551
|
const extensions = await buildChallengeExtensions(ctx);
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
status: 402,
|
|
2555
|
-
headers: {
|
|
2556
|
-
"Content-Type": "application/json",
|
|
2557
|
-
"Cache-Control": "no-store"
|
|
2558
|
-
}
|
|
2559
|
-
});
|
|
2560
|
-
for (const strategy of getAllowedStrategies(ctx.routeEntry.protocols)) {
|
|
2552
|
+
let checkoutSession;
|
|
2553
|
+
if (ctx.routeEntry.checkoutSession) {
|
|
2561
2554
|
try {
|
|
2562
|
-
|
|
2555
|
+
checkoutSession = await ctx.routeEntry.checkoutSession({
|
|
2563
2556
|
request: ctx.request,
|
|
2564
|
-
|
|
2557
|
+
route: ctx.routeEntry.key,
|
|
2565
2558
|
body,
|
|
2566
|
-
price: challengePrice
|
|
2567
|
-
extensions,
|
|
2568
|
-
deps: ctx.deps,
|
|
2569
|
-
report: ctx.report
|
|
2559
|
+
price: challengePrice
|
|
2570
2560
|
});
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
}
|
|
2561
|
+
} catch (err) {
|
|
2562
|
+
const message = errorMessage(err, "Checkout session build failed");
|
|
2563
|
+
const responseBody2 = { success: false, error: message };
|
|
2564
|
+
const errorResponse = import_server5.NextResponse.json(responseBody2, { status: errorStatus(err, 500) });
|
|
2565
|
+
firePluginResponse(ctx, errorResponse, body, responseBody2, { message, cause: err });
|
|
2566
|
+
return errorResponse;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
const contributions = [];
|
|
2570
|
+
for (const strategy of getAllowedStrategies(ctx.routeEntry.protocols)) {
|
|
2571
|
+
try {
|
|
2572
|
+
contributions.push(
|
|
2573
|
+
await strategy.buildChallenge({
|
|
2574
|
+
request: ctx.request,
|
|
2575
|
+
routeEntry: ctx.routeEntry,
|
|
2576
|
+
body,
|
|
2577
|
+
price: challengePrice,
|
|
2578
|
+
extensions,
|
|
2579
|
+
deps: ctx.deps,
|
|
2580
|
+
report: ctx.report
|
|
2581
|
+
})
|
|
2582
|
+
);
|
|
2576
2583
|
} catch (err) {
|
|
2577
2584
|
const message = `${strategy.protocol} challenge build failed: ${errorMessage(err, String(err))}`;
|
|
2578
2585
|
ctx.report("critical", message);
|
|
@@ -2584,6 +2591,26 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
|
|
|
2584
2591
|
}
|
|
2585
2592
|
}
|
|
2586
2593
|
}
|
|
2594
|
+
const responsePayload = {
|
|
2595
|
+
...Object.assign({}, ...contributions.map((contribution) => contribution.body ?? {})),
|
|
2596
|
+
...failure && { error: failure.message ?? null, reason: failure.reason },
|
|
2597
|
+
...checkoutSession && { checkout_session: checkoutSession }
|
|
2598
|
+
};
|
|
2599
|
+
const responseBody = Object.keys(responsePayload).length ? JSON.stringify(responsePayload) : null;
|
|
2600
|
+
const response = new import_server5.NextResponse(responseBody, {
|
|
2601
|
+
status: 402,
|
|
2602
|
+
headers: {
|
|
2603
|
+
"Content-Type": "application/json",
|
|
2604
|
+
"Cache-Control": "no-store"
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
for (const contribution of contributions) {
|
|
2608
|
+
if (contribution.headers) {
|
|
2609
|
+
for (const [name, value] of Object.entries(contribution.headers)) {
|
|
2610
|
+
response.headers.set(name, value);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2587
2614
|
firePluginResponse(ctx, response);
|
|
2588
2615
|
return response;
|
|
2589
2616
|
}
|
|
@@ -3602,7 +3629,8 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
3602
3629
|
validateFn: void 0,
|
|
3603
3630
|
settlement: void 0,
|
|
3604
3631
|
mppInfo: void 0,
|
|
3605
|
-
hasCheckout: false
|
|
3632
|
+
hasCheckout: false,
|
|
3633
|
+
checkoutSession: void 0
|
|
3606
3634
|
};
|
|
3607
3635
|
}
|
|
3608
3636
|
fork() {
|
|
@@ -3693,7 +3721,8 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
3693
3721
|
if (resolvedOptions.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
|
|
3694
3722
|
if (resolvedOptions.payTo) next.#s.payTo = resolvedOptions.payTo;
|
|
3695
3723
|
if (resolvedOptions.mpp) next.#s.mppInfo = resolvedOptions.mpp;
|
|
3696
|
-
if (resolvedOptions.checkout) next.#s.hasCheckout = true;
|
|
3724
|
+
if (resolvedOptions.checkout || resolvedOptions.checkoutSession) next.#s.hasCheckout = true;
|
|
3725
|
+
if (resolvedOptions.checkoutSession) next.#s.checkoutSession = resolvedOptions.checkoutSession;
|
|
3697
3726
|
next.#s.billing = billing;
|
|
3698
3727
|
if (tickCost) next.#s.tickCost = tickCost;
|
|
3699
3728
|
if (unitType) next.#s.unitType = unitType;
|
|
@@ -4128,6 +4157,7 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
4128
4157
|
settlement: this.#s.settlement,
|
|
4129
4158
|
mppInfo: this.#s.mppInfo,
|
|
4130
4159
|
hasCheckout: this.#s.hasCheckout ? true : void 0,
|
|
4160
|
+
checkoutSession: this.#s.checkoutSession,
|
|
4131
4161
|
tickCost: this.#s.tickCost,
|
|
4132
4162
|
unitType: this.#s.unitType
|
|
4133
4163
|
};
|
|
@@ -4475,6 +4505,33 @@ function createLlmsTxtHandler(discovery) {
|
|
|
4475
4505
|
};
|
|
4476
4506
|
}
|
|
4477
4507
|
|
|
4508
|
+
// src/discovery/not-found.ts
|
|
4509
|
+
var import_server12 = require("next/server");
|
|
4510
|
+
function createNotFoundHandler(baseUrl) {
|
|
4511
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
4512
|
+
return async (request) => import_server12.NextResponse.json(
|
|
4513
|
+
{
|
|
4514
|
+
success: false,
|
|
4515
|
+
code: "route_not_found",
|
|
4516
|
+
error: "Route not found. Rediscover this origin and retry with the current discovery document.",
|
|
4517
|
+
requestedUrl: request.url,
|
|
4518
|
+
discovery: {
|
|
4519
|
+
openapi: `${normalizedBase}/openapi.json`,
|
|
4520
|
+
wellKnown: `${normalizedBase}/.well-known/x402`,
|
|
4521
|
+
llmsTxt: `${normalizedBase}/llms.txt`
|
|
4522
|
+
}
|
|
4523
|
+
},
|
|
4524
|
+
{
|
|
4525
|
+
status: 404,
|
|
4526
|
+
headers: {
|
|
4527
|
+
"Access-Control-Allow-Origin": "*",
|
|
4528
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
|
|
4529
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X"
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
);
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4478
4535
|
// src/index.ts
|
|
4479
4536
|
init_accepts();
|
|
4480
4537
|
init_constants();
|
|
@@ -5178,6 +5235,9 @@ function createRouter(config) {
|
|
|
5178
5235
|
llmsTxt() {
|
|
5179
5236
|
return createLlmsTxtHandler(config.discovery);
|
|
5180
5237
|
},
|
|
5238
|
+
notFound() {
|
|
5239
|
+
return createNotFoundHandler(resolvedBaseUrl);
|
|
5240
|
+
},
|
|
5181
5241
|
monitors() {
|
|
5182
5242
|
const result = [];
|
|
5183
5243
|
for (const [, entry] of registry.entries()) {
|
package/dist/index.d.cts
CHANGED
|
@@ -219,6 +219,15 @@ interface MppProtocolInfo {
|
|
|
219
219
|
intent?: string;
|
|
220
220
|
currency?: string;
|
|
221
221
|
}
|
|
222
|
+
type CheckoutSessionResponse = Record<string, unknown>;
|
|
223
|
+
interface CheckoutSessionContext<TBody = unknown> {
|
|
224
|
+
request: NextRequest;
|
|
225
|
+
route: string;
|
|
226
|
+
body: TBody | undefined;
|
|
227
|
+
/** Decimal-dollar price quoted for this 402 challenge. */
|
|
228
|
+
price: string;
|
|
229
|
+
}
|
|
230
|
+
type CheckoutSessionFn<TBody = unknown> = (ctx: CheckoutSessionContext<TBody>) => CheckoutSessionResponse | null | undefined | Promise<CheckoutSessionResponse | null | undefined>;
|
|
222
231
|
interface PaidOptions {
|
|
223
232
|
protocols?: ProtocolType[];
|
|
224
233
|
maxPrice?: string;
|
|
@@ -229,6 +238,14 @@ interface PaidOptions {
|
|
|
229
238
|
mpp?: MppProtocolInfo;
|
|
230
239
|
/** Signal in discovery that clients should use an explicit checkout flow before payment. */
|
|
231
240
|
checkout?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Build dynamic checkout review metadata for the router-owned 402 response body.
|
|
243
|
+
*
|
|
244
|
+
* The returned object is emitted as `{ "checkout_session": ... }` on the unpaid
|
|
245
|
+
* payment challenge response. Payment terms remain authoritative in the x402/MPP
|
|
246
|
+
* challenge headers.
|
|
247
|
+
*/
|
|
248
|
+
checkoutSession?: CheckoutSessionFn;
|
|
232
249
|
}
|
|
233
250
|
type PaidArg = (PaidOptions & {
|
|
234
251
|
price: string;
|
|
@@ -374,6 +391,7 @@ interface RouteEntry {
|
|
|
374
391
|
settlement?: SettlementLifecycle;
|
|
375
392
|
mppInfo?: MppProtocolInfo;
|
|
376
393
|
hasCheckout?: boolean;
|
|
394
|
+
checkoutSession?: CheckoutSessionFn;
|
|
377
395
|
/** Per-tick cost (decimal-dollar). Required when `metered` is true. */
|
|
378
396
|
tickCost?: string;
|
|
379
397
|
/** Cosmetic unit label for 402 challenges and client UIs. */
|
|
@@ -557,8 +575,8 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
|
|
|
557
575
|
* - `{ price }` — fixed price (object form of the string sugar).
|
|
558
576
|
* - `{ field, tiers, default? }` — pick a tier from `body[field]`.
|
|
559
577
|
*
|
|
560
|
-
* Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout
|
|
561
|
-
* alongside the pricing shape. For handler-computed billing use `.upTo()`;
|
|
578
|
+
* Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout`,
|
|
579
|
+
* `checkoutSession`) live alongside the pricing shape. For handler-computed billing use `.upTo()`;
|
|
562
580
|
* for per-tick billing use `.metered()`.
|
|
563
581
|
*
|
|
564
582
|
* @example
|
|
@@ -900,6 +918,7 @@ interface ServiceRouter<TPriceKeys extends string = never> {
|
|
|
900
918
|
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
901
919
|
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
902
920
|
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
|
921
|
+
notFound(): (request: NextRequest) => Promise<NextResponse>;
|
|
903
922
|
monitors(): MonitorEntry[];
|
|
904
923
|
registry: RouteRegistry;
|
|
905
924
|
}
|
|
@@ -928,4 +947,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
|
|
|
928
947
|
*/
|
|
929
948
|
declare function createRouterFromEnv<const P extends Record<string, string> = Record<never, string>>(options: CreateRouterFromEnvOptions<P>): ServiceRouter<Extract<keyof P, string>>;
|
|
930
949
|
|
|
931
|
-
export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
|
|
950
|
+
export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
|
package/dist/index.d.ts
CHANGED
|
@@ -219,6 +219,15 @@ interface MppProtocolInfo {
|
|
|
219
219
|
intent?: string;
|
|
220
220
|
currency?: string;
|
|
221
221
|
}
|
|
222
|
+
type CheckoutSessionResponse = Record<string, unknown>;
|
|
223
|
+
interface CheckoutSessionContext<TBody = unknown> {
|
|
224
|
+
request: NextRequest;
|
|
225
|
+
route: string;
|
|
226
|
+
body: TBody | undefined;
|
|
227
|
+
/** Decimal-dollar price quoted for this 402 challenge. */
|
|
228
|
+
price: string;
|
|
229
|
+
}
|
|
230
|
+
type CheckoutSessionFn<TBody = unknown> = (ctx: CheckoutSessionContext<TBody>) => CheckoutSessionResponse | null | undefined | Promise<CheckoutSessionResponse | null | undefined>;
|
|
222
231
|
interface PaidOptions {
|
|
223
232
|
protocols?: ProtocolType[];
|
|
224
233
|
maxPrice?: string;
|
|
@@ -229,6 +238,14 @@ interface PaidOptions {
|
|
|
229
238
|
mpp?: MppProtocolInfo;
|
|
230
239
|
/** Signal in discovery that clients should use an explicit checkout flow before payment. */
|
|
231
240
|
checkout?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Build dynamic checkout review metadata for the router-owned 402 response body.
|
|
243
|
+
*
|
|
244
|
+
* The returned object is emitted as `{ "checkout_session": ... }` on the unpaid
|
|
245
|
+
* payment challenge response. Payment terms remain authoritative in the x402/MPP
|
|
246
|
+
* challenge headers.
|
|
247
|
+
*/
|
|
248
|
+
checkoutSession?: CheckoutSessionFn;
|
|
232
249
|
}
|
|
233
250
|
type PaidArg = (PaidOptions & {
|
|
234
251
|
price: string;
|
|
@@ -374,6 +391,7 @@ interface RouteEntry {
|
|
|
374
391
|
settlement?: SettlementLifecycle;
|
|
375
392
|
mppInfo?: MppProtocolInfo;
|
|
376
393
|
hasCheckout?: boolean;
|
|
394
|
+
checkoutSession?: CheckoutSessionFn;
|
|
377
395
|
/** Per-tick cost (decimal-dollar). Required when `metered` is true. */
|
|
378
396
|
tickCost?: string;
|
|
379
397
|
/** Cosmetic unit label for 402 challenges and client UIs. */
|
|
@@ -557,8 +575,8 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
|
|
|
557
575
|
* - `{ price }` — fixed price (object form of the string sugar).
|
|
558
576
|
* - `{ field, tiers, default? }` — pick a tier from `body[field]`.
|
|
559
577
|
*
|
|
560
|
-
* Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout
|
|
561
|
-
* alongside the pricing shape. For handler-computed billing use `.upTo()`;
|
|
578
|
+
* Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout`,
|
|
579
|
+
* `checkoutSession`) live alongside the pricing shape. For handler-computed billing use `.upTo()`;
|
|
562
580
|
* for per-tick billing use `.metered()`.
|
|
563
581
|
*
|
|
564
582
|
* @example
|
|
@@ -900,6 +918,7 @@ interface ServiceRouter<TPriceKeys extends string = never> {
|
|
|
900
918
|
wellKnown(): (request: NextRequest) => Promise<NextResponse>;
|
|
901
919
|
openapi(): (request: NextRequest) => Promise<NextResponse>;
|
|
902
920
|
llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
|
|
921
|
+
notFound(): (request: NextRequest) => Promise<NextResponse>;
|
|
903
922
|
monitors(): MonitorEntry[];
|
|
904
923
|
registry: RouteRegistry;
|
|
905
924
|
}
|
|
@@ -928,4 +947,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
|
|
|
928
947
|
*/
|
|
929
948
|
declare function createRouterFromEnv<const P extends Record<string, string> = Record<never, string>>(options: CreateRouterFromEnvOptions<P>): ServiceRouter<Extract<keyof P, string>>;
|
|
930
949
|
|
|
931
|
-
export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
|
|
950
|
+
export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type PaidOptions, type ProtocolType, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type X402FacilitatorsConfig, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };
|
package/dist/index.js
CHANGED
|
@@ -1165,7 +1165,7 @@ async function trySiwxFastPath(ctx, account) {
|
|
|
1165
1165
|
function shouldParseBodyEarly(incomingStrategy, routeEntry, pricing) {
|
|
1166
1166
|
if (incomingStrategy) return false;
|
|
1167
1167
|
if (!routeEntry.bodySchema) return false;
|
|
1168
|
-
return (pricing?.needsBody ?? false) || !!routeEntry.validateFn;
|
|
1168
|
+
return (pricing?.needsBody ?? false) || !!routeEntry.validateFn || !!routeEntry.checkoutSession;
|
|
1169
1169
|
}
|
|
1170
1170
|
|
|
1171
1171
|
// src/pipeline/steps/resolve-early-body.ts
|
|
@@ -2507,30 +2507,37 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
|
|
|
2507
2507
|
return errorResponse;
|
|
2508
2508
|
}
|
|
2509
2509
|
const extensions = await buildChallengeExtensions(ctx);
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
status: 402,
|
|
2513
|
-
headers: {
|
|
2514
|
-
"Content-Type": "application/json",
|
|
2515
|
-
"Cache-Control": "no-store"
|
|
2516
|
-
}
|
|
2517
|
-
});
|
|
2518
|
-
for (const strategy of getAllowedStrategies(ctx.routeEntry.protocols)) {
|
|
2510
|
+
let checkoutSession;
|
|
2511
|
+
if (ctx.routeEntry.checkoutSession) {
|
|
2519
2512
|
try {
|
|
2520
|
-
|
|
2513
|
+
checkoutSession = await ctx.routeEntry.checkoutSession({
|
|
2521
2514
|
request: ctx.request,
|
|
2522
|
-
|
|
2515
|
+
route: ctx.routeEntry.key,
|
|
2523
2516
|
body,
|
|
2524
|
-
price: challengePrice
|
|
2525
|
-
extensions,
|
|
2526
|
-
deps: ctx.deps,
|
|
2527
|
-
report: ctx.report
|
|
2517
|
+
price: challengePrice
|
|
2528
2518
|
});
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
}
|
|
2519
|
+
} catch (err) {
|
|
2520
|
+
const message = errorMessage(err, "Checkout session build failed");
|
|
2521
|
+
const responseBody2 = { success: false, error: message };
|
|
2522
|
+
const errorResponse = NextResponse5.json(responseBody2, { status: errorStatus(err, 500) });
|
|
2523
|
+
firePluginResponse(ctx, errorResponse, body, responseBody2, { message, cause: err });
|
|
2524
|
+
return errorResponse;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
const contributions = [];
|
|
2528
|
+
for (const strategy of getAllowedStrategies(ctx.routeEntry.protocols)) {
|
|
2529
|
+
try {
|
|
2530
|
+
contributions.push(
|
|
2531
|
+
await strategy.buildChallenge({
|
|
2532
|
+
request: ctx.request,
|
|
2533
|
+
routeEntry: ctx.routeEntry,
|
|
2534
|
+
body,
|
|
2535
|
+
price: challengePrice,
|
|
2536
|
+
extensions,
|
|
2537
|
+
deps: ctx.deps,
|
|
2538
|
+
report: ctx.report
|
|
2539
|
+
})
|
|
2540
|
+
);
|
|
2534
2541
|
} catch (err) {
|
|
2535
2542
|
const message = `${strategy.protocol} challenge build failed: ${errorMessage(err, String(err))}`;
|
|
2536
2543
|
ctx.report("critical", message);
|
|
@@ -2542,6 +2549,26 @@ async function buildChallengeResponse(ctx, pricing, body, failure) {
|
|
|
2542
2549
|
}
|
|
2543
2550
|
}
|
|
2544
2551
|
}
|
|
2552
|
+
const responsePayload = {
|
|
2553
|
+
...Object.assign({}, ...contributions.map((contribution) => contribution.body ?? {})),
|
|
2554
|
+
...failure && { error: failure.message ?? null, reason: failure.reason },
|
|
2555
|
+
...checkoutSession && { checkout_session: checkoutSession }
|
|
2556
|
+
};
|
|
2557
|
+
const responseBody = Object.keys(responsePayload).length ? JSON.stringify(responsePayload) : null;
|
|
2558
|
+
const response = new NextResponse5(responseBody, {
|
|
2559
|
+
status: 402,
|
|
2560
|
+
headers: {
|
|
2561
|
+
"Content-Type": "application/json",
|
|
2562
|
+
"Cache-Control": "no-store"
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2565
|
+
for (const contribution of contributions) {
|
|
2566
|
+
if (contribution.headers) {
|
|
2567
|
+
for (const [name, value] of Object.entries(contribution.headers)) {
|
|
2568
|
+
response.headers.set(name, value);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2545
2572
|
firePluginResponse(ctx, response);
|
|
2546
2573
|
return response;
|
|
2547
2574
|
}
|
|
@@ -3560,7 +3587,8 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
3560
3587
|
validateFn: void 0,
|
|
3561
3588
|
settlement: void 0,
|
|
3562
3589
|
mppInfo: void 0,
|
|
3563
|
-
hasCheckout: false
|
|
3590
|
+
hasCheckout: false,
|
|
3591
|
+
checkoutSession: void 0
|
|
3564
3592
|
};
|
|
3565
3593
|
}
|
|
3566
3594
|
fork() {
|
|
@@ -3651,7 +3679,8 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
3651
3679
|
if (resolvedOptions.minPrice) next.#s.minPrice = resolvedOptions.minPrice;
|
|
3652
3680
|
if (resolvedOptions.payTo) next.#s.payTo = resolvedOptions.payTo;
|
|
3653
3681
|
if (resolvedOptions.mpp) next.#s.mppInfo = resolvedOptions.mpp;
|
|
3654
|
-
if (resolvedOptions.checkout) next.#s.hasCheckout = true;
|
|
3682
|
+
if (resolvedOptions.checkout || resolvedOptions.checkoutSession) next.#s.hasCheckout = true;
|
|
3683
|
+
if (resolvedOptions.checkoutSession) next.#s.checkoutSession = resolvedOptions.checkoutSession;
|
|
3655
3684
|
next.#s.billing = billing;
|
|
3656
3685
|
if (tickCost) next.#s.tickCost = tickCost;
|
|
3657
3686
|
if (unitType) next.#s.unitType = unitType;
|
|
@@ -4086,6 +4115,7 @@ var RouteBuilder = class _RouteBuilder {
|
|
|
4086
4115
|
settlement: this.#s.settlement,
|
|
4087
4116
|
mppInfo: this.#s.mppInfo,
|
|
4088
4117
|
hasCheckout: this.#s.hasCheckout ? true : void 0,
|
|
4118
|
+
checkoutSession: this.#s.checkoutSession,
|
|
4089
4119
|
tickCost: this.#s.tickCost,
|
|
4090
4120
|
unitType: this.#s.unitType
|
|
4091
4121
|
};
|
|
@@ -4433,6 +4463,33 @@ function createLlmsTxtHandler(discovery) {
|
|
|
4433
4463
|
};
|
|
4434
4464
|
}
|
|
4435
4465
|
|
|
4466
|
+
// src/discovery/not-found.ts
|
|
4467
|
+
import { NextResponse as NextResponse12 } from "next/server";
|
|
4468
|
+
function createNotFoundHandler(baseUrl) {
|
|
4469
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
4470
|
+
return async (request) => NextResponse12.json(
|
|
4471
|
+
{
|
|
4472
|
+
success: false,
|
|
4473
|
+
code: "route_not_found",
|
|
4474
|
+
error: "Route not found. Rediscover this origin and retry with the current discovery document.",
|
|
4475
|
+
requestedUrl: request.url,
|
|
4476
|
+
discovery: {
|
|
4477
|
+
openapi: `${normalizedBase}/openapi.json`,
|
|
4478
|
+
wellKnown: `${normalizedBase}/.well-known/x402`,
|
|
4479
|
+
llmsTxt: `${normalizedBase}/llms.txt`
|
|
4480
|
+
}
|
|
4481
|
+
},
|
|
4482
|
+
{
|
|
4483
|
+
status: 404,
|
|
4484
|
+
headers: {
|
|
4485
|
+
"Access-Control-Allow-Origin": "*",
|
|
4486
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
|
|
4487
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key, SIGN-IN-WITH-X"
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
);
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4436
4493
|
// src/index.ts
|
|
4437
4494
|
init_accepts();
|
|
4438
4495
|
init_constants();
|
|
@@ -5136,6 +5193,9 @@ function createRouter(config) {
|
|
|
5136
5193
|
llmsTxt() {
|
|
5137
5194
|
return createLlmsTxtHandler(config.discovery);
|
|
5138
5195
|
},
|
|
5196
|
+
notFound() {
|
|
5197
|
+
return createNotFoundHandler(resolvedBaseUrl);
|
|
5198
|
+
},
|
|
5139
5199
|
monitors() {
|
|
5140
5200
|
const result = [];
|
|
5141
5201
|
for (const [, entry] of registry.entries()) {
|