Dokumentacja techniczna platformy SupportME

Pełny opis systemu moduł po module: co robi każdy element, jakie udostępnia trasy, jakie metody zawiera, jakie ma pola i z których tabel korzysta oraz co dokładnie dzieje się podczas działania. Dokument wygenerowany na podstawie analizy kodu źródłowego.

Aktualizacja: 26 czerwca 2026 · Laravel 12 · PHP 8.2+ · architektura multi-tenant
2
moduły aplikacji
32
kontrolery
19
modele danych
~25
tabel w bazach
69
widoków (Blade)
109
tras (web · API · webhooki)
~13 500
linii kodu
3
bazy danych (multi-tenant)

Legenda statusów: Aktywny — działa na produkcji · Demo — zbudowany, tryb pokazowy/testowy · Osobny tenant — pod inną domeną · Gotowy — kod gotowy, włączany konfiguracją. Kwoty pieniężne w bazie przechowywane są w groszach (liczby całkowite).

1.

Architektura multi-tenant

Aktywny

Jedna aplikacja Laravel obsługuje kilka niezależnych serwisów. To, który moduł i która baza danych obsłużą żądanie, zależy od hostu (domeny). Rozstrzyga to middleware ResolveTenant, uruchamiany jako pierwszy — przed sesją i tokenem CSRF — bo musi ustawić bazę i kontekst zanim cokolwiek innego się wykona.

Mapowanie hostów → tenant
Host (domena)ModułTrybBazaRola
pay.please-support-me.comGatewaynfc_payBramka płatności, panel tagów/sklepów
please-support-me.comStorefrontchurchnfc_shop1Cyfrowa Taca, sklep, CRM, rekrutacja (główny serwis)
shop2.please-support-me.comStorefrontproductsnfc_shop2Sklep gadżetów ze stałą ceną (osobny tenant)
Co robi ResolveTenant
  • Czyta host żądania i mapuje go na tenant (moduł + tryb + baza + klucz API bramki).
  • Dynamicznie podmienia bazę połączenia MySQL (config.database.connections.mysql.database) — per żądanie, bez restartu.
  • Przełącza ścieżki widoków Blade zależnie od trybu (church / products), więc ten sam kontroler renderuje inny wygląd.
  • Rejestruje singleton app('tenant') dostępny w całej aplikacji oraz klucz API bramki w config('shop.gateway_api_key').
Połączenia bazy

Połączenie mysql jest przełączane per host (sklepy), a dedykowane połączenie gateway wskazuje zawsze na nfc_pay — modele bramki czytają z niego niezależnie od tego, jaki tenant obsługuje bieżące żądanie.

Pliki
app/Http/Middleware/ResolveTenant.php · config/tenants.php · config/platform.php · config/database.php
2.

Warstwa płatności — przegląd

Aktywny

Płatności obsługuje osobny moduł Gateway (własna baza nfc_pay), z którym sklepy komunikują się przez REST API i webhooki. Sklep nigdy nie rozmawia bezpośrednio z PayU — robi to bramka, co pozwala obsłużyć wiele sklepów jednym, bezpiecznym połączeniem z operatorem. Pełny cykl jest wielowarstwowy: webhook + aktywny polling + podpisy kryptograficzne.

Przepływ end-to-end
  1. Inicjacja. Sklep tworzy transakcję w bramce na wybraną kwotę — POST /api/v1/transactions z nagłówkiem X-Api-Key. Bramka zwraca uuid i payment_url.
  2. Autoryzacja. Klient na stronie bramki płaci BLIK-iem (kod 6-cyfrowy) lub pay-by-link (przekierowanie do aplikacji banku). Bramka tworzy zamówienie w PayU (REST v2.1, OAuth).
  3. Webhook PayU → bramka. PayU wysyła powiadomienie POST /webhooks/payu z nagłówkiem OpenPayu-Signature (weryfikacja MD5/SHA256). Bramka oznacza transakcję jako opłaconą/nieudaną.
  4. Webhook bramka → sklep. Bramka woła notify_url sklepu z podpisem HMAC-SHA256. Sklep weryfikuje podpis i aktualizuje zamówienie (status = paid, paid_at).
  5. Polling (gwarancja). Ekran powrotu odpytuje status; bramka aktywnie rekonsyliuje z PayU (getOrderStatus), a transakcje „oczekujące na potwierdzenie” domyka przez capture().
Tryby płatności

classic — klasyczny przepływ z 3DS; app2app — BLIK / pay-by-link z przekierowaniem do aplikacji bankowej. Tryb ustawiany per sklep w polu payment_mode.

3.

Sklep donacyjny NFC (/)

Aktywny

