Rozliczenia i limity w aplikacji SaaS B2B: jak zaprojektować model subskrypcyjny

  • Strona główna
  • Rozliczenia i limity w aplikacji SaaS B2B: jak zaprojektować model subskrypcyjny
Rozliczenia i limity w aplikacji SaaS B2B: jak zaprojektować model subskrypcyjny

Rozliczenia w aplikacji SaaS B2B rzadko psują się przez zły cennik. Psują się dlatego, że model rozliczeniowy powstał później niż reszta systemu i został doklejony do gotowej struktury danych na kilka tygodni przed startem sprzedaży. Projektujemy aplikacje webowe i systemy B2B od 2006 roku i ten scenariusz widzimy regularnie. Produkt działa, użytkownicy się logują, a nikt w zespole nie potrafi odpowiedzieć, czym właściwie jest jedna jednostka rozliczeniowa. No i wtedy zaczyna się droga część, bo każda odpowiedź wymaga zmian w schemacie bazy i w tych wszystkich miejscach, gdzie logika sprzedażowa zdążyła się rozlać po kontrolerach.

Model subskrypcyjny zaczyna się od jednostki rozliczeniowej, nie od cennika

Pierwsze pytanie nie brzmi „ile kosztuje plan Pro”. Brzmi „co dokładnie kupuje klient”. W B2B odpowiedzi jest kilka i w warstwie implementacji wykluczają się nawzajem. Klient może płacić za stanowisko, za konto firmowe bez względu na liczbę osób, za wolumen wykonanych operacji, za aktywną integrację albo za kombinację tych wymiarów. Każdy wariant to inny model danych, inne miejsce liczenia i inny moment, w którym system musi zareagować.

Model per-seat wygląda w kodzie banalnie. Do czasu. Bo zaraz pada pytanie, czym właściwie jest zajęte stanowisko. Zaproszony, ale nieaktywowany użytkownik? Konto zablokowane w połowie okresu? Osoba z rolą tylko do odczytu? Rozliczanie za zużycie przenosi ciężar na warstwę pomiaru: potrzebne są zdarzenia, agregacja i pewność, że nic nie policzy się dwa razy. A model hybrydowy łączy oba problemy i dorzuca trzeci, czyli zależność między pakietem bazowym a nadwyżką.

Typowy błąd? Wygląda tak. Dział sprzedaży ustala cennik patrząc na konkurencję, marketing publikuje tabelę porównawczą, a zespół techniczny dostaje zadanie „wdrożyć plany” w systemie, który nie ma pojęcia organizacji, tylko konta użytkowników. I wychodzi, że nie da się policzyć niczego per firma, bo dane wiszą przy osobie. To nie jest problem cennika. To problem architektury.

W B2B jedna organizacja to zwykle wielu użytkowników, kilka ról, a często jeszcze oddziały, które chcą osobnych faktur. Struktura powinna to przewidywać od początku, nawet jeśli pierwsi klienci mają po dwie osoby.

Limity i plany taryfowe w warstwie danych: encje, których nie da się dodać później bez migracji

Minimalny zestaw encji, który wytrzymuje kilka lat rozwoju, wygląda następująco:

  • Organizacja - właściciel danych i podmiot na fakturze, oddzielony od kont użytkowników.
  • Subskrypcja - powiązanie organizacji z planem w konkretnym okresie, z historią zmian.
  • Plan - wersjonowana definicja oferty, nie edytowalny wiersz nadpisywany przy zmianie cennika.
  • Uprawnienia planu (entitlements) - lista cech i limitów opisana danymi, nie kodem.
  • Liczniki zużycia - bieżący stan per organizacja i okres rozliczeniowy.

Wersjonowanie planów bywa pomijane i to według mnie najczęstsza przyczyna późniejszych awaryjnych migracji. Klient kupiony na cenniku sprzed roku musi działać po zmianie oferty. Bez ręcznego grzebania w bazie i bez tabeli wyjątków w arkuszu kalkulacyjnym (widziałem takie arkusze, nie polecam). Zamiast modyfikować definicję planu, tworzysz nową wersję, a istniejące subskrypcje zostają przypięte do poprzedniej. Grandfathering przestaje wtedy być problemem. Staje się naturalną konsekwencją modelu.

Klient korporacyjny prędzej czy później wynegocjuje coś poza standardem: podwyższony limit, dodatkową integrację, dłuższą retencję. Takie ustalenia trzymaj jako osobną warstwę nadpisań przypiętą do subskrypcji, a nie wpisuj w globalny plan. Inaczej po roku nikt już nie wie, czy dana wartość to standard oferty, czy relikt jednej negocjacji sprzed dziesięciu miesięcy.

