Schemat bazy danych planów i uprawnień dla upgrade'ów i dodatków
Schemat bazy danych planów i uprawnień wspierający upgrady, dodatki, triale i cofnięcia bez twardo zakodowanych reguł — za pomocą jasnych tabel i zakresów czasowych.

Dlaczego plany i funkcje szybko się komplikują
Na stronie cenowej plany wyglądają prosto: Basic, Pro, Enterprise. Bałagan zaczyna się w momencie, gdy próbujesz przekształcić te nazwy w rzeczywiste reguły dostępu w aplikacji.
Twardo zakodowane sprawdzenia funkcji (np. if plan = Pro then allow X) działają w pierwszej wersji. Potem zmieniają się ceny. Funkcja przechodzi z Pro do Basic, pojawia się nowy dodatek albo oferta sprzedażowa zawiera niestandardowy pakiet. Nagle ta sama reguła jest skopiowana w API, UI, aplikacjach mobilnych i w zadaniach background. Zmieniasz jedno miejsce i zapominasz inne. Użytkownicy to zauważają.
Drugi problem to czas. Subskrypcje nie są etykietą statyczną; zmieniają się w trakcie okresu. Ktoś ulepsza plan dziś, potem degraduje miesiąc później, wstrzymuje lub anuluje z pozostałym opłaconym czasem. Jeśli w bazie przechowujesz tylko „aktualny plan”, tracisz oś czasu i nie odpowiesz później na podstawowe pytania: co mieli w zeszły wtorek? Dlaczego support zatwierdził zwrot?
Dodatki pogarszają sytuację, bo przecinają plany. Dodatek może odblokować dodatkowe miejsca, usunąć limit lub włączyć konkretną funkcję. Ludzie mogą kupić go na dowolnym planie, usunąć później lub zachować po downgrade. Jeśli reguła jest osadzona w kodzie, kończysz z rosnącą stertą wyjątków.
Oto sytuacje, które zwykle łamią naiwny projekt:
- Upgrade w trakcie okresu: dostęp powinien zmienić się natychmiast, rozliczenie proporcjonalne może mieć inne zasady.
- Zaplanowany downgrade: dostęp może pozostać „wyższy” do końca opłaconego okresu.
- Grandfathering: starsi klienci zachowują funkcję, której nowi klienci nie dostają.
- Niestandardowe oferty: jedno konto ma Funkcję A, ale nie ma Funkcji B, mimo że używa tej samej nazwy planu.
- Potrzeby audytowe: support, dział finansów lub zgodności pyta „co dokładnie było włączone i kiedy?”
Cel jest prosty: elastyczny model kontroli dostępu, który zmienia się wraz z cenami, bez przepisywania logiki biznesowej za każdym razem. Chcesz jedno miejsce, by zapytać „czy oni mogą to zrobić?” i bazę danych tłumaczącą odpowiedź.
Na końcu artykułu będziesz miał wzorzec schematu, który możesz skopiować: plany i dodatki stają się wejściami, a uprawnienia (entitlements) jednym źródłem prawdy dotyczących dostępu do funkcji. To samo podejście pasuje też do narzędzi no-code jak AppMaster, bo reguły są w danych i można je spójnie odpytywać z backendu, aplikacji web i mobilnych.
Kluczowe pojęcia: plan, dodatek, uprawnienie i dostęp
Wiele problemów z subskrypcjami zaczyna się od kłopotów ze słownictwem. Jeśli każdy używa tego samego słowa na różne rzeczy, schemat zamienia się w przypadki specjalne.
Warto rozdzielać te pojęcia w schemacie:
- Plan: domyślny pakiet, który ktoś otrzymuje przy subskrypcji (np. Basic lub Pro). Plan zwykle ustala bazowe limity i dołączone funkcje.
- Dodatek: opcjonalny zakup, który zmienia bazę (np. „dodatkowe miejsca” lub „zaawansowane raportowanie”). Dodatki powinny być dołączalne i usuwalne bez zmiany planu.
- Uprawnienie (entitlement): ostateczne, obliczone „co mają teraz” po połączeniu planu + dodatków + nadpisań. To właśnie twoja aplikacja powinna odpytywać.
- Pozwolenie (permission) lub capability: konkretna akcja, którą ktoś może wykonać (np. „eksport danych” lub „zarządzanie rozliczeniami”). Pozwolenia często zależą od roli plus uprawnień.
- Dostęp: rzeczywisty rezultat stosowania reguł (ekran pokazuje lub ukrywa funkcję, wywołanie API jest dozwolone lub zablokowane, stosowany jest limit).
Flagi funkcji (feature flags) są powiązane, ale inne. Feature flag to zwykle włącznik produktu (rollouty, eksperymenty, wyłączanie funkcji w trakcie incydentu). Entitlement to dostęp specyficzny dla klienta, oparty na tym, za co płaci lub co mu przyznano. Używaj flag do zmian zachowania dla grup bez dotykania rozliczeń. Używaj uprawnień, gdy dostęp musi odpowiadać subskrypcji, fakturze lub umowie.
Zakres (scope) to kolejny źródło zamieszania. Rozdzielmy te pojęcia:
- Użytkownik: jedna osoba. Przydatne do ról (admin vs member) i limitów osobistych.
- Konto (account): podmiot opłacający. Przydatne do informacji rozliczeniowych i własności subskrypcji.
- Workspace (projekt/zespół): miejsce pracy. Wiele produktów stosuje uprawnienia tutaj (miejsca, storage, włączone moduły).
Czas ma znaczenie, bo dostęp się zmienia. Modeluj go bezpośrednio:
- Start i koniec: uprawnienie może być aktywne tylko w oknie czasowym (trial, promo, roczny kontrakt).
- Zaplanowana zmiana: ulepszenia mogą zaczynać się teraz; downgrade często zaczyna się przy najbliższym odnowieniu.
- Okresy karencji i anulowanie: możesz pozwolić na ograniczony dostęp po nieudanej płatności, ale tylko do wyraźnej daty końcowej.
Przykład: firma ma plan Pro, dodaje „Zaawansowane Raportowanie” w środku miesiąca, potem planuje downgrade do Basic na kolejny okres. Plan się zmienia później, dodatek zaczyna działać od teraz, a warstwa uprawnień jest jedynym miejscem, by zapytać: „Czy ten workspace może dziś korzystać z zaawansowanych raportów?”
Prosty rdzeń schematu dla planów i funkcji
Dobry schemat zaczyna się od rozdzielenia tego, co sprzedajesz (plany i dodatki), od tego, co ludzie mogą robić (funkcje). Jeśli jasno oddzielisz te dwie idee, upgrady i nowe dodatki staną się zmianami danych, a nie przepisywaniem kodu.
Praktyczny zestaw tabel, który działa w większości produktów subskrypcyjnych:
- products: rzecz sprzedawalna (plan bazowy, plan zespołowy, dodatek dodatkowych miejsc, dodatek priorytetowego wsparcia).
- plans: opcjonalnie, jeśli chcesz, by plany były specjalnym rodzajem produktu z polami tylko dla planów (interwał rozliczeń, publiczny porządek wyświetlania). Wiele zespołów przechowuje plany w
productsi używa kolumnyproduct_type. - features: katalog możliwości (dostęp do API, maks. projektów, eksport, SSO, kredyty SMS).
- product_features (lub
plan_features, jeśli rozdzielisz plany): tabela łącząca, mówiąca jakie funkcje należą do którego produktu, zwykle z wartością.
To w tej tabeli łączącej kryje się większość mocy. Funkcje rzadko są tylko włączone/wyłączone. Plan może zawierać max_projects = 10, a dodatek może dodać +5. Więc product_features powinno wspierać przynajmniej:
feature_value(liczba, tekst, JSON lub oddzielne kolumny)value_type(boolean, integer, enum, json)grant_mode(zastąp vs dodaj), by dodatek mógł „dodać 5 miejsc” zamiast nadpisywać bazowy limit
Modeluj dodatki także jako produkty. Jedyna różnica to sposób zakupu. Produkt planu bazowego jest „jedna sztuka naraz”, podczas gdy dodatek może pozwalać na ilość. Ale oba mapują się do funkcji w ten sam sposób. To unika wyjątków typu „jeśli dodatek X to włącz Y” rozproszonych po kodzie.
Funkcje powinny być danymi, nie stałymi w kodzie. Jeśli twardo zakodujesz sprawdzenia funkcji w wielu usługach, prędzej czy później pojawią się rozbieżności (web mówi tak, mobilny mówi nie, backend się nie zgadza). Gdy funkcje są w bazie, aplikacja zadaje jedno spójne pytanie i zmiany wdrażasz przez edycję wierszy.
Nazwy mają większe znaczenie niż się wydaje. Używaj stabilnych identyfikatorów, które nigdy się nie zmieniają, nawet jeśli zmieni się nazwa marketingowa:
feature_keynp.max_projects,sso,priority_supportproduct_codenp.plan_starter_monthly,addon_extra_seats
Oddziel etykiety wyświetlane (feature_name, product_name). Jeśli używasz AppMaster Data Designer z PostgreSQL, traktowanie tych kluczy jako pól unikalnych szybko się zwróci: możesz bezpiecznie regenerować, zachowując integracje i raportowanie.
Warstwa uprawnień: jedno miejsce, by zapytać „czy mogą?”
Większość systemów subskrypcyjnych schodzi na manowce, gdy „co kupili” jest przechowywane w jednym miejscu, a „co mogą zrobić” obliczane w pięciu różnych ścieżkach kodu. Naprawą jest warstwa uprawnień: jedna tabela (lub widok) reprezentująca efektywny dostęp dla podmiotu w punkcie czasu.
Jeżeli chcesz schematu, który przetrwa upgrady, downgrade'y, triale i jednorazowe przyznania, ta warstwa to element, który czyni wszystko przewidywalnym.
Praktyczna tabela uprawnień
Myśl o każdym wierszu jak o jednym roszczeniu: „ten podmiot ma dostęp do tej funkcji z tą wartością, od tej daty do tej daty, z tego źródła.” Typowy kształt wygląda tak:
- subject_type (np. "account", "user", "org") i subject_id
- feature_id
- value (efektywna wartość tej funkcji)
- source (skąd pochodzi: "direct", "plan", "addon", "default")
- starts_at i ends_at (nullable ends_at dla trwającego dostępu)
Wartość możesz zaimplementować na kilka sposobów: jedna kolumna tekst/JSON plus value_type, lub oddzielne kolumny jak value_bool, value_int, value_text. Trzymaj to prosto i łatwe do zapytania.
Typy wartości obejmujące większość przypadków
Funkcje nie zawsze są włączone/wyłączone. Te typy wartości zwykle pokrywają realne potrzeby rozliczeń i kontroli dostępu:
- Boolean: włączone/wyłączone ("can_export" = true)
- Kwota (quota number): limit ("seats" = 10, "api_calls" = 100000)
- Poziom (tier level): ranga ("support_tier" = 2)
- String: tryb lub wariant ("data_retention" = "90_days")
Precedencja: jak rozwiązywać konflikty
Konflikty są normalne. Użytkownik może mieć plan pozwalający na 5 miejsc, kupić dodatek dający 10 więcej i dodatkowo otrzymać manualne nadanie od supportu.
Ustal jasną zasadę i stosuj ją wszędzie:
- Nadanie bezpośrednie (direct grant) ma pierwszeństwo przed planem
- Następnie dodatki
- Potem wartości domyślne
Proste podejście to przechowywanie wszystkich kandydatów (pochodzących z planu, dodatków, nadpisań) i obliczanie ostatecznego „zwycięzcy” dla subject_id + feature_id przez sortowanie według priorytetu źródła, a potem według najnowszego starts_at.
Konkretny scenariusz: klient degraduje plan dziś, ale już zapłacił za dodatek ważny do końca miesiąca. Dzięki starts_at/ends_at w uprawnieniach, downgrade zaczyna się natychmiast dla funkcji zależnych od planu, podczas gdy dodatek pozostaje aktywny do jego ends_at. Aplikacja odpowiada „czy mogą?” jednym zapytaniem zamiast specjalnych warunków.
Subskrypcje, pozycje i dostęp ograniczony czasowo
Twój katalog planów (plany, dodatki, funkcje) to „co”. Subskrypcje to „kto ma co i kiedy”. Jeśli je rozdzielisz, upgrady i anulowania przestaną być przerażające.
Praktyczny wzorzec: jedna subskrypcja na konto i wiele pozycji subskrypcji pod nią (jedna dla planu bazowego i zero lub więcej dla dodatków). W schemacie daje to czyste miejsce do zapisywania zmian w czasie bez przepisywania reguł dostępu.
Kluczowe tabele modelujące oś czasu zakupów
Możesz to uprościć do dwóch łatwych do zapytania tabel:
- subscriptions: id, account_id, status (active, trialing, canceled, past_due), started_at, current_period_start, current_period_end, canceled_at (nullable)
- subscription_items: id, subscription_id, item_type (plan, addon), plan_id/addon_id, quantity, started_at, ends_at (nullable), source (stripe, manual, promo)
Częsta praktyka: przechowuj każdą pozycję z własnymi datami. Dzięki temu możesz przyznać dodatek tylko na 30 dni lub pozwolić, by plan trwał do końca opłaconego okresu, nawet jeśli klient anulował odnowienie.
Trzymaj rozliczenia i dostęp oddzielnie
Prorata, faktury i próby płatności to problemy rozliczeniowe. Dostęp do funkcji to problem uprawnień. Nie próbuj „obliczać dostępu” z linii faktury.
Zamiast tego niech zdarzenia rozliczeniowe aktualizują rekordy subskrypcji (np. wydłużają current_period_end, tworzą nowy wiersz subscription_item lub ustawiają ends_at). Twoja aplikacja odpowiada na pytania o dostęp z osi czasu subskrypcji (a później z warstwy uprawnień), a nie z matematyki faktur.
Zaplanowane zmiany bez niespodzianek
Upgrady i downgrade'y często zaczynają się w określonym momencie:
- Dodaj pending_plan_id i change_at w tabeli subscriptions dla pojedynczej zaplanowanej zmiany.
- Albo użyj tabeli subscription_changes (subscription_id, effective_at, from_plan_id, to_plan_id, reason), jeśli potrzebujesz historii i wielu przyszłych zmian.
To zapobiega twardemu kodowaniu reguł typu „downgrade następuje na koniec okresu” w przypadkowych miejscach kodu. Harmonogram to dane.
Gdzie pasują triale
Triale to po prostu dostęp ograniczony czasowo z innym źródłem. Dwa czyste podejścia:
- Traktuj trial jako status subskrypcji (trialing) z polami trial_start/trial_end.
- Albo utwórz pozycje/trialowe uprawnienia z started_at/ends_at i source = trial.
Jeśli budujesz to w AppMaster, te tabele dobrze mapują się do Data Designer w PostgreSQL, a daty pozwalają łatwo zapytać „co jest aktywne teraz” bez wyjątków.
Krok po kroku: wdrożenie wzorca
Dobrze zaprojektowany schemat zaczyna się jednym obietnicą: logika funkcji jest w danych, nie rozproszona po ścieżkach kodu. Aplikacja powinna zadawać jedno pytanie — „jakie są efektywne uprawnienia teraz?” — i otrzymać jasną odpowiedź.
1) Zdefiniuj funkcje ze stabilnymi kluczami
Stwórz tabelę feature ze stabilnym, czytelnym kluczem, którego nigdy nie będziesz zmieniać (nawet jeżeli zmieni się etykieta UI). Dobre klucze to export_csv, api_calls_per_month lub seats.
Dodaj typ, żeby system wiedział, jak traktować wartość: boolean (włącz/wyłącz) vs numeric (limity). Trzymaj to proste i spójne.
2) Mapuj plany i dodatki na uprawnienia
Potrzebujesz dwóch źródeł prawdy: co zawiera plan i co daje każdy dodatek.
Prosta sekwencja praktyczna:
- Umieść wszystkie funkcje w jednej tabeli
featureze stabilnymi kluczami i typem wartości. - Stwórz
planiplan_entitlement, gdzie każdy wiersz nadaje wartość funkcji (np.seats = 5,export_csv = true). - Stwórz
addoniaddon_entitlement, które nadają dodatkowe wartości (np.seats + 10,api_calls_per_month + 50000, lubpriority_support = true). - Zdecyduj, jak łączyć wartości: bool-e zwykle OR, numeryczne limity często MAX (większa wartość wygrywa), a ilości miejsc zwykle SUM.
- Zapisuj kiedy uprawnienia zaczynają i kończą, aby upgrady, anulowania i proraty nie łamały sprawdzeń dostępu.
W AppMaster możesz modelować te tabele w Data Designer (PostgreSQL) i trzymać reguły łączenia jako małą tabelę „policy” lub enum używany przez Business Process.
3) Generuj „efektywne uprawnienia”
Masz dwie opcje: obliczać przy odczycie (query i merge za każdym razem) lub generować zbuforowany snapshot, gdy coś się zmienia (zmiana planu, zakup dodatku, odnowienie, anulowanie). Dla większości aplikacji snapshoty są prostsze do rozumienia i szybsze pod obciążeniem.
Powszechne podejście to tabela account_entitlement, która przechowuje ostateczny wynik dla każdej funkcji, plus valid_from i valid_to.
4) Wymuszaj dostęp jedną funkcją
Nie rozpraszaj reguł po ekranach, endpointach i zadaniach tła. Umieść jedną funkcję w kodzie aplikacji, która odczytuje efektywne uprawnienia i decyduje.
can(account_id, feature_key, needed_value=1):
ent = get_effective_entitlement(account_id, feature_key, now)
if ent.type == "bool": return ent.value == true
if ent.type == "number": return ent.value >= needed_value
Gdy wszystko wywołuje can(...), upgrady i dodatki stają się aktualizacjami danych, a nie przepisywaniem kodu.
Przykładowy scenariusz: upgrade plus dodatek bez niespodzianek
Sześcioosobowy zespół wsparcia jest na planie Starter. Starter zawiera 3 miejsca dla agentów i 1000 SMS-ów miesięcznie. W połowie miesiąca zatrudniają 6 agentów i chcą pakiet SMS +5000. Chcesz, żeby to działało bez kodu specjalnego typu „jeśli plan = Pro to…”.
Dzień 1: start na Starter
Tworzysz subscription dla konta z okresem rozliczeniowym (np. 1 stycznia do 31 stycznia). Następnie dodajesz jedną pozycję subscription_item dla planu.
Przy checkout (lub w zadaniu nocnym) zapisujesz nadania uprawnień na ten okres:
entitlement_grant:agent_seats, value3, start1 Jan, end31 Janentitlement_grant:sms_messages, value1000, start1 Jan, end31 Jan
Aplikacja nigdy nie pyta „na jakim planie są?”. Pyta „jakie są ich efektywne uprawnienia teraz?” i otrzymuje seats = 3, SMS = 1000.
Dzień 15: upgrade do Pro i dodatek SMS
15 stycznia przechodzą na Pro (zawiera 10 miejsc i 2000 SMS). Nie edytujesz starych nadanii. Dodajesz nowe rekordy:
- Zamykasz starą pozycję planu: ustaw
subscription_item(Starter) end na15 Jan - Tworzysz nową pozycję planu:
subscription_item(Pro) start15 Jan, end31 Jan - Dodajesz pozycję dodatku:
subscription_item(SMS Pack 5000) start15 Jan, end31 Jan
Potem nadania na ten sam okres są dopisane:
entitlement_grant:agent_seats, value10, start15 Jan, end31 Janentitlement_grant:sms_messages, value2000, start15 Jan, end31 Janentitlement_grant:sms_messages, value5000, start15 Jan, end31 Jan
Co dzieje się natychmiast 15 stycznia?
- Miejsca: efektywna liczba miejsc staje się 10 (wybierasz regułę typu „weź max dla miejsc”). Mogą dodać 3 osoby tego dnia.
- SMS: efektywnie mają 7000 SMS-ów do końca okresu (wybierasz regułę „sumuj dodatki” dla paczek wiadomości).
Nie trzeba przenosić istniejącego użycia. Twoja tabela użycia dalej zlicza wysłane wiadomości; sprawdzenie uprawnienia porównuje użycie w danym okresie z aktualnym efektywnym limitem.
Dzień 25: zaplanowany downgrade, dostęp do końca okresu
25 stycznia planują downgrade do Starter od 1 lutego. Nie ruszasz styczniowych nadanii. Tworzysz przyszłe pozycje (lub przyszłe nadania) na następny okres:
subscription_item(Starter) start1 Feb, end28 Feb- Brak pozycji SMS Pack począwszy od
1 Feb
Wynik: zachowują miejsca Pro i pakiet SMS do 31 stycznia. 1 lutego ich efektywne miejsca spadają do 3 i SMS resetuje się do limitów Starter dla nowego okresu. To proste do rozumienia i ładnie mapuje się na workflow no-code w AppMaster: zmiana dat tworzy nowe wiersze, a zapytanie o uprawnienia pozostaje niezmienne.
Typowe błędy i pułapki
Większość bugów subskrypcyjnych to nie błędy rozliczeń, lecz błędy dostępu spowodowane logiką rozproszoną po produkcie. Najszybszy sposób na zepsucie schematu to odpowiadanie „czy mogą?” w pięciu różnych miejscach.
Klasyczną porażką jest twarde kodowanie reguł w UI, API i zadaniach background osobno. UI ukrywa przycisk, API zapomina zablokować endpoint, a zadanie nocne dalej działa, bo sprawdza coś innego. Kończysz z zagadkowymi raportami „działa czasami”, które trudno odtworzyć.
Inna pułapka to sprawdzanie plan_id zamiast funkcji. Wygląda to prosto (Plan A może eksportować, Plan B nie), ale rozpada się, gdy dodasz dodatek, klienta grandfathered, trial lub wyjątek enterprise. Jeśli kiedykolwiek mówisz „jeśli plan to Pro to pozwól…”, budujesz labirynt, który będziesz utrzymywać na zawsze.
Zagadnienia czasowe i anulowanie
Dostęp też „zawisa”, gdy przechowujesz tylko boolean jak has_export = true i nigdy nie dodajesz dat. Anulowania, zwroty, chargebacki i mid-cycle downgrade'y wymagają zakresów czasowych. Bez starts_at i ends_at przypadkowo przyznasz permanentny dostęp albo odbierzesz go za wcześnie.
Prosta zasada by tego uniknąć:
- Każde nadanie uprawnień musi mieć źródło (plan, dodatek, nadpisanie manualne) i zakres czasowy.
- Każda decyzja dostępu powinna używać „now między start i end” (z jasnymi regułami dla null end).
- Zadania background powinny ponownie sprawdzać uprawnienia w czasie wykonywania, a nie opierać się na stanie z wczoraj.
Nie mieszaj rozliczeń z dostępem
Zespoły też wpadają w kłopoty, mieszając rekordy rozliczeń i reguły dostępu w tej samej tabeli. Rozliczenia potrzebują faktur, podatków, proraty, identyfikatorów dostawcy i stanów retry. Dostęp potrzebuje jasnych kluczy funkcji i okien czasowych. Gdy to się miesza, migracja rozliczeń staje się awarią produktu.
Na koniec wiele systemów pomija ślad audytu. Gdy użytkownik pyta „dlaczego mogę eksportować?”, potrzebujesz odpowiedzi: „Włączone przez Dodatek X od 2026-01-01 do 2026-02-01” albo „Przyznane manualnie przez support, ticket 1842.” Bez tego support i inżynieria zgadują.
Jeśli budujesz to w AppMaster, trzymaj pola audytu w modelu Data Designer i umieść sprawdzenie „czy mogą?” jako pojedynczy Business Process używany przez web, mobile i zadania planowane.
Szybka lista kontrolna przed wdrożeniem
Zanim wdrożysz schemat, przejdź ostatni raz przez realne pytania, nie teorię. Celem jest, by dostęp był wyjaśnialny, testowalny i łatwy do zmiany.
Pytania kontrolne
Wybierz jednego użytkownika i jedną funkcję, potem spróbuj wyjaśnić wynik tak, jakbyś tłumaczył supportowi lub finansom. Jeśli potrafisz odpowiedzieć tylko „są na Pro” (albo, co gorsza, „kod tak mówi”), poczujesz ból przy pierwszym mid-cycle upgrade lub jednorazowej ofercie.
Użyj tej krótkiej listy kontrolnej:
- Czy możesz odpowiedzieć „dlaczego ten użytkownik ma dostęp?” używając tylko danych (pozycje subskrypcji, dodatki, nadpisania i okna czasowe), bez czytania kodu aplikacji.
- Czy wszystkie sprawdzenia dostępu opierają się na stabilnych kluczach funkcji (np.
feature.export_csv) zamiast nazw planów (np. „Starter” lub „Business”)? Nazwy planów się zmieniają; klucze funkcji nie powinny. - Czy uprawnienia mają jasne czasy startu i końca, łącznie z trialami, okresami karencji i zaplanowanymi anulowaniami? Jeśli brakuje czasu, downgrade'y stają się sporne.
- Czy możesz przyznać lub usunąć dostęp dla jednego klienta przez rekord nadpisania, bez rozgałęziania logiki? To pozwala obsługiwać „daj im 10 dodatkowych miejsc w tym miesiącu” bez custom code.
- Czy potrafisz przetestować upgrade i downgrade używając kilku przykładowych wierszy i uzyskać przewidywalne wyniki? Jeśli potrzebujesz skomplikowanego skryptu, model jest zbyt niejawny.
Praktyczny test: stwórz trzech użytkowników (nowy, ulepszony w połowie miesiąca, anulowany) i jeden dodatek (np. „dodatkowe miejsca” lub „zaawansowane raporty”). Potem uruchom zapytanie o dostęp dla każdego. Jeśli wyniki są oczywiste i dają się wytłumaczyć, jesteś gotów.
Jeśli używasz narzędzia takiego jak AppMaster, zachowaj tę samą zasadę: jedno miejsce (jedno zapytanie lub jeden Business Process) odpowiada za „czy mogą?”, aby każdy ekran web i mobile używał tej samej odpowiedzi.
Następne kroki: ułatw utrzymanie upgrade'ów
Najlepszy sposób, żeby utrzymać upgrade'y w ryzach, to zacząć mniejszym niż się wydaje. Wybierz kilkanaście funkcji, które naprawdę dają wartość (5–10 wystarczy) i zbuduj jedną funkcję uprawnień, która odpowiada na jedno pytanie: „Czy to konto może teraz X?” Jeśli nie potrafisz tego odpowiedzieć w jednym miejscu, upgrady zawsze będą ryzykowne.
Gdy to jedno sprawdzenie działa, traktuj ścieżki upgrade'u jako zachowanie produktu, a nie tylko zachowanie rozliczeń. Najszybszy sposób na złapanie dziwnych przypadków to napisanie zestawu testów dostępu opartych na prawdziwych ruchach klientów.
Oto praktyczne kroki, które zwykle się opłacają natychmiast:
- Zdefiniuj minimalny katalog funkcji i odwzoruj każdy plan na klarowny zestaw uprawnień.
- Dodaj dodatki jako oddzielne „pozycje”, które nadają lub rozszerzają uprawnienia, zamiast wbijać je w reguły planu.
- Napisz 5–10 testów dostępu dla typowych ścieżek (upgrade mid-cycle, downgrade przy odnowieniu, dodatek dodany i potem usunięty, trial do płatnej, okres karencji).
- Traktuj zmiany cen jako operacje na danych: aktualizuj wiersze planów, mapowania funkcji i nadania uprawnień, nie kod aplikacji.
- Przyjmij zwyczaj: każdy nowy plan lub dodatek musi mieć przynajmniej jeden nowy test potwierdzający oczekiwane zachowanie.
Jeśli używasz backendu no-code, nadal możesz ładnie zamodelować ten wzorzec. W AppMaster Data Designer dobrze się mieszczą tabele rdzeniowe (plans, features, subscriptions, subscription items, entitlements). Potem Business Process Editor może trzymać flow decyzji o dostępie (załaduj aktywne uprawnienia, zastosuj okna czasowe, zwróć allow/deny), żebyś nie musiał ręcznie rozpraszać sprawdzeń po endpointach.
Zysk widać przy następnej zmianie cen. Zamiast przepisywać reguły, edytujesz dane: funkcja przechodzi z "Pro" do dodatku, zmienia się czas trwania uprawnienia albo plan legacy zachowuje swoje stare nadania. Twoja logika dostępu pozostaje stabilna, a upgrade'y stają się kontrolowaną aktualizacją, a nie sprintem koderskim.
Jeśli chcesz szybko zweryfikować swój schemat, spróbuj zamodelować jeden upgrade i jeden dodatek end-to-end, potem uruchom te testy dostępu zanim dodasz cokolwiek więcej.