Strona główna serwisu. Wyświetla produkty oznaczone tagami NFC (Serduszko, Kubek, Koszulka, Pin, Brelok). Każdy produkt ma własną minimalną kwotę. Domyślny produkt („Serduszko”, min. 1 zł) otwiera się automatycznie w modalu po wejściu. Kwota jest edytowalna w modalu, a walidacja minimum działa zarówno w przeglądarce, jak i na serwerze. Obsługiwane przez CompanyStoreController.

Trasy
MetodaURLNazwaOpis
GET/homeLista produktów + modal KUP
POST/sklep/kup/{slug}shop.buyZakup produktu na wybraną kwotę
Metody kontrolera
  • index()Pobiera aktywne produkty (sortowanie po sort), wyznacza domyślny (is_default) i renderuje stronę z siatką oraz danymi produktów dla modala (JSON).
  • purchase($slug)Waliduje kwotę (≥ minimum produktu, ≤ 5000 zł) po stronie serwera, tworzy zamówienie (product_id = null), woła bramkę i przekierowuje do płatności. Komunikat błędu zawiera nazwę produktu i jego minimum.
Pola produktu (model ShopItem → tabela shop_items)
PoleTypOpis
slugstring, unikalnyIdentyfikator URL (np. serduszko)
namestringNazwa produktu
imagestringŚcieżka do grafiki (raster lub SVG serca)
min_amountint (grosze)Minimalna kwota wpłaty (100 = 1 zł)
is_defaultboolCzy produkt domyślny (auto-modal); tylko jeden naraz
tag_uidstring, nullableUID taga NFC kierującego wprost na ten produkt
activeboolWidoczność w sklepie
sortintKolejność na liście
Co się dzieje (przepływ zakupu)
  1. Użytkownik wchodzi na / — domyślny produkt otwiera się w modalu (raz na sesję, przez sessionStorage), albo otwiera się produkt wskazany w ?produkt={slug}.
  2. Klik karty produktu otwiera modal z edytowalnym polem kwoty (wstępnie = minimum produktu).
  3. Walidacja w przeglądarce: przy kwocie poniżej minimum przycisk KUP jest blokowany i pojawia się komunikat.
  4. POST /sklep/kup/{slug} — serwer ponownie waliduje kwotę (warstwa nie do obejścia), tworzy Order i transakcję w bramce.
  5. Przekierowanie 302 na payment_url bramki (PayU).
Tabele
shop_itemsorders
Pliki
CompanyStoreController.php · Models/ShopItem.php · views/…/shop/sklep.blade.php · public/css/sklep.css · public/css/landing.css (modal)
4.

Cyfrowa Taca — wejście NFC i parafie

Aktywny

Główna funkcja platformy. Darczyńca zbliża telefon do znacznika NFC w kościele, trafia na stronę parafii, wybiera kwotę i płaci, a na końcu widzi ekran „Bóg zapłać”. Wszystkim steruje StorefrontController; dane parafii zarządzane są w panelu (sekcja 16). Każdy etap loguje zdarzenie do analityki.

Trasy
MetodaURLNazwaOpis
GET/mainmainLanding (sekcja 5)
GET/kategoria/{slug}categoryLista parafii w kategorii + wyszukiwarka
GET/t/{tag_uid}tagWejście z taga NFC → przekierowanie
GET/p/{slug}product.showStrona parafii + wybór kwoty
POST/p/{slug}/kupproduct.buyUtworzenie zamówienia i transakcji
Metody kontrolera
  • index()Renderuje landing: kategorie wsparcia z bazy (aktywne, najwyższy poziom) z ikoną, etykietą i opisem.
  • category($slug)Pokazuje parafie danej kategorii (gdy source = parishes) lub pusty stan. Po stronie klienta działa wyszukiwarka po nazwie, mieście i województwie.
  • tag($tagUid)Szuka parafii po tag_uid → przekierowuje na jej stronę i loguje tag_open. Jeśli to tag produktu sklepu → przekierowuje na sklep z modalem produktu. Nieznany tag → strona 404 „tabliczka nieprzypisana”.
  • show($slug)Strona parafii; loguje page_view; renderuje formularz wyboru kwoty z presetami.
  • buy($slug)Waliduje kwotę (2–5000 zł), loguje buy_click, tworzy Order powiązany z parafią i transakcję w bramce, przekierowuje do płatności.
Pola parafii (model Product → tabela products)
PoleTypOpis
namestringNazwa parafii
citystringMiasto
purposestringCel zbiórki (np. „Remont dachu”)
slugstring, unikalnyIdentyfikator URL
description_htmltextOpis parafii (WYSIWYG)
priceint (grosze)Sugerowana kwota tacy (preset bazowy)
tag_uidstring, unikalnyUID taga NFC przypisanego do parafii
main_imagestringZdjęcie główne parafii
activeboolPublikacja (sterowana statusem CRM)
phone, website, voivodeshipstringDane kontaktowe (CRM)
statusenumCRM: kontakt · test · wdrożenie · aktywna
salesperson_idFKHandlowiec opiekujący się parafią
Presety kwot (strona parafii)