Tip: definicję limitów trzymaj w danych, nie w warunkach if rozsianych po aplikacji. Sprawdzenie limitu powinno być jednym wywołaniem odpytującym uprawnienia organizacji, a nie serią porównań nazw planów. Kod z konstrukcją „jeśli plan to Pro lub Enterprise” starzeje się przy pierwszym nowym pakiecie i cicho zawodzi przy każdym kolejnym.

Migracja klientów między planami też zasługuje na zaplanowaną procedurę z podglądem skutków. Nie na skrypt pisany w nocy przed publikacją nowej oferty.

Egzekwowanie limitów: twarde, miękkie i te, które psują wdrożenie u klienta

Przekroczenie limitu można obsłużyć na trzy sposoby i każdy niesie inne konsekwencje biznesowe. Blokada twarda zatrzymuje operację i chroni koszty infrastruktury, ale potrafi zatrzymać proces po stronie klienta w najgorszym możliwym momencie. Ostrzeżenie miękkie przepuszcza działanie i informuje administratora - sprawdza się przy limitach, które nie przekładają się wprost na koszt. Przekroczenie płatne, czyli overage, wymaga precyzyjnego pomiaru i jasnej komunikacji, bo faktura wyższa od oczekiwanej to gotowa reklamacja.

Ważniejsze od samego trybu reakcji jest jednak miejsce sprawdzania limitu. Weryfikacja wyłącznie w interfejsie oznacza, że limit nie istnieje - wystarczy wywołać API bezpośrednio. Kontrola musi działać w warstwie API, w kolejce zadań przetwarzanych asynchronicznie i w zadaniach uruchamianych harmonogramem. Import w tle, generowanie raportu o świcie, integracja odpytująca system co minutę: wszystkie te ścieżki omijają ekran z ładnym komunikatem.

Przy równoległych żądaniach pojawia się klasyczny wyścig. Dwa procesy odczytują ten sam stan licznika, oba widzą wolne miejsce, oba zapisują wartość zwiększoną o jeden i limit leci w górę bez śladu w logach. Rozwiązaniem są operacje atomowe na liczniku albo agregacja wsadowa z akceptowanym marginesem - wybrana świadomie, a nie przypadkiem.

Rozdziel też dwa niezależne mechanizmy. Limity handlowe wynikają z umowy i planu taryfowego. Limity techniczne - rate limiting, maksymalny rozmiar pliku, liczba równoległych połączeń - chronią infrastrukturę i obowiązują niezależnie od tego, ile klient płaci. Mieszanie ich w jednym module prowadzi do sytuacji, w której podniesienie pakietu przypadkiem otwiera furtkę do przeciążenia systemu.

Ryzyko: twarda blokada w środku procesu biznesowego klienta to jedna z najszybszych dróg do wypowiedzenia umowy. Okres karencji i wcześniejsze powiadomienie kosztują niewiele, a ratują relację.

Pomiar zużycia: metering, który wytrzyma reklamację faktury

Zdarzenia zużycia zapisuj niezmiennie i zawsze z identyfikatorem idempotencji. Ponowienie żądania po timeoucie, retry w kolejce, powtórka webhooka - każda z tych sytuacji zdarza się w normalnej pracy systemu i żadna nie może zwiększyć naliczenia. Klucz idempotencji ma pochodzić od operacji biznesowej. Generowany losowo przy zapisie nie chroni przed niczym.

Surowe zdarzenia agreguje się do okresów rozliczeniowych, a zamknięty okres przestaje się zmieniać. Brzmi oczywiście, dopóki nie przyjdzie spóźnione zdarzenie z zeszłego miesiąca. Decyzja, czy trafia do korekty, czy do bieżącego okresu, musi zapaść na etapie projektu. Nie podczas rozmowy z klientem kwestionującym kwotę.

Osobny zestaw pułapek dotyczy czasu. Strefy czasowe organizacji i serwera bywają różne, a granica doby przesuwa naliczenie do innego okresu. Zmiana planu w połowie miesiąca wymaga rozliczenia proporcjonalnego i jednoznacznej reguły: czy niewykorzystana część starego pakietu przechodzi na nowy, czy przepada. Zmiana w dół w trakcie okresu to osobna historia, często rozwiązywana przesunięciem skutku na datę odnowienia.

Klient B2B prędzej czy później zapyta, skąd wzięła się kwota. I co wtedy? Odpowiedź „system tak policzył” kończy zaufanie. Potrzebny jest raport zużycia rozbity do poziomu pojedynczej operacji, z datą, kontekstem i eksportem. To samo narzędzie skraca obsługę zgłoszeń, bo wsparcie sprawdza dane bez angażowania programisty.

