texhub/laklak-b2b

LakLak B2B delivery API SDK for any PHP framework with first-class Laravel support: cities, parcel terminals, pickup points, shipments, label printing and delivery webhooks.

Maintainers

Package info

github.com/TexhubPro/laklak-b2b

Homepage

Issues

pkg:composer/texhub/laklak-b2b

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.1 2026-06-05 15:03 UTC

This package is auto-updated.

Last update: 2026-06-05 15:03:57 UTC


README

🌐 English Β· Русский

License: MIT PHP Laravel

A clean, framework-agnostic PHP SDK for the LakLak B2B delivery API β€” cities, parcel terminals & pickup points, shipments, label printing and delivery webhooks β€” with first-class Laravel support.

What's new in v1.0.1

  • Parcel terminals & pickup points now include visible_id (e.g. PT-102, PP-042) and a cluster object (id, name) β€” read them with $item['visible_id'] / $response->get('cluster.name').
  • New delivery status expired (and on_the_way) in DeliveryStatus; isFinal() now covers delivered and expired.
  • The delivery address changed webhook now carries the full drop_off_location β€” read it via $event->dropOffLocation().

✨ Features

  • πŸ™ Addresses β€” cities, parcel terminals, pickup points (with search)
  • πŸ“¦ Shipments β€” create, update payment status & weight, label print URL
  • πŸ”” Webhooks β€” verify X-Webhook-Key + timestamp, parse delivery status & address changes
  • 🟒 Test / Production switch Β· X-API-Key auth Β· typed enums & responses
  • βœ… Fully unit-tested

πŸ“¦ Installation

composer require texhub/laklak-b2b

Requirements: PHP β‰₯ 8.2 with curl, json, hash.

πŸš€ Quick start

use TexHub\LaklakB2b\LakLak;
use TexHub\LaklakB2b\Enums\Environment;

$laklak = LakLak::make('YOUR_API_KEY', Environment::Test); // Environment::Production when live

// Addresses
$cities = $laklak->addresses()->cities('Dush');
$terminals = $laklak->addresses()->parcelTerminals(cityId: 39720);
$pickups = $laklak->addresses()->pickupPoints(cityId: 39720);

foreach ($cities->data() as $city) {
    echo $city['id'] . ' ' . $city['name'] . PHP_EOL;
}

πŸ“¦ Shipments

use TexHub\LaklakB2b\Requests\ShipmentRequest;
use TexHub\LaklakB2b\Enums\DeliveryType;
use TexHub\LaklakB2b\Enums\PaymentStatus;

$shipment = $laklak->shipments()->create(
    ShipmentRequest::make(
        externalOrderId: 'ORD-100032',
        customerPhone: '+992900123456',
        deliveryType: DeliveryType::ParcelTerminal,
        dropOffLocationId: 2,
        paymentStatus: PaymentStatus::Unpaid,
    )->customerName('Anu Lily')
);

echo $shipment->get('tracking_number');   // B2B-CODE-100001
echo $shipment->get('label_print_url');   // QR label to print & attach

// Update payment status (+ weight when paid):
$updated = $laklak->shipments()->updatePaymentStatus('ORD-100032', PaymentStatus::Paid, '2.5 KG');
echo $updated->get('parcel_terminal_password');

// Print labels for several shipments:
$laklak->shipments()->labelPrintUrl(['B2B-CODE-100001', 'B2B-CODE-100002']);

When payment_status is paid, a package weight is required (validated by the SDK).

πŸ”” Webhooks (LakLak β†’ you)

LakLak calls your URL with headers X-Webhook-Key (the secret you gave them) and X-Webhook-Timestamp.

use TexHub\LaklakB2b\Enums\DeliveryStatus;

// Verify, then parse:
$laklak->webhooks()->assertValid(
    $_SERVER['HTTP_X_WEBHOOK_KEY'] ?? null,
    $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? null,
);

$event = $laklak->webhooks()->parse(file_get_contents('php://input'));

if ($event->isDeliveryStatusChanged()) {
    $event->trackingNumber();
    $event->externalOrderId();
    $event->deliveryStatus();            // DeliveryStatus enum: pending|picked_up|on_the_way|ready_to_pick|delivered|expired
    $event->deliveryStatus()->isFinal(); // true for delivered/expired
    $event->parcelTerminalPassword();
}

if ($event->isDeliveryAddressChanged()) {
    $event->deliveryType();              // DeliveryType enum
    $event->dropOffLocationId();
    $event->dropOffLocation();           // full location array: id, visible_id, type, cluster, …
}

http_response_code(200); // acknowledge

βš™οΈ Error handling

use TexHub\LaklakB2b\Exceptions\ApiException;

try {
    $laklak->shipments()->create($request);
} catch (ApiException $e) {
    $e->httpStatus;
    $e->isValidationError();   // 422
    $e->errors;                // ['field' => ['message', ...]]
    $e->isUnauthorized();      // 401/403
}

🧩 Laravel

Auto-discovered. Publish config:

php artisan vendor:publish --tag=laklak-b2b-config

.env:

LAKLAK_ENVIRONMENT=test
LAKLAK_API_KEY=your_api_key
LAKLAK_WEBHOOK_SECRET=your_webhook_secret

Facade:

use TexHub\LaklakB2b\Laravel\LakLak;

$cities = LakLak::addresses()->cities();
LakLak::shipments()->create($request);

Exclude your webhook route from CSRF (VerifyCsrfToken::$except).

πŸ§ͺ Testing

use TexHub\LaklakB2b\LakLak;
use TexHub\LaklakB2b\Config;
use TexHub\LaklakB2b\Tests\Support\FakeTransport;

$t = (new FakeTransport())->push(['success' => true, 'data' => []]);
$laklak = new LakLak(new Config('KEY'), $t);
$laklak->addresses()->cities(); // assert on $t->last()
composer install && composer test

πŸ“š Architecture

src/
β”œβ”€β”€ LakLak.php               # entry β€” addresses()/shipments()/webhooks()
β”œβ”€β”€ Config.php               # immutable configuration
β”œβ”€β”€ Enums/                   # Environment, DeliveryType, PaymentStatus, DeliveryStatus
β”œβ”€β”€ Http/                    # Transport, CurlTransport, HttpClient, RawResponse
β”œβ”€β”€ Requests/ShipmentRequest # fluent shipment builder
β”œβ”€β”€ Resources/               # Addresses, Shipments
β”œβ”€β”€ Webhook/                 # WebhookHandler (verify + parse), WebhookEvent
β”œβ”€β”€ Responses/               # Response (ArrayAccess), ListResponse (paginated)
β”œβ”€β”€ Exceptions/              # ApiException, TransportException, …
└── Laravel/                 # ServiceProvider + Facade

License

MIT Β© TexHub Pro β€” built by Mahmudi Shodmehr.