Skip to content

Changelog

Changelog

What's new and what's changed, newest first, each entry dated.

  1. API

    Four confirm-payment error codes collapsed into one

    When confirm-payment cannot open a payment on the token and network the customer picked, it now returns one 503: payment_method_unavailable. It replaces no_available_address, address_lease_failed, bridge_unavailable and collection_disabled.

    The situation is the same one all four described — this token on this network cannot take a payment at the moment, and retrying or picking another usually works. What the four names added on top of that was a description of our own machinery, sent to a shopper's browser on your checkout page. So did the messages, which now read the same for every cause.

    Nothing splits: there is no case where the old four would have told you to do different things. The reasons behind the verdict are still recorded on our side, where they are useful for support.

    If your integration branches on any of the four old strings, switch it now. They are no longer emitted and there is no transition period during which both old and new are sent. The type link in the error envelope follows the new name, so a stored link to an old anchor will not resolve.

    The full list is at Error codes.

  2. API

    One withdrawal error code renamed

    The 503 a withdrawal returns when the destination network cannot pay out is now withdrawal_network_unavailable. It was hot_wallet_insufficient_liquidity.

    Nothing about when it fires has changed: same endpoint, same 503, same situation — this token on this network cannot be settled at the moment, and another network or a later retry usually can. What changed is the name and the message, which described our side of the payout instead of yours.

    If your integration branches on the old string, switch it now. The old code is not emitted any more and there is no transition period during which both are sent. The type link in the error envelope follows the new name, so a stored link to the old anchor will not resolve.

    The full list is at Error codes.

  3. Dashboard

    Money reads short, and exact on hover

    Every amount in the dashboard now renders at a length you can scan, with the exact figure behind it whenever the two differ. A column of balances lines up instead of running to a different number of decimals in every row.

    How many decimals an asset shows is decided per asset, not by one rule applied to all of them: a dollar-pegged token and a gold-backed one do not want the same shortening. Nothing is rounded away in the ledger — the short form is display only, and the exact figure is always the one underneath.

  4. Withdrawals

    One dashboard dialog for one payout or a run

    Paying out a single recipient and paying out a list used to be two different screens in the dashboard. They are one dialog now: pick a single payout or a run, review the whole thing in one place, and send. Approving a list of addresses happens in that same dialog rather than one address at a time.

    A payout that already went through can be repeated from the list, so a recurring transfer to a known recipient no longer means retyping an address you have already used and already approved.

    The recipient label now shows in the withdrawals list. How many recipients one run may carry is shown before you send, not after: the dialog subtracts the payouts already in flight against your account limit and puts the remaining number beside the label.

  5. API

    Payment channels in all eight SDKs

    Payment channels are in every official SDK now — all eight. Their webhooks and the deposits feed come with them, across .NET, Go, Java, PHP, Python, Ruby, Rust, and TypeScript. Signing, retries, and typed errors are handled for you, and each README carries the full flow rather than a method list.

    The feed reader in each of them carries the guarantee that matters: a client that keeps its cursor cannot skip a settled deposit. Walk the cursor to the end — without treating an empty page as the end — and every settled deposit has been seen exactly once, whatever happened to the process in between.

  6. Payment channels

    Every rail discloses its own minimum deposit

    A channel's token list no longer returns bare symbols. Each entry now carries its own minimum_deposit — the smallest transfer that route will credit — A minimum belongs to the token and the network together, so the same symbol carries a different floor on every rail it lives on.

    The number is quoted live on every read, so take it from the response you are rendering and never hardcode it. Show it next to the address: a transfer below the minimum is not credited and is not returned automatically.

    A project also now accepts token symbols rather than network-and-symbol pairs, so adding a network no longer means re-approving every token on it.

  7. Dashboard

    Five ways to connect a project, answered once

    Three settings decided how a project was connected. It is one question now, asked once, at creation, with five answers: your backend calls the API, checkout happens inside a Telegram bot, a shop plugin owns it, a cashier takes it in person on the terminal, or an embeddable snippet runs on your own page.

    The answer decides what the project shows you afterwards. A bot project picks an accent colour beside its Telegram links instead of a full checkout palette, and its quick start ends inside Telegram; a plugin project stops offering an embed snippet it will never use.

    The choice is not editable — a project that needs a different one is a different project. The cashier link moved with it: /pos now redirects to /terminal, and a bookmarked URL keeps working.

  8. Payment channels

    Payment channels — a permanent address per payer

    An invoice is one payment: an amount, an expiry, an end. A payment channel is the opposite — a permanent deposit identity for one of your payers, with one lasting address per supported network. No amount, no expiry, no terminal state. Every transfer that arrives becomes a deposit credited to your balance.

    You key a channel by your own external_id, so there is no new identifier to store. That pair — project and external id — is the idempotency key: call it on every checkout and a repeat returns the same channel rather than opening a second one.

    Deposits come back through a feed of confirmed payments only, oldest first, behind a cursor that always advances. Store the cursor and every settled payment is seen exactly once — an empty page is not the end of the feed, so a reader that stops there stops early. Channels can be blocked and unblocked, and the sandbox can simulate a deposit. Create a channel starts here.

  9. Localization

    German and Spanish close the set

    German and Spanish are live. That brings the set to six, alongside English, Russian, and — since 20 July — Turkish and Simplified Chinese.

    Both languages land where the others already run: the public pages, the checkout a payer sees, the merchant dashboard, the email and Telegram notices, and the commerce plugins.

  10. Telegram checkout

    Telegram checkout

    A project can now be connected as a Telegram bot. On such a project the invoice comes back with a deep link instead of a page address: the payment link opens a chat, and the payer picks what to send and on which network right there.

    The deposit step shows a generated QR code, the address with a copy button, and the expiry, so nothing sends the payer to a browser tab or asks them to create an account.

    Status changes reach the same chat, so a payer who paid and closed the app still sees the invoice reach its end. Telegram checkout covers the setup.

  11. Partners

    Partner program

    Refer a merchant and earn a share of the Paymos processing fee it generates. Four tiers run from 20% to 40%, stepping up at 5 and 20 active merchants; the top tier is granted on request rather than by a threshold.

    The share is credited on every invoice the referred merchant settles, at the moment it settles — not monthly, not on a payout schedule, not once some minimum clears. There is no cap on volume and none on total commission. A tier change applies to the whole portfolio, not only to merchants referred after it.

    The commission is carved out of the Paymos fee rather than added on top of it, so what a referred merchant pays is what any merchant pays.

    Your link and current tier are at Partnership.

  12. Dashboard

    Payment analytics

    The analytics page answers three questions: how much came in, what share of invoices got paid, and how long payers took to pay them.

    Conversion is computed over a mature cohort only — invoices whose outcome is already observable. An invoice created twenty minutes ago has not failed, and counting it as unpaid would drag the rate down for no reason and make every fresh hour look like an incident.

    Time to pay is a distribution rather than an average: 0–5 minutes, 5–10, 10–15, 15–20, and 20 or more. Volume breaks down by token, by network, and by project — the leading five in each, not a full list. Invoice activity is bucketed by day of week and hour, in the timezone you pick.

    Open it at Analytics.

  13. Audit

    Audit trail for privileged actions

    Privileged actions are recorded with the actor, the action, the target, the outcome, and the time. Merchant-side and operator-side alike, in one trail.

    Each entry names the action explicitly — merchant.add_whitelisted_address, operator.engage_freeze — and snapshots who the actor was at that moment, including their role. A promotion next month does not rewrite what a Viewer did in March.

    Redaction happens at write time, not at read time. Secrets, tokens, hashes, and signatures are stripped before the record is stored, so the audit trail can never become the place a credential leaks from later.

    What a merchant sees is its own account's history, sign-in activity with device and IP included. The complete platform trail stays operator-only — it holds other merchants' actions, and there is no version of exposing it that is safe.

  14. Notifications

    Notifications by email and Telegram

    Account and money events now reach the merchant on two channels. Every notice exists as both an email and a Telegram message, from the same event, so connecting Telegram adds a delivery path without silencing the mailbox.

    Money movement is covered — a withdrawal created, a withdrawal completed — alongside team changes, webhook deliveries that gave up, and the security set: 2FA switched on or off, a passkey added or removed, a login method linked, a whitelisted address added or revoked, an API secret rotated, a credential killed.

    Routine categories are yours to switch off. The security ones are not, and there is no setting that hides them: a notice that your withdrawal address changed is worth an unwanted email far more than the reverse. SMS is not a channel here.

  15. API

    One error envelope

    Every endpoint that fails answers in the same shape: RFC 9457 Problem Details, served as application/problem+json.

    The field to branch on is code — a stable string whose meaning never changes once published. title and detail are written for humans and may be reworded, expanded, or localized between releases, so an integration that parses detail is an integration that breaks on a copy edit. Single-field validation errors carry field; when several fields fail at once, an errors[] array carries the per-field breakdown instead.

    type deep-links to the documentation row for that exact code, which turns an unfamiliar string in a log into one click. The full list is at /docs/errors/codes.

  16. API

    Idempotent creation

    external_order_id is the idempotency key. Send it when you create an invoice and a repeated call returns what already exists instead of making a second one.

    There is no Idempotency-Key header to generate and store, because the identifier you already hold — the order number, the cart id, the payout reference — is the one that matters. A fresh invoice answers 201 Created; a replay of the same identifier answers 200 OK with the original invoice. Withdrawals work the same way, which is where the property earns its keep: a retried payout call returns the first withdrawal rather than sending funds twice.

    Uniqueness is scoped per project for invoices and per merchant for withdrawals. A network timeout on your side is now something to retry, not something to investigate.

  17. API

    IP allowlist per credential

    Every API credential carries its own allowlist: up to 50 entries, IPv4 or IPv6, single addresses or CIDR ranges. An empty list means the credential is not IP-restricted.

    For a payout key the empty case does not last. A freshly generated payout credential stays inactive until at least one address is added, so a key stolen on the day it was created has nowhere to be called from. Payment keys carry no IP check — they create invoices, they do not move money.

    Editing an allowlist is a step-up operation and lands in the audit trail with the full list that was set, not a diff. Widening the firewall in front of a money-moving credential should leave a record of who widened it.

  18. API

    Scoped API keys

    Credentials come in two kinds. A payment key (pk_) creates invoices and reads their status; a payout key (rk_) creates and cancels withdrawals and reads balances.

    The scope check is central, not per route. Access level is derived from the key type on every request, so a payment key that leaks into a client bundle cannot move funds no matter which path it is aimed at — the request is refused before a handler sees it, rather than by a route that remembered to check.

    API secrets carry the sk_ prefix and webhook signing secrets whsec_. The environment sits in the prefix too, which makes pk_live_… and pk_test_… visibly different things in a config file rather than two similar strings someone has to squint at.

    Manage them at Developer → API Keys.

  19. Auth

    Two-factor authentication

    TOTP can be turned on for any account: a six-digit code from an authenticator app, rotating every thirty seconds.

    Sign-in is the smaller half of it. The larger half is step-up — a short list of operations that demand a fresh code at the moment they run, rather than accepting a session that cleared 2FA an hour earlier. Adding a withdrawal address, removing one, editing an API key's IP allowlist, and turning 2FA off are each their own scope, so a code captured for one of them cannot authorize another.

    Enabling or disabling it sends a security notice by email and Telegram, whatever the notification preferences say. Set it up under account security.

  20. Auth

    Passkey sign-in

    Sign in with a passkey — Face ID, Touch ID, Windows Hello, or a hardware key. WebAuthn and FIDO2, with no password anywhere in the flow, because there has never been one here to phish.

    A passkey is a key pair. The private half stays on the device and never reaches us; the server holds the public half and verifies a signature over a challenge that is good for exactly one use. There is nothing on our side worth stealing and nothing on yours worth typing into a convincing fake login page.

    Several devices can be enrolled on one account, and registering or removing any of them fires a security notice that cannot be switched off. Magic link, Google, and Telegram all still work — a passkey is another way in, not a replacement for the others.

    Enroll under account security.

  21. Support

    Support inside the Telegram bot

    The Paymos bot carries a support thread. Open Requests, write a message, and it reaches the operators who answer from the platform side.

    It is one conversation, not a ticket queue. Replies land back in the same chat, the recent messages stay on screen, and a second message sent before the operator has answered is not delivered — the bot says so plainly instead of swallowing it into a backlog nobody is reading.

    Being linked to a Paymos account is not a prerequisite. Unlinked is a normal state for a payer, so someone stuck mid-payment can ask about an invoice without an account; linking matters only for the parts of the bot that show account state.

  22. Plugins

    Official plugins for eight storefronts

    Plugins are published for WooCommerce, WHMCS, OpenCart, PrestaShop, Magento 2, Shopware 6, CS-Cart, and Easy Digital Downloads.

    Connection is a click, not a copy-paste. The store opens a Paymos tab, the merchant approves the request against the project they already have selected, and the plugin receives credentials for that project directly. Nothing sensitive is typed into a settings form, and the stored values are never handed back to the browser — a leaked release archive is identical for every merchant and carries no keys at all.

    Order state comes from the signed webhook, never from the shopper landing on a return page. A closed browser tab must not be able to lose a paid order, so the callback is the source of truth and the return URL is a courtesy.

    Install guides for all eight are under /docs.

  23. Networks

    Plasma live

    Plasma is accepting USDT. It is the thirteenth mainnet network, which is where the list stands today.

    USDT is the only asset on it. An asset appears at checkout on a given network because it was enabled there, not because the chain could technically carry it — the registry decides, per network and per token, and nothing is inferred from what a contract happens to exist.

    Thirteen networks and five assets now sit behind one invoice call. A merchant integrating today writes the same request as one who integrated in January: the network list is data, not a version of the API.

  24. API

    Server SDKs for eight languages

    Official clients are published for TypeScript, Python, PHP, Go, .NET, Java, Ruby, and Rust.

    Each covers the whole contract rather than a subset: invoices, withdrawals, balances, server time, cursor pagination, the error envelope, retries, request signing, and webhook verification. Signing is the part most worth a library. The canonical string, the timestamp window, and the base64 HMAC are all easy to get subtly wrong by hand, and a subtle mistake reads as 401 with nothing else to go on.

    Webhook verification is a second signature with its own rules, so every verifier takes the raw request bytes before any JSON parsing. Hand it a re-serialized body and the check fails, correctly.

    Repositories, release tags, and runtime requirements are listed at /docs/server-sdks.

  25. Tokens

    XAUT — settlement in gold

    XAUT is accepted on Ethereum. It is the first asset here that is not a stablecoin, and it should not be read as one.

    One XAUT represents a fine troy ounce of gold on a London Good Delivery bar, so an XAUT balance tracks the metal, not the dollar. For a merchant who would rather hold gold than a dollar claim, that is the point. It is also the trade: a balance that moves in both directions, which is not what a dollar-pegged token is for.

    Mechanically there is nothing special about it. Ethereum only, the same amount-banded confirmation policy, the same per-asset balance, the same whitelist on the way out. The accepted set is now four stablecoins and one gold-backed asset.

  26. Networks

    Sui live, USDC only

    Sui is accepting payments, and USDC is the only asset on it.

    Sui is not EVM and not a fork of anything already on the list. Its addresses are 32 bytes rendered as 0x plus 64 hex characters — twice the length of an EVM address, which is enough on its own to keep the two apart in a merchant's records. The coin model is nothing like an ERC20 balance either, so deposit detection runs its own path rather than a borrowed one.

    None of that is visible to a merchant. Enable Sui on a project and it shows up at checkout beside the rest; the invoice, the webhook, and the balance behave exactly as they do on any other network.

  27. Tokens

    USD1 and DAI

    Two more stablecoins are accepted: USD1 on Ethereum and Solana, DAI on Ethereum.

    They are not the same kind of instrument. USD1 is fiat-backed, like USDT and USDC. DAI is collateralized on-chain by crypto rather than by a bank deposit — a different backing model behind the same dollar unit, and worth knowing which one a balance is denominated in.

    Neither is converted on the way in. A DAI invoice credits a DAI balance and pays out DAI, with no internal hop through USDT and no spread taken in between. That brings the accepted set to four stablecoins.

  28. Settlement

    Confirmation depth by amount

    Confirmation policy reads two inputs now: the network and the invoice's dollar value. A $40 invoice and a $40,000 invoice on the same chain no longer wait the same number of blocks.

    Ethereum runs 2 confirmations up to $100, 6 up to $1,000, 12 up to $10,000, and 32 above that — roughly 24 seconds at the shallow end, around six minutes at the deep one. Tron runs 2, then 6, then caps at 19, the solidification standard of 19 of its 27 super representatives. Arbitrum's counts look extreme at 40 / 120 / 240 / 800 only because its blocks are sub-second; the wall-clock horizons match Base and Optimism.

    Chains with native finality skip the bands. BSC and Polygon confirm at network finality, Solana at the finalized commitment, TON at a single block — every masterchain block there is already final.

    The counts are configuration and move only when we move them. The times derived from them are not: blocks come slower under congestion, so read any figure here as an approximation, never as a settlement guarantee.

  29. Networks

    Solana live

    Solana is live. USDT, USDC, and USD1 all settle on it.

    Solana does not count confirmations the way an EVM chain does, and the platform does not pretend otherwise. Deposits are scanned at the finalized commitment and confirmed the moment they are seen there — one tier, no amount bands. What finalized means on Solana is exactly what a confirmed payment means here.

    Deposit addresses are base58 ed25519 public keys rather than 0x strings, so a Solana address cannot be mistaken for an EVM one in a merchant's own records. Balances still roll up per asset: USDC taken on Solana and USDC taken on Base add to a single figure.

  30. Networks

    Avalanche live

    Avalanche accepts payments today. USDT and USDC both settle on the C-Chain.

    Addresses and transaction hashes are EVM-shaped, so a merchant already reconciling Ethereum or Base has nothing new to parse. Confirmation does not run on amount bands here: Snowman finality settles the question for every size of payment at once, so a deposit is credited when the chain reports it final — the single-tier treatment BSC and Polygon get, not the tiered block counts Ethereum and the rollups wait out.

    Withdrawals to Avalanche go only to addresses already on the merchant's whitelist. The route's fee and minimum are shown before the withdrawal is confirmed.

  31. Infra

    Multi-instance fault tolerance

    The platform now runs multi-instance. Block pipelines, sweep workers, webhook deliverers, and outbox processors all coordinate through PostgreSQL advisory locks — exactly one instance is the leader for any given workload at any given time.

    If a leader dies, another instance picks up the lock on the next polling cycle. No split-brain, no duplicate processing, no manual failover. Workers are written to be idempotent by design — if the same block range is processed twice because of a leader handover, the result is identical to processing it once.

    The database is the single source of truth for mutable state. In-memory caches are allowed only for immutable lookups (token decimals, network constants). Anything that can change lives in PostgreSQL.

  32. Pricing

    Per-project fee policies

    Fee policy is now set per project, not only per merchant. The Paymos processing fee on each invoice is computed at creation time and pinned to the invoice — what the customer sees at checkout is what settles on-chain.

    One knob per project: how much of the Paymos processing fee is added on top of the amount the payer sees. Leave it at 0% and the merchant carries the whole fee. Push it to 100% and the payer covers it, and the merchant receives the original invoice amount. Anything in between splits it.

    Fees are stored on the invoice itself, not recomputed later from the deposit amount. Recomputation is a class of accounting bug we don't get to have.

  33. Withdrawals

    Withdrawal watchdog and RBF rebroadcast

    Withdrawals that get stuck on-chain — underpaid gas, nonce conflict, mempool eviction — are now caught by a dedicated watchdog and pushed through automatically.

    On EVM chains the watchdog uses replace-by-fee: if a transaction hasn't been included after a configured timeout, it's rebroadcast with a higher gas price using the same nonce. The original gets superseded in the mempool without changing the operation's identity.

    Broadcast errors are classified by the platform into actionable buckets: revert (handler decides), nonce conflict (rebroadcast with fresh nonce), underpriced (RBF bump), insufficient funds (alert operations). Every classification is exposed in the withdrawal status — no guessing what went wrong.

  34. Auth

    Teams and roles

    Invite teammates to a merchant account without handing out the master credentials. Each member gets a role that expands to a fine-grained set of permissions, enforced at the authorization pipeline level — not just hidden in the UI.

    Withdrawal and whitelist management are Owner-only by default. Admins can manage invoices, webhooks, projects, and team membership, but cannot move money without an explicit grant.

    Manage at Settings.

  35. Sandbox

    Sandbox with payment simulation

    A full sandbox environment runs in parallel with production. Same dashboard, same API surface, separate database, separate signing keys, separate webhook endpoints — nothing crosses the boundary.

    In sandbox you can simulate a payment without touching mainnet at all. Call the simulate endpoint with a stage value (paid, overpaid, underpay, cancel) and the platform computes the right deposit amount and fires the exact lifecycle events production would fire. Webhooks, signatures, retry schedules — everything identical.

    The point: what passes integration tests in sandbox ships in production. No "well, the test environment is different" surprises. Switch with the environment toggle in the top bar.

  36. Branding

    Branded checkout — your logo, your colors

    The hosted payment page now carries the merchant's brand. Logo, style preset, accent, background and surface colors, corner radius — set once per project and applied to every invoice that project issues.

    The widget builder builds on the same brand settings: the configured palette and logo flow into the embeddable widget, so a site running the JS snippet looks consistent with the hosted checkout it links out to.

    Configure branding under Checkout form. The widget builder lives at Low-Code.

  37. Webhooks

    Signed webhooks with at-least-once delivery

    Webhook delivery now uses an outbox pattern. Domain events get written to the outbox in the same database transaction that mutates business state — so an event is durably recorded or the state change never happened. A separate worker picks events up and delivers them.

    Every payload is signed with HMAC-SHA256. The signature is computed over the raw body bytes plus a timestamp header, so replay protection is built in — receivers reject signatures older than the configured window. The secret can be rotated per endpoint from the dashboard.

    Delivery is at-least-once. The retry schedule runs 1 minute, 2, 4, 8, 16, 32 minutes, then 1, 2, 4, 8 hours — eleven attempts in all, spanning about 16 hours. Receivers must be idempotent by event ID, which is included in the payload and the X-Webhook-Id header.

  38. POS

    POS terminal for offline points of sale

    A POS-style terminal is now part of the platform. Single-click invoice creation with a generated QR code — the cashier hands the customer a phone, customer scans, payment lands in the merchant's balance.

    The terminal page is designed for tablets and phones, not desktops. Large amount entry pad, a Pay button carrying the amount keyed so far, status flips to confirmed in real time once the deposit is finalized. A project gets one terminal URL, and it opens on as many phones or tablets as the counter needs — nothing to pair, no per-device setup.

    Open at /pos.

  39. API

    Public Merchant API

    The public REST API is in production. JSON in, JSON out, OpenAPI specification.

    Credentials are scoped by merchant, environment and key type, so a payment key and a payout key are different credentials with different reach — a payment key that leaks into a client bundle creates invoices and nothing else. Rate limits are counted per merchant in fixed-window counters backed by PostgreSQL: one budget, whichever key the call arrived on, and no edge-cache state to keep in sync.

    The auth pipeline is the same one that protects the dashboard: every endpoint carries a permission attribute, ownership is verified against the credential's scope, environment is checked first. The platform refuses to read sandbox data with a production key, full stop.

    Manage credentials at Developer → API Keys. Full docs at /docs.

  40. Widget

    Widget builder with live preview

    The widget builder is in the dashboard. Build a payment widget visually — choose colors, logo, supported networks, default amount, redirect behavior — and see a live preview update as you tweak.

    URL state is encoded into the page so a configured widget is shareable as a single link. Export options: drop-in JavaScript snippet for any site, or an iframe with explicit dimensions for tighter layout control. The widget speaks to a stable public endpoint, so a deployed integration doesn't break when we ship internal changes.

    Open the builder under Low-Code.

  41. Architecture

    CQRS pipeline with compiled authorization

    Every command and query now flows through a typed pipeline: Logging → Validation → Authorization → Retry → Transaction → Handler. Each step is generic over the request type — adding a behavior is one DI registration, not a refactor.

    Authorization is compiled at startup, not resolved by reflection per request. Each command carries a single attribute that maps to a permission scope, an ownership check, and an environment guard. The compiled tables make per-request authorization a dictionary lookup, not an attribute walk.

    Validation runs first because rejecting bad input before it touches the database is the cheapest possible failure. Transaction is the last behavior before the handler — handlers don't open transactions themselves, they get one passed in.

  42. Payment Page

    Hosted payment page

    The hosted payment page shipped today. Customers land on a single URL, pick the network they want to pay from, see the address and QR code, and the page updates live as the deposit arrives.

    How it works under the hood: the payment page is a separate Blazor host running its own SignalR channel. Once the customer picks a network, an address is leased from the platform's per-network pool and the page subscribes to status updates for that specific invoice. No polling, no page refresh, no manual "check status" button.

    One invoice covers every network the project has enabled — issue it once and let the customer settle on Tron, BSC, or Polygon, whichever they happen to hold. The pick is what creates the address: before it there is none, after it there is exactly one, and the amount owed is fixed against that choice.

    Available at /invoice/{invoice-id}.

  43. Dashboard

    Merchant dashboard

    The merchant dashboard is in production. Built on Blazor Server with server-side rendering and an interactive SignalR channel — updates land in the UI without polling and without a separate frontend codebase.

    What ships in the first cut:

    • Overview — daily volume, success rate, pending events, recent deposits (Overview →)
    • Invoices — searchable history with status filters, per-network breakdowns, deposit timeline (Invoices →)
    • Balances — live per-token balances across all enabled networks, with withdrawal history surfaced inline (Balances →)
    • Projects — per-project settings, enabled networks and tokens, branding (Projects →)
    • API keys — credentials, webhook endpoints, event log (API keys →)

    The environment switcher in the top bar swaps between Production and Sandbox without losing your place.

  44. Reliability

    Late-payment guard — expiry decided by chain time

    An invoice can only be resolved if the chain has advanced past the invoice's expiry timestamp. This is enforced at the domain level — not as a check sprinkled in handlers, but as an invariant that fails loudly if violated.

    Why it matters: with confirmation-based finality, a deposit can land "before" expiry from the chain's perspective but be observed by the pipeline "after" expiry — a race condition that, naively handled, creates double-charging or silent fund loss. The platform tracks both the latest processed block and the latest finalized block, and resolution waits for both.

    Regression tests in the suite hammer this invariant on every commit. We don't get to ship a quiet bug here.

  45. Settlement

    Automatic fund consolidation

    Funds arriving on per-invoice addresses are now consolidated automatically into hot wallets without merchant intervention. Sweeping runs as a background worker and is a platform operation — the merchant balance is credited the moment the deposit confirms, not when it's swept.

    On EVM chains the sweep is two-phase: a gas drop transaction first, then the token transfer. The platform pays both. On Tron, the platform covers the network fee out of its own balance so the merchant never sees a gas line item. Failed sweeps are retried with backoff; the watchdog catches anything stuck and surfaces it to operations.

    Result: the merchant gets a balance, not a wallet to maintain.

  46. Rates

    Real-time FX rates

    The rates subsystem is in production. Crypto-to-fiat and crypto-to-crypto conversions now resolve against live market prices instead of stale lookups.

    Primary source is CoinGecko, hit through a round-robin HTTP pool with per-endpoint backoff on rate limits. Quotes are cached in-memory with a short TTL — long enough to absorb the price-fetch latency at checkout, short enough to track real moves. A merchant invoicing in EUR gets a USDT figure resolved at the moment the payer picks a token and network, and that rate is then pinned to the invoice: the market keeps moving, the amount owed does not.

    Stale quotes fall back to the most recent known value; if the upstream stays unavailable past the TTL window, rate-dependent operations fail loudly instead of pricing against a guess.

  47. Tokens

    USDC on every EVM chain

    USDC is now accepted on all six EVM chains we support: Ethereum, BSC, Polygon, Arbitrum, Optimism, and Base. The token registry tracks the canonical contract address per network — no chance of confusing native USDC with a bridged variant.

    Merchants can enable USDC on a per-project basis through the project settings page. Each project carries its own list of accepted tokens; pricing rules apply per token, not per project.

  48. Networks

    TON live — eight networks online

    TON joined the network list. With Tron, six EVM chains, and TON, the platform now settles across eight networks from a single API.

    TON's architecture is nothing like EVM. Smart-contract wallets are deployed per address, jettons live in their own contract pairs (master + per-user wallet), and finality runs through validator sets that rotate every few seconds. Our pipeline carries dedicated TON RPC clients, jetton-wallet derivation, and a TON-specific transfer parser.

    TON transactions use an Ed25519-compatible signing path instead of the secp256k1 scheme used for ECDSA networks, while keeping the same withdrawal destination and audit controls as other Paymos routes.

  49. Networks

    Arbitrum, Optimism, and Base live

    Three L2 rollups joined the network list: Arbitrum One, Optimism, and Base. All three share the EVM pipeline and inherit its prefilter and reorg handling.

    Gas on rollups is materially cheaper than L1 Ethereum, which makes USDC settlement on Base or Arbitrum a real option for low-ticket commerce. Confirmation thresholds are tuned per chain — Base and Optimism settle on sequencer finality once the configured confirmation depth is reached.

    Enable them per project under Projects.

  50. Networks

    Ethereum, BSC, and Polygon live

    Three EVM networks shipped together: Ethereum mainnet, BNB Smart Chain, and Polygon PoS. A single block pipeline drives all three — chain-specific finality strategy plugs in per network.

    Deposit detection runs through eth_getLogs over a sliding window, with a bloom-filter prefilter against the canonical token registry so we only fetch logs for tokens we actually care about. Reorg handling walks back along parent hashes, marks the orphaned blocks, and replays from the new fork point.

    Each chain has its own RPC pool with round-robin load balancing and per-endpoint backoff on rate limits. Adding a new EVM chain is a config change, not a code change.

  51. Tokens

    USDT-TRC20 end-to-end

    The first stablecoin is live: USDT on Tron. Invoices issued in TRC20 USDT are accepted, confirmed against block finality, and credited to merchant balance — sweep fees are paid by the platform, not by the merchant's settled amount.

    Token decimals are read directly from the contract on canonical registry load. The amount math runs against the token's native unit, never against display strings, so rounding errors don't propagate from the UI back into the books.

  52. Networks

    Tron live

    The first network is in production. USDT-TRC20 settles end-to-end through Paymos — invoice creation, deposit detection, confirmation tracking, balance credit.

    How deep a payment must confirm depends on the amount as well as the network, so a small payment clears sooner than a large one. The block pipeline reads from a self-managed RPC pool with automatic failover. Deposit detection runs against a calldata-first parser rather than scanning every log emission — cheaper and more reliable.

    Tron came first. The deepest tier waits for solidification — 19 of Tron's 27 super representatives — which puts a payment above $1,000 at about a minute.

  53. Custody

    Withdrawal destination controls for outbound transfers

    Withdrawals can now go only to merchant-controlled addresses that were approved in advance. An empty whitelist disables outbound transfers instead of falling back to an unrestricted destination.

    Sensitive whitelist changes require step-up authentication and are written to the audit trail. Withdrawal requests remain idempotent, so replaying the same request cannot create a second payout.

    Operators can also stop all outbound transfers or freeze one asset-and-network pair while an incident is investigated.

  54. Ledger

    Double-entry ledger

    A double-entry ledger now records every movement on the platform. Deposits, fees, settlements, refunds, reversals — each one balanced against the other side of the book before the transaction commits.

    Debits equal credits. The check runs inside the same database transaction that writes the entries, so an unbalanced posting cannot land. Drift between merchant balance and on-chain reality is a class of bug we don't get to have.

    The ledger is append-only. Adjustments post as new transactions referencing the original — history stays intact and replayable.

  55. Architecture

    Domain-driven foundations

    The core domain layer is in place. Rich models with private setters, factory methods that validate every input, and value objects for everything monetary.

    Money is a value object pinned to a token, not a raw decimal. Cross-token arithmetic throws at compile-time intent and fails loudly at runtime — adding USDT to TRX is not a thing that compiles or runs.

    Business operations return Outcome<T, Error> rather than throwing exceptions. Exceptions are reserved for actual bugs. Expected failures travel as values through the pipeline.

  56. Engineering

    Engineering kickoff

    Development on Paymos started today. The stack is .NET 10 with Blazor Server for the host layer, PostgreSQL with EF Core 9 for persistence, and MediatR for the command and query pipeline.

    The codebase is laid out as four layers — domain, application, infrastructure, and host — with strict dependency inversion. Domain depends on nothing. Infrastructure adapts to ports defined in application. The host wires it together.

    Test-first is the rule, not the exception. CI runs the full suite on every push.