[ Sklepy internetowe ]

Sklep odzieżowy — kursy walut i przelicznik na żywo

Sklep odzieżowy sprzedający za granicę: kursy z banków centralnych, przelicznik zawsze z datą kursu i cena zamrażana przy zamówieniu, a nie przy renderze.

Live · 2026 · PHP · WordPress · WooCommerce · MySQL · Redis · REST API

Potok

  1. 1

    kursy-zrodla

  2. 2

    kursy-magazyn

  3. 3

    przelicznik

  4. 4

    zamowienie-kurs

4 etapy · 2026

O projekcie

Sklep odzieżowy, który sprzedaje poza jeden kraj, więc odwiedzający ogląda ceny w swojej walucie — z kursem pobieranym automatycznie i przeliczeniem pokazywanym na bieżąco. Cała trudność tego projektu siedzi nie w pobieraniu kursów, tylko w tym, co wolno z nimi zrobić.

Zacznę od rozróżnienia, na którym stoi cały projekt: KURS TO NIE CENA. Jeżeli przeliczenie dzieje się przy każdym wyrenderowaniu strony, to ta sama sukienka kosztuje 49,90 euro na liście, 50,10 w koszyku i 50,20 przy płatności — bo między tymi trzema ekranami minęło kilkanaście minut i kurs drgnął. Klient, który to zauważy, składa reklamację i MA RACJĘ. Dlatego kwota w obcej walucie jest wprost opisana jako orientacyjna do momentu złożenia zamówienia, a w tym momencie KURS ZOSTAJE ZAMROŻONY i idzie z zamówieniem dalej.

Druga rzecz, którą zwykle się przemilcza: KTÓRY to ma być kurs. Kurs średni z banku centralnego jest ładny i darmowy, ale nikt nigdy po nim nie zapłaci — operator płatności przewalutuje po swoim, a wydawca karty po jeszcze innym. Pokazywanie kursu średniego jako ceny do zapłaty to obietnica, której sklep nie kontroluje. Tutaj kurs średni jest tym, czym jest — punktem odniesienia z podaną datą i godziną — a tam, gdzie sklep faktycznie rozlicza się w obcej walucie, liczy się kursem operatora ze spreadem opisanym wprost, nie schowanym w zaokrągleniu.

Zaokrąglanie jest miejscem, w którym najczęściej widać, że ktoś tego nie robił. Jen nie ma części ułamkowej, a dinar kuwejcki ma TRZY miejsca po przecinku; korona islandzka chodzi na liczbach całkowitych. Odruchowe zaokrąglenie do dwóch miejsc jest więc błędne dla sporej części świata i w jednym kierunku produkuje kwoty, których nie da się zapłacić, a w drugim — groszowe rozjazdy narastające przez cały koszyk. Liczba miejsc i sposób zaokrąglenia są cechą WALUTY zapisaną w danych, a nie stałą w kodzie, a arytmetyka idzie na liczbach stałoprzecinkowych, bo cena w typie zmiennoprzecinkowym to pomyłka czekająca na okazję.

Przeliczanie ma też skutek, który boli akurat w modzie: ROZBIJA PUNKTY CENOWE. Cena 199 złotych zamienia się w 46,31 euro — i cała praca nad tym, jak cena wygląda, idzie do kosza. Dlatego dojrzała odpowiedź nie brzmi „przeliczaj wszystko”, tylko: dla rynków, na których sklep naprawdę sprzedaje, obowiązuje WŁASNY CENNIK z cenami ustawionymi ręcznie, a przelicznik służy do orientacji na pozostałych. To rozróżnienie jest w danych, więc da się je zmieniać rynek po rynku, bez dotykania kodu.

