PHPackages                             crovver/crovver-php - 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. [Payment Processing](/categories/payments)
4. /
5. crovver/crovver-php

ActiveLibrary[Payment Processing](/categories/payments)

crovver/crovver-php
===================

Official PHP SDK for the Crovver subscription management platform

v1.0.0(1mo ago)00MITPHPPHP ^8.1CI failing

Since Mar 27Pushed 1mo agoCompare

[ Source](https://github.com/crovver/crovver-php)[ Packagist](https://packagist.org/packages/crovver/crovver-php)[ Docs](https://github.com/crovver/crovver-php)[ RSS](/packages/crovver-crovver-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (4)Versions (2)Used By (0)

crovver-php
===========

[](#crovver-php)

Official PHP SDK for [Crovver](https://crovver.com) — subscription management, feature entitlements, and billing for SaaS applications.

Requirements
------------

[](#requirements)

- PHP 8.0+
- Composer

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

[](#installation)

```
composer require crovver/crovver-php
```

Quick Start
-----------

[](#quick-start)

```
use Crovver\CrovverClient;
use Crovver\CrovverConfig;

$client = new CrovverClient(new CrovverConfig(
    apiKey: 'your-api-key'
));
```

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

[](#configuration)

OptionTypeDefaultDescription`apiKey``string`**required**Your Crovver API key`baseUrl``string``https://app.crovver.com`Crovver base URL`timeout``int``30`HTTP request timeout in seconds`maxRetries``int``3`Max retry attempts for retryable errors`debug``bool``false`Log all requests and responses`logger``callable|null``null`Custom logger — receives `(string $msg, array $ctx)````
$client = new CrovverClient(new CrovverConfig(
    apiKey: 'your-api-key',
    timeout: 15,
    maxRetries: 3,
    debug: true,
    logger: fn(string $msg, array $ctx = []) => error_log("[Crovver] $msg"),
));
```

---

API Reference
-------------

[](#api-reference)

### Tenant Management

[](#tenant-management)

Tenants represent your customers (organisations in B2B, users in D2C).

```
use Crovver\Types\CreateTenantRequest;

// Create a tenant
$response = $client->createTenant(new CreateTenantRequest(
    externalTenantId: 'org_123',
    name: 'Acme Corp',
    ownerExternalUserId: 'user_456',
    ownerEmail: 'admin@acme.com',   // optional
    ownerName: 'Jane Doe',          // optional
    slug: 'acme-corp',              // optional
    metadata: ['plan' => 'trial'],  // optional
));

echo $response->tenant->id;
echo $response->tenant->externalTenantId;

// Get a tenant by external ID
$response = $client->getTenant('org_123');
echo $response->tenant->name;
echo $response->tenant->isActive ? 'Active' : 'Inactive';

// Access members and organisation context
foreach ($response->members as $member) {
    echo $member->externalUserId . ' — ' . $member->role;
    echo $member->email;
}
echo $response->organization['type']; // "b2b" or "d2c"
```

**`CreateTenantRequest` parameters:**

ParameterTypeRequiredDescription`externalTenantId``string`YesYour system's tenant identifier`name``string`YesDisplay name`ownerExternalUserId``string`YesOwner's user ID in your system`ownerEmail``string|null`NoOwner email`ownerName``string|null`NoOwner display name`slug``string|null`NoURL-safe identifier`metadata``array|null`NoArbitrary key-value pairs---

### Plans

[](#plans)

```
$response = $client->getPlans();

foreach ($response->plans as $plan) {
    echo $plan->name;
    echo $plan->description;
    echo $plan->pricing->currency . ' ' . $plan->pricing->amount;
    echo $plan->pricing->interval;       // "monthly" | "yearly"
    echo $plan->pricing->isSeatBased ? 'Seat-based' : 'Flat';
    echo $plan->isActive ? 'Active' : 'Inactive';

    // Feature flags
    foreach ($plan->features as $key => $value) {
        echo "$key: " . ($value ? 'yes' : 'no');
    }

    // Usage limits
    foreach ($plan->limits as $metric => $limit) {
        echo "$metric: $limit";
    }
}
```

---

### Subscriptions

[](#subscriptions)

```
// B2B: pass externalTenantId  |  D2C: pass externalUserId
$response = $client->getSubscriptions('org_123');

foreach ($response->subscriptions as $sub) {
    echo $sub->id;
    echo $sub->status;                           // "active" | "trialing" | "canceled" | etc.
    echo $sub->billing['current_period_end'];
    echo $sub->trial['is_active'] ? 'On trial' : 'Paid';

    // Seat-based capacity
    if ($sub->capacity) {
        echo 'Seats used: ' . $sub->capacity['used'] . ' / ' . $sub->capacity['total'];
    }
}
```

---

### Checkout

[](#checkout)

> Checkout calls are **never retried** automatically to prevent duplicate payments.

```
use Crovver\Types\CreateCheckoutSessionRequest;

// B2B checkout (tenant + user)
$response = $client->createCheckoutSession(new CreateCheckoutSessionRequest(
    requestingUserId: 'user_456',
    planId: 'plan_789',
    provider: 'stripe',
    requestingTenantId: 'org_123',
    successUrl: 'https://app.example.com/success',
    cancelUrl: 'https://app.example.com/cancel',
));

header('Location: ' . $response->checkoutUrl);
exit;

// D2C checkout (user only)
$response = $client->createCheckoutSession(new CreateCheckoutSessionRequest(
    requestingUserId: 'user_456',
    planId: 'plan_789',
    provider: 'stripe',
    userEmail: 'user@example.com',
    userName: 'John Doe',
    successUrl: 'https://app.example.com/success',
    cancelUrl: 'https://app.example.com/cancel',
));
```

**`CreateCheckoutSessionRequest` parameters:**

ParameterTypeRequiredDescription`requestingUserId``string`YesUser initiating the checkout`planId``string`YesTarget plan ID`provider``string`YesPayment provider code (e.g. `"stripe"`)`requestingTenantId``string|null`NoTenant ID for B2B flows`userEmail``string|null`NoPre-fill checkout email`userName``string|null`NoPre-fill customer name`successUrl``string|null`NoRedirect after successful payment`cancelUrl``string|null`NoRedirect after cancelled payment`quantity``int|null`NoSeat quantity for seat-based plans`metadata``array|null`NoArbitrary metadata---

### Entitlements

[](#entitlements)

#### Feature access

[](#feature-access)

```
if ($client->canAccess('org_123', 'export_csv')) {
    // feature is available for this tenant
}
```

#### Metered usage

[](#metered-usage)

```
// Record usage (defaults to incrementing by 1)
$client->recordUsage('org_123', 'api_calls');
$client->recordUsage('org_123', 'api_calls', 10);
$client->recordUsage('org_123', 'storage_gb', 5, ['source' => 'file_upload']);

// Check against plan limits
$usage = $client->checkUsageLimit('org_123', 'api_calls');

echo $usage->current;     // current usage count
echo $usage->limit;       // plan limit (null = unlimited)
echo $usage->remaining;   // remaining before limit
echo $usage->percentage;  // 0–100 (null if unlimited)
echo $usage->allowed;     // false if limit exceeded

if (!$usage->allowed) {
    // block the action or prompt upgrade
}
```

---

### Seat-based Proration

[](#seat-based-proration)

For seat-based plans, use proration checkout to upgrade capacity mid-cycle with prorated billing.

```
// Preview and initiate a capacity upgrade
$response = $client->createProrationCheckout(
    requestingEntityId: 'org_123',
    newCapacity: 20,              // total seats after upgrade
    planId: 'plan_789',           // optional if only one active plan
    successUrl: 'https://app.example.com/success',
    cancelUrl: 'https://app.example.com/cancel',
);

echo $response->prorationAmount;   // charge for remainder of billing period
echo $response->message;

if ($response->requiresPayment) {
    header('Location: ' . $response->checkoutUrl);
    exit;
}
// If no payment required, capacity is upgraded immediately
```

---

### Payment Providers

[](#payment-providers)

```
$response = $client->getSupportedProviders();

foreach ($response->providers as $provider) {
    echo $provider->name . ' (' . $provider->code . ')';
    echo $provider->isEnabled ? 'Enabled' : 'Disabled';
    echo $provider->supportsTestMode ? ' — test mode available' : '';
    echo $provider->supportsRecurringBilling ? ' — recurring billing' : '';
}
```

---

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

[](#error-handling)

```
use Crovver\CrovverError;

try {
    $response = $client->getSubscriptions('org_123');
} catch (CrovverError $e) {
    echo $e->getMessage();      // human-readable error message
    echo $e->getStatusCode();   // HTTP status code (null for network errors)
    echo $e->getErrorCode();    // API error code string (e.g. "TENANT_NOT_FOUND")
    echo $e->isRetryable();     // bool — whether the SDK already retried

    print_r($e->toArray());     // structured array of all fields
} catch (\Throwable $e) {
    // Unexpected errors
}
```

### Retry behaviour

[](#retry-behaviour)

The SDK retries automatically with **exponential backoff + ±25% jitter** (base 1 s, cap 30 s).

ConditionRetried5xx server errorsYes429 Too Many RequestsYes408 Request TimeoutYesNetwork / connection errorsYes4xx client errors (400, 401, 403, 404)NoCheckout / payment endpointsNever---

The server exposes these endpoints:

MethodPathDescription`GET``/plans`List all plans`GET``/tenant?externalTenantId=`Get a tenant`POST``/tenant`Create / provision a tenant`GET``/subscriptions?externalTenantId=`List subscriptions`POST``/can-access`Check feature access`POST``/checkout`Create a checkout session`GET``/providers`List supported payment providers---

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

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

Unknown

Total

1

Last Release

47d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/256702457?v=4)[crovver](/maintainers/crovver)[@crovver](https://github.com/crovver)

---

Top Contributors

[![false9dev](https://avatars.githubusercontent.com/u/59273273?v=4)](https://github.com/false9dev "false9dev (2 commits)")

---

Tags

sdkbillingpaymentssubscriptionsaascrovver

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/crovver-crovver-php/health.svg)

```
[![Health](https://phpackages.com/badges/crovver-crovver-php/health.svg)](https://phpackages.com/packages/crovver-crovver-php)
```

###  Alternatives

[prevailexcel/laravel-nowpayments

A Laravel Package for NOWPayments

1414.2k](/packages/prevailexcel-laravel-nowpayments)

PHPackages © 2026

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