InPost

OficjalnyDarmowy

Nadawanie przesyłek InPost — Paczkomat (gabaryty A, B, C) i Kurier — z karty zamówienia: etykiety, śledzenie statusów i link dla klienta.

@core·Wiktor KoźmińskiBrak recenzji3 firmy z instalacjąAktualizacja 19 października 2026
Zainstaluj w Orbito Otwiera panel Orbito na karcie modułu
Licencja
Bezpłatny
Wersja
2.6.8
Kategoria
Wysyłka

Moduł InPost (core/inpost)

Przewoźnik dla hubu przesyłek core/shipping 2.0.0. Integruje API ShipX — usługę nadania przesyłek i śledzenia dla systemów zarządzania logistyką InPost.

Moduł obsługuje dwa warianty nadania:

  • InPost Kurier — przesyłka do adresu odbiorcy,
  • InPost Paczkomat — przesyłka do wybranego Paczkomatu (skrytka A/B/C).

Cały ruch HTTP idzie przez Core.fetch (proxy rdzenia), więc domena ShipX musi być zatwierdzona przez administratora firmy.

Moduł jest multi-instancyjny (module.json: "singleton": false) — firma może mieć kilka kont InPost, a każda instalacja to osobna instancja z własną konfiguracją. Tożsamością przewoźnika w hubie jest instanceId instancji modułu. Konsument wybiera tę instancję przez requestProvider kontraktu shipping.carrier.

Moduł wymaga SDK @orbito/api-lib w wersji ≥ 4.4.3 (RPC po TLS, gdy rdzeń poda ORBITO_TLS_CA; Files — bucket plików instancji, z którego konsument bierze logo). Dane zamówienia pobiera przez kontrakt hooka orders.getById; nie deklaruje zależności dependsOn od innych modułów. Przesyłkę do rejestru zapisuje konsument kontraktu przewoźnika.

Przy nadaniu do Paczkomatu adres odbiorcy nie trafia do ShipX; miejsce dostawy określa custom_attributes.target_point. Dzięki temu niepełny adres punktu odbioru z marketplace nie uruchamia walidacji adresu przeznaczonej dla dostawy kurierskiej. Odrzucenie przez ShipX pokazuje znane pola i przyczyny walidacji, bez kopiowania danych osobowych z odpowiedzi API.

Hook

core.shipping.getCarrier

Moduł odpowiada na hook hubu jednym obiektem definicji przewoźnika (index.js):

{
    name: config.accountName,           // z konfiguracji `connection.accountName`, domyślnie "InPost"
    moduleCode: Core.getModuleInfo().code,
    instanceId: Core.getInstanceId(),   // TOŻSAMOŚĆ przewoźnika w hubie
    logoFile: 'logo.jpg',               // nazwa pliku w buckecie TEJ instancji
    quickShipOptionsMethod: 'getQuickShipOptions',
    quickShipParamsMethod: 'getQuickShipParams',
    shipmentFormMethod: 'getShipmentForm',
    getTrackingStatusMethod: 'getTrackingStatus',
    generateLabelsMethod: 'generateLabels'
}

Gdy dane dostępowe nie są uzupełnione (brak tokenu lub identyfikatora organizacji) albo odczyt konfiguracji się nie powiedzie, hook zwraca null — instancja nie pojawia się na liście przewoźników hubu.

SDK nie zna po stronie modułu nazwy instancji, dlatego przyjazną nazwę przewoźnika podaje się ręcznie w konfiguracji (connection.accountName). Przy kilku kontach InPost warto ją rozróżnić, np. „InPost — konto główne", „InPost — sklep B".

Logo przewoźnika

Znak marki leży w repozytorium jako assets/logo.jpg (kwadrat 160 × 160). Przy starcie moduł wrzuca go do bucketu swojej instancji (Files.upload, src/logo.js) i podaje hubowi samą nazwę pliku w logoFile — bajty nigdy nie jadą przez hub. Front pobiera plik osobnym żądaniem po instanceId i nazwie, widokiem OVE type: image.