Pobieranie kursów wygląda na prostą rzecz do czasu pierwszej soboty. Banki centralne publikują RAZ na dzień roboczy, o określonej godzinie, i nie publikują w weekendy ani w święta — a kalendarz świąt jest inny w każdym kraju. Zadanie, które po prostu pyta o „kurs na dziś”, w sobotę nie dostaje nic i albo się wywraca, albo po cichu bierze wartość sprzed tygodnia. Tutaj magazyn trzyma OSTATNI ZNANY KURS razem z jego wiekiem, a wiek ma twardy sufit: po jego przekroczeniu przelicznik nie pokazuje liczby, tylko znika i zostawia cenę w walucie sklepu.

To jest zresztą ogólniejsza zasada tego projektu: NA CENIE NIE WOLNO OTWIERAĆ SIĘ NA WYPADEK AWARII. Kiedy źródła kursów milczą albo różnią się od siebie bardziej, niż wynosi próg, system nie zgaduje i nie wybiera „lepszej” wartości. Brak przeliczenia jest niewygodny; przeliczenie nieprawdziwe jest kosztowne — i to w sposób, który wraca zwrotem, reklamacją albo wpisem w rejestrze skarg.

Osobno warto powiedzieć, co tutaj znaczy „NA ŻYWO”, bo to określenie bywa sprzedawane nieuczciwie. Nie znaczy zapytania do zewnętrznego serwisu przy każdym wejściu na stronę — to jest wolne, kosztowne i w szczycie ruchu kończy się odcięciem przez limit zapytań, czyli brakiem cen dokładnie wtedy, gdy są najbardziej potrzebne. Kursy walut zmieniają się w skali minut, a nie milisekund, więc „na żywo” znaczy tu: kurs aktualny, wzięty z krótkiego bufora po stronie serwera i ZAWSZE POKAZANY RAZEM Z MOMENTEM POBRANIA. Data przy kursie jest częścią informacji, nie ozdobą.

Waluta nie jest tym samym co kraj ani co język, choć interfejsy notorycznie je zlepiają. Ktoś w Niemczech może chcieć zapłacić w złotych, a Polak mieszkający w Irlandii oglądać sklep po polsku i płacić w euro. Rozpoznanie po adresie IP jest zgadywaniem, więc służy tu wyłącznie do ustawienia wartości domyślnej — wybór waluty należy do człowieka, jest zapamiętywany i nie zmienia się sam pod nim przy kolejnej wizycie.

Na końcu jest rzecz, która zaskakuje przy pierwszym zamknięciu miesiąca: JEDNO ZAMÓWIENIE NIESIE TRZY RÓŻNE KURSY. Kurs POKAZANY klientowi przy składaniu zamówienia, kurs ROZLICZENIOWY operatora płatności, po którym pieniądze faktycznie wpłynęły, i kurs KSIĘGOWY, po którym przychód trafia do ksiąg — w Polsce średni kurs NBP z ostatniego dnia roboczego poprzedzającego transakcję. To są trzy różne liczby i sklep, który trzyma jedną, nie umie się rozliczyć. Stąd też odpowiedź na zwroty: oddaje się DOKŁADNIE TĘ KWOTĘ, która została pobrana, w walucie, w której została pobrana — a nie równowartość przeliczoną po kursie z dnia zwrotu, bo ta potrafi różnić się o kilka procent i zawsze na czyjąś niekorzyść.

Problem

Sklep sprzedający za granicę albo pokazuje ceny tylko w swojej walucie i traci klientów na etapie „ile to właściwie jest”, albo przelicza je przy każdym renderze — i wtedy ta sama rzecz kosztuje inaczej na liście, w koszyku i przy płatności. Do tego kurs średni banku centralnego, którym zwykle się przelicza, nie jest kursem, po którym ktokolwiek zapłaci.

Rozwiązanie

Kurs jest pobierany automatycznie, trzymany z własnym wiekiem i twardym sufitem nieświeżości, a przy każdej kwocie widać moment jego pobrania. Przeliczenie jest jawnie orientacyjne do złożenia zamówienia, w którym kurs zostaje zamrożony i niesiony dalej — aż do zwrotu, rozliczenia z operatorem i zapisu księgowego, bo to trzy różne kursy.

