--- paths: - "licensing-service-startos/licensing-service/src/payment/**" - "licensing-service-startos/licensing-service/src/merchant_profiles.rs" - "licensing-service-startos/licensing-service/src/btcpay/**" - "licensing-service-startos/licensing-service/src/api/purchase.rs" - "licensing-service-startos/licensing-service/src/api/btcpay_authorize.rs" - "licensing-service-startos/licensing-service/src/api/zaprite_authorize.rs" - "licensing-service-startos/licensing-service/src/api/payment_provider.rs" - "licensing-service-startos/licensing-service/src/api/merchant_profiles.rs" - "licensing-service-startos/licensing-service/src/subscriptions.rs" - "licensing-service-startos/licensing-service/src/reconcile.rs" - "licensing-service-startos/licensing-service/migrations/0020_merchant_profiles.sql" - "licensing-service-startos/licensing-service/migrations/0021_invoice_provider_link.sql" - "licensing-service-startos/licensing-service/migrations/0022_btcpay_state_profile.sql" --- # Payments & the multi-provider / merchant-profile model Full design spec: `plans/multi-provider-payment-model.md`. The companion email plan `plans/keysat-smtp-emails.md` is **superseded** — Keysat does not send email; operators receive events via webhooks and run their own email pipelines (see the "Operability & alerts" item in `ROADMAP.md`). ## Model One Keysat instance can host multiple businesses. The data model (migrations 0020–0022): ``` merchant_profiles (1) ──< (N) payment_providers (1) ──< (N) products (1) ──< (N) subscriptions [profile + provider snapshotted on create] ``` - **merchant_profiles** — business identity: name, branding, post-purchase redirect. (The `smtp_*` columns from migration 0020 are **dormant** — never read by any send path; the email plan was dropped.) Exactly one `is_default`, auto-created at first boot from the operator-name setting. - **payment_providers** — one row per configured BTCPay/Zaprite account, attached to a profile (`kind` ∈ `btcpay`|`zaprite`). - **merchant_profile_rail_preferences** — tie-breaker `(profile, rail) → provider` when a profile has two providers serving the same rail. Products attach to a profile via `repo::set_product_merchant_profile` (validates the profile exists → 404, else UPDATE), called **post-write** from the create / update handlers — same pattern as the entitlements catalog. The admin product form renders the profile `