SDK liczy sha256 lokalnie i najpierw próbuje podpiąć bajty, które rdzeń już zna, więc wrzut przy każdym starcie przenosi plik jeden raz, a nie przy każdym restarcie kontenera.

Nieudany wrzut kończy się ostrzeżeniem w logu i niczego nie przerywa — przewoźnik rejestruje się normalnie, a hub pokazuje wtedy inicjały zamiast logo.

Pole logoFile to rozszerzenie kontraktu getCarrier z huba 2.2.0 — starszy hub pomija je bez błędu i rysuje inicjały.

Metody

MetodaUprawnienieOpis
getQuickShipOptions (orderId, sendTypes?)tylko hub (ctx.caller)Opcje szybkiego nadania: jedna do trzy (kurier lub Paczkomat A/B/C). Filtruje po warunku hubu (carrierOptions.sendTypes). Brak konfiguracji → []
getQuickShipParams ()tylko hub (ctx.caller)Schemat opcji przewoźnika dla warunków doboru: pole sendTypes z wartościami parcel_locker / courier / both
quickShip (orderId, carrierId, template?)tylko hub (ctx.caller)Nadanie przesyłki w ShipX na domyślnych ustawieniach modułu (kafelek szybkiego nadania); zwraca CreatedShipment hubu
getShipmentForm (orderId, carrierId?)shipment.formSchemat OVE formularza nadania (kontrakt hubu core/shipping, shipmentFormMethod); brak carrierId → wariant wynika z zamówienia (punkt odbioru → Paczkomat, inaczej Kurier), tak jak w getQuickShipOptions; błąd konfiguracji lub zamówienia → { error }
createShipment (orderId, carrierId, template?, targetPoint?, parcel?, parcels?, sendingMethod?, codAmount?, insuranceAmount?, reference?)tylko hub (ctx.caller)Nadanie przesyłki w ShipX wartościami z formularza nadania (kontrakt hubu, wołana przez router ship); zwraca CreatedShipment hubu
getTrackingStatus (trackNo, carrierData?)tylko hub (ctx.caller)Status przesyłki z ShipX; ShipX pyta po numerze listu, fallback na ID przesyłki dla sandboxa
generateLabels (shipments[{ _id, trackNo, carrierData }])label.downloadEtykiety (PDF/ZPL/EPL wg konfiguracji) przesyłek; zwraca { labels: LabelFile[] }

Wywołania moduł→moduł mają ustawiony przez rdzeń ctx.caller i przechodzą bez tabeli uprawnień użytkownika. Metody kontraktu (getQuickShipOptions, getQuickShipParams, quickShip, createShipment, getTrackingStatus) woła wyłącznie hub — wywołanie bez ctx.caller kończy się błędem INTERNAL_CALL_REQUIRED. generateLabels i getShipmentForm przechodzą zarówno z hubu (ctx.caller), jak i od użytkownika z uprawnieniem odpowiednio label.download i shipment.form.

Metody ładuje Utils.loadMethods z katalogu methods/ — nazwa pliku jest nazwą metody.

quickShip — warianty nadania

Warianty nadania (carrierId) to lokalny słownik modułu, a nie tożsamość przewoźnika:

carrierIdNazwaWarunektemplate
inpost_courierInPost Kurierdomyślny—
inpost_lockerInPost Paczkomatzamówienie ma delivery.pickupPoint.id (usługa PUDO)small, medium lub large (gabaryt skrytki)

Szablony Paczkomatu odpowiadają gabarytom handlowym (wymiary wewnętrzne [cm]):

SzablonGabarytWymiary
smallA8 × 38 × 64
mediumB19 × 38 × 64
largeC41 × 38 × 64

Wynik zgodny z CreatedShipment hubu:

