forgemesh / pay-per-crawl-core
Framework-free core for ForgeMesh Pay-per-Crawl: charge AI crawlers per page with x402 / MPP USDC micropayments on Base, paid straight to your wallet.
Requires
- php: >=8.1
- ext-hash: *
- ext-json: *
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-17 21:13:10 UTC
README
Framework-free core for charging AI crawlers per page with x402 (v1 and v2) and MPP USDC micropayments on Base. Humans and verified search engines read free. Bots and agents get an HTTP 402 until they pay, and the money goes straight to your wallet.
This package is the engine behind the framework editions:
| Edition | Package |
|---|---|
| Laravel middleware | forgemesh/pay-per-crawl-laravel |
| Drupal 10/11 module | forgemesh/pay-per-crawl-drupal |
| WordPress plugin | forgemesh-pay-per-crawl |
Use the core directly if you run Symfony, Slim, CodeIgniter, plain PHP, or anything else: write a thin adapter (about 60 lines).
Money flow
- The site owner keeps 100%. Each payment is an EIP-3009 USDC transfer from the paying agent to the
pay_toaddress you configure. - No custody, no account, no signup, no private key on your server. Only your public wallet address is configured.
- The facilitator (PayAI by default, no key and no seller fee on Base mainnet) verifies and submits the transfer. A page is served only when the facilitator returns a non-empty transaction hash.
- ForgeMesh takes no cut. There is an optional paid Pro tier (hosted dashboard, alerts, crawler-intel feed); nothing leaves your site without a Pro license key.
If you use Cloudflare, AI Crawl Control (and any bot-fight or WAF rule that blocks AI crawlers) must be set to Allow for the crawlers you want to charge. A crawler blocked at the edge never reaches your server and never sees the 402.
Install
composer require forgemesh/pay-per-crawl-core
Until the package is listed on Packagist, add the repository first:
composer config repositories.ppc-core vcs https://github.com/forgemeshlabs/pay-per-crawl-php composer require forgemesh/pay-per-crawl-core:^0.1
Requires PHP 8.1+. No other dependencies (cURL is used when present, PHP streams otherwise).
Use
use ForgeMesh\PayPerCrawl\{Gate, Request, NativeHttpClient, ArrayCache}; $gate = new Gate( settings: ['mode' => 'gate', 'pay_to' => '0xYourBaseWallet', 'price_usd' => 0.01, 'mpp_secret' => 'long-random-string'], cache: $yourCache, // CacheInterface: get/set/add(atomic)/delete (replay lock + DNS cache) http: new NativeHttpClient(), // HttpClientInterface store: $yourHitStore, // optional HitStoreInterface: one row per classified request ); $result = $gate->handle(new Request($method, $uri, $absoluteUrl, $headers, $remoteIp)); if ($result->respond) { // 402 (or 403 for a blocked crawler) // send $result->status, $result->headers, $result->body and stop } // otherwise render your page and add $result->passHeaders to the response // (payment receipts: PAYMENT-RESPONSE, X-PAYMENT-RESPONSE, Payment-Receipt)
Also included: Discovery (/.well-known/x402.json, /llms.txt, /agents.txt, robots.txt lines), Classifier (85 known agents, reverse-DNS verification for search engines, impostors demoted), X402 (v1/v2/MPP wire formats), Keccak (pure PHP, for the MPP nonce binding), and Pro (optional hosted sync).
Settings
| Key | Default | Meaning |
|---|---|---|
mode |
monitor |
monitor classifies and logs only; gate charges. Falls back to monitor without a valid pay_to. |
pay_to |
'' |
Your Base wallet address. |
price_usd |
0.01 |
Per page; minimum 0.005. |
facilitator_url / facilitator_auth |
PayAI / none | Any x402 facilitator that supports Base exact. |
class_policy |
search/social/monitor free, others paid |
free, paid or block per class (search social monitor seo ai automation bot). |
bot_overrides |
[] |
['GPTBot' => 'paid', 'Bytespider' => 'block']. |
allow_paths / exempt_paths |
[] |
Path prefixes never gated; matched on a segment boundary (/pricing exempts /pricing/plans, not /pricing-secret). |
trusted_ip_header |
'' |
Header holding the real client IP behind a proxy. It must be a header your proxy overwrites or appends to. For a multi-value header such as X-Forwarded-For the right-most entry (added by your proxy) is used; the left-most is client-controlled and ignored. Single-value edge headers (CF-Connecting-IP) are taken as-is. Never set it if the origin is reachable directly. |
mpp_secret, ip_salt |
required / falls back to mpp_secret |
Secrets for MPP challenge signing and the daily IP hash. |
site_url |
request origin | Pin the canonical origin behind a rewriting proxy. |
Exemptions are anchored and normalised: exact files (/robots.txt, /llms.txt, /agents.txt, /favicon.ico), *sitemap*.xml, and the /.well-known/ directory. Paths with encoded slashes or dots, dot segments, or doubled slashes are never exempt.
Test
php tests/run.php # 156 checks, no network
HTTP matrix against a running app and a mock facilitator: tests/integration/matrix.sh (used by the Laravel and Drupal repos).
Verified on Base mainnet
Real paid calls through the PayAI facilitator against the Laravel edition, $0.01 each to the same wallet, receipts checked on mainnet.base.org:
| Rail | Transaction |
|---|---|
x402 v1 (X-PAYMENT) |
0xfc7d0a33ab2baeb50f91109db26d4e882e627f66c0cb68b13363a8f7b09e3b68 |
x402 v2 (PAYMENT-SIGNATURE) |
0xff1dfa134999dc4ad19bd8a9f9ae05e286e5c9e9714bc13c4979a3c073d7b63d |
License
MIT. (The Drupal and WordPress editions are GPL-2.0-or-later as those ecosystems require; they depend on this MIT core.)
Built by ForgeMesh Labs - hello@forgemesh.io