diff --git a/examples/typescript/servers/cloudfront-lambda-edge/README.md b/examples/typescript/servers/cloudfront-lambda-edge/README.md
index c8eff1ac90..72d4f02c9a 100644
--- a/examples/typescript/servers/cloudfront-lambda-edge/README.md
+++ b/examples/typescript/servers/cloudfront-lambda-edge/README.md
@@ -91,12 +91,12 @@ export const NETWORK = 'eip155:84532'; // Base Sepolia (testnet)
Define which routes require payment:
```typescript
-const ROUTES: RoutesConfig = {
+export const ROUTES: RoutesConfig = {
'/api/*': {
accepts: {
scheme: 'exact',
- network: 'eip155:84532',
- payTo: '0xYourAddress',
+ network: NETWORK,
+ payTo: PAY_TO,
price: '$0.001',
},
description: 'API access',
@@ -117,12 +117,52 @@ Bundle and deploy both Lambda functions:
import { originRequestHandler, originResponseHandler } from './index';
```
+---
+
## Networks
-| Network | ID | Use |
-| ------------ | -------------- | ---------- |
-| Base Sepolia | `eip155:84532` | Testing |
-| Base Mainnet | `eip155:8453` | Production |
+| Network | ID | Use |
+| -------------- | ----------------------------------------- | ---------- |
+| Base Sepolia | `eip155:84532` | Testing |
+| Base Mainnet | `eip155:8453` | Production |
+| Solana Devnet | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | Testing |
+| Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Production |
+
+---
+
+## Running on Mainnet
+
+To accept real payments, you need a mainnet facilitator. Each facilitator may have different authentication requirements. Browse available facilitators at the [x402 Ecosystem — Facilitators](https://www.x402.org/ecosystem?filter=facilitators).
+
+Update `config.ts` with your chosen facilitator, a mainnet network, and your wallet address:
+
+```typescript
+export const FACILITATOR_URL = 'https://your-facilitator-url';
+export const NETWORK = 'eip155:8453'; // Base mainnet
+export const PAY_TO = '0xYourMainnetWalletAddress';
+```
+
+If your facilitator requires authentication, you can pass a `facilitatorConfig` object (with `url` and `createAuthHeaders`) via the middleware config. Update `config.ts`:
+
+```typescript
+// Example: using a facilitator package that provides a config with auth
+import { createFacilitatorConfig } from 'your-facilitator-package';
+
+export const FACILITATOR_CONFIG = createFacilitatorConfig('api-key-id', 'api-key-secret');
+```
+
+Then pass it in `origin-request.ts` and `origin-response.ts`:
+
+```typescript
+const x402 = createX402Middleware({
+ facilitatorUrl: FACILITATOR_URL,
+ network: NETWORK,
+ routes: ROUTES,
+ facilitatorConfig: FACILITATOR_CONFIG, // overrides facilitatorUrl when provided
+});
+```
+
+> **Note**: Lambda@Edge does not support environment variables. If your facilitator reads credentials from `process.env`, you can pass them explicitly via the config function, or fetch them from AWS Secrets Manager at runtime.
---
@@ -136,33 +176,44 @@ cloudfront-lambda-edge/
│ ├── origin-response.ts # Handler for origin-response event
│ ├── config.ts # Routes, addresses, network config
│ └── lib/ # Reusable x402 middleware
-│ ├── middleware.ts # createX402Middleware factory
+│ ├── index.ts # Package exports
+│ ├── middleware.ts # createX402Middleware factory
│ ├── server.ts # createX402Server factory
│ ├── adapter.ts # CloudFrontHTTPAdapter
│ └── responses.ts # Lambda@Edge response helpers
```
+---
+
## Middleware Pattern
-The x402 logic is composable middleware, so you can integrate it with your existing Lambda@Edge logic:
+The `lib/` folder follows the same pattern as `@x402/express`, `@x402/hono`, etc.:
```typescript
-import { createX402Middleware } from './lib';
-
-const x402 = createX402Middleware({ getServer: createServer });
+import { createX402Middleware, MiddlewareResultType } from './lib';
+
+// Create middleware with config
+const x402 = createX402Middleware({
+ facilitatorUrl: 'https://x402.org/facilitator',
+ network: 'eip155:84532',
+ routes: {
+ '/api/*': {
+ accepts: { scheme: 'exact', network: 'eip155:84532', payTo: '0x...', price: '$0.01' },
+ description: 'API access',
+ },
+ },
+});
+// Use in handlers
export const handler = async (event: CloudFrontRequestEvent) => {
const request = event.Records[0].cf.request;
+ const distributionDomain = event.Records[0].cf.config.distributionDomainName;
// Your custom logic first (auth, WAF, logging, etc.)
- if (request.headers['x-api-key']?.[0]?.value !== 'secret') {
- return { status: '401', body: 'Unauthorized' };
- }
- // x402 payment check
const result = await x402.processOriginRequest(request, distributionDomain);
- if (result.type === 'respond') {
+ if (result.type === MiddlewareResultType.RESPOND) {
return result.response; // 402 Payment Required
}
@@ -220,13 +271,6 @@ getHeader(name: string): string | undefined {
-
-Browser Paywall
-
-HTML paywall is disabled by default due to Lambda@Edge's 1MB response limit. For browser-based payment flows, consider hosting the paywall HTML on S3 and using CloudFront origin routing to serve it.
-
-
-
Payment Flow Internals
diff --git a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/config.ts b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/config.ts
index 6f5304a2b4..efb3456695 100644
--- a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/config.ts
+++ b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/config.ts
@@ -9,8 +9,8 @@ import type { RoutesConfig } from '@x402/core/server';
// Payment configuration
export const FACILITATOR_URL = 'https://x402.org/facilitator';
-export const PAY_TO = '0xD8213b3b85e5bD05D60b6dD89F1cF71fcd5b57B0' //'0xYourPaymentAddressHere';
-export const NETWORK = 'eip155:84532'; // Base Sepolia testnet. Use 'eip155:8453' for mainnet.
+export const PAY_TO = '0xYourPaymentAddressHere';
+export const NETWORK = 'eip155:84532'; // Base Sepolia testnet
// Route configuration
export const ROUTES: RoutesConfig = {
diff --git a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/middleware.ts b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/middleware.ts
index 63a3349b31..3b07a2a649 100644
--- a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/middleware.ts
+++ b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/middleware.ts
@@ -60,7 +60,7 @@ const PENDING_SETTLEMENT_HEADER = 'x-x402-pending-settlement';
*
* @example
* ```typescript
- * import { createX402Middleware } from '@x402/lambda-edge';
+ * import { createX402Middleware } from './lib';
*
* const x402 = createX402Middleware({
* facilitatorUrl: 'https://x402.org/facilitator',
diff --git a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/server.ts b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/server.ts
index 061bc07ddb..641cc234c4 100644
--- a/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/server.ts
+++ b/examples/typescript/servers/cloudfront-lambda-edge/lambda/src/lib/server.ts
@@ -1,4 +1,4 @@
-import type { RoutesConfig } from '@x402/core/server';
+import type { RoutesConfig, FacilitatorConfig } from '@x402/core/server';
import { x402ResourceServer, x402HTTPResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
import { ExactEvmScheme } from '@x402/evm/exact/server';
@@ -12,6 +12,8 @@ export interface X402ServerConfig {
network: string;
/** Route configuration defining which paths require payment */
routes: RoutesConfig;
+ /** Optional facilitator config with auth headers (for facilitators that require authentication) */
+ facilitatorConfig?: FacilitatorConfig;
}
/**
@@ -19,21 +21,28 @@ export interface X402ServerConfig {
*
* @example
* ```typescript
+ * // Testnet (no auth)
* const server = await createX402Server({
* facilitatorUrl: 'https://x402.org/facilitator',
* network: 'eip155:84532',
- * routes: {
- * '/api/*': {
- * accepts: { scheme: 'exact', network: 'eip155:84532', payTo: '0x...', price: '$0.01' }
- * }
- * }
+ * routes: { ... },
+ * });
+ *
+ * // Mainnet with auth (pass a facilitator config from your facilitator package)
+ * const server = await createX402Server({
+ * facilitatorUrl: 'https://your-facilitator-url',
+ * network: 'eip155:8453',
+ * routes: { ... },
+ * facilitatorConfig: createFacilitatorConfig('api-key-id', 'api-key-secret'),
* });
* ```
*/
export async function createX402Server(config: X402ServerConfig): Promise {
- const facilitator = new HTTPFacilitatorClient({ url: config.facilitatorUrl });
+ const facilitator = new HTTPFacilitatorClient(
+ config.facilitatorConfig ?? { url: config.facilitatorUrl },
+ );
const resourceServer = new x402ResourceServer(facilitator)
- .register(config.network, new ExactEvmScheme());
+ .register(config.network as `${string}:${string}`, new ExactEvmScheme());
const httpServer = new x402HTTPResourceServer(resourceServer, config.routes);
await httpServer.initialize();