{
    carrier: 'InPost Kurier',          // etykieta wariantu nadania
    trackNo: 'string',                 // numer listu przewozowego (10 cyfr)
    trackingUrl: 'https://inpost.pl/sledzenie-przesylek?number=...',
    status: 'sent',
    parcels: [{ x, y, z, weight }],
    codAmount: 0,                      // tylko dla zamówień pobraniowych (usługa COD)
    carrierData: {
        carrierId,                     // wariant nadania (inpost_courier / inpost_locker)
        template,                      // gabaryt skrytki, tylko dla inpost_locker
        inpostShipmentId,              // identyfikator przesyłki w ShipX (klucz do etykiety i śledzenia)
        service,                       // usługa ShipX (inpost_courier_standard / inpost_locker_standard)
        environment,                   // 'sandbox' | 'production'
        shipXStatus                    // status przesyłki zwrócony przez ShipX
    }
}

Idempotencji po stronie InPost nie ma — lock per zamówienie trzyma hub (router ship, TTL 180 s), wspólny dla nadania kafelkiem (quickShip) i formularzem (createShipment).

Błędy ShipX (walidacja danych, transport, brak konfiguracji, nieznany carrierId/template) sygnalizujemy wyjątkiem z treścią czytelną dla użytkownika; szczegóły techniczne (kod, status HTTP, externalId) trafiają do Log.error. Częściowy sukces — ShipX utworzył przesyłkę, ale nie zwrócił numeru listu — też jest wyjątkiem, a identyfikator przesyłki ShipX jest doklejany do komunikatu, żeby użytkownik sprawdził ją w Managerze Paczek, zanim nada ją ponownie.

getQuickShipOptions — opcje szybkiego nadania

Zwraca listę opcji nadania dla zamówienia, filtrowaną po warunku hubu (carrierOptions.sendTypes):

  • Paczkomat (gdy order.delivery.pickupPoint.id): trzy opcje A/B/C, każda opisuje gabaryt skrytki

i domyślną wagę z zamówienia, np. ["8 × 38 × 64 cm — 2.5 kg"].

  • Kurier (brak punktu): jedna opcja z domyślną paczką modułu, np. ["20 × 20 × 20 cm — 2.5 kg"].

Filtr sendTypes (semantyka warunku doboru w hubie):

Wartość sendTypesWynik
parcel_lockertylko opcje Paczkomatu; gdy zamówienie nie ma punktu, zwraca []
couriertylko opcja Kuriera; gdy zamówienie ma punkt, zwraca []
both lub brakoba warianty (lub jeden, jeśli dostępny)

Brak konfiguracji → [] (hub pokazuje opcje pozostałych przewoźników).

getQuickShipParams — schemat warunków

Zwraca schemat pola sendTypes dla formularza warunków doboru w hubie (format bloków Config):

{
    sendTypes: {
        type: PropertyType.STRING,
        name: { pl: 'Proponuj rodzaj wysyłki', en: 'Suggested shipment type' },
        options: { values: [
            { id: 'parcel_locker', label: 'Paczkomat' },
            { id: 'courier', label: 'Kurier' },
            { id: 'both', label: 'Oba rodzaje wysyłki' }
        ] },
        required: true
    }
}

getTrackingStatus

Odpytuje ShipX o najnowsze zdarzenie przesyłki i zwraca TrackingStatus hubu: status (zmapowany z kodem statusu ShipX), statusDate, carrierStatus (surowy kod z ShipX).

Brak zdarzeń w ShipX nie jest błędem — świeżo nadana przesyłka nie ma jeszcze historii, więc metoda zwraca { status: 'unknown', statusDate: new Date() }.

Kanałem zapytań jest numer listu (trackNo). W środowisku sandbox ShipX nie udostępnia historii śledzenia, dlatego gdy zapytanie się nie powiedzie, moduł próbuje odczytać status po ID przesyłki (carrierData.inpostShipmentId) bezpośrednio z API ShipX.

generateLabels

Etykiety pobierane są przesyłka po przesyłce (klucz: carrierData.inpostShipmentId), bo ShipX wydaje jeden plik na przesyłkę. Wynik to { labels: [{ shipmentId, contentBase64, mimeType, filename }] }.

