Tryb demonstracyjny — płatności w trybie testowym Stripe (karta 4242 4242 4242 4242).

Spareload — giełda usług transportowych · Prospekt projektu

Część II: Założenia techniczne i stos technologiczny

Wersja robocza 0.1 · 14 listopada 2026


1. Stos technologiczny

Warstwa Technologia Uzasadnienie
Backend Python 3.14 + Django 6.1 Dojrzały ORM, transakcje, panel administracyjny, i18n, bezpieczeństwo „z pudełka”.
Baza danych PostgreSQL 18 Transakcje ACID, blokady wierszy (SELECT … FOR UPDATE) przy operacjach finansowych.
Geo MVP: haversine w Pythonie → PostGIS (GeoDjango) Dopasowanie tras w promieniu i w korytarzu trasy.
Frontend (MVP) Szablony Django + Bootstrap 5 Najszybsza droga do czytelnego UI; bez osobnego builda JS.
Frontend (docelowo) HTMX lub React/Next + Django REST Framework / Ninja API dla aplikacji mobilnej kierowcy.
Płatności Stripe Connect (manual capture, separate charges & transfers) Escrow bez licencji KNF; KYB przewoźników po stronie Stripe.
Rejestry VIES (REST, bez klucza) + GUS BIR 1.1 (SOAP, klucz) Weryfikacja VAT i dane firm przy rejestracji.
Zadania w tle Celery + Redis (MVP: usługa scheduler uruchamia run_automations co 15 min) Webhooki Stripe, auto-potwierdzenia, wygasanie dokumentów, przypomnienia.
Pliki Lokalny wolumen → S3-compatible (np. Scaleway/OVH, region UE) Zdjęcia CMR/POD; dane w UE.
Uruchomienie Docker + docker compose Identyczne środowisko dev/prod.
Produkcja Gunicorn + Nginx (host, certbot), zarządzany PostgreSQL, Sentry Standard.

2. Architektura aplikacji

config/        ustawienia Django, routing główny
accounts/      User (własny model), Company, CompanyDocument (weryfikacja KYB)
market/        City, EmptyRun (pusty przebieg), CargoRequest (zlecenie ładunku),
               Offer (oferta przewoźnika), Booking (zlecenie przewozu),
               BookingEvent (dziennik), Evidence (dowody), Dispute (reklamacja)
               services.py — maszyna stanów zlecenia (jedyne miejsce zmian statusu)
               geo.py      — odległości, objazd, dopasowanie tras
payments/      PaymentHold (blokady/obciążenia), Payout (transfery/zwroty)
               gateway.py  — abstrakcja bramki: FakeGateway (demo) / StripeGateway
templates/     UI (Bootstrap 5)
docs/          prospekt + szkice regulaminów

Zasada: widoki są cienkie. Każda zmiana statusu zlecenia i każda operacja pieniężna przechodzi przez market/services.py. Funkcje wykonują się w transaction.atomic() z blokadą wiersza zlecenia, zapisują BookingEvent i wołają bramkę płatności przez interfejs. Dzięki temu Stripe można podmienić bez dotykania widoków.

3. Model domenowy

Company (SHIPPER | CARRIER | PLATFORM)
 ├─ User (wielu użytkowników na firmę)
 ├─ CompanyDocument (LICENSE, EU_LICENSE, OCP, REGISTRY) + valid_until + status
 │
 ├─ EmptyRun (przewoźnik)        origin → destination, okno dat, pojazd, ładowność,
 │                                stawka EUR/km, max objazd
 ├─ CargoRequest (zleceniodawca) origin → destination, okno dat, towar, budżet
 │    └─ Offer (przewoźnik)      cena, opcjonalnie powiązany EmptyRun
 │
 └─ Booking  ◄── z EmptyRun (rezerwacja kursu) lub z Offer (akceptacja oferty)
      ├─ PaymentHold   CLIENT_PAYMENT | CARRIER_DEPOSIT
      ├─ Payout        CARRIER_PAYOUT | PLATFORM_FEE | REFUND | PENALTY
      ├─ Evidence      PICKUP | DELIVERY | DISPUTE (plik, notatka, GPS, czas)
      ├─ Dispute       powód, roszczenie, odpowiedź przewoźnika, rozstrzygnięcie
      └─ BookingEvent  pełny dziennik audytowy

4. Maszyna stanów zlecenia (Booking)

                    ┌─────────────── odrzucenie ─────────► REJECTED
                    │
 [rezerwacja] ─► PENDING_CARRIER ── anulowanie zleceniodawcy ─► CANCELLED
                    │ potwierdzenie (blokada kaucji)
                    ▼
                 CONFIRMED ──── anulowanie przez przewoźnika ────► FAILED
                    │   └── reklamacja „niepodjęcie ładunku” ─┐    (kaucja → zleceniodawca)
                    │ odbiór (dowód)                          │
                    ▼                                         │
                 IN_TRANSIT                                   │
                    │ dostawa (dowód POD wymagany)            │
                    ▼                                         ▼
                 DELIVERED ── reklamacja (≤48 h) ────────► DISPUTED
                    │                                         │ uznanie przez przewoźnika
                    │ potwierdzenie / auto po 48 h            │ lub decyzja operatora
                    ▼                                         ▼
                 COMPLETED  ◄──────── rozliczenie ──────── COMPLETED (częściowy zwrot)
                                                           lub FAILED (pełny zwrot + kara)

