Skip to content

Releases: cainydev/billbee-php-sdk

v4.0.0

Choose a tag to compare

@cainydev cainydev released this 20 Aug 20:50
34479b9

A full modernization of the SDK. Endpoint method names are preserved, so most migrations are mechanical: fix the construction, fix the response handling, fix the exception names. See UPGRADING.md for the details.

Requires PHP 8.3+.

composer require cainy/billbee-php-sdk

Transport

Built on PSR-18 and PSR-17. Guzzle is no longer a hard dependency; inject any PSR-18 client or let one be auto-discovered. That also means Http::fake() and other test doubles work without touching the network.

A middleware pipeline handles retry, rate limiting, logging, and user agent. Requests that fail with 429 or 5xx are retried automatically with exponential backoff and full jitter, honoring the Retry-After header. Non-idempotent requests are only retried when it is provably safe, so a retry never duplicates a write. A client-side token bucket paces requests to keep you under quota.

Serialization

jms/serializer is replaced by symfony/serializer. All 442 wire names across 48 models were verified identical to v3 by mechanical diff, so the wire format is unchanged.

Dates are DateTimeImmutable throughout, and the six fields with custom wire formats keep them. Unknown enum values and unparseable dates now throw instead of silently falling back to a default or null.

API surface

An endpoint is a lazy collection. Building or filtering one performs no HTTP
request; pages are fetched as you iterate, and stop the moment you break.

foreach ($billbee->orders()->where($query) as $order) {
    // walks every page at constant memory, paced by the rate limiter
}

Method names no longer repeat the resource:

v3 v4
orders()->getOrders($q) foreach ($billbee->orders()->where($q))
orders()->getOrder(1) orders()->find(1)
orders()->setOrderState(1, 3) orders()->setState(1, OrderState::PAID)
products()->deleteProduct(1) products()->delete(1)
webHooks()->deleteWebHookById($id) webhooks()->delete($id)
customers()->getCustomerAddresses($id) customers()->addresses($id)

Single reads return the model rather than an envelope. find() returns an
Order; findOrNull() returns null instead of throwing on 404. page()
returns BillbeeResponse<T> when paging metadata is the point, and count()
reads totalRows from a single row request rather than traversing. Nested
resources are collections too.

Writes return void. Since v4 throws on failure, a boolean that could only ever
be true carried no information.

A non-zero ErrorCode in the response envelope now throws ApiException. In v3
it was returned and ignored, so failed writes looked like successes.

Exceptions form a typed hierarchy rooted at BillbeeException, each carrying
statusCode, requestId, and responseBody:

Exception Raised on
ApiException envelope reported a non-zero ErrorCode
AuthenticationException 401, 403
NotFoundException 404
RateLimitException 429, carries retryAfter
ValidationException 400, 422, carries errors
ServerException 5xx
TransportException network failure
SerializationException malformed payload

Typed values

Backed enums replace raw scalars wherever the value set is fixed, so mistakes
surface at the call site: setState(int, OrderState), Shipment::$carrierId,
Product::$condition, Payment::$paymentMethod, Order::$paymentMethod, and
Invoice::$paymentTypeId. Each is covered by a round-trip test proving the wire
format is unchanged.

Fields backed by an open vendor list stay strings, because a strict enum would
reject legitimate new values. They expose a typed accessor instead:

$order->seller->partner();   // ?Partner, null when unrecognised
$order->seller->platform;    // the raw string, always present

Security

Credentials are redacted from log output. v3 logged request options wholesale, leaking HTTP Basic credentials and the API key into debug logs.

Webhook signature verification is now available via WebhookVerifier, using a timing-safe comparison. v3 had none, which left consumer webhook endpoints unauthenticated.

Note that Billbee does not publicly document its webhook signature scheme, so this implements the de-facto standard HMAC-SHA256(rawBody, secret). Override WebhookVerifier::expectedDigest() if it differs, and please open an issue if you confirm the real scheme.

Fixes

  • getOrderByOrderNumber() used urlencode() on a URL path segment, encoding spaces as + rather than %20. An external order reference containing a space resolved to the wrong URL. All path segments now use rawurlencode().
  • Removed a stray use T; import referencing a class that does not exist.
  • The README documented enableBatchMode() and executeBatch(), which had already been removed from the source. Calling them was a fatal error.

Tooling

PHP 8.3+, PHPUnit 13, PHPStan at level max with no baseline, and GitHub Actions CI across PHP 8.3, 8.4, and 8.5.

549 tests, 4501 assertions.

Breaking changes

  • Client is now Billbee, and ClientConfiguration is now Config.
  • The Get*Response classes are gone, replaced by BillbeeResponse<T>.
  • ConnectionException, QuotaExceededException, and InvalidIdException are replaced by TransportException, RateLimitException, and NotFoundException.
  • The dateTimeClass option is removed. Dates are always DateTimeImmutable; the option was already non-functional in v3.
  • The verifySSL option is removed. TLS settings belong to the injected PSR-18 client.
  • withLogger(), withDateTimeClass(), and withRequestLogging() are replaced by a single generic with().

v3.1.1

Choose a tag to compare

@cainydev cainydev released this 20 Sep 13:38

Full Changelog: v3.1.0...v3.1.1

v3.0.0 - BREAKING - Alpha version for modern PHP Billbee SDK

Choose a tag to compare

@cainydev cainydev released this 11 Sep 19:50
v3.0.0 - Alpha release of the modern Billbee PHP SDK