Charge AI crawlers in Laravel, Drupal or plain PHP
A framework-free PHP core with a Laravel middleware package and a Drupal 10/11 module on top. WordPress has its own plugin.
Works with: Laravel 10+, Drupal 10.1+ / 11, Symfony, Slim or plain PHP via the core
Install
composer require forgemesh/pay-per-crawl-laravel # or forgemesh/pay-per-crawl-drupal
Add the gate
PAY_PER_CRAWL_MODE=monitor
PAY_PER_CRAWL_PAY_TO=0xYourWalletOnBase
PAY_PER_CRAWL_PRICE_USD=0.01
# php artisan migrate, then: php artisan pay-per-crawl:stats --days=7composer require forgemesh/pay-per-crawl-drupal
drush pm:install pay_per_crawl
# then /admin/config/services/pay-per-crawl: wallet, price, Gateuse ForgeMesh\PayPerCrawl\{Gate, Request, NativeHttpClient};
$gate = new Gate(
settings: ['mode' => 'gate', 'pay_to' => '0xYourWalletOnBase', 'price_usd' => 0.01, 'mpp_secret' => 'long-random-string'],
cache: $yourCache, http: new NativeHttpClient(),
);
$result = $gate->handle(new Request($method, $uri, $absoluteUrl, $headers, $remoteIp));
if ($result->respond) { /* send $result->status, $result->headers, $result->body and stop */ }
// else render the page and add $result->passHeadersTurn it on
- 1composer require the Laravel or Drupal package. Laravel: php artisan migrate. Drupal: drush pm:install pay_per_crawl.
- 2Enter your wallet (Laravel .env, Drupal config form). Both start in monitor mode.
- 3Laravel: php artisan pay-per-crawl:stats. Drupal: /admin/reports/pay-per-crawl. Then switch to gate.
Settings
| mode | monitor (default) or gate. Without a valid wallet, gate stays monitor. |
|---|---|
| pay_to | Your 0x wallet on Base. |
| price_usd | Per page, default 0.01, minimum 0.005. |
| facilitator_url / facilitator_auth | Any x402 facilitator supporting Base exact. Default PayAI. |
| class_policy / bot_overrides | free, paid or block per class and per crawler. |
| allow_paths | Prefixes never gated, matched on a segment boundary. |
| trusted_ip_header | Only a header your proxy overwrites or appends. Right-most entry used. Never set it if the origin is reachable directly. |
| mpp_secret | Secret for MPP challenge signing and the daily IP hash. |
| cache_store (Laravel) | Use redis, database or file for the replay lock. The array store does not survive between requests. |
Things to know
- Drupal gates as an http_middleware at priority 250, ahead of the page cache. A kernel.request subscriber would never see cached pages and bots would read them free.
- Laravel: public/llms.txt or public/robots.txt files shadow the routes because the web server serves them first.
- PHP rewrites the status to 401 when WWW-Authenticate is sent; the core sends the 402 after it. Keep that order in your own adapter.
- Behind Cloudflare, set AI Crawl Control and any "Block AI bots" rule to Allow for the crawlers you want to charge. A crawler blocked at the edge never reaches the gate and never sees the 402.
- Humans never see a 402. Search engines are verified by reverse DNS; a request that only claims to be Googlebot is charged.
- Start in monitor mode. It logs every bot with what it would have paid and never blocks. Switch to gate when the numbers make sense.
Proof it settles
Real settlements on Base mainnet through the PayAI facilitator, $0.01 each, October 2026:
- x402 v1 (Laravel edition)0xfc7d0a33ab2baeb50f91109db26d4e882e627f66c0cb68b13363a8f7b09e3b68
- x402 v2 (Laravel edition)0xff1dfa134999dc4ad19bd8a9f9ae05e286e5c9e9714bc13c4979a3c073d7b63d