Domyślne przyciski: 10, 20, 50, 100, 200 zł + kafelek „inna kwota” zamieniający się w pole liczbowe. Zakres dozwolony: 2–5000 zł. Wybrana kwota aktualizuje etykietę przycisku „Wesprzyj — X zł”.

Co się dzieje (pełny przepływ tacy)
  1. Telefon przy tagu NFC otwiera /t/{tag_uid}.
  2. tag() znajduje parafię, loguje tag_open (lokalnie + asynchronicznie do bramki) i przekierowuje na /p/{slug}.
  3. show() loguje page_view i pokazuje stronę z presetami.
  4. Po wyborze kwoty POST /p/{slug}/kup: buy() loguje buy_click, tworzy zamówienie i transakcję, przekierowuje do PayU.
  5. Po płatności następuje powrót na ekran zwrotu (sekcja 10) — „Bóg zapłać”.
Tabele
productscategoriesorderseventsproduct_images
Pliki
StorefrontController.php · Models/Product.php, Category.php, Order.php, Event.php · views/…/shop/{home,category,product}.blade.php · Jobs/SendGatewayEvent.php
5.

Strona główna /main

Aktywny

Landing „Technologia, która pomaga czynić dobro” — zbudowany pixel-perfect z makiet Figmy (desktop i mobile). Sekcje są dynamiczne: kategorie „Kogo wspieramy?” pobierane są z bazy i zarządzane z panelu.

Sekcje
  • Hero — nagłówek z misją platformy.
  • Kogo wspieramy? — kafelki kategorii (ikona, etykieta HTML, opis); linki do /kategoria/{slug}.
  • Jak to działa? — 4 kroki: zbliż telefon do NFC → wybierz wsparcie → zapłać → otrzymaj podziękowanie.
  • Tekst zamykający + stopka z danymi firmy i linkami.
Modal „Wesprzyj” i podgląd linku (OG)

Przycisk „Wesprzyj” w nagłówku otwiera modal domyślnego produktu (/?produkt=serduszko). W <head> ustawione są tagi Open Graph i Twitter z grafiką serca z logo — przy udostępnianiu linku na WhatsApp / Facebook pojawia się ładny podgląd z serduszkiem.

Tabele
categories
Pliki
StorefrontController::index() · views/…/shop/home.blade.php · layouts/landing.blade.php · public/css/landing.css · public/img/og-supportme.png
6.

Inwestorzy i akcjonariusze

Aktywny

Statyczna strona marketingowa (GET /inwestorzy, nazwa trasy investors) zbudowana 1:1 z makiety Figmy. Hero „Inwestorzy i akcjonariusze”, sekcja misji kapitałowej („Wierzymy, że kapitał może służyć dobru”) oraz karty akcjonariuszy z kwotami inwestycji (kapitałowa + wsparcie usługowe). Renderowana bez kontrolera (Route::view).

Pliki
routes/web.php (Route::view) · views/…/shop/inwestorzy.blade.php · public/css/inwestorzy.css
7.

Rekrutacja i kariera

Aktywny

Pełny system rekrutacyjny: publiczna lista ofert (zarządzana z panelu), strony ofert, formularz aplikacji z uploadem CV oraz powiadomienie e-mail z załączonym CV. Obsługiwany przez CareersController. CV trafia na prywatny dysk (niedostępny publicznie), a pobrać je można tylko z panelu.

Trasy
MetodaURLNazwaOpis
GET/pracacareersLista aktywnych ofert
GET/praca/oferta/{position}careers.showSzczegóły oferty
GET/POST/praca/aplikujcareers.apply.generalAplikacja spontaniczna (bez oferty)
GET/POST/praca/{position}/aplikujcareers.applyAplikacja na konkretną ofertę
Metody kontrolera
  • index()Lista aktywnych stanowisk; karty z typem zatrudnienia i lokalizacją; wykrywa „pracę zdalną” z treści.
  • show($position)Pełny opis oferty (WYSIWYG), chipy meta, sekcja „inne oferty”.
  • applyForm($position?)Renderuje formularz aplikacji (na ofertę lub spontaniczny).
  • applyStore($position?)Waliduje dane i plik CV, zapisuje zgłoszenie, przenosi CV na prywatny dysk, wysyła e-mail (jeśli skonfigurowany mailer), pokazuje podziękowanie.
