Softech Blog
Digital Assets & Financial Infrastructure

Jak zaprojektować internal ledger dla płatności stablecoin

Dlaczego saldo blockchain i dashboard providera nie zastępują product-owned ledgera — oraz jak modelować payment intent, immutable entries, credit, refundy i separację treasury.

4 min czytania
Jak zaprojektować internal ledger dla płatności stablecoin
Podsumowanie

Najważniejsze informacje z artykułu

Product-owned ledger zapisuje biznesowe znaczenie przepływu wartości. Nie powinien kopiować salda walleta; powinien zapisywać accepted payment events, credits, debits, fees, refundy i adjustments z trwałymi referencjami do providera lub dowodu blockchain.

Najważniejsze wnioski
  • Na każdym ledger entry zapisuj business reason i external evidence.
  • Oddziel available product balance od wallet/treasury balances.
  • Używaj immutable entries i compensating events zamiast destrukcyjnych update’ów.
  • Wymuszaj idempotency na granicy ledgera.
  • Regularnie uzgadniaj ledger z external rails.
Kluczowe obserwacje

Kluczowe obserwacje i tezy

Najważniejsze obserwacje podsumowujące doświadczenia, decyzje i rezultaty opisane w materiale.

Wallet balance odpowiada „ile tokenów tu jest?”; ledger odpowiada „dlaczego produkt jest winien lub zaksięgował tę wartość?”.
Provider state jest dowodem; ledger state jest biznesową interpretacją produktu.
Treasury movement nie może cofnąć prawidłowej płatności klienta.
Każdy external event potrzebuje idempotentnego efektu ledgerowego.

Blockchain balance nie jest product balance

Wallet może mieć 10 000 USDC i nadal mówić bardzo mało o produkcie. Nie wie, czy środki należą do dziesięciu klientów, jednej faktury, treasury transfer, refund return czy operacyjnego top-up.

Wallet balance odpowiada „gdzie są aktywa?”. Internal ledger odpowiada „dlaczego produkt rozpoznaje tę wartość?”.

Po co jest internal ledger

Ledger tworzy trwałą biznesową interpretację external payment evidence. Provider order lub blockchain transaction dowodzi, że coś wydarzyło się poza bazą danych. Ledger zapisuje, co to zdarzenie oznacza wewnątrz produktu.

Przykładowe ledger events:

  • PAYMENT_ACCEPTED
  • CUSTOMER_CREDITED
  • PAYMENT_FEE_RECORDED
  • REFUND_INITIATED
  • REFUND_COMPLETED
  • MANUAL_ADJUSTMENT
  • TREASURY_TRANSFER_RECORDED

Nie modeluj ledgera jako jednego mutowalnego balance

Operacja user.balance += amount traci reason, evidence i historię. Preferuj append-only entries i wyliczaj saldo z nich albo z projection, którą można odtworzyć.

Rekomendowane core entities

PaymentIntent

Oczekiwana płatność biznesowa: customer, invoice/order, amount, currency, expiry i allowed rail.

ExternalPayment

Evidence object: provider order ID lub blockchain tx hash, network, asset, amount, timestamps i external state.

LedgerEntry

Immutable business value event z debit/credit direction, amount, unit, reason code, referencją do PaymentIntent i ExternalPayment, idempotency key i timestampem.

BalanceProjection

Derived view dla wydajności. Projection można odbudować z ledger entries; nie powinno być jedynym dowodem.

SettlementRecord

Osobny obiekt reprezentujący provider settlement, withdrawal lub treasury movement. Customer payment może być valid zanim settlement się zakończy.

Idempotency należy do granicy ledgera

Duplicate callbacks i powtórne chain observations są normalne. Ten sam external event nie może wygenerować efektu ekonomicznego dwa razy. Zapisuj unique key, np. provider:event:id lub chain:txHash:logIndex:businessAction, i wymuszaj uniqueness transakcyjnie.

Credit i settlement to różne fakty

W managed flow provider może potwierdzić customer payment i produkt może credited order. EUR settlement może wydarzyć się później. Opóźnienie settlementu nie unieważnia płatności klienta. Rozdziel paymentAcceptanceState od settlementState.

Tak samo przy custom wallets: valid deposit może credited customer, a późniejszy treasury sweep może się nie udać. Sweep jest operacyjnym movementem, nie powodem do cofnięcia payment.

Refundy jako compensating entries

Refund nie usuwa pierwotnego credit. Twórz nowy refund lifecycle i nowe ledger entries. Dzięki temu zachowujesz pełne wyjaśnienie gross payment, fee, partial refunds i net retained value.

Multi-currency i jednostki stablecoin

Zdecyduj, w jakiej jednostce działa dany ledger. Dla B2B invoicing można utrzymać commercial ledger w EUR, a USDC payment evidence osobno. W native stablecoin produkcie ledger może być denominowany w USDC. Nie mieszaj jednostek w jednym balance bez jawnych conversion entries i rate evidence.

Proponowany ledger entry

