ages/shipping-gateway 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ages/shipping-gateway

Composer 安装命令:

composer require ages/shipping-gateway

包简介

Unified shipping carrier API for CzechPost, GLS, PPL

README 文档

README

Unified PHP library for shipping carrier integrations — GLS, PPL, Czech Post, Gebrüder Weiss.
Single entry point, config-driven credentials and pickup address, compatible with Nette + Nextras.

API Documentation

Dopravce Odkaz
GLS https://api.mygls.hu/index_cz.html
PPL https://ppl-cpl-api.apidog.io/
Česká pošta https://www.postaonline.cz/dokumentaceapi/b2b/
Gebrüder Weiss https://developer.my.gw-world.com/#/apis

Requirements

  • PHP 8.4+
  • Nette Utils ^4.0
  • Guzzle ^7.9
  • mPDF ^8.3

Installation

composer require ages/shipping-gateway

Or add a path repository for local development:

{
    "repositories": [
        {
            "type": "path",
            "url": "../shipping-gateway"
        }
    ],
    "require": {
        "ages/shipping-gateway": "*"
    }
}

Configuration (Nette / neon)

All credentials and pickup address are configured in config.neon — nothing is hardcoded.

services:
    pickupAddress: Ages\ShippingGateway\Common\PickupAddress(
        name: 'MyShop s.r.o.'
        street: Ulice
        city: Město
        zip: '12345'
        country: CZ
        phone: '+420 123 456 789'
        email: 'info@myshop.cz'
        houseNumber: '10'
        # company: 'my-company-code'   # volitelne, pouziva se jen pro GW tracking URL
    )

    glsConfig: Ages\ShippingGateway\Gls\Config\GlsConfig(
        username: 'user@example.com'
        password: secret
        clientNumber: 12345678
        pickupAddress: @pickupAddress
        # url: https://api.test.mygls.cz/ParcelService.svc/json/   # test endpoint
    )

    pplConfig: Ages\ShippingGateway\Ppl\Config\PplConfig(
        clientId: CLIENT_ID
        clientSecret: CLIENT_SECRET
        pickupAddress: @pickupAddress
        # url: https://api-dev.dhl.com/ecs/ppl/myapi2/             # test endpoint
    )

    czechPostConfig: Ages\ShippingGateway\CzechPost\Config\CzechPostConfig(
        apiToken: api-token-here
        secretKey: secret-key-here
        idContract: 123456789
        customerId: L12345
        postCode: '53307'
        locationNumber: 1
        certificatePath: %wwwDir%/cert/postsignum-bundle.pem
        # packageLimit: 5
    )

    gwConfig: Ages\ShippingGateway\GebruderWeiss\Config\GebruderWeissConfig(
        clientId: CLIENT_ID
        clientSecret: CLIENT_SECRET
        customerId: 12345
        branchCode: PRG
        pickupAddress: @pickupAddress
        ssccPrefix: '22260000'          # číselný prefix pro generování SSCC kódů
        # product: GW_PRO_LINE
        # incoterm: DAP
        # goodsDescription: Goods
        # logoPath: %wwwDir%/img/logo-gw.png   # čtvercové logo na etiketu (volitelné)
    )

    - Ages\ShippingGateway\ShippingGateway

Certifikát České pošty (postsignum-bundle.pem) je třeba umístit ručně do projektu.
Cesta se nastavuje přes certificatePath — nikdy se nekopíruje do balíčku.

Gebrüder Weiss — clientId / clientSecret odpovídají hodnotám Consumer Key a Consumer Secret z portálu developer.my.gw-world.com (záložka vaší aplikace po subscribe na API).
Je třeba mít subscribe na obě API: TRANSPORT_ORDER_CREATE i TRACKNTRACE.

Gebrüder Weiss — ssccPrefix je povinný číselný řetězec (doporučeno 6–10 číslic) sloužící jako základ pro automatické generování SSCC kódů (18 číslic dle GS1).
Podpora jej sdělí nebo si zvolte vlastní firemní prefix, např. 22260000.
Volitelný logoPath přijímá absolutní cestu k obrázku — ten se zobrazí v levém horním rohu štítku ve čtvercové oblasti cca 24 × 24 mm.

