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 |