proofage / laravel-client
Laravel client package for ProofAge API with HMAC authentication
Installs: 23
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/proofage/laravel-client
Requires
- php: ^8.1
- illuminate/config: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
A Laravel package for integrating with the ProofAge API, featuring automatic HMAC authentication and a fluent interface.
Installation
Install the package via Composer:
composer require proofage/laravel-client
Configuration
Publish the configuration file:
php artisan vendor:publish --provider="ProofAge\Laravel\ProofAgeServiceProvider" --tag="config"
Configure your environment variables:
PROOFAGE_API_KEY=your-api-key PROOFAGE_SECRET_KEY=your-secret-key PROOFAGE_BASE_URL=https://api.proofage.xyz PROOFAGE_VERSION=v1
Setup Verification
After configuration, verify your setup using the built-in command:
php artisan proofage:verify-setup
Successful Setup Output
When everything is configured correctly, you should see:
✅ Configuration is valid
✅ Workspace connection successful
✅ Webhook URL is configured https://yoursite.com/webhooks/proof-age
✅ Webhook route found: POST webhooks/proof-age -> App\Http\Controllers\WebhookController@handleProofAgeWebhook
✅ Webhook route is protected with VerifyWebhookSignature middleware
✅ ProofAge setup verified successfully!
What the Command Checks
The verification command ensures:
- Configuration - API keys and base URL are properly set
- Workspace Connection - Can successfully connect to ProofAge API
- Webhook URL - Webhook endpoint is configured in your workspace
- Route Existence - Laravel route exists for the webhook path
- HTTP Method - Route accepts POST requests
- Security Middleware - Route is protected with HMAC signature verification
Troubleshooting
If you see errors about missing middleware, add it to your webhook route:
Route::post('/webhooks/proof-age', [WebhookController::class, 'handleProofAgeWebhook']) ->middleware('proofage.verify_webhook');
Usage
Basic Usage
use ProofAge\Laravel\Facades\ProofAge; // Get workspace information $workspace = ProofAge::workspace()->get(); // Create a verification $verification = ProofAge::verifications()->create([ 'callback_url' => 'https://your-app.com/webhook', 'metadata' => ['user_id' => 123] ]); // Get verification details $verification = ProofAge::verifications()->find('verification-id'); // Accept consent for verification ProofAge::verifications('verification-id')->acceptConsent([ 'consent_version_id' => 1, 'text_sha256' => 'hash-value' ]); // Upload media ProofAge::verifications('verification-id')->uploadMedia([ 'type' => 'selfie', 'file' => $uploadedFile ]); // Submit verification ProofAge::verifications('verification-id')->submit();
Using the Client Directly
use ProofAge\Laravel\ProofAgeClient; $client = app(ProofAgeClient::class); $workspace = $client->workspace()->get();
Webhook Security
The package includes middleware to verify HMAC signatures on incoming webhook requests from ProofAge.
Using the Middleware
Apply the middleware to your webhook routes:
// In your routes/web.php or routes/api.php Route::post('/proofage/webhook', [WebhookController::class, 'handle']) ->middleware('proofage.verify_webhook');
Or apply it to a route group:
Route::middleware(['proofage.verify_webhook'])->group(function () { Route::post('/proofage/decision-webhook', [WebhookController::class, 'handleDecision']); Route::post('/proofage/track-webhook', [WebhookController::class, 'handleStatusChanged']); });
How It Works
The middleware:
- Checks that
PROOFAGE_SECRET_KEYis configured - Verifies the
X-HMAC-Signatureheader is present - Generates the expected signature using the same algorithm as ProofAge
- Compares signatures using
hash_equals()for timing-safe comparison - Returns appropriate error responses for invalid requests
Multiple Workspaces
Some applications need separate verification flows for different user roles. For example, a marketplace where buyers go through a basic age check while sellers require full identity verification -- each with its own ProofAge workspace, credentials, and webhook endpoint.
The package supports this out of the box. All shared settings (base_url, version, timeout, etc.) are inherited from the default proofage config, so additional workspaces only need their own api_key and secret_key.
Example: marketplace with buyer and seller verification
1. Configure credentials for each workspace
The default workspace (buyers) is configured via config/proofage.php as usual. For sellers, add a second set of credentials anywhere in your application config -- config/services.php is a common choice:
// config/services.php 'proofage_seller' => [ 'api_key' => env('PROOFAGE_SELLER_API_KEY'), 'secret_key' => env('PROOFAGE_SELLER_SECRET_KEY'), ],
# .env # Buyer workspace (default) PROOFAGE_API_KEY=pk_live_... PROOFAGE_SECRET_KEY=sk_live_... # Seller workspace PROOFAGE_SELLER_API_KEY=pk_live_... PROOFAGE_SELLER_SECRET_KEY=sk_live_...
2. Create verifications with the correct client
The ProofAge facade and app(ProofAgeClient::class) singleton always use the default (buyer) workspace. For the seller workspace, use ProofAgeClientFactory:
use ProofAge\Laravel\Facades\ProofAge; use ProofAge\Laravel\ProofAgeClientFactory; // Buyer verification -- uses default proofage.* config $buyerVerification = ProofAge::verifications()->create([ 'callback_url' => 'https://marketplace.com/webhooks/proofage', ]); // Seller verification -- uses services.proofage_seller config $sellerClient = app(ProofAgeClientFactory::class)->make('services.proofage_seller'); $sellerVerification = $sellerClient->verifications()->create([ 'callback_url' => 'https://marketplace.com/webhooks/proofage-seller', ]);
3. Set up separate webhook routes
Each workspace sends webhooks signed with its own secret key. Use the middleware's config prefix parameter to verify signatures with the correct credentials:
// routes/api.php // Buyer webhooks -- verified with default proofage.* keys Route::post('/webhooks/proofage', [BuyerWebhookController::class, 'handle']) ->middleware('proofage.verify_webhook'); // Seller webhooks -- verified with services.proofage_seller keys Route::post('/webhooks/proofage-seller', [SellerWebhookController::class, 'handle']) ->middleware('proofage.verify_webhook:services.proofage_seller');
4. Verify setup for each workspace
# Check the buyer (default) workspace php artisan proofage:verify-setup # Check the seller workspace php artisan proofage:verify-setup --config=services.proofage_seller
The command checks configuration, API connectivity, webhook route existence, and that the middleware uses the matching config prefix -- so you'll be warned if the keys would mismatch.
Config resolution
When a custom config prefix is used, the following resolution rules apply:
| Key | Resolution |
|---|---|
api_key |
Read from the specified prefix (required) |
secret_key |
Read from the specified prefix (required) |
base_url |
Specified prefix, falls back to proofage.base_url |
version |
Specified prefix, falls back to proofage.version |
timeout |
Specified prefix, falls back to proofage.timeout |
retry_attempts |
Specified prefix, falls back to proofage.retry_attempts |
retry_delay |
Specified prefix, falls back to proofage.retry_delay |
webhook_tolerance |
Specified prefix, falls back to proofage.webhook_tolerance (default: 300s) |
Additional workspaces only need api_key and secret_key. If a workspace connects to a different ProofAge environment (e.g. staging), add base_url under the same prefix and it will take priority over the default.
API Methods
Workspace
workspace()->get()- Get workspace informationworkspace()->getConsent()- Get consent information
Verifications
verifications()->create(array $data)- Create a new verificationverifications()->find(string $id)- Get verification by IDverifications(string $id)->acceptConsent(array $data)- Accept consentverifications(string $id)->uploadMedia(array $data)- Upload media filesverifications(string $id)->submit()- Submit verification for processing
Error Handling
The client throws specific exceptions for different error types:
use ProofAge\Laravel\Exceptions\ProofAgeException; use ProofAge\Laravel\Exceptions\AuthenticationException; use ProofAge\Laravel\Exceptions\ValidationException; try { $verification = ProofAge::verifications()->create($data); } catch (AuthenticationException $e) { // Handle authentication errors } catch (ValidationException $e) { // Handle validation errors } catch (ProofAgeException $e) { // Handle other API errors }
Webhook Exception Handling
The webhook middleware throws WebhookVerificationException on invalid requests. To return JSON error responses, register a renderable in your application's exception handler:
Laravel 11+ (bootstrap/app.php):
use ProofAge\Laravel\Exceptions\WebhookVerificationException; ->withExceptions(function (Exceptions $exceptions) { $exceptions->renderable(function (WebhookVerificationException $e) { return response()->json([ 'error' => [ 'code' => $e->errorCode, 'message' => $e->getMessage(), ], ], $e->statusCode); }); })
Laravel 10 (app/Exceptions/Handler.php):
use ProofAge\Laravel\Exceptions\WebhookVerificationException; public function register(): void { $this->renderable(function (WebhookVerificationException $e) { return response()->json([ 'error' => [ 'code' => $e->errorCode, 'message' => $e->getMessage(), ], ], $e->statusCode); }); }
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.