Policz też koszt własny. Przy dużym wolumenie zapisywanie każdego zdarzenia w tej samej bazie, która obsługuje aplikację, robi się realnym obciążeniem. Oddzielny magazyn danych dla zdarzeń pomiarowych, z inną strategią indeksowania i retencji, bywa po prostu tańszy niż skalowanie bazy głównej.

Integracja z płatnościami i księgowością: gdzie jest źródło prawdy

Operator płatności obsługuje transakcje, karty i część logiki subskrypcji, ale stan uprawnień klienta ma wynikać z Twojej bazy. Aplikacja pytająca zewnętrzne API o to, czy użytkownik ma dostęp do funkcji, przestaje działać przy każdej awarii dostawcy i uzależnia model danych od cudzej struktury. Zewnętrzny system jest źródłem prawdy o pieniądzach. Twój - o uprawnieniach.

Webhooki przychodzą z opóźnieniem, w złej kolejności i po kilka razy. To nie awaria, tylko normalna charakterystyka rozproszonej komunikacji. Obsługa musi być idempotentna, odporna na zdarzenie starsze niż aktualny stan i uzupełniona o okresową rekoncyliację, która porównuje stan lokalny ze stanem u operatora. Bez tego drugiego mechanizmu prędzej czy później zjawi się klient, który zapłacił, a dostępu nie dostał. Sprawdzone.

Specyfika B2B wykracza daleko poza kartę płatniczą. Przelew tradycyjny, faktura z odroczonym terminem, zamówienie roczne z płatnością z góry, dane do faktury z numerem NIP, weryfikacja kontrahenta. Sprzedaż zagraniczna dokłada VAT OSS i odwrotne obciążenie, które zmienia stawkę zależnie od statusu nabywcy. Te reguły należą do modułu fakturowego, nie do kontrolera obsługującego formularz zamówienia.

Nieudana płatność to proces, nie błąd. Sensowna sekwencja wygląda tak:

  1. Automatyczne ponowienie po kilku dniach, z powiadomieniem administratora organizacji.
  2. Okres karencji z pełnym dostępem i wyraźnym komunikatem w aplikacji.
  3. Ograniczenie funkcji zapisu przy zachowaniu odczytu i eksportu danych.
  4. Zawieszenie konta z zachowaniem danych przez ustalony czas.

Integrację z systemem fakturowym i eksport do księgowości trzymaj jako osobny, testowalny moduł z jasnym kontraktem. Zmiana biura rachunkowego albo formatu eksportu nie powinna wtedy dotykać logiki subskrypcji.

Bezpieczeństwo, izolacja danych i audyt w rozliczeniach wielofirmowych

Separacja danych między organizacjami musi być wymuszona na poziomie zapytań, a nie ukrycia elementów w interfejsie. Identyfikator organizacji powinien wchodzić do warunku filtrowania w warstwie dostępu do danych, najlepiej automatycznie, żeby pojedyncze zapomniane zapytanie nie odsłoniło cudzych faktur. Testy powinny obejmować próbę odczytu zasobu innej firmy przez podmianę identyfikatora w adresie. Najprostszy scenariusz świata i najczęściej zaniedbywany.

Uprawnienia do danych rozliczeniowych rzadko są jednolite. Administrator firmy widzi faktury i może zmieniać plan. Osoba techniczna potrzebuje statystyk zużycia i kluczy API, ale niekoniecznie kwot. Dział zakupów chce dokumentów i historii płatności bez dostępu do konfiguracji produktu. Jeden poziom „admin” dla wszystkich tych ról wystarcza dokładnie do pierwszego większego klienta.

Dziennik zdarzeń w module rozliczeń to nie luksus. Kto zmienił plan, kto podniósł limit, kiedy i na jakiej podstawie - te informacje rozstrzygają spory z klientem i porządkują pracę zespołu wsparcia. Wpisy mają być niezmienne i zawierać zarówno stan przed zmianą, jak i po niej.

Osobna kategoria to dostęp maszynowy. Klucze API i tokeny integracji też konsumują limity, więc muszą być przypisane do organizacji, mieć własne ograniczenia i dawać się unieważnić bez wyłączania całego konta. Klucz utworzony przez byłego pracownika i działający miesiącami po jego odejściu to realny problem bezpieczeństwa. Audytorzy pytają o to niemal zawsze.