Pola oferty (JobPosition → job_positions)
PoleTypOpis
titlestringNazwa stanowiska
locationstringLokalizacja
employment_typestringRodzaj zatrudnienia
description_htmltextOpis (WYSIWYG)
activeboolWidoczność publiczna
sortintKolejność
Pola zgłoszenia (JobApplication → job_applications)
PoleTypOpis
job_position_idFK, nullableOferta (null = aplikacja spontaniczna)
name, email, phonestringDane kandydata
messagetext, nullableList motywacyjny
cv_pathstringŚcieżka pliku CV na prywatnym dysku
cv_original_namestringOryginalna nazwa pliku
is_readboolCzy odczytane w panelu
statusenumpending · accepted · rejected
Walidacja i e-mail

CV: wymagane, do 5 MB, typy pdf/doc/docx (sprawdzane rozszerzenie i MIME). Zgoda RODO: wymagana. Po zapisie generowany jest mail JobApplicationReceived na config('shop.careers_email') z CV w załączniku i adresem kandydata w Reply-To.

Powiadomienia e-mail Gotowy — kod wysyłki maila jest kompletny; na produkcji działa tryb „tylko panel” (zgłoszenia + CV trafiają do skrzynki w panelu). Wysyłkę mailem włącza się konfiguracją mailera.
Tabele
job_positionsjob_applications
Pliki
CareersController.php · Models/JobPosition.php, JobApplication.php · Mail/JobApplicationReceived.php · views/…/shop/{praca,oferta,aplikuj}.blade.php · views/emails/job-application.blade.php
8.

Kontakt

Aktywny

Publiczny formularz kontaktowy (ContactController). Wiadomości zapisywane są do bazy i trafiają do skrzynki w panelu z licznikiem nieprzeczytanych. Temat może być wstępnie wypełniony z linku z oferty pracy (?stanowisko=…).

Trasy i metody
  • show()GET /kontakt (nazwa contact.show) — formularz.
  • store()POST /kontakt (nazwa contact.store) — walidacja i zapis wiadomości.
Pola (ContactMessage → contact_messages)
PoleTypWalidacja
namestringwymagane, max 255
emailstringwymagane, e-mail, max 255
phonestring, nullablemax 50
subjectstring, nullablemax 255
messagetextwymagane, max 5000
is_readbooloznaczane w panelu
Tabele
contact_messages
Pliki
ContactController.php · Models/ContactMessage.php · views/…/shop/kontakt.blade.php
9.

Regulamin

Aktywny

Statyczny dokument prawny (GET /regulamin, nazwa regulamin) — kompletny regulamin sklepu internetowego przeniesiony 1:1 z dokumentu źródłowego. 12 paragrafów + wzór formularza odstąpienia.

Zawartość
  • §1 Postanowienia ogólne · §2 Definicje · §3 Usługi elektroniczne · §4 Zamówienia
  • §5 Ceny i płatności · §6 Wymagania techniczne · §7 Odstąpienie (14 dni) · §8 Reklamacje i zwroty
  • §9 Ochrona danych (RODO) · §10 Własność intelektualna · §11 ODR · §12 Postanowienia końcowe
  • Załącznik: wzór formularza odstąpienia; dane firmy: MLI – Marcin Lula Informatyka, NIP 8741624637
Pliki
routes/web.php (Route::view) · views/…/shop/regulamin.blade.php · public/css/subpages.css
10.

Powrót z płatności

Aktywny

Po powrocie z bramki OrderReturnController synchronizuje status zamówienia i pokazuje właściwy ekran: sukces („Bóg zapłać”), oczekiwanie (z pollingiem) lub niepowodzenie (z opcją ponowienia). To zabezpiecza przed sytuacją, gdy klient wróci wcześniej niż dotrze webhook.

Trasy i metody
  • show($order)GET /zwrot/{order} (nazwa order.return) — synchronizuje status z bramki; jeśli opłacone, loguje purchase; renderuje ekran wg statusu.
  • status($order)GET /zwrot/{order}/status (nazwa order.status) — zwraca JSON {status} dla pollingu.
Ekrany (wg statusu zamówienia)
StatusWidokZachowanie
paidreturn-successAnimacja świecy, kwota, nazwa parafii, nr potwierdzenia
pendingreturn-pendingSpinner + polling co 2 s (do ~60 s); po zmianie statusu przeładowanie
failedreturn-failureKomunikat o niepowodzeniu, przyciski „spróbuj ponownie” / „inna parafia”
Pola zamówienia (Order → orders)
PoleTypOpis
idUUIDIdentyfikator zamówienia
product_idFK, nullableParafia (null dla sklepu donacyjnego)
transaction_idUUIDTransakcja w bramce
amountint (grosze)Kwota wpłaty
statusenumpending · paid · failed
paid_attimestamp, nullableCzas potwierdzenia
Tabele
ordersevents
Pliki
OrderReturnController.php · views/…/shop/return-{success,pending,failure}.blade.php
11.

