InPost
OficjalnyDarmowyNadawanie przesyłek InPost — Paczkomat (gabaryty A, B, C) i Kurier — z karty zamówienia: etykiety, śledzenie statusów i link dla klienta.
- 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
| Metoda | Uprawnienie | Opis |
|---|---|---|
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.form | Schemat 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.download | Etykiety (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:
carrierId | Nazwa | Warunek | template |
|---|---|---|---|
inpost_courier | InPost Kurier | domyślny | — |
inpost_locker | InPost Paczkomat | zamówienie ma delivery.pickupPoint.id (usługa PUDO) | small, medium lub large (gabaryt skrytki) |
Szablony Paczkomatu odpowiadają gabarytom handlowym (wymiary wewnętrzne [cm]):
| Szablon | Gabaryt | Wymiary |
|---|---|---|
small | A | 8 × 38 × 64 |
medium | B | 19 × 38 × 64 |
large | C | 41 × 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ść sendTypes | Wynik |
|---|---|
parcel_locker | tylko opcje Paczkomatu; gdy zamówienie nie ma punktu, zwraca [] |
courier | tylko opcja Kuriera; gdy zamówienie ma punkt, zwraca [] |
both lub brak | oba 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
renderIfsekcji poniżej, - Paczkomat: gabaryt skrytki A/B/C i kod Paczkomatu (prefill z
custom_attributes.target_point), - Kurier: wymiary
x/y/zw 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
| Pole | Opis |
|---|---|
accountName | Nazwa przewoźnika widoczna w module przesyłek (name w getCarrier), domyślnie „InPost" |
environment | sandbox (sandbox-api-shipx-pl.easypack24.net) lub production (api-shipx-pl.easypack24.net); domyślnie sandbox |
organizationId | Identyfikator organizacji w Managerze Paczek InPost (wymagany, liczba) |
token | Token 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:
| Pole | Opis |
|---|---|
defaultService | Usł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ąć |
defaultSendingMethod | Sposób nadania przekazywany do custom_attributes.sending_method w ShipX (np. parcel_locker, pok, branch); brak lub pusty = ShipX decyduje |
labelFormat | Format etykiety (pdf / zpl / epl); domyślnie pdf |
parcel — Domyślna paczka
Paczka używana przy szybkim nadaniu kurierem. Wymiary [cm] i waga zapasowa [kg]:
| Pole | Opis |
|---|---|
sizeX | Szerokość [cm]; domyślnie 20 |
sizeY | Wysokość [cm]; domyślnie 20 |
sizeZ | Długość [cm]; domyślnie 20 |
weight | Waga 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ę:
| Pole | Opis |
|---|---|
companyName | Firma nadawcy |
firstName | Imię osoby kontaktowej |
lastName | Nazwisko osoby kontaktowej |
email | Adres e-mail |
phone | Numer telefonu |
street | Ulica |
buildingNumber | Nr budynku |
postCode | Kod pocztowy |
city | Miasto |
Uprawnienia
Rejestrowane w src/register-permissions.js, egzekwowane przez requirePermission(code, ctx):
| Kod | Opis |
|---|---|
shipment.form | Formularz nadania InPost |
label.download | Pobieranie 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
instanceIdinstancji modułu, a niecarrierId;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
getQuickShipParamszwraca 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),getShipmentFormDatazniknęła — hub 2.3.0 dodał kontraktowi poleshipmentFormMethod, 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. quickShipParamsMethodw pełni — schemat opcji przewoźnika do warunków (hub jeszcze tego nie renderuje pozasendTypes).- 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.
