Dziennik
Technologia2 min czytania

Jak wdrażamy funkcje dotykające pieniędzy, nie zmieniając nikomu ceny przez przypadek

Błąd w cenie to taki błąd, którego nie da się cofnąć, gdy karta już została obciążona. Oto dyscyplina, dzięki której dodajemy do rezerwacji rabaty, ceny per osoba i promocje, a żadna cena nie drgnie, dopóki sami tego nie zechcemy.

ZIZespół inżynierów Bookatu

Większość błędów kosztuje Cię przeprosiny. Błąd w cenie kosztuje zwrot, chargeback i klienta, który już nie wierzy liczbie na ekranie. Stawki są bardzo nierówne, więc proces wokół wszystkiego, co dotyka pieniędzy, jest u nas świadomie wolniejszy niż reszta pracy. Tak to wygląda, na przykładzie świeżej funkcji: promocji na całą kategorię, dzięki której salon przecenia wszystkie zabiegi na twarz naraz.

REBUILT ON THE SERVER, EVERY BOOKINGBase priceStaff priceCategory saleBest couponCharged
Cena każdej rezerwacji powstaje w tej samej ustalonej kolejności, a cała suma jest przeliczana na serwerze od nowa.

Domyślnie wyłączone, na produkcji, nic nie robi

Pierwsza linia funkcji to flaga. Czyta zmienną środowiskową i domyślnie jest wyłączona. To znaczy, że możemy scalić i wdrożyć kod długo przed tym, zanim cokolwiek zrobi, a każda cena zostaje co do bajtu taka sama jak dzień wcześniej. Nie ma huczneg premiery, o którą trzeba się denerwować, bo start to osobny, nudny krok, który robimy, gdy jesteśmy pewni.

ts
export const FEATURE_LIVE =  process.env.FEATURE_LIVE === "true"; // Read at the one place it matters. When the flag is off, the branch// is skipped entirely: no extra query, no price change, nothing.if (FEATURE_LIVE) {  const pct = await activeCategorySalePercent(orgId, "service", service.category);  if (pct > 0) fullPriceCents = applyCategorySaleCents(fullPriceCents, pct);}
1Behind a flagoff by default2Merge & deploystill off3Audit passes0 money bugs4Flip the flagthe go-liveLive
Start to osobny, nudny krok. Kod trafia na produkcję długo przedtem, zanim cokolwiek robi.

Jedna funkcja, jedno zadanie

Arytmetyka rabatu mieszka w jednej czystej funkcji. Bierze grosze i procent, zwraca grosze. Nie ma bazy danych, nie ma żądania, nie ma zegara, więc każdy jej skrajny przypadek testuje się trywialnie w izolacji: promocja zerowa, promocja normalna, ograniczenie u góry, wartość ujemna, która nigdy nie powinna wystąpić. Kiedy matematyka to jedna mała funkcja, którą mieścisz w głowie, przestajesz przy niej zgadywać.

ts
export function applyCategorySaleCents(  baseCents: number,  salePercent: number | null | undefined,): number {  const base = cents(baseCents);  const pct = Math.max(0, Math.min(100, Math.round(salePercent ?? 0)));  if (pct === 0) return base;  return cents((base * (100 - pct)) / 100);}

Zawsze przeliczaj na serwerze

Przeglądarka pokazuje cenę, żeby klient wiedział, ile zapłaci, ale ta liczba jest wyłącznie wyświetlaczem. Kiedy przychodzi rezerwacja, serwer wczytuje prawdziwą usługę, ustala cenę danej osoby z zespołu, nakłada aktywną promocję na kategorię i dopiero wtedy liczy rabat, wszystko z danych, które sam pobrał. Jeśli ktoś po drodze podmienił żądanie, nic to nie zmienia, bo cena, którą obciążamy kartę, nigdy nie była ceną wysłaną przez przeglądarkę. To najważniejszy nawyk w kodzie od pieniędzy: granicą zaufania jest serwer i to serwer liczy sumę.

BROWSERShows a price so the clientknows what they will pay.A display only. Never trusted.requestSERVER · SOURCE OF TRUTH1 · Load the real service2 · Resolve staff + sale price3 · Apply the best coupon4 · Charge exactly that
Liczba, którą pokazuje przeglądarka, to tylko wyświetlacz. Serwer pobiera własne dane i liczy sumę jeszcze raz.

Spróbuj to złamać, zanim zrobi to klient

Zielone testy mówią Ci, że przypadki, o których pomyślałeś, działają. Nie mówią nic o tym, który przegapiłeś. Dlatego zanim flaga cenowa zostanie włączona, robimy wrogie przejście, którego jedynym zadaniem jest znaleźć sposób na pomylenie pieniędzy. Osobne testy biorą to z różnych stron: jeden szuka jakiejkolwiek ścieżki, która zawyża rachunek klienta, drugi takiej, która okrada firmę, trzeci sprawdza, czy przy wyłączonej fladze funkcja naprawdę jest martwa, a czwarty czepia się zaokrągleń i dryfu na granicach. Promocja nałożona na kupon przy zaliczce to dokładnie miejsce, w którym chowa się ułamek grosza, więc tam patrzymy najuważniej.

New pricing featureOvercharges?Loses money?Leaks when off?Rounding drift?Clean sweep → GO
Czterech sceptyków, każdy z innym sposobem na pomyłkę. Tylko czysty przebieg to GO.

Nic z tego nie jest sprytne. To flaga, mała funkcja, serwer, który sam liczy, i nawyk psucia własnej pracy, zanim trafi do ludzi. Razem wzięte, robią różnicę między dodaniem funkcji dotykającej pieniędzy a nieprzespanymi nocami z jej powodu.

feature flagsbezpieczny kod finansowyprojektowanie silnika cenwalidacja po stronie serweragranica zaufania
Gotowy, żeby to wykorzystać?

Bookatu daje Ci firmową stronę rezerwacji, zadatki, karnety, karty podarunkowe i przypomnienia, przy 0% prowizji od Twoich rezerwacji.

Zacznij za darmo