Tip: przy sprzedaży do większych firm przygotuj wcześniej odpowiedzi o retencję danych rozliczeniowych i możliwość ich eksportu. Te pytania padają w ankietach bezpieczeństwa i potrafią opóźnić podpisanie umowy o tygodnie, jeśli zespół musi je dopiero implementować.

Utrzymanie, zmiany cennika i skalowanie modelu w czasie

Panel administracyjny do obsługi wyjątków zwraca się szybciej, niż się wydaje. Przedłużenie triala, korekta licznika po incydencie, indywidualny limit dla jednego klienta, ręczne odblokowanie konta po wyjaśnionej płatności - codzienność każdego SaaS. Bez narzędzia każda taka sytuacja to zgłoszenie do programisty i zapytanie SQL na produkcji. Czyli ryzyko i koszt tam, gdzie w zupełności wystarczy formularz z zapisem w dzienniku zdarzeń.

Scenariusze brzegowe pokryj testami, bo to właśnie one generują najwięcej reklamacji. Zmiana planu dokładnie w dniu odnowienia, zwrot środków, anulowanie z końcem okresu przy zachowaniu dostępu do tego momentu, ponowna aktywacja po przerwie, podwyższenie pakietu przy nierozliczonej nadwyżce. Każdy z tych przypadków dotyka kilku encji naraz i łatwo o stan niespójny, którego nikt nie zauważy przez miesiąc.

Środowisko testowe operatora płatności warto podłączyć od pierwszego dnia, razem z zestawem danych odtwarzających typowe sytuacje: odrzucona karta, płatność wymagająca dodatkowego uwierzytelnienia, zwrot częściowy. Sprawdzanie zmian w rozliczeniach na produkcji kończy się korektami faktur, których nikt nie lubi wystawiać.

Własna implementacja czy gotowy system billingowy? Rozstrzyga to złożoność cennika i koszt utrzymania. Prosty model per-seat z kilkoma planami mieści się we własnym kodzie bez wysiłku. Rozliczanie wielowymiarowego zużycia, wielu walut, faktur korygujących i umów ramowych to obszar, gdzie wyspecjalizowane narzędzie zwykle wygrywa. Policz oba warianty, zamiast decydować odruchowo.

Na koniec obserwowalność: alerty o rozjeździe między zmierzonym zużyciem a naliczeniami powinny zadziałać, zanim zgłosi to klient.

Podsumowanie: rozliczenia projektuje się razem z produktem

Kolejność decyzji jest dość stała i warto się jej trzymać. Najpierw jednostka rozliczeniowa, czyli jednoznaczna odpowiedź na pytanie, za co płaci klient. Potem model danych z organizacją, wersjonowanym planem i uprawnieniami opisanymi danymi. Następnie egzekwowanie limitów we wszystkich warstwach wykonawczych, nie tylko w interfejsie. Dalej metering odporny na powtórzenia i gotowy do obrony przed reklamacją. Na końcu integracje z płatnościami oraz księgowością i panel do obsługi wyjątków, bez którego zespół utrzymania pracuje ręcznie na bazie.

Najdroższe poprawki w tym obszarze to te, które wymagają migracji historii rozliczeniowej działającego klienta. Zmiana definicji jednostki rozliczeniowej po dwóch latach oznacza przeliczenie zdarzeń, uzgodnienie stanu z operatorem płatności, korekty dokumentów i rozmowy z klientami o kwotach, które nagle wyglądają inaczej. Koszt takiego przedsięwzięcia bywa porównywalny z budową nowego modułu, a ryzyko jest sporo wyższe, bo pracuje się na danych finansowych w ruchu.

Dobrze zaprojektowany billing daje coś, co widać dopiero po czasie: możliwość zmiany cennika bez przepisywania aplikacji. Nowy plan powstaje jako wiersz w danych, promocja jako nadpisanie na subskrypcji, a eksperyment cenowy nie wymaga wdrożenia nowej wersji systemu. To przewaga operacyjna, nie techniczna ciekawostka.

Jeśli planujesz aplikację SaaS B2B, przebudowę modelu subskrypcyjnego w działającym systemie albo integrację rozliczeń z płatnościami i księgowością - odezwij się do nas. Zaprojektujemy MVP, rozbudujemy istniejący system B2B, podłączymy integracje API, zautomatyzujemy powtarzalne procesy lub zmodernizujemy to, co już działa, ale przestało nadążać za ofertą. Jeśli w grę wchodzą też rozwiązania oparte na sztucznej inteligencji, zaplanujemy je razem z resztą architektury, a nie obok niej.

Zarezerwuj darmową konsultację

Zostaw numer telefonu lub umów spotkanie.