Format etykiety bierze się z defaults.labelFormat (pdf / zpl / epl). mimeType odpowiadająco: application/pdf, application/x-zpl, application/x-epl. Zawsze podajemy filename z właściwym rozszerzeniem, bo hub zna tylko pdf/png/jpg.

Brak inpostShipmentId — przesyłka jest logowana Log.warn i pomijana; błąd pojedynczej etykiety jest logowany i pomijany; batch nie jest przerywany, a hub zaloguje braki. Nazwa pliku: inpost-<trackNo|_id>.<format> (znaki specjalne zamieniane na _).

getShipmentForm i createShipment — formularz nadania

getShipmentForm buduje schemat OVE formularza (src/form-view.js, czysta funkcja bez SDK — więc testowalna bez rdzenia) na danych startowych z buildInpostFormData (nadawca z konfiguracji, odbiorca z zamówienia, domyślna paczka, referencja, dostępne sposoby nadania). Przycisk „Nadaj” w schemacie woła router hubu (core/shipping → ship) z instanceId tej instancji i metodą createShipment; wynik zgłasza nazwanymi akcjami karty przesyłek huba (shipmentCreated / shipmentFailed), bo formularz nie zna identyfikatorów jej widoków.

Zakres pól wersji 1 (jedna paczka na nadanie — wiele paczek to osobny etap):

  • wariant nadania (Paczkomat / Kurier), przełącza renderIf sekcji poniżej,
  • Paczkomat: gabaryt skrytki A/B/C i kod Paczkomatu (prefill z custom_attributes.target_point),
  • Kurier: wymiary x/y/z w cm i waga w kg,
  • sposób nadania z listy ShipX (sendingMethods) — pole ukryte, gdy lista jest pusta,
  • pobranie (prefill z zamówienia) i ubezpieczenie,
  • referencja (prefill reference),
  • nadawca i odbiorca — tylko podgląd, bez edycji w tej wersji.

createShipment (tylko hub, requireModuleCaller) czyta te same dane startowe co formularz i dokłada do nich wartości przesłane z przeglądarki, żeby nadanie nie mogło rozjechać się z tym, co użytkownik widział; składa packageData przez src/packageData.js (buildPackageData, preparePackageDataForShipX) i nadaje przez wspólny z quickShip mechanizm w src/dispatch.js (loadShipmentContext, dispatchShipment — walidacja wariantu, wywołanie ShipX i budowa CreatedShipment są jednym kodem dla obu metod).

Konfiguracja

Bloki rejestruje src/register-config.js (Config.registerConfigBlocks), wszystkie w zasięgu global. Konfigurację czyta loadConfig (src/module-config.js); moduł uznaje się za skonfigurowany, gdy ma token i organizationId.

connection — Połączenie z ShipX

PoleOpis
accountNameNazwa przewoźnika widoczna w module przesyłek (name w getCarrier), domyślnie „InPost"
environmentsandbox (sandbox-api-shipx-pl.easypack24.net) lub production (api-shipx-pl.easypack24.net); domyślnie sandbox
organizationIdIdentyfikator organizacji w Managerze Paczek InPost (wymagany, liczba)
tokenToken API ShipX — Bearer token (wymagany, SECRET)

Walidacja bloku sprawdza obecność organizationId (musi być liczbą) i token, a następnie realnie testuje połączenie wywołaniem getOrganization w ShipX. Nieudane połączenie zgłaszane jest jako błąd pola token (i logowane Log.warn).

defaults — Domyślne nadanie

Wartości używane przy szybkim nadaniu oraz jako startowe w formularzu nadania:

PoleOpis
defaultServiceUsługa ShipX (inpost_courier_standard / inpost_courier_c2c / inpost_locker_standard). Obecnie nieużywana — usługę wybiera wariant nadania (carrierId) zarówno przy szybkim nadaniu, jak i w formularzu nadania (getShipmentForm / createShipment); pole zostaje w konfiguracji do decyzji: podpiąć albo usunąć
defaultSendingMethodSposób nadania przekazywany do custom_attributes.sending_method w ShipX (np. parcel_locker, pok, branch); brak lub pusty = ShipX decyduje
labelFormatFormat etykiety (pdf / zpl / epl); domyślnie pdf

