Packagecompense/sdk (namespace Compense\Sdk)
Version1.1.0 · API v1
RequirementsPHP 8.1+ (declare(strict_types=1), readonly DTOs)
HTTPAny PSR-18 client + PSR-17 factories, discovered with php-http/discovery (Guzzle suggested)
RepositoryAppiasoft/compense-saas-sdk-php · guides in docs/ · examples in examples/ (plain PHP and Laravel)

Install #

Shell
composer require compense/sdk guzzlehttp/guzzle
# or with Symfony HttpClient:
composer require compense/sdk symfony/http-client nyholm/psr7

Client #

PHP
<?php
declare(strict_types=1);

use Compense\Sdk\CompenseClient;
use Compense\Sdk\RetryConfig;

$client = new CompenseClient(
    apiKey: getenv('COMPENSE_API_KEY'),        // cmp_...
    environment: 'sandbox',                    // 'production' (default) | 'sandbox' | 'dev'; or baseUrl: '...'
    timeout: 30.0,                             // applies when the SDK builds Guzzle
    retry: new RetryConfig(maxAttempts: 3, retryWrites: false),
    httpClient: null,                          // any PSR-18 (container, mocks…)
    logger: $psrLogger,                        // PSR-3: deprecation warnings
);

If you pass your own httpClient, set the timeout on it. The PSR-17 factories (requestFactory:, streamFactory:) are discovered automatically if you don’t provide them.

Laravel #

Register a singleton in a service provider and receive it through injection:

PHP
// app/Providers/CompenseServiceProvider.php
$this->app->singleton(CompenseClient::class, static fn (): CompenseClient => new CompenseClient(
    apiKey: (string) config('services.compense.key'),
    environment: (string) config('services.compense.environment', 'sandbox'),
    timeout: 30.0,
    retry: new RetryConfig(maxAttempts: 3),
    logger: Log::channel('stack'),
));

// In a controller:
public function store(Request $request, CompenseClient $compense) { /* ... */ }

In Symfony, register CompenseClient as a service, passing it your Psr\Http\Client\ClientInterface.

Conventions #

  • camelCase methods with PHP 8 named arguments: $client->wallet->bills->pay(document: '1032456789', billerCode: 'EPM', reference: '123456789', amount: '90000').
  • Writes → Compense\Sdk\Model\Transaction; reads → models (Balance, Resolution, Page, OffsetPage…) or array. Every model keeps the body in ->raw.
  • Administrative module: extra fields go as named arguments using the contract’s names (first_name: 'Ana', bank_account: '...').
  • Decisions: Compense\Sdk\Model\Decision::APPROVE / Decision::REJECT. Affiliates: new Compense\Sdk\Model\AffiliateRef('CC', '1032456789') or an array.
  • Amounts: decimal int, string or Stringable; Money::normalize() produces the same text as Python.

Errors #

Namespace Compense\Sdk\Exception: CompenseException › CompenseConfigException, TransportException (a PSR-18 failure, with the original in getPrevious()) and CompenseApiException › AuthException, NotFoundException, IdempotencyConflictException, ValidationException (errors()), RateLimitException (retryAfter), UpstreamException, ServerException. Properties: status, title, detail, traceId, problem.

Pagination #

Page (hasNext()) and OffsetPage (hasMore, nextOffset) are iterable and countable; walk through them with a do … while loop (Pagination).

Webhooks #

PHP
use Compense\Sdk\Webhook\Webhook;
use Compense\Sdk\Webhook\WebhookVerificationException;

$event = Webhook::verify(file_get_contents('php://input'), getallheaders(), [$newSecret, $oldSecret]);
// Laravel: Webhook::verify($request->getContent(), $request->headers->all(), $secrets)
// PSR-7:   Webhook::verifyRequest($request, $secrets)
$event->eventId; $event->event(); $event->resource(); $event->isTest();

A complete Laravel controller is in Verifying the signature.

Testing #

Inject any fake PSR-18 client (for example php-http/mock-client or your own Psr\Http\Client\ClientInterface) into httpClient: and check the requests it receives. For integration tests, use a sandbox key.

SDK development #

Everything runs in Docker: make test (PHPUnit on PHP 8.3), make test81 (PHP 8.1) and make analyse (PHPStan at the maximum level).