PHPackages                             nombaone/nombaone-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. [API Development](/categories/api)
4. /
5. nombaone/nombaone-php

ActiveLibrary[API Development](/categories/api)

nombaone/nombaone-php
=====================

Official PHP SDK for the NombaOne subscription-billing API — recurring billing for Nigeria over card, direct debit, and bank transfer.

v0.1.2(3d ago)05MITPHP &gt;=8.2

Since Jul 7Compare

[ Source](https://github.com/nombaone/nombaone-php)[ Packagist](https://packagist.org/packages/nombaone/nombaone-php)[ Docs](https://docs.nombaone.xyz)[ RSS](/packages/nombaone-nombaone-php/feed)WikiDiscussions Synced today

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

nombaone/nombaone-php
=====================

[](#nombaonenombaone-php)

The official PHP SDK for the [Nomba One](https://nombaone.xyz) subscription-billing API — recurring billing for Nigeria over card, direct debit, bank transfer, and more, with dunning that recovers and a ledger that never loses a kobo.

```
composer require nombaone/nombaone-php
```

Requires PHP 8.2+ and any [PSR-18](https://www.php-fig.org/psr/psr-18/) HTTP client + [PSR-17](https://www.php-fig.org/psr/psr-17/) factories, which the SDK auto-discovers ([Guzzle](https://docs.guzzlephp.org/), Symfony HttpClient, Nyholm, …). Server-side only — there is no publishable key.

Quickstart
----------

[](#quickstart)

Grab a sandbox key (`nbo_sandbox_…`) from the [dashboard](https://console.nombaone.xyz), set it as `NOMBAONE_API_KEY`, and you are three objects away from a live subscription:

```
use NombaOne\Nombaone;

$nomba = new Nombaone(getenv('NOMBAONE_API_KEY'));

$plan = $nomba->plans->create(['name' => 'Pro']);
$price = $nomba->plans->prices->create($plan->id, [
    'unitAmountInKobo' => 250_000, // ₦2,500.00 per month
    'interval'         => 'month',
]);
$customer = $nomba->customers->create([
    'email' => 'ada@example.com',
    'name'  => 'Ada Lovelace',
]);

// Sandbox: mint a deterministic test card, then subscribe.
$method = $nomba->sandbox->createPaymentMethod(['customerId' => $customer->id]);
$subscription = $nomba->subscriptions->create([
    'customerId'      => $customer->id,
    'priceId'         => $price->id,
    'paymentMethodId' => $method->id,
]);

echo $subscription->status; // "active"
```

The client derives the host from your key prefix — `nbo_sandbox_…` talks to `https://sandbox.api.nombaone.xyz`, `nbo_live_…` to `https://api.nombaone.xyz`.

You pass **associative arrays** in and receive **typed, readonly objects** out (`$subscription->status`, `$customer->email`) — with your IDE autocompleting every field.

Sandbox first
-------------

[](#sandbox-first)

The sandbox runs the real billing engine. `$nomba->sandbox->*` gives you the levers to make a month happen in a second:

```
// A card that declines like a thin balance does — "not yet", not "no".
$nomba->sandbox->createPaymentMethod([
    'customerId' => $customer->id,
    'behavior'   => 'decline_insufficient_funds', // or success | requires_otp | decline_expired_card | decline_do_not_honor
]);

// The test clock: force the next billing cycle through the real engine.
$cycle = $nomba->sandbox->advanceCycle($subscription->id);
echo $cycle->outcome; // "paid" | "past_due" | …

// Fire a real, signed webhook at your registered endpoints.
$nomba->sandbox->simulateWebhook(['type' => 'invoice.payment_failed']);
```

These methods throw locally (before any network call) if used with a live key.

Money is integer kobo
---------------------

[](#money-is-integer-kobo)

Every amount in the API is an **integer in kobo**: `₦1.00 = 100`. `250_000` is ₦2,500 — not ₦250,000. No floats, no decimal strings, `currency` is always `"NGN"`. Multiply naira by 100 exactly once, at the edge of your system; every money field is suffixed `InKobo` so a mixup is hard to type.

Pagination
----------

[](#pagination)

Every `list()` works three ways:

```
// One page.
$page = $nomba->invoices->list(['status' => 'open', 'limit' => 50]);
$page->data;                    // list
$page->pagination->hasMore;
$page->pagination->nextCursor;

// Manual paging.
if ($page->hasNextPage()) {
    $next = $page->nextPage();  // same filters, next cursor
}

// Or let the SDK thread the cursors — foreach walks every page.
foreach ($nomba->invoices->list(['status' => 'open']) as $invoice) {
    // every item across every page; pages are fetched as you consume them
}
```

Errors are a feature
--------------------

[](#errors-are-a-feature)

Failures throw typed exceptions carrying everything the API said — the stable `errorCode` to branch on, a `hint` telling you exactly what to do next (folded into the message), a `docUrl` into the error reference, per-field details on validation failures, and the `requestId` to quote to support:

```
use NombaOne\ErrorCode;
use NombaOne\Exceptions\NotFoundException;
use NombaOne\Exceptions\RateLimitException;
use NombaOne\Exceptions\ValidationException;

try {
    $nomba->subscriptions->create(['customerId' => $id, 'priceId' => $priceId]);
} catch (ValidationException $e) {
    print_r($e->fields);              // ['paymentMethodId' => ['Required']]
} catch (RateLimitException $e) {
    echo $e->retryAfter;              // seconds
} catch (NotFoundException $e) {
    if ($e->errorCode === ErrorCode::CUSTOMER_NOT_FOUND) { /* … */ }
}
```

> PHP's `\Exception` reserves `$code` for its own numeric code, so the machine-readable error code is on `$e->errorCode`. Compare it against the `NombaOne\ErrorCode` constants.

StatusExceptionNotes400`BadRequestException`malformed request401`AuthenticationException`missing / invalid / wrong-environment key403`PermissionDeniedException`missing scope, foreign resource404`NotFoundException`wrong id or wrong environment409`ConflictException`state conflicts, idempotency reuse422`ValidationException``$e->fields` has the per-field messages429`RateLimitException``$e->retryAfter`, `$e->limit`, `$e->remaining`5xx`ServerException`safe to retry (the SDK already did)—`ConnectionException` / `TimeoutException`transport-levelAll of these extend `NombaOne\Exceptions\ApiException` (except the transport ones), which extends `NombaOne\Exceptions\NombaOneException` — catch the base to handle any SDK error.

Idempotency &amp; retries
-------------------------

[](#idempotency--retries)

The SDK auto-generates an `Idempotency-Key` for every POST and **reuses it across its automatic retries** (network failures, timeouts, 408/429/5xx, and its own in-flight idempotency conflict — 2 retries by default, honoring `Retry-After`), so a blip can never double-charge. Pass your own key when the operation must stay idempotent across *process* restarts:

```
$nomba->settlements->createPayout(
    ['amountInKobo' => 5_000_000, 'bankCode' => '058', 'accountNumber' => '0123456789'],
    ['idempotencyKey' => "payout-{$myPayoutRow->id}"], // ⚠ doubles as the payout's durable merchantTxRef
);
```

> **Payouts:** the `Idempotency-Key` doubles as the payout's durable `merchantTxRef`. Always pass an explicit, stable key (e.g. your own payout id) — an auto-generated key protects SDK-level retries, but a brand-new process retrying with a fresh key would create a **second payout**.

Every method also accepts a per-call options array as its final argument: `idempotencyKey`, `headers`, `timeout` (seconds), `maxRetries`.

Webhooks
--------

[](#webhooks)

Verify before you parse, and dedupe on the event id — delivery is at-least-once, never exactly-once. The helper needs only the signing secret, **never an API key**:

```
use NombaOne\Webhooks\Webhooks;
use NombaOne\Webhooks\WebhookEventType;

$webhooks = new Webhooks(); // or $nomba->webhooks

$event = $webhooks->constructEvent(
    $rawBody,                                  // the RAW request body — never re-serialize
    $signatureHeader,                          // the `X-Nombaone-Signature` header
    getenv('NOMBAONE_WEBHOOK_SECRET'),         // shown once when you created the endpoint
);

if ($store->alreadyProcessed($event->dedupeKey())) {
    http_response_code(200);                   // at-least-once ⇒ dedupe on the event id
    return;
}

match ($event->type) {
    WebhookEventType::INVOICE_PAID            => unlock($event->data['reference']),
    WebhookEventType::INVOICE_ACTION_REQUIRED => notify($event->data['checkoutLink']),
    WebhookEventType::INVOICE_PAYMENT_FAILED  => flag($event->data['reason']),
    default                                   => null,
};

http_response_code(200); // respond 2xx fast; do heavy work async
```

`constructEvent` checks the `X-Nombaone-Signature` (`t=,v1=`, HMAC-SHA256 over `"{t}.{rawBody}"`) in constant time, rejects stale timestamps (300s tolerance, configurable), and accepts multiple `v1=` pairs so secret rotation never drops a delivery. `generateTestHeader()` lets you unit-test your handler.

**Capture the raw body before your framework parses it:**

FrameworkRaw bodyLaravel`$request->getContent()`Symfony`$request->getContent()`Slim / PSR-7`(string) $request->getBody()`Plain PHP`file_get_contents('php://input')`Manage endpoints via `$nomba->webhookEndpoints` (create/rotate return the secret **exactly once**).

The full surface
----------------

[](#the-full-surface)

`customers` (+credit, discount) · `plans` (+nested `prices`) · `prices` · `subscriptions` (pause/resume/cancel/resubscribe/change, `schedule`, `dunning`, upcoming invoice, events) · `invoices` · `coupons` · `paymentMethods` (hosted-checkout cards, virtual accounts) · `mandates` (NIBSS direct debit) · `settlements` (escrow, refunds, payouts) · `webhookEndpoints` (+deliveries, replay) · `events` (+catalog) · `organization` (+billing policy) · `metrics` · `sandbox` — every operation in the [API reference](https://docs.nombaone.xyz), 1:1.

Worth knowing:

- **Mandates are asynchronous.** They start `consent_pending` and activate when the customer's bank confirms — listen for `payment_method.updated`, don't poll, don't charge early.
- **Bank transfer is a push rail.** `paymentMethods->createVirtualAccount()` issues a NUBAN; collection completes when the transfer arrives and reconciles.
- **`past_due` is not canceled.** Read `subscriptions->dunning->retrieve()` and honor `graceAccessUntil` before cutting anyone off. Involuntary churn is `status: canceled` with `cancellationReason: involuntary`.
- **Some request filters use `…Ref`, not `…Id`.** Prices filter by `planRef`; payment-methods and mandates take `customerRef`. The SDK mirrors these wire names faithfully.

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

[](#configuration)

```
new Nombaone($apiKey, [
    'baseUrl'    => 'https://sandbox.api.nombaone.xyz', // override the derived host
    'timeout'    => 30.0,   // per-attempt seconds
    'maxRetries' => 2,      // automatic retry budget
    'httpClient' => $client, // a NombaOne\Http\HttpClient, or any PSR-18 client
    'defaultHeaders' => ['X-My-Tag' => 'checkout'],
]);
```

`$apiKey` defaults to `NOMBAONE_API_KEY`. You can also pass a single options array: `new Nombaone(['apiKey' => …, 'timeout' => 10])`. Read-only `$nomba->mode` and `$nomba->baseUrl` reflect the resolved environment.

Examples &amp; development
--------------------------

[](#examples--development)

Runnable scripts live in [`examples/`](examples) — quickstart, pagination, the subscription lifecycle, a webhook receiver, and a dunning rehearsal with the test clock. To develop the SDK itself:

```
composer install
composer cs:check       # coding standard (PHP-CS-Fixer, PSR-12)
composer phpstan        # static analysis (PHPStan, max level)
composer test           # unit + conformance tests
NOMBAONE_API_KEY=nbo_sandbox_… composer test:integration   # live suite
```

Requirements &amp; versioning
-----------------------------

[](#requirements--versioning)

PHP ≥ 8.2 (tested on 8.2, 8.3, 8.4). Semantic versioning; the API itself is versioned at `/v1` and additive changes never break you. MIT licensed. Copyright © Nomba One.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance99

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

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

3

Last Release

3d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8f314b57dd71f256af6d6190653c211f4ad2a6d585fe1b0d5e03deaa09f7cb36?d=identicon)[emekaorji](/maintainers/emekaorji)

---

Tags

apisdkbillingpaymentssubscriptionsNigerianombaonenomba

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  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.

35789.4k2](/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.8k28.0M322](/packages/openai-php-client)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[mollie/mollie-api-php

Mollie API client library for PHP. Mollie is a European Payment Service provider and offers international payment methods such as Mastercard, VISA, American Express and PayPal, and local payment methods such as iDEAL, Bancontact, SOFORT Banking, SEPA direct debit, Belfius Direct Net, KBC Payment Button and various gift cards such as Podiumcadeaukaart and fashioncheque.

60216.0M88](/packages/mollie-mollie-api-php)[getbrevo/brevo-php

Official Brevo provided RESTFul API V3 php library

1003.9M50](/packages/getbrevo-brevo-php)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)

PHPackages © 2026

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