Usage

Unified shipment creation

Předáš ShipmentRequest, označíš dopravce a dostaneš ShipmentLabel[] — jeden štítek per balík.

use Ages\ShippingGateway\Common\Carrier;
use Ages\ShippingGateway\Common\Shipment\CashOnDelivery;
use Ages\ShippingGateway\Common\Shipment\Parcel;
use Ages\ShippingGateway\Common\Shipment\RecipientAddress;
use Ages\ShippingGateway\Common\Shipment\ShipmentRequest;
use Ages\ShippingGateway\Common\Shipment\ShipmentValue;

$request = new ShipmentRequest(
    reference: '2025-00123',
    recipient: RecipientAddress::fromFullName(
        name: 'Jan Novák',
        street: 'Hlavní',
        houseNumber: '42',
        city: 'Praha',
        zip: '10000',
        country: 'CZ',
        phone: '+420600123456',
        email: 'jan@example.com',
    ),
    parcels: [
        new Parcel(weight: 2.5),
        new Parcel(weight: 1.8),
    ],
    value: new ShipmentValue(amount: 1490.0),
    cod: new CashOnDelivery(amount: 1490.0, variableSymbol: '2025001'),
    note: 'Poznámka pro přepravu',
    // Pro výdejní místa:
    // parcelShopCode: 'B69623',
    // parcelShopZip: '69623', // u České pošty explicitně PSČ provozovny
);

/** @var \Ages\ShippingGateway\ShippingGateway $gateway */
$labels = $gateway->createShipment(Carrier::Gls, $request);

foreach ($labels as $label) {
    echo $label->trackingNumber;             // číslo zásilky
    file_put_contents('label.pdf', $label->labelPdf); // raw PDF bytes
}

Dostupné hodnoty Carrier: Carrier::Gls, Carrier::Ppl, Carrier::CzechPost, Carrier::GebruderWeiss.

RecipientAddress

// Ze jména — rozdělí podle první mezery (Jan / Novák)
RecipientAddress::fromFullName('Jan Novák', $street, $city, $zip, $country, $phone, $email);

// Nebo přímo s firstName / lastName
new RecipientAddress(
    firstName: 'Jan',
    lastName: 'Novák',
    street: 'Hlavní',
    city: 'Praha',
    zip: '10000',
    country: 'CZ',
    phone: '+420600123456',
    email: 'jan@example.com',
    company: 'Firma s.r.o.',      // volitelné
    houseNumber: '42',            // volitelné
    type: RecipientType::Company, // Person (výchozí) nebo Company
);

Parcel — typy zásilek

use Ages\ShippingGateway\Common\Shipment\Dimensions;
use Ages\ShippingGateway\Common\Shipment\ParcelType;

new Parcel(weight: 2.5);                                         // standardní balík
new Parcel(weight: 15.0, type: ParcelType::PackageOversize);     // neskladný / atypický
new Parcel(weight: 80.0, type: ParcelType::PalletEur);           // EUR paleta (GebrüderWeiss)
new Parcel(weight: 60.0, type: ParcelType::PalletOneWay);        // jednorázová paleta
new Parcel(weight: 50.0, type: ParcelType::PalletHalf);          // půl paleta
new Parcel(weight: 40.0, type: ParcelType::PalletCustom);        // vlastní paleta

// S rozměry — hodnoty v cm; u Gebrüder Weiss se odesílají jako metry (automatická konverze ÷ 100)
new Parcel(weight: 5.0, dimensions: new Dimensions(60, 40, 30));

Palety jsou podporovány pouze přes Gebrüder Weiss.
GLS a PPL paletové typy nepodporují — handler vyhodí InvalidArgumentException.
Česká pošta podporuje Package a PackageOversize (neskladná zásilka, služba 10), palety nepodporuje.

ShipmentLabel — výstup

$label->carrier;        // Carrier::Gls | Carrier::Ppl | Carrier::CzechPost
$label->trackingNumber; // číslo zásilky pro sledování
$label->labelPdf;       // raw PDF bytes — ulož nebo pošli do prohlížeče

// Uložení štítku
file_put_contents('/path/to/' . $label->trackingNumber . '.pdf', $label->labelPdf);