Klient bramki i webhooki (po stronie sklepu)

Aktywny

Sklep komunikuje się z bramką przez serwis GatewayClient oraz odbiera powiadomienia zwrotne (webhooki) o opłaceniu. Zdarzenia analityczne (np. tag_open) wysyłane są asynchronicznie, by nie spowalniać odpowiedzi.

Metody GatewayClient
  • createTransaction($data)POST do API bramki z danymi: product_external_id, product_name, amount, currency, return_url, notify_url, tag_uid. Zwraca uuid i payment_url.
  • getTransaction($uuid)Pobiera bieżący status transakcji (używane przy synchronizacji ekranu zwrotu).
  • sendEvent($type, $tagUid)Wysyła zdarzenie do bramki (np. tag_open).
  • verifyWebhookSignature($payload, $sig)Weryfikuje podpis HMAC-SHA256 przychodzącego webhooka kluczem API sklepu.
Odbiór webhooka

GatewayWebhookController::handle() (POST /webhooks/gateway) weryfikuje podpis, a następnie ustawia Order.status = paid + paid_at i loguje zdarzenie purchase. SendGatewayEvent (job kolejkowy, dispatchAfterResponse) wysyła eventy do bramki już po odesłaniu odpowiedzi do użytkownika.

Pliki
Services/GatewayClient.php · Http/Controllers/GatewayWebhookController.php · Jobs/SendGatewayEvent.php
12.

Moduł Gateway — bramka PayU

Aktywny

Samodzielna bramka płatnicza działająca jako odrębna aplikacja (pay.please-support-me.com, baza nfc_pay). Integruje PayU, obsługuje wiele sklepów, tagi NFC, transakcje, zdarzenia i statystyki. Zawiera 15 kontrolerów, własne API, panel oraz warstwę dostawców płatności (provider).

Dostawca płatności — PayUProvider
  • createTransaction()Tworzy zamówienie w PayU (POST /api/v2_1/orders, OAuth client_credentials); zwraca provider_order_id i URL przekierowania.
  • getOrderStatus()Pobiera status zamówienia z PayU (aktywna rekonsyliacja).
  • capture()Domyka płatność oczekującą na potwierdzenie (PUT statusu → COMPLETED).
  • payByLinks()Zwraca listę banków do ekranu wyboru (pay-by-link).
  • handleWebhook()Weryfikuje OpenPayu-Signature i parsuje powiadomienie. Obsługa BLIK Level 0 i pay-by-link.

Interfejs PaymentProviderInterface pozwala podmienić dostawcę; alternatywą jest MockProvider (tryb testowy).

Logika transakcji — TransactionService
  • reconcileWithProvider()Aktywnie sprawdza status u PayU (gdy webhook się spóźnia).
  • markPaid() / markFailed()Idempotentnie ustawia status transakcji.
  • logEvent()Zapisuje zdarzenie transakcji do bazy.
  • notifyShop()Wysyła webhook wychodzący do sklepu, podpisany HMAC-SHA256.
Pola transakcji (Transaction → transactions)
PoleTypOpis
idUUIDIdentyfikator transakcji (= uuid u sklepu)
shop_id, tag_idFKSklep i opcjonalny tag NFC
product_external_idstringIdentyfikator produktu po stronie sklepu
product_namestringNazwa pozycji (np. „Taca — Parafia X”)
amount, currencyint / stringKwota (grosze) i waluta (PLN)
statusenumcreated · pending · paid · failed · abandoned
modestringclassic / app2app
return_url, notify_urlstringPowrót i webhook sklepu
provider_order_idstringID zamówienia w PayU
paid_attimestampCzas opłacenia
Tabele
shopstagstransactionseventsleadsantitheft_checks
Pliki
Modules/Gateway/Payments/{PayUProvider,MockProvider,PaymentProviderInterface,TransactionDto,WebhookResult}.php · Services/{TransactionService,StatsService}.php · Models/{Shop,Tag,Transaction,Event,Lead,AntitheftCheck}.php
13.

Gateway: API i webhooki

Aktywny

Bramka udostępnia REST API dla sklepów (autoryzacja kluczem) oraz endpointy płatności i webhooki. Wszystkie powiadomienia są podpisywane i weryfikowane kryptograficznie.