Funkcje

  • kurs zamrażany przy złożeniu zamówienia i niesiony z nim aż do zwrotu
  • każda przeliczona kwota pokazana razem z datą i godziną kursu
  • liczba miejsc po przecinku i zaokrąglenie jako cecha waluty, nie stała w kodzie
  • arytmetyka stałoprzecinkowa — cena nigdy nie przechodzi przez typ zmiennoprzecinkowy
  • własne cenniki dla rynków, na których sklep faktycznie sprzedaje
  • ostatni znany kurs z twardym sufitem wieku; po przekroczeniu przelicznik znika
  • rozbieżność między źródłami ponad próg wstrzymuje przeliczanie zamiast zgadywać
  • waluta jako wybór człowieka; adres IP ustawia najwyżej wartość domyślną
  • trzy kursy przy zamówieniu: pokazany, rozliczeniowy i księgowy

Moduły

  • kursy-zrodla

    warstwa pobierania

    Ściąga kursy z banków centralnych i ze źródła zapasowego, i porównuje je ze sobą.

    • harmonogram dopasowany do godzin publikacji, osobno dla każdego banku centralnego
    • kalendarz dni wolnych per kraj — brak publikacji w sobotę to nie jest awaria
    • dwa źródła zestawiane ze sobą; rozjazd ponad próg zatrzymuje aktualizację
    • odpowiedź źródła zapisywana w całości, żeby dało się odtworzyć, skąd wziął się kurs
    • ponowienia z narastającą przerwą; cisza źródła nie kasuje ostatniego znanego kursu
  • kursy-magazyn

    magazyn kursów

    Trzyma kursy z ich wiekiem i rozstrzyga, kiedy kurs przestaje nadawać się do pokazania.

    • wpis dopisywany, nigdy nadpisywany — historia kursu jest potrzebna przy reklamacji
    • ostatni znany kurs zawsze dostępny, ale zawsze z podanym wiekiem
    • twardy sufit nieświeżości; po jego przekroczeniu kurs przestaje być wydawany
    • liczba miejsc po przecinku i reguła zaokrąglenia zapisane przy walucie
    • kursy trzymane jako liczby stałoprzecinkowe, bez przejścia przez typ zmiennoprzecinkowy
  • cennik-rynkow

    warstwa cen

    Rozdziela rynki z własnym cennikiem od tych, na których cena jest tylko przeliczana.

    • rynek z własnym cennikiem ma ceny ustawione ręcznie, z zachowanymi punktami cenowymi
    • rynek bez cennika dostaje przeliczenie opisane jako orientacyjne
    • przypisanie rynku do trybu stoi w danych, więc zmiana nie wymaga wydania kodu
    • cennik rynku niesie własną walutę, własny VAT i własne progi dostawy
    • brak ceny dla rynku nie ukrywa produktu — pokazuje go w walucie sklepu
  • przelicznik

    warstwa prezentacji

    Pokazuje kwotę w wybranej walucie tak, żeby nikt nie wziął jej za cenę wiążącą.

    • przy kwocie zawsze moment pobrania kursu — to część informacji, nie ozdoba
    • krótki bufor po stronie serwera zamiast zapytania do źródła przy każdym wejściu
    • wybór waluty należy do człowieka i jest zapamiętywany między wizytami
    • adres IP ustawia najwyżej wartość domyślną i nigdy nie nadpisuje wyboru
    • brak świeżego kursu ukrywa przeliczenie zamiast pokazywać liczbę z wczoraj
  • zamowienie-kurs

    zamrożenie kursu

    W momencie złożenia zamówienia zamienia kurs orientacyjny w kurs wiążący.

    • kurs, jego źródło i moment pobrania zapisane przy zamówieniu, nie doliczane później
    • kwota do zapłaty wyliczona raz i niezmienna — ponowne wejście w koszyk jej nie rusza
    • widełki bezpieczeństwa: skok kursu ponad próg wymaga potwierdzenia przez klienta
    • zamówienie niesie obie kwoty — w walucie sklepu i w walucie zapłaty
    • dokument sprzedaży powstaje z zamrożonych wartości, nie z przeliczenia na nowo
  • podatek-rynki

    warstwa podatkowa

    Pilnuje, żeby przeliczona kwota była kwotą właściwą dla kraju odbiorcy.

    • stawka VAT liczona według kraju dostawy, nie kraju sklepu
    • próg sprzedaży zagranicznej pilnowany w danych, z ostrzeżeniem przed przekroczeniem
    • cena pokazana klientowi zawiera podatek jego kraju, zanim zostanie przeliczona
    • wysyłka poza obszar celny opisana wprost: cło i podatek po stronie odbiorcy
    • zmiana stawki nie przelicza wstecz dokumentów już wystawionych
  • zwroty-rozliczenia

    warstwa rozliczeń

    Domyka pieniądze: zwrot, wpływ od operatora i zapis księgowy — trzy różne kursy.

    • zwrot oddaje DOKŁADNIE pobraną kwotę w walucie pobrania, bez przeliczania na nowo
    • kurs rozliczeniowy operatora zestawiany z kursem pokazanym; różnica jest raportowana
    • kurs księgowy brany z ostatniego dnia roboczego przed transakcją, osobno od pozostałych
    • różnice kursowe pokazane jako osobna pozycja, a nie rozmyte w marży
    • zwrot częściowy liczony z pozycji zamówienia, żeby suma zwrotów nie przekroczyła wpłaty

