Payment architecture
How HyoNova Commerce works — one commerce core over payment provider adapters, capabilities instead of provider names, routing with a safe fallback, checkout, subscriptions, invoices and security.
On this page
A payment method appears only after HyoNova has set up its payment provider.
HyoNova Commerce is how HyoNova sells its plans to companies: checkout, subscriptions, invoices, payments, refunds and provider events. This page explains its design for developers and IT teams who need to understand how payments behave. It is not a public API: companies use it through the product, and HyoNova operators through Control Center.
Two systems that never share anything#
| Platform billing | Company finance | |
|---|---|---|
| Seller → buyer | HyoNova → a company | A company → its own customers |
| Records | Plans, prices, subscriptions, HyoNova invoices, payments, refunds | The company's customer invoices, payments and expenses |
| Payment accounts | HyoNova's provider accounts | None |
The two keep separate data, credentials and ledgers. Company finance code never loads a platform payment provider, and company records are isolated per company by row-level security in the database.
One core over provider adapters#
Product (company owners) Control Center (HyoNova operators)
\ /
v v
┌──────────── Commerce core ────────────┐
│ catalogue · billing profiles · │
│ checkout · orders · subscriptions · │
│ seats · invoices · payments · │
│ refunds · credits · discounts · │
│ dunning · provider events · │
│ reconciliation · settlements · │
│ revenue metrics │
└──────────────────┬────────────────────┘
│ asks for capabilities, never provider names
┌──────────────────▼────────────────────┐
│ Provider adapters │
│ FastSpring · Triple-A · │
│ Bank transfer (native) · Stripe │
└───────────────────────────────────────┘The commerce core is the single source of truth. A provider is an adapter described by a manifest: its capabilities (for example checkout, subscriptions, proration, refunds, partial_refunds, customer_portal, merchant_of_record, bank_transfer, manual_confirmation, reconciliation, health_check), the payment methods it takes (card, crypto, bank_transfer), how renewals are collected (provider or invoice_per_cycle), its settings and secret fields, its webhook, and the only API hosts it may call. The core asks "can this provider refund partially?" — never "is this FastSpring?". Asking for a capability a provider does not declare returns the typed error CAPABILITY_UNSUPPORTED.
| Provider | Methods | Renewals | Status |
|---|---|---|---|
| FastSpring | card | Collected by FastSpring (Merchant of Record) | Supported · Ready to configure |
| Triple-A | crypto | A HyoNova invoice per period | Supported · Ready to configure |
| Bank transfer | bank_transfer | A HyoNova invoice per period, confirmed by an operator | Supported · Ready to configure |
| Stripe | card | Collected by Stripe | Disabled |
Routing and the safe fallback#
Each payment method is routed to one provider instance. A route is usable only when its instance is enabled, fully configured and not degraded. Enabled secondary instances are fallback candidates, but HyoNova falls back only when the routed provider could not be reached while creating the checkout, and only to a provider that collects renewals the same way. After a provider checkout exists, the provider never changes — a buyer cannot be charged twice.
Checkout#
- The owner chooses a price and a method. The server validates the company, price, plan, currency, seats, billing profile and route, and refuses a second open checkout.
- HyoNova writes its checkout record first (and, for bank transfer and crypto, issues a HyoNova invoice), then asks the provider for a hosted checkout, carrying HyoNova's checkout id as provider metadata.
- The buyer pays on the provider's page (card, crypto) or by bank transfer.
- The success redirect is only for the user's experience. Nothing activates because the browser came back. The subscription changes only on a verified provider webhook, an operator's bank confirmation, or reconciliation — all through the same event path. See Payment webhooks.
Subscriptions#
States: trial, active, past_due, canceled, paused, plus a cancel_at_period_end flag.
- Cancel at period end keeps access until the period ends; the provider's deactivation (or HyoNova, for invoice-collected subscriptions) ends it.
- Past due starts a grace period; paid modules keep working only while the grace end date is set and in the future. After it, paid modules stop. Data is never deleted and billing stays reachable.
- Trial expiry is enforced only in the general product stage; during early access trials keep working.
- Plan changes: an upgrade of a provider-collected subscription applies now with the provider's proration; downgrades, interval switches and every change of an invoice-collected subscription apply at the period end. A plan switch during a trial is free. HyoNova never invents a proration amount.
- Seats: one definition of a billable seat — active, invited or pending members holding an assigned seat. Guests, removed and suspended members never count.
Invoices#
- Provider invoices (FastSpring as Merchant of Record) are mirrored: amounts, status and a link to the provider's legal document.
- HyoNova invoices (bank transfer, crypto) get an atomic, gap-free number
PREFIX-YYYY-000123, a unique payment referenceHN-XXXX-XXXX, and frozen copies of the customer and issuer details. Their identity and amounts cannot change after issue. Tax is 0 unless a tax rule exists; HyoNova has no tax engine of its own.
Security#
- Credentials live only in the secret store (AWS Secrets Manager in production), one entry per provider instance. The database keeps a reference. Credentials are never returned by any API, logged or audited.
- Isolation: every company-owned commerce table is protected by row-level security.
- Permissions:
billing.readandbilling.managefor companies; operators also needproviders.manageorplans.manage, a recent step-up and a reason for every change. - CSRF protection is skipped only for the signed webhook path.
- Outbound calls go only to the hosts each provider allow-lists, over HTTPS, with timeouts and bounded retries for idempotent calls.
- PCI scope is limited to hosted checkout pages. HyoNova stores only the card brand, last four digits and expiry.
Observability#
The core emits counters and structured log lines without secrets or personal data: checkout_created, checkout_provider_error, checkout_completed, checkout_expired, payment_succeeded, payment_failed, webhook_received, webhook_invalid, webhook_duplicate, webhook_processed, subscription_changed, invoice_paid, refund_succeeded, reconciliation_mismatch, reconciliation_error, provider_api_error, provider_latency, provider_configured, provider_test.
Read next#
Payment webhooks
How HyoNova receives payment provider webhooks — raw body, signature verification, deduplication, ordering, atomic processing, and which HTTP answers make providers retry.
Requires setupBuilt, but works only after a supporting service is configured for your workspace.Provider events
The canonical billing events HyoNova maps every payment provider to, how FastSpring, Triple-A and Stripe events translate, and the billing timeline and notices they produce.
Requires setupBuilt, but works only after a supporting service is configured for your workspace.Money handling
How HyoNova represents and converts money — integer minor units with an ISO 4217 currency, string arithmetic for provider decimals, rounding rules, and no currency conversion.
Requires setupBuilt, but works only after a supporting service is configured for your workspace.Idempotency in payments
Every way HyoNova Commerce makes repeated requests, retried webhooks and concurrent workers safe — keys, unique records, versions and once-only notifications.
Requires setupBuilt, but works only after a supporting service is configured for your workspace.Adding a payment provider
How a new payment provider joins HyoNova Commerce — one adapter module with a manifest, canonical events and contract tests, registered in one list, without touching the core.
Requires setupBuilt, but works only after a supporting service is configured for your workspace.Was this page helpful?
Thanks. Page ratings are not collected yet, so nothing was sent. If something is missing or wrong, see the Get help page.