Trasy API i płatności
MetodaURLOpis
POST/api/v1/transactionsUtworzenie transakcji (nagłówek X-Api-Key)
GET/api/v1/transactions/{uuid}Status transakcji
POST/api/v1/eventsRejestracja zdarzenia (np. tag_open)
GET/pay/{uuid}Ekran płatności (wybór metody)
POST/pay/{uuid}/confirmPotwierdzenie płatności (BLIK / pay-by-link)
GET/pay/{uuid}/returnPowrót z PayU
POST/webhooks/payuWebhook PayU (OpenPayu-Signature)
Kontrolery
Api/TransactionController.php · Api/EventController.php · PaymentController.php · WebhookController.php · ActivationStatusController.php
14.

Gateway: panel zarządzania

Aktywny

Panel bramki (osobne logowanie) służy do zarządzania sklepami, tagami NFC, statystykami i leadami. Zawiera też moduły demonstracyjne (anti-theft, tryb testowy płatności).

Sekcje panelu bramki
  • ShopControllerCRUD sklepów: nazwa, slug, klucz API, tryb płatności (classic/app2app), URL bazowy.
  • TagControllerZarządzanie tagami NFC: przypisanie do sklepu, etykieta, aktywność.
  • StatsControllerStatystyki płatności per sklep — transakcje, przychód.
  • LeadControllerLeady z landingu bramki + eksport do CSV.
  • DashboardControllerPulpit startowy panelu bramki.
  • LandingControllerStrona główna bramki (GET /) + zapis leada (POST /lead).
Moduły demonstracyjne

Demo Anti-theft (AntiTheftController, model AntitheftCheck) — szkielet kontroli integralności tagów NFC; obecnie zwraca status OK, gotowy do rozbudowy.

Demo Tryb testowy płatności (MockPaymentController, MockProvider) — pozwala przejść cały przepływ bez realnej bramki PayU (środowiska testowe).

Pola sklepu i taga
ModelPola
Shopname, slug, base_url, api_key, payment_mode (classic/app2app)
Tagshop_id, tag_uid, target_url, label, active
Leadname, email, phone, company, message
15.

Panel: logowanie i dashboard

Aktywny

Panel sklepu (/panel) chroniony jest logowaniem (middleware auth). Po zalogowaniu dashboard pokazuje kondycję sprzedaży. Łącznie panel udostępnia ponad 50 tras.

Logowanie (LoginController)
  • show()GET /panel/login — formularz (przekierowanie do dashboardu, jeśli zalogowany).
  • login()POST /panel/login — walidacja e-mail + hasło, sesja.
  • logout()POST /panel/logout — wylogowanie, unieważnienie sesji, regeneracja CSRF.
Dashboard (DashboardController)

GET /panel (nazwa panel.dashboard). Pokazuje metryki łączne i z 30 dni oraz wykres dziennej sprzedaży, korzystając z ShopStatsService:

  • otwarcia tagów NFC (tag_open), wyświetlenia (page_view), kliknięcia „Kup” (buy_click)
  • opłacone zamówienia, przychód łączny (suma amount), konwersja % (zakupy / otwarcia)
  • seria dziennych zakupów (30 dni) do wykresu słupkowego
Tabele
userseventsorders
16.

Panel: Parafie + CRM

Aktywny

Najbogatsza sekcja panelu (ProductController): pełny CRUD parafii wraz z galerią zdjęć, opisem WYSIWYG, statystykami oraz wbudowanym CRM (statusy leada, notatki, przypisany handlowiec). Publikacja parafii sterowana jest statusem — ustawienie „aktywna” włącza widoczność publiczną.

Metody
  • index()Lista parafii z filtrem statusu i wyszukiwarką (nazwa/miasto/województwo) + liczniki statusów.
  • create() / store()Dodawanie parafii.
  • edit() / update()Edycja (z notatkami CRM).
  • toggle()Włącz/wyłącz widoczność.
  • deleteImage()Usunięcie zdjęcia z galerii.
  • stats()Statystyki konwersji parafii (eventy, wykres).
  • status()Szybka zmiana statusu CRM (AJAX) — steruje publikacją.
  • storeNote() / destroyNote()Notatki CRM (AJAX, JSON).
  • uploadEditorImage()Upload obrazu z edytora WYSIWYG (zwraca URL).
Statusy CRM (lejek wdrożenia)

kontakttestwdrożenieaktywna (każdy z własnym kolorem plakietki; „aktywna” = publikacja).

Notatki CRM (ParishNote → parish_notes)
PoleOpis
product_idParafia, której dotyczy
typekontakt · telefon · mail · spotkanie · inne
bodyTreść notatki
authorAutor
Galeria (ProductImage → product_images)

Pola: product_id, path, sort. Wielokrotny upload, sortowanie, usuwanie pojedynczych zdjęć.

Tabele
productsparish_notesproduct_imagessalespeopleeventsorders
Pliki
Panel/ProductController.php · views/…/panel/products/{index,form,stats}.blade.php · ShopStatsService.php
17.

