Codemojex · C6.2 · a dive of The Revenue Ledger and the KeyShop
Four rails, one order, exactly-once.
The KeyShop is the multi-rail pay-in that feeds the revenue ledger. A DB-stored PKG catalog sits behind one orders table with a rail discriminator; the money is pinned on the ORD at creation, and settlement is exactly-once on the OTX (rail, external_id) partial unique index. Telegram Stars is shipped end to end; the TON, USDT, and RUB verifiers are forward. The gross books to the same revenue_ledger.
Grounding — echo/…/codemojex/key_shop.ex (price_minor/3, create_order/3, settle_payment/1) · rails.ex (the frozen rail set + self_check!/0) · the PKG/ORD/OTX/WHK schemas · telegram_controller.ex · the keyshop story.
price_minor(pkg, "stars", rates) returns stars_price verbatim: 1_449 Stars — the canonical face. No rate touches the base rail.Codemojex.Rails holds the closed rail set ~w(stars ton usdt rub) as frozen module data — the minor unit, the factor, the decimals live in code, never in a mutable column. Rails.self_check!/0 is the boot vector: Codemojex.Application runs it at start-up, and it raises unless every rail’s factor == 10 ** decimals — the EchoData.BrandedId.self_check! pattern applied to money scaling.
The catalog and the order
key_shop.ex · create_order/3A Package is a versionable template — keys, stars_price (whole Stars, the canonical face), discount_pct, three nullable per-rail minor-unit overrides, enabled, sort. Editing it touches later orders only, because create_order/3 pins the money on the ORD at creation: the key count, the gross price_minor, and — for a non-Stars rail — the rate snapshot (rate_minor / rate_pair / rate_source / rate_quoted_at), read once from the key_shop_rates config. The order lands in created, and no keys are minted. The pricing itself is a pure function — no Repo, no clock, no HTTP:
def price_minor(package, "stars", _rates) do
case Map.get(package, :stars_price) do
p when is_integer(p) and p > 0 -> {:ok, p} # the canonical face, verbatim
_ -> {:error, :no_price}
end
end
def price_minor(package, rail, rates) when rail in ~w(ton usdt rub) do
case override(package, rail) do
p when is_integer(p) and p > 0 ->
{:ok, p} # a pinned per-rail override wins
_ ->
with rate when is_integer(rate) and rate > 0 <- Map.get(rates, rail, :no_rate) do
# minor = usd_face_cents × (rail minor units per USD cent), integer-exact, floored.
{:ok, usd_face_cents(package, rates) * rate}
else
_ -> {:error, :no_rate}
end
end
end
def price_minor(_package, _rail, _rates), do: {:error, :bad_rail}
The client supplies only {package_id, rail} — the buy_keys route derives the key count and the price server-side, so a tampered client cannot name its own price (the keyshop story: create_order mints no keys; the client mint path is gone). The order statuses are the closed set created → paid → failed/refunded, checked at the changeset and again by the database. The seeded launch catalog is seven bundles, enabled and sorted — editable without a deploy.
The Stars flow, end to end
telegram_controller.exThe purchase begins at the facade. Codemojex.create_order/3 delegates to the KeyShop, and the buy_keys route returns the order plus a Stars XTR invoice link from Codemojex.Telegram.create_invoice_link/1 — with the ORD id as the invoice payload, so every later callback resolves back to the order it pays.
Telegram then sends pre_checkout_query. The webhook answers through KeyShop.valid_pre_checkout?/2, the tamper guard: true only when the order is still created and the presented amount equals the pinned price_minor. Fail-closed — a mismatch, an already-paid order, or an absent order is answered false.
successful_payment is the order-coupled confirmation. The controller hands it to settle_payment/1 — the OTX-gated fulfilment below. One rail, three callbacks, no polling: create_order → create_invoice_link → pre_checkout → successful_payment.
The full provider receipt is preserved verbatim on the OTX row’s raw_payload (jsonb) — the reconciliation read order_reconciliation/1 returns the pinned order, its receipts, and the revenue row it booked, keyed by the ORD ref.
Exactly-once settlement
key_shop.ex · settle_payment/1settle_payment/1 is one Repo.transaction under a FOR UPDATE order lock. It inserts the OTX receipt with on_conflict: :nothing on the (rail, external_id) partial unique index, then re-counts — the truth is the ledger, not the returned struct. Only if the count rose do the three writes fire. Select a delivery to run the settle over the fixed payment.
credit_purchase mints +100 keys (one TXN, ref = the ORD), house_post books +1_449 stars gross (one RVL row), mark_paid! flips the order. 3 rows; {:ok, :fulfilled}.def settle_payment(%{order_id: order_id, rail: rail, external_id: external_id, amount_minor: amount_minor} = params) do
payload = Map.get(params, :payload)
Repo.transaction(fn ->
case lock_order(order_id) do
nil ->
Repo.rollback(:no_order)
order ->
case insert_otx(order_id, rail, external_id, amount_minor, payload) do
:suppressed ->
# the (rail, external_id) dedup fired — a replay, mint NOTHING / book NOTHING
:already_fulfilled
:wrote ->
# 1. MINT the keys (a players credit — always non-negative, A-7) keyed on the ORD.
{:ok, _} = Wallet.credit_purchase(order.player, order.keys, order_id)
# 2. BOOK the gross to the SAME revenue_ledger (cm-7 D-1/D-3) — account="platform",
# currency=the rail, reason="purchase", ref=the order id. ZERO DDL on revenue_ledger.
Wallet.house_post(Wallet.house_account(), order.currency, amount_minor, "purchase", order_id)
# 3. FLIP the order to paid (the only money-column-adjacent UPDATE; the price/rate stay pinned).
mark_paid!(order)
:fulfilled
end
end
end)
end
defp lock_order(id), do: Repo.one(from o in Order, where: o.id == ^id, lock: "FOR UPDATE")
The index is the sole race-safe exactly-once authority — not a status == "paid" read-then-act, which carries a TOCTOU window the index closes. The insert’s conflict target names the migration’s predicate byte-for-byte: {:unsafe_fragment, "(rail, external_id) WHERE external_id IS NOT NULL"}. The keyshop story pins the behaviour: the 100-key bundle at gross 1449 Stars books one +1449 stars revenue row; a replayed successful_payment mints once and books once; the receipt, the key credit, and the revenue booking are all-or-nothing.
The forward rails
schemas/webhook.ex · WHKThe pricing and the order flow are rail-complete today: the Order changeset admits all four rails, price_minor/3 prices each, and the settlement core is rail-generic — it takes {order_id, rail, external_id, amount_minor} and cares only that the pair is fresh. What only Stars has is a live confirmation surface: Telegram’s successful_payment.
webhooks table: WHK is the designed-forward shape for the first push rail (on-chain TON), where an event arrives decoupled from an order and is deduped by (rail, event_id) at ingress — no migration creates that table this rung, and the schema has no live call site. For the Stars launch, WHK folds into the OTX (rail, external_id) index. Cash-out — money leaving the platform — is forward too (cm.8).
The gross a rail collects books into the same revenue_ledger read in C6.1; where the money goes out — cash-out and the treasury — is C6.3.
References
sources · relatedSources
- Telegram — Mini Appsthe Stars invoice, pre_checkout, and successful_payment flow the shipped rail rides
- hexdocs — Repo.transactionthe all-or-nothing transaction discipline behind the settle
Related
- C6.1 — The revenue ledgerthe ledger the gross books into
- EchoMQ — the protocolexactly-once on the bus
- C5.2 — The transactional walletthe wallet the mint credits