// Sledování zásilky (po doručení)
$tracking = $gateway->tracking($label->carrier, $label->trackingNumber);

Více balíků — GLS / PPL / Czech Post: API vrátí jeden kombinovaný PDF soubor pro všechny balíky.
Každý ShipmentLabel má svůj trackingNumber, labelPdf je u všech shodné (kombinovaný tisk).

Více balíků — Gebrüder Weiss: každý balík dostane vlastní SSCC kód v etiketě a samostatné PDF etiket.
ShipmentLabel[] má tolik prvků, kolik je zásilek — pro tracking mají všechny stejný trackingNumber (reference / orderId), labelPdf je u prvního prvku a SSCC zůstává součástí etikety.

Unified tracking

$tracking = $gateway->tracking(Carrier::Gls, '1234567890');
$tracking = $gateway->tracking(Carrier::Ppl, 'KEA12345678');
$tracking = $gateway->tracking(Carrier::CzechPost, 'DR123456789CZ');
$tracking = $gateway->tracking(Carrier::GebruderWeiss, 'ORDER-2026-001'); // reference zásilky / orderId

$trackingUrl = $gateway->trackingUrl(Carrier::Gls, '1234567890');
$trackingUrl = $gateway->trackingUrl(Carrier::Ppl, 'KEA12345678');
$trackingUrl = $gateway->trackingUrl(Carrier::CzechPost, 'DR123456789CZ');
$trackingUrl = $gateway->trackingUrl(Carrier::GebruderWeiss, 'ORDER-2026-001');

// GW public tracking deep-link needs pickupAddress.company in config.
// PPL public tracking URL uses ?shipmentId={trackingNumber}.

if ($tracking !== null) {
    $tracking->getDelivered();           // bool
    $tracking->getDeliveredDate();       // ?DateTimeImmutable
    $tracking->getWeight();              // float (kg)
    $tracking->getParcelNumber();        // string
    $tracking->getDeliveryCountryCode(); // string (ISO)

    foreach ($tracking->getParcelStatuses() as $status) {
        $status->getStatusDate();        // ?DateTimeImmutable
        $status->getStatusDescription(); // string
        $status->getCustomInfo();        // ?string
        $status->getDelivered();         // bool
        $status->getDamaged();           // bool
    }
}

trackingUrl() vrací veřejnou stránku sledování dopravce.
U GLS a České pošty je URL předvyplněná číslem zásilky, u PPL a Gebrüder Weiss vede na jejich oficiální tracking stránku.

Gebrüder Weiss: tracking probíhá přes reference/orderId vrácené v $label->trackingNumber.
API vrací aktuální stav zásilky; null znamená, že zásilka ještě není v systému GBW (zpoždění po odeslání objednávky).

Extending in your project

Balíček obsahuje API vrstvu (HTTP komunikace, entity, config) a unified shipment handlery.
Aplikační logika — mapování objednávky/faktury na zásilku, ukládání do DB — patří do projektu.

GLS — příklad rozšíření

namespace App\Api\Gls;

use Ages\ShippingGateway\Gls\GlsApi;
use Ages\ShippingGateway\Gls\Entity\ParcelEntity;
use Ages\ShippingGateway\Gls\Entity\ServiceEntity;

class Gls extends GlsApi
{
    const string Carrier = 'GLS';
    const string TrackUrl = 'https://gls-group.eu/CZ/cs/sledovani-zasilek/?match=';

    public function createConsignmentFromInvoice(Invoice $invoice): ?Consignment
    {
        $services = ServiceEntity::of();
        if ($invoice->cashOnDelivery) {
            $services->addServiceCOD($invoice->priceTax, $invoice->variableSymbol, $invoice->currencyIso);
        }

        $parcel = ParcelEntity::of(
            strval($this->config->clientNumber),
            $invoice->code,
            $invoice->packageQty,
            $this->getPickupAddress(),  // z configu
            $deliveryAddress,
            $services,
        );

        $data = $this->printLabels($parcel);
        // $data->PrintLabelsInfoList[0]->ParcelNumber  ← tracking number
        // implode(array_map('chr', $data->Labels))     ← PDF bytes
    }
}

