crakter/bringapi

PHP wrapper for Bring's developer APIs (Shipping Guide, Booking, Tracking, Reports, Postal Code, Address, Pickup Point, Modify Delivery, Order Management).

Maintainers

Package info

github.com/crakter/bringapi

pkg:composer/crakter/bringapi

Statistics

Installs: 6 251

Dependents: 0

Suggesters: 0

Stars: 5

Open Issues: 0


README

CI

A PHP client library for Bring's developer APIs: Shipping Guide, Booking, Tracking, Reports, Postal Code, the new Address API, Pickup Point, Modify Delivery, and Order Management (REST).

Used in production by a large Norwegian wholesaler.

Install

composer require crakter/bringapi

Requirements

  • PHP 8.2 or newer
  • A PSR-18 HTTP client (Guzzle 7 is the suggested default)
  • simplexml extension (built-in on most distributions)
  • phpoffice/phpspreadsheet only if you call Reports endpoints that return XLS

Supported APIs

API Coverage Bring docs
Shipping Guide (v2) price / delivery time / products link
Booking book, pickup order, customers link
Tracking track, signature image link
Reports list, generate, status, download, invoices link
Postal Code (legacy) single lookup link
Address (new) postal-code lookup, suggestions, mailbox-delivery dates link
Pickup Point all / by id / by postal code / by location (NO/SE/DK/FI only) link
Modify Delivery stop, change address, update contact (NO/SE/DK only) link
Order Management (REST) get order, packaging list link

The SOAP variant of Order Management is intentionally out of scope.

Quick start

use Bring\Api\ApiClient;
use Bring\Api\Auth\Credentials;
use Bring\Api\Enum\Country;

$bring = ApiClient::withCredentials(new Credentials(
    uid: 'me@example.com',
    apiKey: getenv('BRING_API_KEY'),
    clientUrl: 'https://example.com',
));

// Modern address lookup
$result = $bring->address()->postalCode(Country::NO, '0150');
echo $result->city; // "OSLO"

// Pickup points near a postal code
foreach ($bring->pickupPoint()->byPostalCode(Country::NO, '0150')->pickupPoints as $pp) {
    echo "{$pp->name}{$pp->address}\n";
}

// Tracking
$tracking = $bring->tracking()->track('TESTPACKAGE-AT-PICKUPPOINT');
echo $tracking->latestEvent()?->description;

Test mode

Calls that support X-Bring-Test-Indicator (Booking, Modify Delivery) are toggled at the facade:

$bring = ApiClient::withCredentials($creds)->withTestMode(true);
// Every request now carries X-Bring-Test-Indicator: true

Booking a shipment

use Bring\Api\Dto\{Address, Contact, Dimensions, Package};
use Bring\Api\Endpoint\Booking\BookingRequest;
use Bring\Api\Enum\{Country, Product};

$request = BookingRequest::single(
    schemaVersion: '1',
    customerNumber: 'PARCELS_NORWAY-10001234567',
    product: Product::HOME_DELIVERY_PARCEL,
    sender: new Address(
        name: 'Acme AS', addressLine: 'Sandakerveien 24c', addressLine2: null,
        postalCode: '0473', city: 'Oslo', countryCode: Country::NO,
        contact: new Contact(name: 'Pickup Person', phoneNumber: '+4799999999'),
    ),
    recipient: new Address(
        name: 'John Doe', addressLine: 'Storgata 1', addressLine2: null,
        postalCode: '5003', city: 'Bergen', countryCode: Country::NO,
    ),
    packages: [new Package(
        weightInKg: 2,
        dimensions: new Dimensions(lengthInCm: 30, widthInCm: 20, heightInCm: 15),
    )],
);

$resp = $bring->booking()->book($request);
foreach ($resp->consignments as $c) {
    echo "Consignment {$c->confirmation}\n";
}

Credentials and logging

Credentials wraps the API key behind #[\SensitiveParameter] (PHP 8.2+ scrubs it from stack traces) and masks it in print_r / var_dump output — debug dumps only show a SHA-256 fingerprint.

Pass any PSR-3 logger to ApiClient::withCredentials() and it is automatically wrapped in a RedactingLogger that strips X-Mybring-* headers and the raw API key from every log line:

$bring = ApiClient::withCredentials($creds, logger: $monolog);

BringApiException never embeds the raw response body in getMessage() — Bring occasionally echoes credentials in error envelopes. Callers that want the response body can call BringApiException::getResponse() explicitly.

Error handling

use Bring\Api\Exception\{BringApiException, BringTransportException, BringException};

try {
    $bring->shippingGuide()->price($request);
} catch (BringApiException $e) {
    // Bring returned 4xx/5xx — parsed error codes in $e->getErrors()
    error_log("Bring rejected request (HTTP {$e->getStatusCode()})");
    foreach ($e->getErrors() as $err) {
        error_log("  {$err->code}: {$err->message}");
    }
} catch (BringTransportException $e) {
    // PSR-18 network failure (DNS, TLS, timeout); $e->getPrevious() has the cause
} catch (BringException $e) {
    // Catch-all for anything this library throws
}

v3 → v4 migration

See UPGRADE-4.0.md for the full mapping. v3 classes (Crakter\BringApi\*) still ship and still work — they are marked @deprecated and will be removed in 5.0.

Examples

Set credentials in your environment first:

export BRING_UID="me@example.com"
export BRING_API_KEY="1234abc-abcd-1234-5678-abcd1234abcd"
export BRING_CUSTOMER_NUMBER="PARCELS_NORWAY-10001123123"

Then run any example from the project root:

php examples/v4_PostalCode.php 0150
php examples/v4_PickupPoint.php 0150
php examples/v4_Tracking.php TESTPACKAGE-AT-PICKUPPOINT
php examples/v4_ShippingGuidePrice.php 0150 5003
php examples/v4_BookAndPickup.php   # uses test mode, no labels generated

Development

composer install
composer qa                                     # full gate: cs + phpstan + psalm + phpunit
composer test                                   # all tests (legacy + v4)
composer test-coverage                          # coverage in coverage/ + coverage.xml
composer phpstan                                # PHPStan level 8 on v4
composer psalm                                  # Psalm errorLevel 4 on v4
composer cs                                     # php-cs-fixer dry run
composer docs                                   # build API docs into docs/build

API documentation

Generated with phpDocumentor 3 (the abandoned Sami generator was dropped in 4.0). bin/build-docs downloads the official phar into tools/phpdoc.phar on first run — we deliberately do NOT require phpDocumentor through Composer because its transitive dependency tree conflicts with most application stacks.

composer docs              # build into docs/build
composer docs-clean        # wipe + rebuild
bin/build-docs --force     # any extra phpdoc flags pass through

Open docs/build/index.html in a browser, or let CI publish it: .github/workflows/docs.yml builds on every push to master/main and deploys to GitHub Pages (enable Pages in repo settings → Pages → Source = GitHub Actions to activate). Every workflow run also uploads the rendered docs as an artifact named api-docs.

Project documents

License

MIT