Adds the primitives a host webhook needs to keep the ledger current in
real time, so the importer is a backstop rather than the only path:
- ledgerRowFromBalanceTransaction(): the canonical row builder (the
import command now delegates to it), with refund→charge buyer fallback.
- recordBalanceTransaction(): idempotent upsert on the txn_ id that never
downgrades a known customer_id/email to null (so a webhook's resolved
buyer isn't clobbered by a later importer pass that couldn't resolve it).
- syncLedgerForCharge(): retrieves a charge + its refunds and upserts their
balance transactions, user-independent, never throws (logs + returns 0).
Facade @methods + 3 LedgerMetricsTest cases (upsert idempotency, no-downgrade,
non-charge-id guard). Full suite 1461 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
customerLedgerTotals() sums the user-independent Stripe ledger for one
buyer by ANY of their customer id(s) OR email(s) — so subscription
payments (which product_purchases stores as amount=0) and deleted- or
guest-customer charges are still attributed. ledgerAmountByCustomer()
returns [customer_id => amount] for per-user rollups in one query.
Both are additive and filter on ledgerRevenueTypes(); empty identity
returns zero rather than matching everything. Covered by two new
LedgerMetricsTest cases (full suite 1458 green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror every Stripe balance transaction (signed gross/fee/net) into a new
stripe_transactions table decoupled from any local user, so revenue net of
fees and refunds is a plain SUM(net) and a deleted customer never removes
their money history.
- StripeTransaction model (config-resolved, HasUuids) + guarded, publishable
migration; config tables.stripe_transactions + models.stripe_transaction +
ledger.revenue_types.
- Shop::revenueLedgerByDay / ledgerTotals / ledgerEarliest (+ facade @method).
- shop:import-stripe-ledger: pages ALL balance transactions globally (not
per-customer), upserts on txn_ id, --dry-run.
- LedgerMetricsTest (4). Full suite green (1456).
Additive only; existing consumers unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>