parcel — Domyślna paczka

Paczka używana przy szybkim nadaniu kurierem. Wymiary [cm] i waga zapasowa [kg]:

PoleOpis
sizeXSzerokość [cm]; domyślnie 20
sizeYWysokość [cm]; domyślnie 20
sizeZDługość [cm]; domyślnie 20
weightWaga zapasowa [kg] — waga liczona z produktów zamówienia, ta wartość służy jako fallback; domyślnie 1

Brak wypełnionych wartości domyślnych jest bezpieczny: wymiary schodzą do 20 cm, a waga do 1 kg.

sender — Dane nadawcy

Dane przekazywane do ShipX przy każdym nadaniu. Gdy pola są puste, ShipX używa danych organizacji z Managera Paczek. Wypełnij, aby nadpisać nadawcę:

PoleOpis
companyNameFirma nadawcy
firstNameImię osoby kontaktowej
lastNameNazwisko osoby kontaktowej
emailAdres e-mail
phoneNumer telefonu
streetUlica
buildingNumberNr budynku
postCodeKod pocztowy
cityMiasto

Uprawnienia

Rejestrowane w src/register-permissions.js, egzekwowane przez requirePermission(code, ctx):

KodOpis
shipment.formFormularz nadania InPost
label.downloadPobieranie etykiet InPost

Mapowanie statusów

src/constants.js funkcja mapShipXStatus tłumaczy kody statusu ze ShipX na status hubu (core.shipping/src/lib/shipment-status.js). Kody pogrupowane są w: waiting, sent, in-road, ready_to_pickup, delivered, delivery-problems, returned, canceled, error, unknown.

Kod spoza katalogu daje unknown — przesyłka zostaje wtedy w statusach „otwartych", więc cron hubu odpyta ją ponownie.

Źródło listy kodów: dokumentacja ShipX API oraz testy (wszystkie kody z API są pokryte mapowaniem).

Testy

source ~/.nvm/nvm.sh && nvm use 22 && npm test

Testy node:test wymagają Node ≥ 22. SDK @orbito/api-lib jest podmieniany mockiem (test/helpers/api-mock.js, ładowanym przez --require), więc testy nie dotykają rdzenia ani sieci.

Różnice względem 1.x

  • moduł odpowiada na jeden hook hubu — core.shipping.getCarrier. Stary model hooków

(core.shipping.issueLabel, core.shipping.trackShipment, core.shipping.getShipmentFormData) zniknął, bo hub 2.0.0 ich nie emituje,

  • tożsamością przewoźnika jest instanceId instancji modułu, a nie carrierId; carrierId

(inpost_courier / inpost_locker) to już tylko lokalny wariant nadania przekazywany w params opcji,

  • moduł jest multi-instancyjny (singleton: false) — jedna firma może mieć kilka kont InPost; nazwę

przewoźnika podaje nowe pole connection.accountName,

  • nowa metoda getQuickShipParams zwraca schemat warunków doboru (sendTypes) dla hubu,
  • rezerwacje nadania (validateIssueReservation) zostały usunięte — lock per zamówienie trzyma hub,

więc idempotencja po stronie ShipX nie jest już potrzebna,

  • błędy nadania są wyjątkami zamiast wyniku { carrierData: { error } } — walidatory hubu i tak

odrzucają odpowiedź bez trackNo,

  • przesyłkę zapisuje hub (router ship), moduł niczego nie rejestruje po nadaniu,
  • statusy odpytuje cron hubu — moduł nie ma własnych zadań cyklicznych,
  • ctx.system → ctx.caller (zmiana SDK 3.x),
  • getShipmentFormData zniknęła — hub 2.3.0 dodał kontraktowi pole shipmentFormMethod, więc

