<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Codee]]></title><description><![CDATA[Codee]]></description><link>https://newsletter.codee.dev</link><image><url>https://substackcdn.com/image/fetch/$s_!to_b!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8bd3e947-2f2c-492c-9dbf-9f6f49cfe5ef_338x338.png</url><title>Codee</title><link>https://newsletter.codee.dev</link></image><generator>Substack</generator><lastBuildDate>Thu, 30 Apr 2026 13:46:27 GMT</lastBuildDate><atom:link href="https://newsletter.codee.dev/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Krzysztof Polak]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[codeesh@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[codeesh@substack.com]]></itunes:email><itunes:name><![CDATA[Krzysztof Polak]]></itunes:name></itunes:owner><itunes:author><![CDATA[Krzysztof Polak]]></itunes:author><googleplay:owner><![CDATA[codeesh@substack.com]]></googleplay:owner><googleplay:email><![CDATA[codeesh@substack.com]]></googleplay:email><googleplay:author><![CDATA[Krzysztof Polak]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Jak zbudowaliśmy POS oparty o framework Medusa.js]]></title><description><![CDATA[Zabieram was dzisiaj do &#347;wiata Medusa.js opisuj&#261;c rozwi&#261;zanie o tym jak stworzyli&#347;my rozszerzenie POS do obs&#322;ugi Vending&#243;w dla naszego klienta.]]></description><link>https://newsletter.codee.dev/p/jak-zbudowalismy-pos-oparty-o-framework</link><guid isPermaLink="false">https://newsletter.codee.dev/p/jak-zbudowalismy-pos-oparty-o-framework</guid><dc:creator><![CDATA[Krzysztof Polak]]></dc:creator><pubDate>Fri, 06 Mar 2026 09:44:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Oz6v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Oz6v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Oz6v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Oz6v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg" width="1189" height="595" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:595,&quot;width&quot;:1189,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21522,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.codee.dev/i/190024585?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Oz6v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Oz6v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f1c402-cf60-41e8-9461-017a0d26d823_1189x595.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cze&#347;&#263;!</p><p>Jest to pierwszy artyku&#322; i nie ostatni o rozwi&#261;zaniach Headless dla sklep&#243;w i stron internetowych.</p><h2>Czym jest framework Medusa.js?</h2><p>W skr&#243;cie Medusa.js to framework do budowy sklep&#243;w internetowych o strukturze Headless gdzie Back-end jest oparty o Node.js oraz TypeScript. Natomiast Front-end mo&#380;e by&#263; zbudowany z czegokolwiek - bazowo Medusa.js wspiera Next.js ale bez problemu mo&#380;na wybra&#263; Astro albo Nuxt.js.</p><h2>Dlaczego wybrali&#347;my Medusa.js?</h2><p>Wybrali&#347;my ten framework z kilku powod&#243;w. Pierwszym z nich jest to, &#380;e jest i pozostanie Open Source, maj&#261;c ju&#380; na starcie gotowe modu&#322;y. <br>Zesp&#243;&#322; Medusa.js wykonuje &#347;wietn&#261; robot&#281;!</p><p>Drugim powodem jest to, &#380;e jest to rozwi&#261;zanie Headless, czyli back-end i front-end wsp&#243;&#322;pracuj&#261; ze sob&#261;, w niezale&#380;ny spos&#243;b opieraj&#261;c si&#281; o REST-API.</p><p>Trzecim powodem to framework oparty o Node.js, w pe&#322;ni otypowany i stworzony z my&#347;l&#261; o modularno&#347;ci i elastyczno&#347;ci. Dla naszego zespo&#322;u by&#322;o to wa&#380;ne gdy&#380; na co dzie&#324; pracujemy w technologiach opartych o Node.js, React.js, Next.js.</p><h2>Jaki by&#322; pow&#243;d?</h2><p>Potrzeb&#261; by&#322;o zbudowanie sklepu internetowego w kt&#243;rym mo&#380;na zrobi&#263; zakupy online jednocze&#347;nie &#322;&#261;cz&#261;c zakupy w &#347;wiecie offline. </p><p>Do obs&#322;ugi Vending&#243;w potrzebowali&#347;my elastycznego i wydajnego rozwi&#261;zania. Istotna jest tak&#380;e modu&#322;owo&#347;&#263; kt&#243;ra pozwoli&#322;o na:</p><ul><li><p>zarz&#261;dzanie wieloma kana&#322;ami sprzeda&#380;y (jeden kana&#322; sprzeda&#380;y odpowiada dok&#322;adnie jednej lokalizacji), </p></li><li><p>podpi&#281;ciem wybranej formy p&#322;atno&#347;ci,</p></li><li><p>przedstawienia produkt&#243;w kt&#243;re s&#261; dost&#281;pne, w sklepie przed zakupem,</p></li><li><p>mo&#380;liwo&#347;ci&#261; wy&#347;wietlenie u&#380;ytkownikowi wcze&#347;niej zakupionych produkt&#243;w w jego panelu po zalogowaniu (czy to w kanale online czy offline). </p></li></ul><h2>Architektura:</h2><p>Vending posiada trzy tryby: zakupowy, zwrotowy i serwisowy.</p><ol><li><p>Zakupowy - klient mo&#380;e kupi&#263; produkt znajduj&#261;cy si&#281; w lod&#243;wce, bior&#261;c go z konkretnej wagi.</p></li><li><p>Zwrotowy - klient mo&#380;e zwr&#243;ci&#263; pusty pojemnik po produkcie odk&#322;adaj&#261;c z powrotem na wag&#281;.</p></li><li><p>Serwisowy - administrator mo&#380;e otworzy&#263; urz&#261;dzenie i np. uzupe&#322;ni&#263; magazyn, przypisa&#263; odpowiednie produkty do danej wagi czy te&#380; zrobi&#263; synchronizacj&#281; produkt&#243;w. </p></li></ol><h2>Dodatkowe rozwi&#261;zania technologiczne:</h2><p>Dodatkowe rozwi&#261;zania kt&#243;re wdro&#380;yli&#347;my:</p><ul><li><p>Firestore - mi&#281;dzy u&#380;ytkownikiem, a back-endem do szybkiej wymiany danych,</p></li><li><p>MQTT - mi&#281;dzy maszyn&#261;, a back-endem zastosowali&#347;my lekki protok&#243;&#322; komunikacyjny cz&#281;sto u&#380;ywany w systemach IoT,</p></li><li><p>Stripe - system do obs&#322;ugi p&#322;atno&#347;ci</p></li></ul><h2>Jakie wdro&#380;yli&#347;my funkcjonalno&#347;ci?</h2><p>W naszym rozwi&#261;zaniu znalaz&#322;o si&#281; kilka ciekawych funkcjonalno&#347;ci:</p><ol><li><p>Sesje - potrzebowali&#347;my jedno &#378;r&#243;d&#322;o prawdy dla sesji. <br>W momencie gdy otwieramy urz&#261;dzenie to musimy komunikowa&#263; si&#281; z nim w trybie asynchronicznym. W bazie danych zapisujemy dane tj. </p><ol><li><p>ID sesji, </p></li><li><p>ID urz&#261;dzenia, </p></li><li><p>ID hab&#8217;u, </p></li><li><p>ID locker, </p></li><li><p>Typ sesji, </p></li><li><p>Aktualny kod, </p></li><li><p>Tryb w jakim zosta&#322;o otwarte urz&#261;dzenie,</p></li><li><p>ID u&#380;ytkownika,</p></li></ol></li><li><p>Podgl&#261;d sesji - administrator jest wstanie podejrze&#263; wszystkie obecne utworzone sesje. Je&#347;li wydarzy&#322;a si&#281; jaka&#347; sytuacja i urz&#261;dzenie nie wys&#322;a&#322;o do nas informacji o zamkni&#281;ciu to mo&#380;emy w tym miejscu to sprawdzi&#263; i zrobi&#263; analiz&#281;.</p></li><li><p>W&#322;&#261;czanie i wy&#322;&#261;czanie trybu serwisowego - administrator jest wstanie otworzy&#263; urz&#261;dzenie i zrobi&#263; w nim porz&#261;dek czy te&#380; wymieni&#263; produkty.</p></li><li><p>Konfiguracja maszyn - dodali&#347;my mo&#380;liwo&#347;&#263; dodawania nowych urz&#261;dze&#324; i przypisywania ich do danej lokalizacji. Dzi&#281;ki temu, jeden back-end mo&#380;e odpowiada&#263; za obs&#322;ug&#281; wielu Vending&#243;w z jednego miejsca.</p></li><li><p>Przypisywanie produkt&#243;w - produkty s&#261; przypisywane do konkretnej wagi kt&#243;rych mo&#380;e by&#263; wiele. Ka&#380;dy produkt opr&#243;cz danych podstawowych zawiera jeszcze np. minimaln&#261; i maksymaln&#261; wag&#281; produktu.</p></li><li><p>Synchronizacja z urz&#261;dzeniem - &#378;r&#243;d&#322;em prawdy dla produkt&#243;w jest modu&#322; do zarz&#261;dzania produktami w Medusa.js. Po przypisaniu produkt&#243;w do wag, za pomoc&#261; jednego przycisku mo&#380;emy zrobi&#263; synchronizacj&#281; z urz&#261;dzeniem.</p></li><li><p>Powiadomienia - za pomoc&#261; naszego pluginu do Automatyzacji i Notyfikacji wdro&#380;yli&#347;my customowe notyfikacje informuj&#261;ce o stanie urz&#261;dzenia. Na przyk&#322;ad gdy urz&#261;dzenie wejdzie w tryb Offline, to administrator dostaje powiadomienie do komunikatora Slack, &#380;e co&#347; si&#281; z nim dzieje. Dzi&#281;ki temu bardzo szybko mo&#380;na zareagowa&#263;.</p></li></ol><h2>Czego si&#281; nauczyli&#347;my?</h2><p>Najwa&#380;niejsza lekcja jest taka, &#380;e.. trzeba robi&#263; testy, i jeszcze raz testy. Maszyny s&#261; zawodne i nigdy nie wiesz co Ci&#281; mo&#380;e spotka&#263;. Razem z zespo&#322;em odpowiedzialnym za software po stronie hardware&#8217;u, mieli&#347;my niezliczon&#261; seri&#281; r&#243;&#380;nych edge-case&#8217;&#243;w. Musieli&#347;my przewidzie&#263; r&#243;&#380;ne zachowania u&#380;ytkownika, software&#8217;u i hardware&#8217;u.<br><br>Nie raz by&#322;o tak, &#380;e drzwi powinny si&#281; otworzy&#263;, a tu.. klops - sygna&#322; zosta&#322; wys&#322;any, jednak software po stronie hardware&#8217;u go nie wykry&#322;.. albo wykry&#322; ale aktualizacja si&#281; nie powiod&#322;a.</p><p>Innym przyk&#322;adem jest to, &#380;e urz&#261;dzenie wysy&#322;a&#322;o do nas informacje o zamkni&#281;tych drzwiach ale pojawi&#322; si&#281; trywialny b&#322;&#261;d po stronie back-endu, i sesja si&#281; nie zamyka&#322;a poprawnie. Tak samo by&#322;o w drug&#261; stron&#281;, drzwi z jakiego&#347; powodu si&#281; nie zamkn&#281;&#322;y i sesja mog&#322;a nie zosta&#263; zamkni&#281;ta.</p><p>Kolejn&#261; zagwozdk&#261; by&#322;o pytanie - co je&#347;li maszyn&#261; kto&#347; szturchnie, sygna&#322; zostanie wys&#322;any czy nie zostanie? </p><p>To ile pr&#243;b robili&#347;my w przypadku kalibracji i nadawaniu wag, uWagom, a&#380; ci&#281;&#380;ko policzy&#263;.</p><h2>Podsumowuj&#261;c</h2><p>By&#322;o to dla nas bardzo ciekawe wyzwanie gdy&#380; wcze&#347;niej nie mieli&#347;my okazji pracowa&#263; w projektach w kt&#243;rych potrzeb&#261; by&#322;o komunikacja z systemami IoT, a to jest totalnie inne podej&#347;cie gdy&#380; tutaj nie mogliby&#347;my polega&#263; na synchronicznych odpowiedziach tak, jak w przypadku standardowych rozwi&#261;zaniach webowych. </p><p>Medusa.js to bardzo elastyczne i modu&#322;owe rozwi&#261;zanie oparte na nowoczesnej technologii pozwalaj&#261;ce na bardzo wiele zachowuj&#261;c przy tym jako&#347;&#263; i szybko&#347;&#263; dzia&#322;ania.</p><div><hr></div><p>Mam nadziej&#281;, &#380;e artyku&#322; si&#281; spodoba&#322; i dowiedzia&#322;e&#347; si&#281; czego&#347; nowego o mo&#380;liwo&#347;ciach jakie oferuje ten framework, a tak&#380;e jak podeszli&#347;my do rozwi&#261;zania problem&#243;w.</p>]]></content:encoded></item></channel></rss>