A modern, robust, and well-documented PHP/Laravel client library for the ShipTime REST API. Built with modern PHP 8.1+ features including strict typing, enums, and readonly properties.
- 🚀 Modern PHP 8.1+ - Leverages the latest PHP features
- 🎯 Strict Typing - Full type safety with scalar types and return types
- 📦 Complete API Coverage - All public ShipTime API endpoints
- 🔧 Laravel Integration - Service provider for seamless Laravel integration
- 🛡️ Robust Error Handling - Specific exceptions for different error types
- 📝 Comprehensive Documentation - Fully documented with PHPDoc
- ✅ Well Tested - Comprehensive test suite
- 🎨 Clean Architecture - Service-oriented design with DTOs
- PHP 8.1 or higher
- Laravel 9.x, 10.x, 11.x, or 12.x (for Laravel integration)
- Guzzle HTTP client 7.x
Install the package via Composer:
composer require tigusigalpa/shiptime-phpThe package will automatically register its service provider.
Publish the configuration file:
php artisan vendor:publish --tag=shiptime-configAdd your ShipTime API credentials to your .env file:
SHIPTIME_API_KEY=your_api_key
SHIPTIME_API_SECRET=your_api_secretuse Tigusigalpa\ShipTime\ShipTime;
$shiptime = new ShipTime(
apiKey: 'your_api_key',
apiSecret: 'your_api_secret'
);
// Get shipping rates
$rates = $shiptime->rates()->getRates($rateRequest);
// Create a shipment
$shipment = $shiptime->shipments()->createShipment($shipRequest);
// Track a shipment
$tracking = $shiptime->track()->track($shipmentId);Inject the ShipTime client via dependency injection:
use Tigusigalpa\ShipTime\ShipTime;
class ShippingController extends Controller
{
public function __construct(
private ShipTime $shiptime
) {}
public function getRates(Request $request)
{
$rates = $this->shiptime->rates()->getRates($rateRequest);
return response()->json($rates);
}
}Or use the facade:
use Tigusigalpa\ShipTime\Facades\ShipTime;
$rates = ShipTime::rates()->getRates($rateRequest);Create and manage preliminary shipment orders.
use Tigusigalpa\ShipTime\ShipTime;
$shiptime = new ShipTime($apiKey, $apiSecret);
// Get paginated list of orders
$orders = $shiptime->orders()->getOrders(
page: 0,
size: 10,
status: 'PENDING',
orderDate: '2024-01-15'
);
foreach ($orders->content as $order) {
echo "Order ID: {$order['orderId']}\n";
}use Tigusigalpa\ShipTime\DTOs\OrderRequest;
use Tigusigalpa\ShipTime\DTOs\ShipTo;
use Tigusigalpa\ShipTime\DTOs\Package;
use Tigusigalpa\ShipTime\DTOs\Dimensions;
$shipTo = new ShipTo(
streetAddress: '123 Main St',
city: 'New York',
state: 'NY',
postalCode: '10001',
countryCode: 'US',
companyName: 'Acme Corp',
attention: 'John Doe',
phone: '555-1234'
);
$package = new Package(
weight: 5.5,
dimensions: new Dimensions(length: 12, width: 8, height: 6),
type: 'PACKAGE'
);
$orderRequest = new OrderRequest(
orderNumber: 'ORD-12345',
shipTo: $shipTo,
unitOfMeasurement: 'IMPERIAL',
packages: [$package]
);
$response = $shiptime->orders()->createOrder($orderRequest);
echo "Order created with ID: {$response->orderId}\n";$cancelResponse = $shiptime->orders()->cancelOrder(orderId: 12345);
if ($cancelResponse->success) {
echo "Order cancelled successfully\n";
}Get shipping rates for your shipments.
use Tigusigalpa\ShipTime\DTOs\RateRequest;
use Tigusigalpa\ShipTime\DTOs\Address;
use Tigusigalpa\ShipTime\DTOs\LineItem;
use Tigusigalpa\ShipTime\Enums\PackageType;
use Tigusigalpa\ShipTime\Enums\UnitOfMeasurement;
$from = new Address(
companyName: 'My Company',
streetAddress: '123 Sender St',
city: 'Los Angeles',
countryCode: 'US',
state: 'CA',
postalCode: '90001',
attention: 'Sender Name',
phone: '555-0001'
);
$to = new Address(
companyName: 'Customer Co',
streetAddress: '456 Receiver Ave',
city: 'New York',
countryCode: 'US',
state: 'NY',
postalCode: '10001',
attention: 'Receiver Name',
phone: '555-0002'
);
$lineItem = new LineItem(
length: 12.0,
width: 8.0,
height: 6.0,
weight: 5.5
);
$rateRequest = new RateRequest(
from: $from,
to: $to,
packageType: PackageType::PACKAGE,
lineItems: [$lineItem],
unitOfMeasurement: UnitOfMeasurement::IMPERIAL,
shipDate: '2024-02-20'
);
$rateResponse = $shiptime->rates()->getRates($rateRequest);
foreach ($rateResponse->availableRates as $quote) {
echo "Carrier: {$quote->carrierName}\n";
echo "Service: {$quote->serviceName}\n";
echo "Total: {$quote->totalCharge->currency->value} {$quote->totalCharge->amount}\n";
echo "Transit Days: {$quote->transitDays}\n\n";
}Create, retrieve, and manage shipments.
use Tigusigalpa\ShipTime\DTOs\ShipRequest;
// Option 1: Using a quote ID from a previous rate request
$shipRequest = new ShipRequest(
quoteId: 'quote_abc123'
);
// Option 2: Using rate request with carrier and service selection
$shipRequest = new ShipRequest(
rateRequest: $rateRequest,
carrierId: 'UPS',
serviceId: 'UPS_GROUND',
ref1: 'Reference 1',
ref2: 'Reference 2'
);
$shipResponse = $shiptime->shipments()->createShipment($shipRequest);
echo "Shipment ID: {$shipResponse->shipId}\n";
echo "Tracking Number: {$shipResponse->trackingNumbers[0]}\n";
echo "Label URL: {$shipResponse->labelUrl}\n";$shipments = $shiptime->shipments()->getShipments(
page: 0,
size: 20,
shipDate: '2024-02-15'
);
foreach ($shipments->content as $shipment) {
echo "Shipment: {$shipment['shipId']}\n";
}$shipment = $shiptime->shipments()->getShipment(shipmentId: 12345);
echo "Status: {$shipment->status->value}\n";
echo "Carrier: {$shipment->carrierName}\n";
echo "Service: {$shipment->serviceName}\n";$labelPdf = $shiptime->shipments()->getLabel(shipmentId: 12345);
// Save to file
file_put_contents('label.pdf', $labelPdf);
// Or send as download
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="shipping-label.pdf"');
echo $labelPdf;$invoicePdf = $shiptime->shipments()->getCustomsInvoice(shipmentId: 12345);
file_put_contents('customs-invoice.pdf', $invoicePdf);$cancelResponse = $shiptime->shipments()->cancelShipment(shipmentId: 12345);
if ($cancelResponse->success) {
echo "Shipment cancelled\n";
}Track shipments and get delivery status.
$trackResponse = $shiptime->track()->track(
shipmentId: 12345,
includeTime: true
);
$tracking = $trackResponse->trackingRecord;
echo "Current Status: {$tracking->currentStatus->value}\n";
foreach ($tracking->history as $event) {
echo "{$event->timestamp} - {$event->name} at {$event->location}\n";
}
// Proof of delivery
if ($tracking->podInfo) {
echo "Signed by: {$tracking->podInfo->signedBy}\n";
echo "Delivered at: {$tracking->podInfo->timestamp}\n";
}Schedule and manage pickups.
use Tigusigalpa\ShipTime\DTOs\PickupRequest;
use Tigusigalpa\ShipTime\DTOs\PickupDetail;
use Tigusigalpa\ShipTime\Enums\PickupLocation;
$pickupDetail = new PickupDetail(
location: PickupLocation::FRONT_DOOR,
pickupDate: '2024-02-20',
readyTime: '09:00',
closeTime: '17:00'
);
$pickupRequest = new PickupRequest(
shipId: 12345,
pickupDetail: $pickupDetail
);
$pickup = $shiptime->pickups()->schedulePickup($pickupRequest);
echo "Pickup scheduled: {$pickup->confirmationNumber}\n";$pickups = $shiptime->pickups()->getPickups(page: 0, size: 10);
foreach ($pickups->content as $pickup) {
echo "Pickup ID: {$pickup['id']}\n";
}$response = $shiptime->pickups()->cancelPickup(pickupId: 'pickup123');
if ($response->success) {
echo "Pickup cancelled\n";
}$availablePickups = $shiptime->pickups()->getAvailablePickupsForShipment(
shipmentId: 12345
);
if ($availablePickups->pickup) {
echo "Available pickup: {$availablePickups->pickup->confirmationNumber}\n";
}Get available box configurations.
$boxes = $shiptime->boxes()->getBoxes();
foreach ($boxes as $box) {
echo "Box: {$box->name}\n";
echo "Dimensions: {$box->length}x{$box->width}x{$box->height}\n";
echo "Weight: {$box->weight}\n\n";
}Get account information and available services.
$servicesResponse = $shiptime->accounts()->getAvailableServices();
foreach ($servicesResponse->carrierServices as $service) {
echo "Carrier: {$service->carrierName}\n";
echo "Service: {$service->serviceName}\n\n";
}Lookup location information by postal code.
use Tigusigalpa\ShipTime\Enums\CountryCode;
$location = $shiptime->location()->lookup(
countryCode: CountryCode::US,
postalCode: '10001'
);
echo "City: {$location->city}\n";
echo "State: {$location->state}\n";The library provides specific exception classes for different error scenarios:
use Tigusigalpa\ShipTime\Exceptions\AuthenticationException;
use Tigusigalpa\ShipTime\Exceptions\ValidationException;
use Tigusigalpa\ShipTime\Exceptions\NotFoundException;
use Tigusigalpa\ShipTime\Exceptions\ForbiddenException;
use Tigusigalpa\ShipTime\Exceptions\ServerErrorException;
use Tigusigalpa\ShipTime\Exceptions\ShipTimeException;
try {
$rates = $shiptime->rates()->getRates($rateRequest);
} catch (AuthenticationException $e) {
// Invalid API credentials
echo "Authentication failed: {$e->getMessage()}\n";
} catch (ValidationException $e) {
// Invalid request data
echo "Validation error: {$e->getMessage()}\n";
} catch (NotFoundException $e) {
// Resource not found
echo "Not found: {$e->getMessage()}\n";
} catch (ForbiddenException $e) {
// Access forbidden
echo "Forbidden: {$e->getMessage()}\n";
} catch (ServerErrorException $e) {
// Server error (5xx)
echo "Server error: {$e->getMessage()}\n";
} catch (ShipTimeException $e) {
// Any other ShipTime API error
echo "API error: {$e->getMessage()}\n";
}For international shipments, you need to provide customs information:
use Tigusigalpa\ShipTime\DTOs\CustomsInvoice;
use Tigusigalpa\ShipTime\DTOs\DutiesAndTaxes;
use Tigusigalpa\ShipTime\DTOs\InvoiceItem;
use Tigusigalpa\ShipTime\DTOs\InvoiceContact;
use Tigusigalpa\ShipTime\Enums\Currency;
use Tigusigalpa\ShipTime\Enums\ReasonForExport;
use Tigusigalpa\ShipTime\Enums\DutiesPaidBy;
$invoiceContact = new InvoiceContact(
companyName: 'Exporter Inc',
streetAddress: '123 Export St',
city: 'Toronto',
countryCode: 'CA',
state: 'ON',
postalCode: 'M5H 2N2',
attention: 'Export Manager',
phone: '416-555-0001'
);
$dutiesAndTaxes = new DutiesAndTaxes(
dutiable: true,
paidBy: DutiesPaidBy::CONSIGNEE
);
$invoiceItem = new InvoiceItem(
quantity: 2,
code: '123456',
description: 'Electronic Components',
origin: 'CA',
provinceOrState: 'ON',
unitPrice: 5000 // in cents
);
$customsInvoice = new CustomsInvoice(
dutiesAndTaxes: $dutiesAndTaxes,
invoiceContact: $invoiceContact,
currency: Currency::CAD,
invoiceItems: [$invoiceItem],
reasonForExport: ReasonForExport::COMMERCIAL
);
// Add customs invoice to rate request
$rateRequest = new RateRequest(
from: $from,
to: $to,
packageType: PackageType::PACKAGE,
lineItems: [$lineItem],
unitOfMeasurement: UnitOfMeasurement::METRIC,
shipDate: '2024-02-20',
customsInvoice: $customsInvoice
);Run the test suite:
composer testRun tests with coverage:
composer test:coverageContributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
The MIT License (MIT). Please see License File for more information.
For support, please contact sovletig@gmail.com or open an issue on GitHub.