Panel: Kategorie

Aktywny

Drzewo kategorii „Kogo wspieramy?” (CategoryController) sterujące sekcjami na stronie głównej. Obsługuje zagnieżdżanie (parent/child), zmianę kolejności i ikony, z ochroną przed cyklami.

Metody
  • index()Spłaszczone drzewo z wcięciami (rekurencja).
  • store() / update() / destroy()CRUD; usunięcie przenosi dzieci na poziom wyższy.
  • reorder()Zamiana kolejności (swap pozycji) w górę/dół.
Pola (Category → categories)
PoleOpis
parent_idKategoria nadrzędna (zagnieżdżenie)
label / label_html / label_textEtykiety (tekst + wersja HTML)
slugIdentyfikator URL (auto z nazwy)
introOpis sekcji
iconIkona (upload)
sourcenone (pusta) lub parishes (lista parafii)
positionKolejność w drzewie
activeWidoczność
Tabele
categories
18.

Panel: Handlowcy

Aktywny

CRUD handlowców (SalespersonController) z przypisaniem obsługiwanych województw i licznikiem przypisanych parafii. Integruje się z CRM parafii i mapą pokrycia.

Pola (Salesperson → salespeople)
PoleOpis
nameImię i nazwisko
email, phoneKontakt (opcjonalne)
voivodeshipsTablica obsługiwanych województw (JSON, 16 do wyboru)
activeCzy aktywny
Tabele
salespeopleproductspotential_parishes
19.

Panel: Parafie do obdzwonienia + mapa pokrycia

Aktywny

Lista leadów parafialnych (PotentialParishController) zaimportowanych z OpenStreetMap, z lejkiem obdzwaniania i interaktywną mapą pokrycia (Leaflet). Filtry: województwo, status, handlowiec, obecność telefonu. Paginacja po 50.

Metody
  • index()Lista z filtrami i paginacją; liczniki per status.
  • updateStatus()Zmiana statusu/handlowca/notatki/telefonu (AJAX); ustawia called_at przy pierwszym kontakcie.
  • coverageData()Zwraca punkty (JSON) do mapy — tylko rekordy ze współrzędnymi, z kolorem wg statusu.
Mapa pokrycia

GET /panel/coverage — mapa Leaflet z klastrowaniem markerów, licznikami per województwo i status, popupami (nazwa, miasto, telefon, status, handlowiec) i filtrami przeładowującymi dane AJAX-em.

Lejek obdzwaniania (status)

nowado_obdzwonieniazadzwonionozainteresowanadodana / odrzucona.

Pola (PotentialParish → potential_parishes)
PoleOpis
name, city, address, voivodeshipDane adresowe
denominationWyznanie
phoneTelefon
lat, lonWspółrzędne (mapa)
statusStatus leada (lejek)
salesperson_idProwadzący handlowiec
noteNotatka
called_atData pierwszego kontaktu
Tabele
potential_parishessalespeople
20.

Panel: Sklep — produkty NFC

Aktywny

CRUD produktów sklepu donacyjnego (ShopItemController): minimalna kwota, tag NFC, produkt domyślny (tylko jeden), upload grafiki, kolejność i aktywność. Cena wpisywana w złotych, zapisywana w groszach.

Metody
  • index()Lista produktów (sort, miniatura, min. kwota, tag, domyślny, status).
  • store() / update()Zapis; ustawienie „domyślny” zdejmuje flagę z pozostałych produktów.
  • toggle() / destroy()Aktywacja / usunięcie.
Pola formularza

nazwa, slug (auto), min_amount_pln (→ grosze), tag_uid, kolejność, grafika (do 5 MB), is_default, active.

Tabele
shop_items
21.

Panel: Praca — stanowiska

Aktywny

CRUD ofert pracy (PositionController) z opisem WYSIWYG, lokalizacją, typem zatrudnienia, kolejnością i licznikiem aplikacji per oferta. Oferty pojawiają się publicznie na /praca.

Metody
  • index()Lista stanowisk z liczbą aplikacji.
  • store() / update() / toggle() / destroy()Pełny CRUD + włącz/wyłącz.
Tabele
job_positionsjob_applications
22.

Panel: Aplikacje rekrutacyjne

Aktywny

Skrzynka zgłoszeń rekrutacyjnych (ApplicationController) z pobieraniem CV z prywatnego dysku, statusami i filtrami. Licznik nieprzeczytanych widoczny w nawigacji panelu.

Metody
  • index()Skrzynka (najnowsze na górze), filtry po ofercie i statusie, liczniki.
  • show()Szczegóły zgłoszenia; oznacza jako przeczytane.
  • cv()Pobranie pliku CV z prywatnego dysku.
  • updateStatus()Zmiana statusu: do sprawdzenia / zaakceptowany / odrzucony.
  • destroy()Usunięcie zgłoszenia wraz z plikiem CV.