PPL — příklad rozšíření

namespace App\Api\Ppl;

use Ages\ShippingGateway\Ppl\PplApi;

class Ppl extends PplApi
{
    public function createConsignmentFromInvoice(Invoice $invoice): ?Consignment
    {
        $parcel = ParcelEntity::of(
            $invoice->code,
            $invoice->packageQty,
            $this->getPickupAddress(),
            $deliveryAddress,
            SpecificDeliveryEntity::of($psdCode),
            $cod,
        );

        $batchId = $this->createBatch($parcel);
        $status  = $this->getStatus($batchId);   // poll dokud není Complete
        // $status->items[0]->shipmentNumber      ← tracking number
        // $status->completeLabel->labelUrls[0]   ← URL štítku → getLabel($url)
    }
}

Czech Post — příklad rozšíření

namespace App\Api\CzechPost;

use Ages\ShippingGateway\CzechPost\CzechPostApi;

class CzechPost extends CzechPostApi
{
    public function createConsignmentFromInvoice(Invoice $invoice): ?Consignment
    {
        $header = $this->prepareParcelServiceHeader(); // z configu

        $res = $this->parcelService($header, $consignmentEntity->toArray(), $multipart);

        // $res['responseHeader']['resultHeader']['responseCode'] === 1  ← úspěch
        // $res['responseHeader']['resultParcelData'][n]['parcelCode']   ← tracking number
        // $res['responseHeader']['responsePrintParams']['file']         ← base64 PDF
    }
}

Architecture overview

src/
├── ShippingGateway.php               ← facade: tracking() + createShipment()
├── Common/
│   ├── Carrier.php                   ← enum: Gls | Ppl | CzechPost | GebruderWeiss
│   ├── CarrierInterface.php
│   ├── ShipmentHandlerInterface.php  ← createShipment(ShipmentRequest): ShipmentLabel[]
│   ├── ParcelTrackingInterface.php
│   ├── ParcelStatusInterface.php
│   ├── PickupAddress.php
│   ├── ShippingException.php
│   └── Shipment/
│       ├── ShipmentRequest.php       ← vstupní DTO
│       ├── ShipmentLabel.php         ← výstupní DTO (carrier, trackingNumber, labelPdf)
│       ├── RecipientAddress.php      ← fromFullName() factory
│       ├── Parcel.php                ← weight, type, dimensions
│       ├── Dimensions.php
│       ├── ShipmentValue.php
│       ├── CashOnDelivery.php
│       ├── ParcelType.php            ← Package | PackageOversize | PalletEur | ...
│       └── RecipientType.php         ← Person | Company
├── Gls/
│   ├── Config/GlsConfig.php
│   ├── GlsApi.php
│   ├── Handler/GlsShipmentHandler.php
│   └── Entity/ Values/
├── Ppl/
│   ├── Config/PplConfig.php
│   ├── PplApi.php
│   ├── Handler/PplShipmentHandler.php
│   └── Entity/ Values/
├── CzechPost/
│   ├── Config/CzechPostConfig.php
│   ├── CzechPostApi.php
│   ├── CzechPostException.php
│   ├── Handler/CzechPostShipmentHandler.php
│   └── Entity/ Values/
└── GebruderWeiss/
    ├── Config/GebruderWeissConfig.php
    ├── GebruderWeissApi.php
    ├── Handler/GebruderWeissShipmentHandler.php
    ├── Label/GebruderWeissLabelGenerator.php  ← mPDF etiketa 100×150 mm
    └── Tracking/
        ├── GbwParcelTracking.php
        └── GbwParcelStatus.php

Co je v balíčku: HTTP komunikace, entity, config, unified tracking, unified shipment creation.
Co patří do projektu: mapování Invoice → ShipmentRequest, ukládání zásilek do DB, Storage.

License

Private package — Ages s.r.o.

ages/shipping-gateway 适用场景与选型建议

ages/shipping-gateway 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 60 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 27 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 ages/shipping-gateway 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 ages/shipping-gateway 我们能提供哪些服务?
定制开发 / 二次开发

基于 ages/shipping-gateway 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 60
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 37
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Unknown
  • 更新时间: 2026-04-27