PHPackages                             codearachnid/check-commerce-php-sdk - 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. codearachnid/check-commerce-php-sdk

ActiveLibrary[API Development](/categories/api)

codearachnid/check-commerce-php-sdk
===================================

PHP SDK for the Check Commerce (OBP Link) API — ACH, RTP, paper check and IAT payments, consumers, subscriptions, hosted payment pages and merchant boarding.

v0.2.0(today)050↑2660%1MITPHPPHP ^8.1CI passing

Since Aug 1Pushed todayCompare

[ Source](https://github.com/codearachnid/check-commerce-php-sdk)[ Packagist](https://packagist.org/packages/codearachnid/check-commerce-php-sdk)[ RSS](/packages/codearachnid-check-commerce-php-sdk/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (9)Versions (4)Used By (1)

Check Commerce PHP SDK
======================

[](#check-commerce-php-sdk)

A modern PHP SDK for the [Check Commerce (OBP Link) API](https://sandbox.checkcommerce.com/api/docs/index.html) — ACH, RTP, paper check and IAT payments, stored consumers, recurring subscriptions, hosted payment pages, batch processing and merchant boarding.

- **Typed end to end** — native enums for payment types, transaction statuses and schedules; immutable response objects with real properties.
- **Zero-friction auth** — bearer tokens are acquired, cached and refreshed automatically; pluggable token storage for sharing tokens across processes.
- **Safe retries** — exponential backoff with jitter for rate limits, server errors and network failures, applied only where a retry cannot double-charge.
- **Rich errors** — every API failure maps to a typed exception carrying the error code, detail, correlation id and per-field validation errors.
- **Framework agnostic** — built on PSR-18/PSR-17, works with any HTTP client; no framework required.
- **Forward compatible** — new API fields and enum values never break the SDK; everything stays reachable through the raw payload.

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

[](#requirements)

- PHP 8.1+
- A [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP client and [PSR-17](https://www.php-fig.org/psr/psr-17/) factories ([Guzzle](https://github.com/guzzle/guzzle) is recommended and auto-discovered)

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

[](#installation)

```
composer require codearachnid/check-commerce-php-sdk guzzlehttp/guzzle
```

Already have a PSR-18 client (Guzzle, Symfony HttpClient, Buzz, ...)? Omit `guzzlehttp/guzzle` — the SDK discovers whatever implementation is installed.

Quick start
-----------

[](#quick-start)

```
use CheckCommerce\CheckCommerceClient;

$client = CheckCommerceClient::sandbox(
    apiKey: getenv('CHECK_COMMERCE_API_KEY'),
    merchantNumber: getenv('CHECK_COMMERCE_MERCHANT_NUMBER'),
);

// The API requires the merchant number in every transaction payload;
// reuse the one the client was configured with:
$result = $client->transactions->debit([
    'merchantNumber' => $client->config->merchantNumber,
    'amount' => 42.50,
    'referenceNumber' => 'INV-1001',
    'consumerInfo' => [
        'name' => 'Jane Doe',
        'bankAccountNumber' => '1234567890',
        'bankRoutingNumber' => 121000248,
    ],
]);

echo $result->transactionId;    // 123456789
echo $result->status->value;    // "Processed"
```

Or configure entirely from the environment:

```
// Reads CHECK_COMMERCE_API_KEY, CHECK_COMMERCE_MERCHANT_NUMBER, and
// CHECK_COMMERCE_ENVIRONMENT ("production" or "sandbox", default production).
$client = CheckCommerceClient::fromEnv();

// Anything not in the environment can be passed as an override:
$client = CheckCommerceClient::fromEnv(['timeout' => 60, 'max_retries' => 3]);
```

Use `CheckCommerceClient::production(...)` for live traffic, or construct with full configuration:

```
use CheckCommerce\CheckCommerceClient;
use CheckCommerce\Environment;
use CheckCommerce\Scope;

$client = new CheckCommerceClient([
    'api_key' => getenv('CHECK_COMMERCE_API_KEY'),
    'merchant_number' => getenv('CHECK_COMMERCE_MERCHANT_NUMBER'),
    'environment' => Environment::Sandbox,
    'scopes' => [Scope::Transactions, Scope::HostedPages],
    'timeout' => 30,
    'max_retries' => 2,
]);
```

Authentication
--------------

[](#authentication)

You never call the authentication endpoint yourself. The first API call requests a bearer token, caches it, refreshes it shortly before expiry, and transparently re-authenticates once if the API rejects a token mid-flight.

```
// Optional: validate credentials eagerly (e.g. at deploy time)
$token = $client->authenticate();
echo $token->expiresAt->format(DATE_ATOM);
```

By default tokens live in memory for the current process. For long-running or multi-process apps, supply a shared store:

```
use CheckCommerce\Auth\AccessToken;
use CheckCommerce\Auth\TokenStoreInterface;

final class CacheTokenStore implements TokenStoreInterface
{
    public function __construct(private \Psr\SimpleCache\CacheInterface $cache) {}

    public function get(string $key): ?AccessToken
    {
        $data = $this->cache->get($key);
        return is_array($data) ? AccessToken::fromArray($data) : null;
    }

    public function put(string $key, AccessToken $token): void
    {
        $this->cache->set($key, $token->toArray());
    }

    public function forget(string $key): void
    {
        $this->cache->delete($key);
    }
}

$client = new CheckCommerceClient($config, tokenStore: new CacheTokenStore($cache));
```

Transactions
------------

[](#transactions)

```
use CheckCommerce\Enums\PaymentType;
use CheckCommerce\Enums\TransactionType;

$mid = $client->config->merchantNumber; // as configured (e.g. from CHECK_COMMERCE_MERCHANT_NUMBER)

// Sugar for the common operations — sets transactionType for you:
$client->transactions->debit([...]);
$client->transactions->credit([...]);
$client->transactions->void(['merchantNumber' => $mid, 'originalTransaction' => ['transactionId' => 123456789]]);
$client->transactions->refund(['merchantNumber' => $mid, 'originalTransaction' => ['referenceNumber' => 'INV-1001']]);

// Full control — any transaction type, any payment rail:
$client->transactions->create(
    ['merchantNumber' => $mid, 'transactionType' => TransactionType::Prenote, /* ... */],
    PaymentType::Rtp,
);

// Status lookups:
$status = $client->transactions->status(transactionId: 123456789);
$status = $client->transactions->status(referenceNumber: 'INV-1001', requestType: PaymentType::Ach);
$auth   = $client->transactions->authStatus(transactionId: 123456789);

if ($status->isDeclined()) {
    echo $status->processingFailure?->detail; // "Threshold Exceeded"
}
```

Consumers
---------

[](#consumers)

Store consumer bank details once, then reference them by id in transactions and subscriptions:

```
$created = $client->consumers->create([
    'name' => 'Jane Doe',
    'email' => 'jane@example.com',
    'bankAccountNumber' => '1234567890',
    'bankRoutingNumber' => 121000248,
]);

$consumer = $client->consumers->retrieve($created->consumerId);
$client->consumers->update($created->consumerId, ['phoneNumber' => '5125551234']);

// Charge a stored consumer:
$client->transactions->debit([
    'merchantNumber' => $client->config->merchantNumber,
    'amount' => 42.50,
    'consumerInfo' => ['consumerId' => $created->consumerId],
]);
```

List endpoints return one page plus lazy access to the rest — `autoPagingIterator()` streams every record and fetches pages on demand:

```
$page = $client->consumers->list(['city' => 'Austin', 'pageSize' => 100]);

foreach ($page->autoPagingIterator() as $consumer) {
    echo $consumer->name, "\n";
}
```

Subscriptions
-------------

[](#subscriptions)

```
use CheckCommerce\Enums\SubscriptionEndCode;
use CheckCommerce\Enums\SubscriptionStatus;
use CheckCommerce\Enums\TransactionType;

$created = $client->subscriptions->create([
    'startTime' => new DateTimeImmutable('first day of next month'),
    'amount' => 25.00,
    'schCode' => 'Monthly:1',
    'endCode' => SubscriptionEndCode::Indefinite,
    'transactionType' => TransactionType::Debit,
    'status' => SubscriptionStatus::Active,
    'consumerInfo' => ['consumerId' => $consumerId],
]);

$subscription = $client->subscriptions->retrieve($created->subscriptionId);
$client->subscriptions->update($created->subscriptionId, ['amount' => 30.00]);

foreach ($client->subscriptions->list(['includeSuspended' => true]) as $subscription) {
    echo $subscription->scheduleCode, ' → ', $subscription->status?->value, "\n";
}
```

Hosted payment pages
--------------------

[](#hosted-payment-pages)

```
$link = $client->hostedPages->createLink([
    'customer' => ['name' => 'Jane Doe', 'email' => 'jane@example.com'],
    'order' => [
        'subTotal' => 89.95,
        'tax' => 10.00,
        'total' => 99.95,
        'returnURL' => 'https://example.com/thanks',
    ],
    'orderItems' => [
        ['name' => 'Widget', 'quantity' => 1, 'price' => 89.95],
    ],
]);

header('Location: '.$link->url);
```

Batches
-------

[](#batches)

```
use CheckCommerce\Enums\FileDelimiter;

// JSON batch:
$mid = $client->config->merchantNumber;
$batch = $client->batches->submit([
    ['merchantNumber' => $mid, 'transactionType' => 'Debit', 'amount' => 42.50, 'consumerInfo' => [...]],
    ['merchantNumber' => $mid, 'transactionType' => 'Debit', 'amount' => 19.99, 'consumerInfo' => [...]],
]);

// File upload:
$batch = $client->batches->uploadFile('/path/to/batch.csv', FileDelimiter::Comma);

// Poll until processed:
$status = $client->batches->status($batch->batchId);
echo $status->status->value; // "Pending" | "Processing" | "Processed" | "Declined"
```

Merchant boarding
-----------------

[](#merchant-boarding)

```
$result = $client->boarding->board(['merchants' => [/* boarding records */]]);

foreach ($result->boardingFailures as $failure) {
    echo $failure->companyName, ': ', $failure->processingFailure?->detail, "\n";
}
```

Error handling
--------------

[](#error-handling)

Every SDK exception implements `CheckCommerce\Exception\CheckCommerceException`; API errors map to a typed hierarchy:

ExceptionWhen`ValidationException`400/422 — invalid request, has per-field errors`AuthenticationException`401 — bad API key, merchant number, or token`AuthorizationException`403 — missing scope or disabled feature`NotFoundException`404 — resource does not exist`RateLimitException`429 — too many requests (`getRetryAfter()`)`ServerException`5xx — API-side failure`ApiException`any other error status (base class of the above)`TransportException`network failure, no API response received`InvalidArgumentException`misuse detected before a request is sent```
use CheckCommerce\Exception\ApiException;
use CheckCommerce\Exception\ValidationException;

try {
    $client->transactions->debit([...]);
} catch (ValidationException $e) {
    foreach ($e->getValidationErrors() as $error) {
        echo $error->property, ': ', $error->detail, "\n";
    }
} catch (ApiException $e) {
    // Everything you need for a support ticket:
    log_error($e->getMessage(), [
        'status' => $e->getStatusCode(),
        'code' => $e->getErrorCode(),
        'correlation_id' => $e->getCorrelationId(),
    ]);
}
```

Pass your own correlation id to trace a request end to end:

```
$client->transactions->debit([...], options: ['correlation_id' => $uuid]);
```

Retries
-------

[](#retries)

Failed requests are retried with exponential backoff and jitter, up to `max_retries` (default 2):

- **429** responses are retried for every method (the request was rejected, not processed) and honor `Retry-After`.
- **5xx** responses and **network failures** are retried for `GET` requests only — a write that may have reached the API is never blindly resent.

Set `'max_retries' => 0` to disable retries entirely.

Forward compatibility
---------------------

[](#forward-compatibility)

Typed properties cover the documented API. Anything the API adds later remains reachable — response objects expose the full payload:

```
$consumer = $client->consumers->retrieve($id);
$consumer['brandNewField'];   // array access hits the raw payload
$consumer->toArray();         // the whole decoded response
```

Unknown enum values parse to `null` instead of throwing; the raw value stays available (e.g. `TransactionResult::$statusRaw`).

Custom HTTP client
------------------

[](#custom-http-client)

Inject any PSR-18 client and PSR-17 factories — useful for proxies, middleware, or tests:

```
$client = new CheckCommerceClient(
    $config,
    httpClient: $myPsr18Client,
    requestFactory: $myPsr17Factory,
    streamFactory: $myPsr17Factory,
);
```

The test suite ships a `FakeHttpClient` pattern you can copy for your own integration tests — no HTTP mocking library required.

Laravel
-------

[](#laravel)

This package is intentionally framework-free. For Laravel applications, use the companion package [`codearachnid/check-commerce-laravel-sdk`](https://packagist.org/packages/codearachnid/check-commerce-laravel-sdk) — it wraps this SDK with a service provider, publishable config mapping the `CHECK_COMMERCE_*` environment variables, a `CheckCommerce` facade, a cache-backed token store shared across workers, and a testing fake for feature tests:

```
composer require codearachnid/check-commerce-laravel-sdk
```

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md). Run the suite with:

```
composer install
composer test   # PHPUnit
composer stan   # PHPStan (level 8)
```

License
-------

[](#license)

Released under the [MIT License](LICENSE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 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 ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

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

---

Top Contributors

[![codearachnid](https://avatars.githubusercontent.com/u/755025?v=4)](https://github.com/codearachnid "codearachnid (14 commits)")

---

Tags

apisdkpaymentsacheCheckrtpcheck-commerce

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/codearachnid-check-commerce-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/codearachnid-check-commerce-php-sdk/health.svg)](https://phpackages.com/packages/codearachnid-check-commerce-php-sdk)
```

###  Alternatives

[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

36826.2k2](/packages/telnyx-telnyx-php)[openai-php/client

OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API

5.8k29.9M338](/packages/openai-php-client)[getbrevo/brevo-php

Official PHP SDK for the Brevo API.

1004.1M56](/packages/getbrevo-brevo-php)[n1ebieski/ksef-php-client

PHP API client that allows you to interact with the API Krajowego Systemu e-Faktur

9082.3k](/packages/n1ebieski-ksef-php-client)[trycourier/courier

Courier PHP SDK

16667.9k](/packages/trycourier-courier)[anthropic-ai/sdk

Anthropic PHP SDK

174884.4k21](/packages/anthropic-ai-sdk)

PHPackages © 2026

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