jej dane startowe (buildInpostFormData) zasilają teraz pełny schemat OVE formularza zwracany przez getShipmentForm, a nadanie z formularza obsługuje nowa metoda createShipment.

Poza zakresem tej iteracji

  • Ikony opcji (iconSrc) — usunęliśmy, stare URL-e z inpost.pl były nietrwałe.
  • quickShipParamsMethod w pełni — schemat opcji przewoźnika do warunków (hub jeszcze tego nie renderuje poza sendTypes).
  • Formularz nadania (getShipmentForm) pokazuje dane nadawcy i odbiorcy tylko do wglądu;

edycja stron przesyłki pozostaje poza zakresem.

Podpaczki w ręcznym nadaniu (2.3.1)

InPost Kurier (inpost_courier_standard) z umową kurierską: do 99 paczek. Paczkomat i Kurier C2C: jedna paczka. Źródła: proces integracji, gabaryty i usługi.

Formularz otwierany z dropdownu pokazuje „Paczka #1”, „Paczka #2” itd. „Dodaj podpaczkę” kopiuje bieżące wymiary i wagę ostatniej paczki; każdą kopię można edytować niezależnie. Usuwanie jest dostępne, gdy pozostałaby przynajmniej jedna paczka. Zmiana wariantu dostawy zachowuje listę, ale nadanie wielu paczek w usłudze jednopaczkowej jest blokowane po stronie serwera. Pobranie i ubezpieczenie dotyczą całej przesyłki.

createShipment przyjmuje parcels[]. Dotychczasowy parcel i szybkie nadanie zachowują obsługę jednej paczki. Wymiary i waga każdej podpaczki muszą być dodatnie. Hub zapisuje pełne parcels[]; zwrócone przez przewoźnika numery podpaczek trafiają do carrierData.waybills. Śledzenie statusu nadal korzysta z głównego numeru przesyłki. Etykiety są pobierane dla całej przesyłki/sesji.

Publiczny link śledzenia

Hook shipping.carrier.getTrackingUrl przyjmuje { courierName, trackNo }. Jest odczytem bez efektów: zwraca { trackingUrl } z istniejącej lokalnej funkcji budującej URL albo null, gdy nazwa nie należy do tego przewoźnika lub numer jest pusty. Nazwy porównuje po normalizacji wielkości liter i odstępów. Nie wymaga połączenia z API przewoźnika, nie sprawdza statusu przesyłki i nie wybiera konta nadawcy. Odkrywanie instancji odbywa się przez shipping.carrier.getTrackingUrl.providers, a żądanie przez standardowy kontrakt z ID instancji i uwierzytelnionym kontekstem.

Plany opakowań

Hub przesyłek może przekazać potwierdzony packingPlan przez kontrakt shipping.carrier. Wymiary i waga brutto pochodzą z planu, a adapter ponownie sprawdza zgodność z aktualnym zamówieniem przed nadaniem. Zmiana zamówienia wymaga ponownego potwierdzenia. Obsługa jest deklarowana przez packingPlans: true oraz packingPlanVersions: [1, 2]. Wersja 2 obsługuje zestawy z osobnymi trybami połączenia kartonów; do API trafia wynikowa lista paczek. Nadal obowiązują limity usług i liczby paczek; starszy hub zachowuje dotychczasowy sposób nadania.

Wspiera wiele paczek w jednej przesyłce, z ograniczeniami konkretnej usługi.

Gdy usługa nie obsługuje podpaczek w jednym nadaniu, aktualny Shipping może nadać każdą fizyczną paczkę na osobnym liście (separateShipments, packingParcelIndex). Pełny plan jest ponownie walidowany przed każdym nadaniem. Pobranie jest dzielone według sum maksymalnych obciążeń kartonów w paczce, w groszach, tak aby suma na wszystkich listach była dokładnie równa kwocie zamówienia. Każdy zapisany list ma własną etykietę i zdarzenie do istniejącej automatyzacji wydruku. Częściowy sukces lub niepewna odpowiedź zatrzymuje serię i blokuje automatyczne ponowienie.