Skip to content

Transsmart Client Library

Overview

Standalone PHP client for the Transsmart shipping rates API.

Package: atraxion/transsmart-client

Installation

composer require atraxion/transsmart-client

Configuration

use Atraxion\Transsmart\Config;
use Atraxion\Transsmart\TranssmartClient;

$config = new Config(
    username: 'api_user',
    password: 'api_password',
    accountId: '12345',
    baseUrl: 'https://api.transsmart.com',
    timeout: 30
);

$client = new TranssmartClient($config);

Core Features

Calculate Shipping Rates

use Atraxion\Transsmart\Request\RateRequest;
use Atraxion\Transsmart\Model\Address;
use Atraxion\Transsmart\Model\Package;

$request = new RateRequest(
    sender: new Address(
        company: 'Atraxion',
        street: 'Industrielaan 1',
        zipCode: '2800',
        city: 'Mechelen',
        country: 'BE'
    ),
    recipient: new Address(
        company: 'Customer BV',
        street: 'Hoofdstraat 100',
        zipCode: '1234AB',
        city: 'Amsterdam',
        country: 'NL'
    ),
    packages: [
        new Package(
            weight: 15.5, // kg
            length: 60,   // cm
            width: 40,
            height: 30,
            quantity: 1
        ),
        new Package(
            weight: 8.0,
            length: 50,
            width: 50,
            height: 20,
            quantity: 2
        ),
    ],
    serviceLevel: 'standard', // or 'express'
    incoterm: 'DAP'
);

$rates = $client->calculateRates($request);

foreach ($rates as $rate) {
    echo $rate->getCarrier(); // 'DHL', 'PostNL'
    echo $rate->getService(); // 'Express', 'Standard'
    echo $rate->getPrice()->getAmount(); // 1250 (€12.50)
    echo $rate->getDeliveryDays(); // 1-2
}

Get Cheapest Rate

$cheapest = $client->getCheapestRate($request);

if ($cheapest) {
    echo $cheapest->getCarrier();
    echo $cheapest->getPrice()->format(); // "€12.50"
}

Get Fastest Rate

$fastest = $client->getFastestRate($request);

if ($fastest) {
    echo $fastest->getDeliveryDays(); // 1
    echo $fastest->getEstimatedDelivery()->format('Y-m-d');
}

Create Shipment

use Atraxion\Transsmart\Request\ShipmentRequest;

$shipment = $client->createShipment(new ShipmentRequest(
    rate: $selectedRate,
    reference: 'ORDER-12345',
    sender: $senderAddress,
    recipient: $recipientAddress,
    packages: $packages,
    pickupDate: new \DateTimeImmutable('+1 day'),
    instructions: 'Handle with care'
));

// Get shipping label
$labelPdf = $client->getLabel($shipment->getShipmentId(), 'pdf');
file_put_contents('label.pdf', $labelPdf);

Track Shipment

$tracking = $client->track('SHIP-123456');

echo $tracking->getStatus(); // 'in_transit'
echo $tracking->getCurrentLocation(); // 'Amsterdam Hub'

foreach ($tracking->getEvents() as $event) {
    echo $event->getTimestamp()->format('Y-m-d H:i');
    echo $event->getDescription();
    echo $event->getLocation();
}

Models

ShippingRate

readonly class ShippingRate
{
    public function __construct(
        public string $carrier,
        public string $carrierCode,
        public string $service,
        public string $serviceCode,
        public Money $price,
        public ?Money $fuelSurcharge,
        public int $deliveryDaysMin,
        public int $deliveryDaysMax,
        public ?\DateTimeImmutable $estimatedDelivery,
        public bool $saturdayDelivery,
        public bool $trackingAvailable,
        public array $features = []
    ) {}

    public function getDeliveryDays(): string
    {
        if ($this->deliveryDaysMin === $this->deliveryDaysMax) {
            return (string) $this->deliveryDaysMin;
        }
        return "{$this->deliveryDaysMin}-{$this->deliveryDaysMax}";
    }

    public function getTotalPrice(): Money
    {
        if ($this->fuelSurcharge) {
            return $this->price->add($this->fuelSurcharge);
        }
        return $this->price;
    }
}

Package

readonly class Package
{
    public function __construct(
        public float $weight,  // kg
        public float $length,  // cm
        public float $width,   // cm
        public float $height,  // cm
        public int $quantity = 1,
        public ?string $description = null,
        public ?float $value = null,
        public ?string $hsCode = null
    ) {}

    public function getVolumetricWeight(int $factor = 5000): float
    {
        $volume = $this->length * $this->width * $this->height;
        return ($volume / $factor) * $this->quantity;
    }

    public function getChargeableWeight(int $volumetricFactor = 5000): float
    {
        $actual = $this->weight * $this->quantity;
        $volumetric = $this->getVolumetricWeight($volumetricFactor);
        return max($actual, $volumetric);
    }
}

