110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
/**
|
|
* x402 Hono middleware — payment gate for rSocials provisioning.
|
|
* Ported from rspace-online/shared/x402/hono-middleware.ts
|
|
*
|
|
* When X402_PAY_TO env is set, protects routes with x402 micro-transactions.
|
|
* When not set, acts as a no-op passthrough.
|
|
*
|
|
* Phase 4: Will be applied to POST /v1/spaces for paid provisioning.
|
|
*/
|
|
|
|
import type { Context, Next, MiddlewareHandler } from "hono";
|
|
|
|
export interface X402Config {
|
|
payTo: string;
|
|
network: string;
|
|
amount: string;
|
|
facilitatorUrl: string;
|
|
resource?: string;
|
|
description?: string;
|
|
}
|
|
|
|
export function createX402Middleware(config: X402Config): MiddlewareHandler {
|
|
return async (c: Context, next: Next) => {
|
|
const paymentHeader = c.req.header("X-PAYMENT");
|
|
|
|
if (!paymentHeader) {
|
|
const requirements = {
|
|
x402Version: 1,
|
|
scheme: "exact",
|
|
network: config.network,
|
|
maxAmountRequired: config.amount,
|
|
resource: config.resource || c.req.url,
|
|
description:
|
|
config.description || "Payment required to provision a space",
|
|
payTo: config.payTo,
|
|
maxTimeoutSeconds: 300,
|
|
};
|
|
|
|
return c.json(
|
|
{ error: "Payment Required", paymentRequirements: requirements },
|
|
402,
|
|
{ "X-PAYMENT-REQUIREMENTS": JSON.stringify(requirements) }
|
|
);
|
|
}
|
|
|
|
try {
|
|
const verifyRes = await fetch(`${config.facilitatorUrl}/verify`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
payment: paymentHeader,
|
|
requirements: {
|
|
scheme: "exact",
|
|
network: config.network,
|
|
maxAmountRequired: config.amount,
|
|
payTo: config.payTo,
|
|
},
|
|
}),
|
|
});
|
|
|
|
if (!verifyRes.ok) {
|
|
const err = await verifyRes.text();
|
|
return c.json(
|
|
{ error: "Payment verification failed", details: err },
|
|
402
|
|
);
|
|
}
|
|
|
|
const result = (await verifyRes.json()) as { valid?: boolean };
|
|
if (!result.valid) {
|
|
return c.json({ error: "Payment invalid or insufficient" }, 402);
|
|
}
|
|
|
|
c.set("x402Payment" as never, paymentHeader);
|
|
await next();
|
|
} catch (e) {
|
|
console.error("[x402] Verification error:", e);
|
|
return c.json(
|
|
{ error: "Payment verification service unavailable" },
|
|
503
|
|
);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function setupX402FromEnv(
|
|
overrides?: Partial<X402Config>
|
|
): MiddlewareHandler | null {
|
|
const payTo = process.env.X402_PAY_TO;
|
|
if (!payTo) {
|
|
console.log("[x402] Disabled — X402_PAY_TO not set");
|
|
return null;
|
|
}
|
|
|
|
const config: X402Config = {
|
|
payTo,
|
|
network: process.env.X402_NETWORK || "eip155:84532",
|
|
amount: process.env.X402_PROVISION_PRICE || "5.00",
|
|
facilitatorUrl:
|
|
process.env.X402_FACILITATOR_URL || "https://x402.org/facilitator",
|
|
description: "Payment required to provision a Postiz space",
|
|
...overrides,
|
|
};
|
|
|
|
console.log(
|
|
`[x402] Enabled — payTo=${payTo}, network=${config.network}, amount=${config.amount}`
|
|
);
|
|
return createX402Middleware(config);
|
|
}
|