Skip to main content

Installation

composer require crovver/crovver-php
Requirements: PHP 8.0+, Composer.

Initialize the Client

use Crovver\CrovverClient;
use Crovver\CrovverConfig;

$client = new CrovverClient(new CrovverConfig(
    apiKey: getenv('CROVVER_SECRET_KEY'),
));

Configuration Options

OptionTypeDefaultDescription
apiKeystringrequiredYour secret API key (sk_live_...)
timeoutint30Request timeout in seconds
maxRetriesint3Retry attempts for server errors
debugboolfalseLog requests and responses
loggercallablenullCustom logger: fn($msg, $ctx)

Tenant Management

use Crovver\Types\CreateTenantRequest;

// Create a tenant (B2B: workspace or user)
$tenant = $client->createTenant(new CreateTenantRequest(
    externalTenantId: 'workspace_abc123',
    name: 'Acme Corp',
));

// Retrieve a tenant
$tenant = $client->getTenant('workspace_abc123');

Plans

$plans = $client->getPlans();

foreach ($plans->plans as $plan) {
    echo $plan->name . ' - $' . ($plan->priceAmount / 100) . '/mo';
}

Subscriptions

$result = $client->getSubscriptions('workspace_abc123');

foreach ($result->subscriptions as $sub) {
    echo $sub->status; // active, trialing, canceled, past_due...
}

Feature Entitlements

$canAccess = $client->canAccess('workspace_abc123', 'advanced_analytics');

if ($canAccess) {
    // serve the feature
}

Usage Tracking

// Record usage
$client->recordUsage('workspace_abc123', 'api_calls', 1);

// Check against limit
$limit = $client->checkUsageLimit('workspace_abc123', 'api_calls');
echo $limit->used . ' / ' . $limit->limit;

Checkout

use Crovver\Types\CreateCheckoutSessionRequest;

$session = $client->createCheckoutSession(new CreateCheckoutSessionRequest(
    externalTenantId: 'workspace_abc123',
    planId: 'plan_pro',
    successUrl: 'https://yourapp.com/billing/success',
    cancelUrl: 'https://yourapp.com/billing/cancel',
));

// Redirect user to $session->checkoutUrl

Error Handling

use Crovver\CrovverError;

try {
    $result = $client->getSubscriptions('workspace_abc123');
} catch (CrovverError $e) {
    $e->getMessage();     // Human-readable message
    $e->getStatusCode();  // HTTP status code (null for network errors)
    $e->getErrorCode();   // API error code string
    $e->isRetryable();    // Whether the SDK already retried
}
5xx errors, 429, and 408 responses are retried automatically with exponential backoff. Checkout endpoints are never retried to prevent duplicate charges.