Architektura

Podstawa
WooCommerce na WordPressie z warstwą walutową jako osobnym modułem; sklep działa dalej, gdy warstwa kursów milczy
Źródła kursów
banki centralne według ich godzin publikacji plus źródło zapasowe; dwa źródła zestawiane, rozjazd ponad próg wstrzymuje aktualizację
Kalendarz
dni wolne per kraj — brak publikacji w weekend i święto jest stanem normalnym, nie awarią
Świeżość
ostatni znany kurs z podanym wiekiem i twardym sufitem; po przekroczeniu przelicznik znika zamiast zgadywać
Arytmetyka
liczby stałoprzecinkowe od źródła po dokument; cena nie przechodzi przez typ zmiennoprzecinkowy ani razu
Zaokrąglanie
liczba miejsc i reguła jako cecha waluty w danych — jen bez części ułamkowej, dinar kuwejcki z trzema miejscami
Ceny rynkowe
rynki z własnym cennikiem mają ceny ustawiane ręcznie; przelicznik obsługuje wyłącznie rynki bez cennika
Status kwoty
orientacyjna do złożenia zamówienia, wiążąca po zamrożeniu kursu; różnica opisana w interfejsie, nie w regulaminie
Zamrożenie
kurs, źródło i moment zapisane przy zamówieniu; skok kursu ponad widełki wymaga potwierdzenia klienta
Podatek
stawka według kraju dostawy, progi sprzedaży zagranicznej pilnowane w danych, cło poza obszarem celnym opisane wprost
Zwroty
oddawana jest dokładnie pobrana kwota w walucie pobrania — nigdy równowartość po kursie z dnia zwrotu
Trzy kursy
pokazany klientowi, rozliczeniowy operatora i księgowy z ostatniego dnia roboczego; różnice kursowe jako osobna pozycja
Wydajność
krótki bufor w Redisie zamiast zapytania do źródła na wejście; limit zapytań nie może wyłączyć cen w szczycie ruchu

Technologie

  • PHP
  • WordPress
  • WooCommerce
  • MySQL
  • Redis
  • REST API

Autor

  • Zdjęcie: Mariusz PerzyńskiMariusz PerzyńskiClient Relations & Operations

[ Kontakt ]

Chcesz podobne rozwiązanie w swojej firmie?

Opowiedz nam o swoim pomyśle — wrócimy z konkretną propozycją i wyceną.

Automatic AI

hello@automaticai.pl · automaticai.pl