Rozliczenie: jedna funkcja settle(booking, refund, forfeit_deposit)

  • paid = cena − refund
  • obciążenie klienta: paid (częściowe obciążenie autoryzacji) lub zwrot refund (tryb obciąż i wstrzymaj),
  • prowizja platformy: 5% × paid,
  • transfer do przewoźnika: paid − prowizja,
  • kaucja: zwolnienie albo obciążenie i przekazanie zleceniodawcy (kara umowna).

5. Abstrakcja płatności

class PaymentGateway(Protocol):
    def authorize(hold) -> ref          # blokada (manual capture) lub obciążenie
    def capture(hold, amount)           # obciążenie (częściowe dopuszczalne)
    def release(hold)                   # anulowanie autoryzacji
    def refund(hold, amount)            # zwrot (tryb obciąż-i-wstrzymaj)
    def transfer(payout) -> ref         # transfer na connected account
  • FakeGateway — tryb demo: zapisuje operacje w bazie bez prawdziwych pieniędzy.
  • StripeGateway — zaimplementowana (tryb testowy), parametry wywołań walidowane testem na stripe-mock:
  • płatność zleceniodawcy przez Stripe Checkout (stan zlecenia „Oczekuje na płatność” do czasu powrotu lub webhooka),
  • konto wypłat przewoźnika: Express, onboarding przez AccountLink,
  • karta przewoźnika do kaucji: Checkout mode=setup, kaucja jako PaymentIntent off-session,
  • PaymentIntent(capture_method="manual") dla autoryzacji,
  • PaymentIntent.capture(amount_to_capture=…) dla częściowego obciążenia,
  • Transfer(destination=acct_…, transfer_group=booking.reference) dla wypłat,
  • Refund w trybie obciąż-i-wstrzymaj.
  • Tryb wybierany automatycznie: ≤ 7 dni do załadunku → AUTHORIZE, > 7 dni → CHARGE_AND_HOLD.
  • Webhook /stripe/webhook/: checkout.session.completed, account.updated, payment_intent.canceled. Obsługa jest idempotentna (powrót z Checkout i webhook mogą przyjść oba).
  • Transfer do odbiorcy bez aktywnego konta Stripe dostaje status „czeka” i wykonuje się automatycznie po aktywacji konta.
  • Do zrobienia: charge.dispute.* (chargebacki), odnawianie autoryzacji wygasających po 7 dniach.

6. Dopasowanie tras (matching)

MVP liczy odległości metodą haversine × współczynnik drogowy 1,25:

objazd = d(A, C) + d(C, D) + d(D, B) − d(A, B)
  A→B  pusty przebieg przewoźnika
  C→D  ładunek zleceniodawcy

Oferta pasuje, gdy objazd ≤ max_detour_km przewoźnika i okna dat się pokrywają. Wyniki sortowane są rosnąco po objeździe. Parametry rankingu są jawne (wymóg P2B).

Etap 2: PostGIS + routing drogowy (OSRM / GraphHopper, self-hosted), korytarz jako bufor wokół geometrii trasy (ST_DWithin), indeksy GiST.

7. Bezpieczeństwo

  • Uprawnienia sprawdzane na poziomie obiektu: każdy widok zlecenia weryfikuje, czy użytkownik należy do firmy-strony.
  • Wszystkie zmiany stanu tylko przez POST + CSRF.
  • Operacje pieniężne: select_for_update, idempotencja (klucze idempotencji Stripe = booking.reference + operacja).
  • Pliki dowodowe: walidacja typu i rozmiaru (≤ 10 MB), w produkcji serwowane przez podpisane URL-e.
  • Docelowo: 2FA dla kont firmowych, logi audytowe operatora.

8. Konta demo (tryb DEMO_MODE=1)

Hasło wszystkich kont: demo12345. Na stronie logowania są przyciski szybkiego logowania.

Login Rola Firma
klient1 zleceniodawca Meble Kowalski sp. z o.o. (Wrocław)
klient2 zleceniodawca Berliner Feinkost GmbH (Berlin)
klient3 zleceniodawca AgroPol S.A. (Poznań)
abc przewoźnik ABC Transport sp. z o.o. (Warszawa) — zweryfikowany
rhone przewoźnik Rhône Logistique SARL (Lyon) — zweryfikowany
brno przewoźnik Morava Cargo s.r.o. (Brno) — zweryfikowany
nowy przewoźnik Nowy Trans (Łódź) — niezweryfikowany, wygasłe OCP
operator operator platformy panel sporów i weryfikacji + panel admina (ADMIN_URL)

9. Plan rozwoju (roadmap)

Etap Zakres
0 — MVP demo (ten etap) Modele, maszyna stanów, FakeGateway, UI zleceniodawcy/przewoźnika/operatora, dane demo, Docker.
1 — Pilot ✅ Stripe Connect (test mode), ✅ rejestracja firm + VIES + GUS, ✅ wdrożenie produkcyjne (Docker + nginx). Dalej: e-mail, S3, PostGIS.
2 — Produkcja Webhooki Stripe, e-CMR, i18n (PL/EN/DE/FR), routing OSRM, monitoring, 2FA.
3 — Skala API dla TMS, aplikacja mobilna kierowcy, raporty CO₂/ESG, abonamenty.