Rozliczenia na podstawie zużycia ze Stripe: praktyczny model danych
Rozliczenia na podstawie zużycia ze Stripe wymagają czystego przechowywania zdarzeń i rekonsyliacji. Poznaj prosty schemat, przepływ webhooków, backfille i sposoby na podwójne naliczanie.

Co tak naprawdę budujesz (i dlaczego to się psuje)
Rozliczenia na podstawie użycia brzmią prosto: zmierz, ile klient użył, pomnóż przez cenę i obciąż konto na koniec okresu. W praktyce budujesz mały system księgowy. Musi pozostać poprawny nawet gdy dane przychodzą późno, przychodzą dwukrotnie lub wcale.
Większość awarii nie zdarza się podczas checkoutu ani w dashboardzie. Dzieją się w modelu danych pomiaru. Jeśli nie potrafisz pewnie odpowiedzieć: „Które zdarzenia użycia zostały policzone dla tej faktury i dlaczego?”, w końcu będziesz naliczać za dużo, za mało albo stracisz zaufanie.
Rozliczenia na podstawie użycia zwykle zawodzą w kilku przewidywalnych scenariuszach: zdarzenia giną po awarii, retry powodują duplikaty, późne zdarzenia pojawiają się po obliczeniu sum, albo różne systemy się nie zgadzają i nie możesz pogodzić różnic.
Stripe jest świetny w kwestii cen, faktur, podatków i pobierania pieniędzy. Ale Stripe nie zna surowego użycia twojego produktu, chyba że mu je wyślesz. To wymusza decyzję o źródle prawdy: czy Stripe jest księgą, czy twoja baza danych jest księgą, którą Stripe odzwierciedla?
Dla większości zespołów najbezpieczniejszy podział to:
- Twoja baza danych jest źródłem prawdy dla surowych zdarzeń użycia i ich cyklu życia.
- Stripe jest źródłem prawdy dla tego, co faktycznie zostało zafakturowane i zapłacone.
Przykład: śledzisz „wywołania API”. Każde wywołanie generuje zdarzenie użycia z stabilnym unikatowym kluczem. W momencie fakturowania sumujesz tylko kwalifikujące się zdarzenia, które nie zostały jeszcze policzone, a potem tworzysz lub aktualizujesz pozycję na fakturze w Stripe. Jeśli retry lub webhook przyjdzie dwukrotnie, zasady idempotencji sprawiają, że duplikat jest nieszkodliwy.
Decyzje do podjęcia przed zaprojektowaniem tabel
Zanim stworzysz tabele, ustal definicje, które zadecydują, czy rozliczenia będą dały się później wyjaśnić. Większość „tajemniczych błędów faktur” wynika z niejasnych reguł, a nie złego SQL.
Zacznij od jednostki, za którą pobierasz opłatę. Wybierz coś łatwego do zmierzenia i trudnego do zakwestionowania. „Wywołania API” komplikuje retry, żądania zbiorcze i błędy. „Minuty” mają problem z nakładaniem się okien. „GB” wymaga jasnej bazy (GB vs GiB) i metody pomiaru (średnia vs szczyt).
Następnie zdefiniuj granice. System musi wiedzieć dokładnie, do którego okna należy zdarzenie. Czy użycie liczy się na godzinę, dzień, okres rozliczeniowy, czy na akcję użytkownika? Jeśli klient zmienia plan w połowie miesiąca, czy dzielisz okno, czy stosujesz jedną cenę do całego miesiąca? Te wybory decydują o tym, jak grupujesz zdarzenia i jak wyjaśniasz sumy.
Zdecyduj także, co jest właścicielem których faktów. Powszechny wzorzec ze Stripe to: twoja aplikacja jest właścicielem surowych zdarzeń i sum pochodnych, a Stripe jest właścicielem faktur i statusu płatności. To podejście działa najlepiej, gdy nie edytujesz historii w sposób cichy. Rejestruj korekty jako nowe wpisy i zachowuj oryginalny rekord.
Krótki zestaw niepodważalnych zasad pomaga utrzymać uczciwość schematu:
- Możliwość śledzenia: każda rozliczona jednostka da się powiązać z przechowywanymi zdarzeniami.
- Audytowalność: możesz odpowiedzieć „dlaczego to zostało policzone?” nawet po miesiącach.
- Odwracalność: błędy naprawiasz jawnie przez korekty.
- Idempotencja: ten sam input nie może być policzony dwa razy.
- Jasna własność: jeden system jest właścicielem każdego faktu (użycie, ceny, fakturowanie).
Przykład: jeśli rozliczasz za „wysłane wiadomości”, zdecyduj, czy retryy się liczą, czy nieudane dostawy się liczą i który znacznik czasu ma pierwszeństwo (czas klienta vs czas serwera). Spisz to, a potem zakoduj w polach zdarzeń i walidacji, a nie w czyjejś pamięci.
Prosty model danych dla zdarzeń użycia
Rozliczenia na podstawie użycia są najprostsze, gdy traktujesz użycie jak księgowość: surowe fakty są append-only, a sumy są pochodne. Ten wybór zapobiega większości sporów, bo zawsze możesz wyjaśnić, skąd wzięła się liczba.
Praktyczny punkt startowy używa pięciu głównych tabel (nazwy mogą się różnić):
- customer: wewnętrzne id klienta, Stripe customer id, status, podstawowe metadane.
- subscription: wewnętrzne id subskrypcji, Stripe subscription id, oczekiwany plan/ceny, timestample startu/końca.
- meter: to, co mierzysz (wywołania API, miejsca, GB-godziny). Zawiera stabilny klucz miernika, jednostkę i sposób agregacji (sum, max, unique).
- usage_event: jeden wiersz na zmierzoną akcję. Przechowuj customer_id, subscription_id (jeśli znane), meter_id, quantity, occurred_at (kiedy się wydarzyło), received_at (kiedy je zaingerowano), source (app, batch import, partner) i stabilny zewnętrzny klucz do deduplikacji.
- usage_aggregate: sumy pochodne, zwykle według customer + meter + bucket czasowego (dzień lub godzina) i okresu rozliczeniowego. Przechowuj zsumowaną ilość oraz wersję lub last_event_received_at, by wspierać przeliczanie.
Trzymaj usage_event niezmienny. Jeśli później odkryjesz błąd, zapisz zdarzenie kompensujące (np. -3 miejsca przy anulacji) zamiast edytować historię.
Przechowuj surowe zdarzenia dla audytu i sporów. Jeśli nie możesz przechowywać ich wiecznie, zachowaj je przynajmniej tak długo, jak wynosi okres cofania rozliczeń i okno reklamacji/zwrotów.
Trzymaj sumy pochodne oddzielnie. Agregaty są szybkie do fakturowania i dashboardów, ale są wymienne. Powinieneś móc odbudować usage_aggregate z usage_event w dowolnym momencie, także po backfillu.
Idempotencja i stany cyklu życia zdarzeń
Dane użycia są hałaśliwe. Klienci retryją żądania, kolejki dostarczają duplikaty, a webhooki Stripe mogą przychodzić poza kolejnością. Jeśli twoja baza danych nie potrafi udowodnić „to zdarzenie użycia już zostało policzone”, w końcu naliczysz dwukrotnie.
Nadaj każdemu zdarzeniu użycia stabilne, deterministyczne event_id i wymuś unikatowość na nim. Nie polegaj tylko na autoinkrementowanym id jako jedynym identyfikatorze. Dobry event_id jest odprowadzony od akcji biznesowej, np. customer_id + meter + source_record_id (lub customer_id + meter + timestamp_bucket + sequence). Jeśli ta sama akcja zostanie wysłana ponownie, wygeneruje ten sam event_id i insert stanie się bezpiecznym no-opem.
Idempotencja musi obejmować każdą ścieżkę ingestu, nie tylko publiczne API. Wywołania SDK, importy zbiorcze, zadania workerów i procesory webhooków też są retryowane. Użyj jednej reguły: jeśli input może być retryowany, potrzebuje klucza idempotencji zapisanego w bazie i sprawdzanego przed zmianą sum.
Prosty model stanów życia ułatwia bezpieczne retry i obsługę wsparcia. Trzymaj go jawnie i zapisuj powód, gdy coś nie przejdzie walidacji:
received: zapisane, jeszcze nie sprawdzonevalidated: przechodzi schemat, klienta, miernik i reguły okien czasowychposted: policzone do sum okresu rozliczeniowegorejected: trwale zignorowane (z kodem powodu)
Przykład: twój worker padł po walidacji, ale przed ustawieniem posted. Przy retryu znajduje ten sam event_id w stanie validated, a potem kontynuuje do posted bez tworzenia drugiego zdarzenia.
Dla webhooków Stripe użyj tej samej filozofii: zapisz Stripe event.id i oznacz jako przetworzony tylko raz, żeby powtórne dostawy były nieszkodliwe.
Krok po kroku: przyjmowanie zdarzeń pomiarowych end-to-end
Traktuj każde zdarzenie pomiarowe jak pieniądze: zwaliduj je, zapisz oryginał, a potem wyprowadź sumy ze źródła prawdy. To utrzymuje przewidywalność rozliczeń, gdy systemy retryją lub wysyłają dane z opóźnieniem.
Niezawodny przebieg przyjmowania
Zwaliduj każde przychodzące zdarzenie zanim dotkniesz jakichkolwiek sum. Minimum: stabilny identyfikator klienta, nazwa miernika, ilość numeryczna, znacznik czasu i unikatowy klucz zdarzenia do idempotencji.
Najpierw zapisz surowe zdarzenie, nawet jeśli planujesz agregować później. Ten surowy rekord będzie tym, co przeprocesujesz, audytujesz i użyjesz do naprawy błędów bez zgadywania.
Niezawodny przebieg wygląda tak:
- Przyjmij zdarzenie, zwaliduj wymagane pola, ujednolić jednostki (np. sekundy vs minuty).
- Wstaw surowy wiersz usage_event używając klucza zdarzenia jako ograniczenia unikatowego.
- Agreguj do bucketa (dziennie lub według okresu rozliczeniowego) sumując quantity.
- Jeśli raportujesz użycie do Stripe, zapisz, co wysłałeś (meter, quantity, period i identyfikatory odpowiedzi Stripe).
- Loguj anomalie (odrzucone zdarzenia, konwersje jednostek, późne przybycia) dla audytu.
Utrzymuj agregację powtarzalną. Powszechne podejście: wstaw surowe zdarzenie w jednej transakcji, a potem enqueueuj zadanie do aktualizacji bucketów. Jeśli zadanie zostanie uruchomione dwukrotnie, powinno wykryć, że surowe zdarzenie już zostało zastosowane.
Kiedy klient pyta, dlaczego zapłacił za 12 430 wywołań API, powinieneś móc pokazać dokładny zestaw surowych zdarzeń uwzględnionych w tym oknie rozliczeniowym.
Rekonsyliacja webhooków Stripe z twoją bazą
Webhooki to paragon za to, co Stripe faktycznie zrobił. Twoja aplikacja może tworzyć szkice i wysyłać użycie, ale stan faktury staje się rzeczywisty dopiero, gdy Stripe to potwierdzi.
Większość zespołów koncentruje się na małym zbiorze typów webhooków, które wpływają na wynik rozliczeń:
invoice.created,invoice.finalized,invoice.paid,invoice.payment_failedcustomer.subscription.created,customer.subscription.updated,customer.subscription.deletedcheckout.session.completed(jeśli startujesz subskrypcje przez Checkout)
Zapisuj każdy otrzymany webhook. Trzymaj surowy payload oraz to, co zaobserwowałeś przy jego nadejściu: Stripe event.id, event.created, wynik weryfikacji sygnatury i znacznik czasu odbioru po stronie serwera. Ta historia ma znaczenie przy debugowaniu rozbieżności lub odpowiadaniu na „dlaczego zostałem obciążony?”.
Solidny, idempotentny wzorzec rekonsyliacji wygląda tak:
- Wstaw webhook do tabeli
stripe_webhook_eventsz unikatowym ograniczeniem naevent_id. - Jeśli insert się nie powiódł, to retry. Zatrzymaj się.
- Zweryfikuj sygnaturę i zapisz pass/fail.
- Przetwórz zdarzenie, wyszukując swoje wewnętrzne rekordy po Stripe IDs (customer, subscription, invoice).
- Zastosuj zmianę stanu tylko jeśli przesuwa rekord do przodu.
Dostarczanie poza kolejnością jest normalne. Używaj reguły „maksymalny stan wygrywa” plus znaczniki czasu: nigdy nie cofnij rekordu.
Przykład: otrzymujesz invoice.paid dla invoice in_123, ale twój wewnętrzny wiersz faktury jeszcze nie istnieje. Utwórz wiersz oznaczony jako „zobaczone od Stripe”, a potem dołącz go do właściwego konta używając Stripe customer ID. To utrzymuje spójność księgi bez podwójnego przetwarzania.
Od sum użycia do pozycji na fakturze
Przekształcanie surowego użycia w pozycje na fakturze to głównie kwestia czasu i granic. Zdecyduj, czy potrzebujesz sum w czasie rzeczywistym (dashboardy, alerty wydatków) czy tylko w czasie fakturowania (faktury). Wiele zespołów robi obie rzeczy: zapisują zdarzenia ciągle, a sumy gotowe do faktury obliczają w zadaniu cyklicznym.
Wyrównaj okno użycia z okresem rozliczeniowym Stripe. Nie zgaduj miesięcy kalendarzowych. Użyj billing period z itemu subskrypcji i sumuj tylko zdarzenia, których timestampy mieszczą się w tym oknie. Przechowuj timestampy w UTC i traktuj okno rozliczeniowe jako UTC.
Trzymaj historię niezmienną. Jeśli później znajdziesz błąd, nie edytuj starych zdarzeń ani nie przepisuj wcześniejszych sum. Stwórz rekord korekty, który wskaże oryginalne okno i doda lub odjmie ilość. Łatwiej to audytować i wyjaśnić.
Zmiany planów i proration to miejsca, gdzie często gubi się śledzenie. Jeśli klient zmienia plan w połowie cyklu, podziel użycie na podokna odpowiadające każdej aktywnej cenie. Twoja faktura może zawierać dwie pozycje użycia (albo jedną pozycję plus korektę), każdą powiązaną z konkretną ceną i zakresem czasowym.
Praktyczny przebieg:
- Pobierz okno faktury z
period_startiperiod_endod Stripe. - Zagreguj kwalifikujące się zdarzenia użycia do sumy za to okno i cenę.
- Wygeneruj pozycje faktury z tej sumy plus korekty.
- Zapisz identyfikator przebiegu obliczeń, żeby później odtworzyć liczby.
Backfille i późne dane bez utraty zaufania
Późne dane są normalne. Urządzenia tracą łączność, zadania batchowe opóźniają się, partnerzy wysyłają pliki ponownie, a logi są replayowane po awarii. Klucz to traktować backfille jako pracę korekcyjną, a nie sposób na „dopasowanie liczb”.
Bądź jawny co do źródeł backfilli (logi aplikacji, eksporty z hurtowni, systemy partnerów). Zapisuj źródło przy każdym zdarzeniu, żeby móc wyjaśnić, dlaczego przyszło późno.
Przy backfillu trzymaj dwa znaczniki czasu: kiedy użycie się wydarzyło (czas, za który chcesz naliczyć) i kiedy je zaingerowano. Oznacz zdarzenie jako backfilled, ale nie nadpisuj historii.
Wol pref rebuildowanie sum z surowych zdarzeń zamiast stosowania delt do dzisiejszej tabeli agregatów. Replaye to sposób na odzyskanie po błędach bez zgadywania. Jeśli twój pipeline jest idempotentny, możesz uruchomić ponownie dzień, tydzień lub cały okres rozliczeniowy i otrzymać te same sumy.
Gdy faktura już istnieje, korekty powinny iść według jasnej polityki:
- Jeśli faktura nie jest sfinalizowana, przelicz i zaktualizuj sumy przed finalizacją.
- Jeśli jest sfinalizowana i została zaniżona, wystaw dodatek (nową pozycję faktury) z jasnym opisem.
- Jeśli jest sfinalizowana i została zawyżona, wystaw notę kredytową i powołaj się na oryginalną fakturę.
- Nie przenoś użycia do innego okresu, żeby uniknąć korekty.
- Przechowuj krótki powód korekty (partner resend, opóźniona dostawa logów, naprawa błędu).
Przykład: partner dosyła brakujące zdarzenia za 28–29 stycznia dopiero 3 lutego. Wstawiasz je z occurred_at w styczniu, ingested_at w lutym i źródłem backfill = „partner”. Faktura za styczeń jest już zapłacona, więc tworzysz małą dodatkową fakturę za brakujące jednostki z powodem zapisanym przy rekordzie rekonsyliacji.
Typowe błędy powodujące podwójne naliczanie
Podwójne naliczanie zdarza się, gdy system traktuje „przyszło zdarzenie” jak „akcja się wydarzyła”. Przy retryach, opóźnionych webhookach i backfillach musisz oddzielić akcję klienta od przetwarzania.
Zwykłe przyczyny:
- Retry traktowane jako nowe użycie. Jeśli każde zdarzenie nie niesie stabilnego action id (request_id, message_id) i baza nie wymusza unikatowości, policzysz dwukrotnie.
- Czas zdarzenia pomieszany z czasem przetwarzania. Raportowanie po czasie ingestu zamiast po occurred time sprawia, że późne zdarzenia trafiają do złego okresu, a potem są liczone ponownie podczas replayu.
- Surowe zdarzenia usuwane lub nadpisywane. Jeśli trzymasz tylko bieżącą sumę, nie możesz udowodnić, co się wydarzyło, i reprocessing może zawyżyć sumy.
- Zakładanie kolejności webhooków. Webhooki mogą być duplikowane, przychodzić poza kolejnością lub przedstawiać stany cząstkowe. Rekonsyliuj po Stripe object IDs i trzymaj strażnik „już przetworzone”.
- Brak jawnego modelu anulowań, zwrotów i korekt. Jeśli tylko dodajesz użycie i nigdy nie rejestrujesz negatywnych korekt, skończysz „poprawiając” sumy importami i policzysz ponownie.
Przykład: logujesz „10 wywołań API” i później wystawiasz korektę -2 z powodu awarii. Jeśli backfillujesz przez ponowne wysłanie całego dnia i jednocześnie zastosujesz korektę, klient zobaczy 18 wywołań (10 + 10 - 2) zamiast 8.
Szybka lista kontrolna przed wdrożeniem
Zanim włączysz rozliczenia na podstawie użycia dla rzeczywistych klientów, zrób ostatnie sprawdzenie podstaw, które zapobiegają kosztownym błędom fakturowania. Większość awarii to nie „problemy Stripe”. To problemy danych: duplikaty, brakujące dni i ciche retry.
Utrzymaj listę krótką i wykonalną:
- Wymuś unikatowość na zdarzeniach użycia (np. constraint na
event_id) i trzymaj się jednej strategii identyfikatorów. - Zapisuj każdy webhook, weryfikuj jego sygnaturę i przetwarzaj idempotentnie.
- Traktuj surowe użycie jako niezmienne. Poprawiaj przez korekty (pozytywne lub negatywne), nie przez edycję.
- Uruchamiaj codzienne zadanie rekonsyliacji porównujące wewnętrzne sumy (per klient, per meter, per dzień) ze stanem rozliczeń w Stripe.
- Dodaj alerty dla luk i anomalii: brakujące dni, ujemne sumy, nagłe skoki lub duża różnica między „zdarzenia zaingerowane” a „zdarzenia zafakturowane”.
Prosty test: wybierz jednego klienta, uruchom ingest ponownie dla ostatnich 7 dni i potwierdź, że sumy się nie zmieniają. Jeśli się zmieniają, masz wciąż problem z idempotencją lub stanami życia zdarzeń.
Przykładowy scenariusz: realistyczny miesiąc użycia i faktur
Mały zespół wsparcia używa portalu klienta, który pobiera $0.10 za konwersację obsłużoną. Sprzedają to jako rozliczenia na podstawie użycia ze Stripe, ale zaufanie buduje się tym, co dzieje się, gdy dane są nieczyste.
1 marca klient zaczyna nowy okres rozliczeniowy. Za każdym zamknięciem konwersacji agentem twoja aplikacja emituje zdarzenie użycia:
event_id: stabilne UUID z twojej aplikacjicustomer_idisubscription_item_idquantity: 1 konwersacjaoccurred_at: czas zamknięciaingested_at: kiedy to po raz pierwszy zobaczono
3 marca worker retryje po timeoutcie i wysyła tę samą konwersację ponownie. Ponieważ event_id jest unikatowy, drugi insert staje się no-opem i sumy się nie zmieniają.
W połowie miesiąca Stripe wysyła webhooki dotyczące podglądu faktury, a potem sfinalizowanej faktury. Handler webhooków zapisuje stripe_event_id, type i received_at, i oznacza je jako przetworzone dopiero po zatwierdzeniu transakcji w bazie. Jeśli webhook zostanie dostarczony dwukrotnie, drugie dostarczenie jest ignorowane, bo stripe_event_id już istnieje.
18 marca importujesz późną partię z klienta mobilnego, który był offline. Zawiera 35 konwersacji z 17 marca. Te zdarzenia mają starsze occurred_at, ale są nadal ważne. System wstawia je, przelicza dzienne sumy za 17 marca, a dodatkowe użycie zostaje uwzględnione przy następnej fakturze, bo to wciąż otwarty okres rozliczeniowy.
22 marca odkrywasz, że jedna konwersacja została zapisana dwukrotnie z powodu błędu, który wygenerował dwa różne event_id. Zamiast usuwać historię, zapisujesz zdarzenie korekcyjne z quantity = -1 i powodem typu „duplicate detected”. To zachowuje ślad audytu i sprawia, że zmiana na fakturze jest wytłumaczalna.
Następne kroki: implementuj, monitoruj i iteruj bezpiecznie
Zacznij od małego: jeden miernik, jeden plan, jeden segment klientów, który dobrze rozumiesz. Cel to prosta spójność — twoje liczby zgadzają się ze Stripe miesiąc po miesiącu, bez niespodzianek.
Zbuduj mało, potem utwardź
Praktyczne pierwsze wdrożenie:
- Zdefiniuj jeden kształt zdarzenia (co się liczy, w jakiej jednostce, w jakim czasie).
- Zapisuj każde zdarzenie z unikatowym kluczem idempotencyjnym i jasnym statusem.
- Agreguj do sum dziennych (lub godzinnych), żeby można było wyjaśnić faktury.
- Rekonsyliuj z Stripe webhookami według harmonogramu, nie tylko w czasie rzeczywistym.
- Po wystawieniu faktury traktuj okres jako zamknięty i kieruj późne zdarzenia przez ścieżkę korekty.
Nawet przy narzędziach no-code możesz utrzymać silną integralność danych, jeśli uczynisz stany nieprawidłowe niemożliwymi: wymuszaj unikatowe klucze idempotencyjne, wymagaj kluczy obcych do customer i subscription oraz unikaj aktualizowania zaakceptowanych surowych zdarzeń.
Monitoring, który zaoszczędzi ci czasu później
Dodaj proste ekrany audytu wcześnie. Spłacają się przy pierwszym pytaniu „Dlaczego moja faktura jest wyższa w tym miesiącu?”. Przydatne widoki to: wyszukiwanie zdarzeń po kliencie i okresie, widok sum per okres według dnia, śledzenie statusu przetwarzania webhooków oraz przegląd backfilli i korekt z informacją kto/kiedy/dlaczego.
Jeśli implementujesz to w AppMaster (appmaster.io), model pasuje naturalnie: zdefiniuj surowe zdarzenia, agregaty i korekty w Data Designer, potem użyj Business Processes do idempotentnego ingestu, zaplanowanej agregacji i rekonsyliacji webhooków. Nadal otrzymujesz prawdziwą księgę i ślad audytu, bez ręcznego pisania całej infrastruktury.
Gdy pierwszy miernik jest stabilny, dodaj następny. Zachowaj te same reguły cyklu życia, te same narzędzia audytowe i nawyk: zmieniaj jedną rzecz na raz, a potem weryfikuj end-to-end.
FAQ
Traktuj to jak małą księgowość. Trudność nie polega na pobraniu opłaty z karty; polega na prowadzeniu dokładnego, wyjaśnialnego zapisu tego, co zostało policzone, nawet gdy zdarzenia przychodzą z opóźnieniem, przychodzą dwukrotnie lub wymagają korekt.
Bezpieczny domyślny podział to: twoja baza danych jest źródłem prawdy dla surowych zdarzeń użycia i ich statusów, a Stripe jest źródłem prawdy dla faktur i wyników płatności. Ten podział utrzymuje rozliczenia możliwe do wyśledzenia, a Stripe zajmuje się cenami, podatkami i windykacją.
Powinien być stabilny i deterministyczny, żeby retry dawał ten sam identyfikator. Często pochodzi z rzeczywistej akcji biznesowej, np. customer_id + meter_key + source_record_id, tak że ponowne wysłanie staje się bezpiecznym no-opem zamiast dodatkowego użycia.
Nie edytuj ani nie usuwaj zaakceptowanych zdarzeń użycia. Zarejestruj zdarzenie kompensujące (w tym negatywną ilość, jeśli potrzeba) i zachowaj oryginał, dzięki czemu później będziesz mógł wyjaśnić historię bez domysłów.
Zachowaj surowe zdarzenia jako append-only i trzymaj agregaty jako dane pochodne, które można odbudować. Agregaty służą do wydajności i raportowania; surowe zdarzenia do audytu, sporów i odbudowy po błędach lub backfillach.
Przechowuj przynajmniej dwa znaczniki czasu: kiedy zdarzenie się wydarzyło i kiedy je zaingerowałeś, oraz zapisz źródło. Jeśli faktura nie została sfinalizowana, przelicz przed finalizacją; jeśli została, traktuj to jako korektę (dodatkowa opłata lub nota kredytowa) zamiast przesuwać użycie między okresami bez wyjaśnienia.
Zapisuj każdy otrzymany payload webhook i wymuszaj idempotentne przetwarzanie używając event.id od Stripe jako klucza unikatowego. Webhooki często się powtarzają lub przychodzą poza kolejnością, więc handler powinien stosować zmiany stanu tylko wtedy, gdy przesuwają rekord do przodu.
Używaj period_start i period_end subskrypcji od Stripe jako okna, i dziel użycie gdy zmienia się aktywna cena. Celem jest, żeby każda pozycja na fakturze dała się przypisać do konkretnego przedziału czasowego i ceny, dzięki czemu sumy są wyjaśnialne.
Zapisz identyfikator przebiegu kalkulacji lub równoważne metadane, żeby możliwe było odtworzenie sum. Jeśli ponowne uruchomienie ingestu dla tego samego okna zmienia sumy, prawdopodobnie masz problem z idempotencją lub stanami życia zdarzeń.
Zamodeluj surowe zdarzenia, agregaty, korekty i tabelę skrzynki odbiorczej webhooków w Data Designer, a następnie zaimplementuj ingest i rekonsyliację w Business Processes z ograniczeniami unikatowości dla idempotencji. Możesz zbudować audytowalny rejestr i harmonogramowaną rekonsyliację bez ręcznego pisania całej logiki.