PoleCel
entryIdImmutable identifier
accountIdCustomer, merchant, clearing lub fee account
directionDebit / credit
amount + unitWartość w jednostce ledgera
reasonCodePAYMENT, REFUND, FEE, ADJUSTMENT…
paymentIntentIdKontekst biznesowy
externalReferenceProvider order/event lub chain tx
idempotencyKeyBlokuje duplicate economic effect
createdAtAudit ordering

Reconciliation dowodzi poprawności ledgera

Internal ledger bez external reconciliation również może się rozjechać. Scheduled jobs powinny porównywać totals i entries z provider lub chain evidence. Proces opisuje Reconciliation płatności stablecoin.

Provider vs custom wallet

Przy CoinGate provider orders i ledger transactions są external evidence. Przy custom wallets external evidence stanowią blockchain transactions i chain observations. Internal ledger pozostaje koncepcyjnie taki sam.

Czego ledger nie powinien robić

  • Nie przechowuje private keys.
  • Nie wylicza customer credit bezpośrednio z wallet balance.
  • Nie nadpisuje historii, żeby „naprawić” błąd.
  • Nie skleja payment, treasury i settlement w jeden status.
  • Nie udaje statutory accounting.

Ledger invariants

Produkcyjny ledger powinien wymuszać invariants w kodzie i — gdzie to możliwe — w bazie danych. Przykłady: external event może wygenerować dany economic effect tylko raz; posted entries są immutable; debits i credits używają znanej jednostki; adjustments referują wpis lub event, który korygują; payment nie może zostać credited przed spełnieniem acceptance policy.

Account structure

Nawet bez pełnego accounting general ledger jawne logical accounts upraszczają model. Typowe konta to customer available balance, merchant clearing, provider clearing, fees, refunds i treasury. Przesuwanie wartości między logicznymi kontami jest bardziej audytowalne niż update kilku niezależnych balance fields.

Concurrency i transactions

Payments są systemem współbieżnym. Dwa callbacki mogą przyjść jednocześnie albo callback może ścigać się z reconciliation job. Ledger posting powinien działać w database transaction z uniqueness constraints na idempotency keys. Jeśli aktualizujesz projection, rób to w tej samej transaction albo przez replayable event pipeline.

Pytania audytowe, na które ledger powinien odpowiadać

  • Dlaczego ten customer został credited?
  • Która external transaction utworzyła wpis?
  • Czy payment był refunded, partially refunded lub adjusted?
  • Które entries składają się na current product balance?
  • Czy accepted payment został reconciled z settlementem?

Podsumowanie

Internal ledger jest granicą, która pozwala używać wielu payment rails bez utraty spójności biznesowej. Zbudowany wokół jawnych value events może później obsługiwać CoinGate, direct USDC deposits, cards i bank transfers przy tym samym core business model.

Model rozwiązania

Kluczowe elementy i zależności

Stablecoin Product Ledger

Oddziel dowód, akceptację biznesową i przepływ środków.

Warstwa 1
Payment intent

Czego biznes oczekiwał.

Warstwa 2
External evidence

Provider order lub blockchain transaction.

Warstwa 3
Ledger event

Dlaczego wartość została credited/debited.

Warstwa 4
Product balance

Wyliczone business balance lub entitlement.

Warstwa 5
Treasury state

Gdzie fizycznie są aktywa; osobno od product balance.

Źródła i kontekst

Informacje wspierające analizę

USDC is described by Circle as an e-money token under MiCA for the EEA.

MiCA establishes an EU framework for crypto-assets and related services not already covered by other EU financial-services legislation.

CoinGate exposes a ledger transactions API for tracking credits and debits associated with its account, which illustrates the operational separation between orders and ledger movements.

Circle Wallets distinguishes wallet custody/control models from application business logic, reinforcing the need to model product state separately from wallet infrastructure.

FAQ

Czy internal ledger to to samo co system księgowy?
Nie. Product ledger jest operacyjnym source of truth dla zdarzeń wartości w produkcie. Może zasilać księgowość, ale nie zastępuje statutory accounting ani ewidencji podatkowej.
Czy mogę używać tylko salda walleta?
Nie. Wallet balance nie identyfikuje business reason transferu, customer credit, akceptacji płatności ani późniejszego treasury movement.
Czy ledger entries powinny być edytowalne?
Preferuj immutable entries oraz compensating/adjustment entries. Zachowuje to audit trail i upraszcza reconciliation.
Czytaj dalej

Powiązane artykuły

Materiały, które rozwijają temat i uzupełniają go o dodatkowy kontekst praktyczny.

Autor

Matt Dudzicz · Softech.app

Founder

Founder Softech.app, skoncentrowany na product engineeringu, infrastrukturze digital assets, custom software i systemach biznesowych AI-native.

LinkedIn
Następny krok
Projektujesz stablecoin payments jako część produktu?
Porównamy managed gateway z custom on-chain architecture i zaprojektujemy payment state, ledger oraz reconciliation.