Address

readonly class Address
{
    public function __construct(
        public string $street,
        public string $zipCode,
        public string $city,
        public string $country, // ISO 2-letter
        public ?string $company = null,
        public ?string $name = null,
        public ?string $houseNumber = null,
        public ?string $phone = null,
        public ?string $email = null,
        public ?string $state = null,
        public bool $residential = false
    ) {}
}

Carrier Constants

final class Carrier
{
    public const DHL = 'DHL';
    public const DHL_EXPRESS = 'DHL_EXPRESS';
    public const DHL_FREIGHT = 'DHL_FREIGHT';
    public const POSTNL = 'POSTNL';
    public const BPOST = 'BPOST';
    public const UPS = 'UPS';
    public const FEDEX = 'FEDEX';
    public const GLS = 'GLS';
    public const DPD = 'DPD';
}

final class ServiceLevel
{
    public const STANDARD = 'standard';
    public const EXPRESS = 'express';
    public const ECONOMY = 'economy';
    public const OVERNIGHT = 'overnight';
    public const SAMEDAY = 'sameday';
}

Error Handling

use Atraxion\Transsmart\Exception\ApiException;
use Atraxion\Transsmart\Exception\AuthenticationException;
use Atraxion\Transsmart\Exception\NoRatesException;
use Atraxion\Transsmart\Exception\ValidationException;

try {
    $rates = $client->calculateRates($request);
} catch (AuthenticationException $e) {
    // Invalid credentials
} catch (NoRatesException $e) {
    // No rates available for this route
    $fallbackPrice = $this->fallbackCalculator->calculate($request);
} catch (ValidationException $e) {
    // Invalid request parameters
    $errors = $e->getValidationErrors();
} catch (ApiException $e) {
    // Other API error
    $this->logger->error('Transsmart error', [
        'code' => $e->getCode(),
        'message' => $e->getMessage(),
    ]);
}

Caching

use Atraxion\Transsmart\Cache\CachedTranssmartClient;

$cachedClient = new CachedTranssmartClient(
    client: $client,
    cache: $psr16Cache,
    ttl: 300 // 5 minutes
);

// Rates are cached by request hash
$rates = $cachedClient->calculateRates($request);

Testing

Mock Client

use Atraxion\Transsmart\Testing\MockTranssmartClient;

$mock = new MockTranssmartClient();

$mock->addRates([
    new ShippingRate(
        carrier: 'DHL',
        service: 'Express',
        price: Money::EUR(1250),
        deliveryDaysMin: 1,
        deliveryDaysMax: 2
    ),
]);

// Use in tests
$rates = $mock->calculateRates($request);

Gherkin Scenarios

Feature: Transsmart Client
  As a developer
  I want to get shipping rates from Transsmart
  Using a clean client library

  Scenario: Get rates for domestic shipment
    Given a shipment from Amsterdam to Rotterdam
    And package weighs 5kg
    When I request shipping rates
    Then I should receive multiple rate options
    And each rate should have carrier and price

  Scenario: Get rates for international shipment
    Given a shipment from Belgium to Germany
    When I request shipping rates
    Then I should receive international carriers
    And customs information should be included

  Scenario: Volumetric weight calculation
    Given a light but large package
    When I calculate chargeable weight
    Then volumetric weight should be used
    And it should be higher than actual weight

  Scenario: No rates available
    Given a shipment to unsupported destination
    When I request shipping rates
    Then NoRatesException should be thrown

  Scenario: Create shipment and get label
    Given I have selected a shipping rate
    When I create a shipment
    Then I should receive shipment ID
    And I should be able to download label PDF

  Scenario: Track shipment
    Given shipment "SHIP-123" exists
    When I track the shipment
    Then I should see tracking events
    And I should see current status

Configuration Reference

Parameter Type Required Default Description
username string Yes - API username
password string Yes - API password
accountId string Yes - Account ID
baseUrl string No production API base URL
timeout int No 30 Request timeout in seconds

API Endpoints Used

Endpoint Method Purpose
/v3/rates POST Calculate shipping rates
/v3/shipments POST Create shipment
/v3/shipments/{id} GET Get shipment details
/v3/shipments/{id}/label GET Get shipping label
/v3/tracking/{id} GET Track shipment
/v3/carriers GET List available carriers