PHPackages                             proofage/laravel-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. proofage/laravel-client

ActiveLibrary[API Development](/categories/api)

proofage/laravel-client
=======================

Laravel client package for ProofAge API with HMAC authentication

v0.2.8(4d ago)0640MITPHPPHP ^8.1CI failing

Since Jan 27Pushed 3w agoCompare

[ Source](https://github.com/ProofAge/laravel-client)[ Packagist](https://packagist.org/packages/proofage/laravel-client)[ RSS](/packages/proofage-laravel-client/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (39)Versions (15)Used By (0)

ProofAge Laravel Client — Age Verification for Laravel
======================================================

[](#proofage-laravel-client--age-verification-for-laravel)

**Platform:**  | **Packagist:**

A Laravel package for integrating with the ProofAge API, featuring automatic HMAC authentication and a fluent interface.

Full API reference:

About ProofAge
--------------

[](#about-proofage)

ProofAge is an online age verification platform enabling websites to confirm users meet minimum age requirements through a hosted, privacy-focused KYC process — without server-side document handling. It supports alcohol/tobacco/cannabis commerce, adult content platforms, gambling sites, and age-restricted subscriptions.

This package provides a first-class Laravel integration: a service provider with auto-discovery, a facade, HMAC-signed webhook middleware, and a setup verification command.

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require proofage/laravel-client
```

Configuration
-------------

[](#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
------------------

[](#setup-verification)

After configuration, verify your setup using the built-in command:

```
php artisan proofage:verify-setup
```

### Successful Setup Output

[](#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

[](#what-the-command-checks)

The verification command ensures:

1. **Configuration** - API keys and base URL are properly set
2. **Workspace Connection** - Can successfully connect to ProofAge API
3. **Webhook URL** - Webhook endpoint is configured in your workspace
4. **Route Existence** - Laravel route exists for the webhook path
5. **HTTP Method** - Route accepts POST requests
6. **Security Middleware** - Route is protected with HMAC signature verification

### Troubleshooting

[](#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
-----

[](#usage)

### Basic Usage

[](#basic-usage)

```
use ProofAge\Laravel\Facades\ProofAge;
use ProofAge\Laravel\Resources\VerificationResource;

// 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');

// Get age estimation details
$estimation = ProofAge::verifications('verification-id')->estimation();
// [
//     'verification_id' => '...',
//     'attempt_id' => '...',
//     'age_threshold' => [
//         'minimum' => 18,
//         'passed' => true,
//         'confidence' => 0.98,
//     ],
//     'gender' => [
//         'value' => VerificationResource::GENDER_FEMALE, // 0 = female, 1 = male
//         'confidence' => 0.93,
//     ],
// ]

// 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

[](#using-the-client-directly)

```
use ProofAge\Laravel\ProofAgeClient;

$client = app(ProofAgeClient::class);
$workspace = $client->workspace()->get();
```

Webhook Security
----------------

[](#webhook-security)

The package includes middleware to verify HMAC signatures on incoming webhook requests from ProofAge.

### Using the Middleware

[](#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

[](#how-it-works)

The middleware:

1. Checks that `PROOFAGE_SECRET_KEY` is configured
2. Verifies the `X-HMAC-Signature` header is present
3. Generates the expected signature using the same algorithm as ProofAge
4. Compares signatures using `hash_equals()` for timing-safe comparison
5. Returns appropriate error responses for invalid requests

Multiple Workspaces
-------------------

[](#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

[](#example-marketplace-with-buyer-and-seller-verification)

#### 1. Configure credentials for each workspace

[](#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

[](#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

[](#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

[](#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

[](#config-resolution)

When a custom config prefix is used, the following resolution rules apply:

KeyResolution`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
-----------

[](#api-methods)

### Workspace

[](#workspace)

- `workspace()->get()` - Get workspace information
- `workspace()->getConsent()` - Get consent information

### Verifications

[](#verifications)

- `verifications()->create(array $data)` - Create a new verification
- `verifications()->find(string $id)` - Get verification by ID
- `verifications(string $id)->acceptConsent(array $data)` - Accept consent
- `verifications(string $id)->uploadMedia(array $data)` - Upload media files
- `verifications(string $id)->submit()` - Submit verification for processing

Error Handling
--------------

[](#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

[](#webhook-exception-handling)

The webhook middleware throws `WebhookVerificationException` on invalid requests. By default, the exception renders a JSON error response:

```
{
    "error": {
        "code": "INVALID_SIGNATURE",
        "message": "HMAC signature is invalid"
    }
}
```

To customize this response, 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
-------

[](#testing)

```
composer test
```

Additional Resources
--------------------

[](#additional-resources)

- **Platform:**
- **Live Demo:**
- **Node SDK:** `@proofage/node` on npm

### Integrations for other platforms

[](#integrations-for-other-platforms)

PlatformRepositoryUse-case**Node.js**[ProofAge/node-client](https://github.com/ProofAge/node-client)Node.js age verification client — HMAC-signed API calls, webhook verification for Express, Hono, Next.js and other Node.js frameworks**WordPress**[ProofAge/wordpress-plugin](https://github.com/ProofAge/wordpress-plugin)Age gate plugin for WordPress — WooCommerce age verification, age-restricted pages, adult content gating**Laravel**this repoLaravel age verification client — HMAC-signed API calls, webhook handling, middleware for age-restricted routes**Next.js**[ProofAge/demo](https://github.com/ProofAge/demo)Full-stack age verification demo with JS SDK, server routes, and webhook receiverLicense
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance97

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~12 days

Total

14

Last Release

4d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cc55b39567d76f515ef20678eaa99148a34a8b66214db80dfff71c8f11c2239a?d=identicon)[dragg](/maintainers/dragg)

---

Top Contributors

[![dragg](https://avatars.githubusercontent.com/u/3448384?v=4)](https://github.com/dragg "dragg (30 commits)")

---

Tags

age-verificationapiidentity-verificationlaravel-package

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/proofage-laravel-client/health.svg)

```
[![Health](https://phpackages.com/badges/proofage-laravel-client/health.svg)](https://phpackages.com/packages/proofage-laravel-client)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k91.9k1](/packages/mike-bronner-laravel-model-caching)[api-platform/laravel

API Platform support for Laravel

58171.5k14](/packages/api-platform-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M132](/packages/laravel-pulse)[unisharp/laravel-filemanager

A file upload/editor intended for use with Laravel 5 to 10 and CKEditor / TinyMCE

2.2k3.5M85](/packages/unisharp-laravel-filemanager)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