Tabele
job_applicationsjob_positions
23.

Panel: Wiadomości

Aktywny

Skrzynka wiadomości z formularza kontaktowego (MessageController) z licznikiem nieprzeczytanych.

Metody
  • index()Lista wiadomości (najnowsze na górze).
  • show()Szczegóły; oznacza jako przeczytane.
  • destroy()Usunięcie.
Tabele
contact_messages
24.

Eventy i analityka

Aktywny

System rejestruje każde istotne zdarzenie — od zbliżenia telefonu po finalną wpłatę — co zasila statystyki w panelu i pozwala mierzyć konwersję. Zdarzenia trzymane są w tabeli events (z indeksem po typie i dacie), a agregacją zajmuje się ShopStatsService.

Typy zdarzeń (sklep)
TypKiedy
tag_openZbliżenie telefonu do tagu NFC
page_viewWyświetlenie strony parafii / produktu
buy_clickKliknięcie „Wesprzyj / Kup”
purchasePotwierdzona wpłata
Metody ShopStatsService
  • summary($productId, $days)Agregaty: otwarcia, wyświetlenia, kliknięcia, wpłaty, przychód, konwersja %.
  • dailyPurchases($productId, $days)Seria dziennych zakupów do wykresu.
  • formatPln($grosze)Formatowanie kwoty (grosze → zł).
Tabele
eventsorders
25.

Pełny schemat bazy danych

Aktywny

18 migracji budujących ~25 tabel w trzech bazach (multi-tenant). Poniżej wszystkie tabele z kluczowymi kolumnami.

Baza nfc_pay (bramka)
TabelaKluczowe kolumny
shopsname, slug, base_url, api_key, payment_mode
tagsshop_id, tag_uid, target_url, label, active
transactionsid (uuid), shop_id, tag_id, product_external_id, amount, currency, status, mode, provider_order_id, paid_at
eventsshop_id, tag_id, transaction_id, type, created_at
leadsname, email, phone, company, message
antitheft_checksshop_id, status, foreign_tags_found, checked_at
Baza nfc_shop1 (Taca / church) — także nfc_shop2 (products)
TabelaKluczowe kolumny
productsname, city, purpose, slug, description_html, price, tag_uid, main_image, active, phone, website, voivodeship, status, salesperson_id
product_imagesproduct_id, path, sort
ordersid (uuid), product_id (nullable), transaction_id, amount, status, paid_at
eventsproduct_id, type, created_at
shop_itemsslug, name, image, min_amount, is_default, tag_uid, active, sort
categoriesparent_id, slug, label, label_html, intro, icon, source, position, active
salespeoplename, email, phone, voivodeships (JSON), active
potential_parishesname, city, address, voivodeship, denomination, phone, lat, lon, status, salesperson_id, note, called_at
parish_notesproduct_id, type, body, author
job_positionstitle, location, employment_type, description_html, active, sort
job_applicationsjob_position_id, name, email, phone, message, cv_path, cv_original_name, is_read, status
contact_messagesname, email, phone, subject, message, is_read
Tabele systemowe (Laravel)
userscachecache_locksjobs
26.

Stack technologiczny i statystyki

Aktywny

Lekki, nowoczesny stos oparty o Laravel 12 — bez ciężkiego frontendu SPA, co przekłada się na szybkość i prostotę utrzymania.

Technologie
Laravel 12 (PHP 8.2+) MySQL / MariaDB (3 bazy) Architektura multi-tenant Blade + CSS (bez SPA) Vite 7 + Tailwind 4 PayU REST API v2.1 BLIK · pay-by-link · 3DS Leaflet (mapy) Quill (WYSIWYG) Kolejki + Joby (async) Webhooki HMAC-SHA256 Open Graph / SEO Deploy: rsync + SSL
Statystyki kodu
WarstwaLiczby
Moduły aplikacji2 (Gateway · Storefront)
Kontrolery32 (15 Gateway + 17 Storefront)
Modele danych19
Serwisy i dostawcy płatnościGatewayClient, ShopStatsService, TransactionService, StatsService + PayUProvider, MockProvider
Migracje / tabele18 / ~25
Widoki Blade69
Trasy (web · API · webhooki)109
Arkusze stylów CSS6
Bazy danych3 (multi-tenant)
Linie kodu (PHP + Blade + CSS)~13 500

Dokument wygenerowany automatycznie na podstawie analizy kodu źródłowego. Moduły oznaczone „Aktywny” działają na produkcji please-support-me.com; „Demo” są zbudowane i działają w trybie pokazowym/testowym.