Billing Architecture

Strategy

Subscription model plus event enrollment payments. No usage-based metering — shooters never lose access to their data. Subscriptions happen on the web (dashboard or marketing site), not through app stores. Event enrollment can happen in-app (real-world service, no app store cut). Billing logic lives in the server crate (private) behind a BillingProvider trait interface. White-label deployments do not use this system — they are billed via traditional enterprise contracts.

Payment Providers

Authorize.net (Current)

T.REX ARMS already uses Authorize.net for existing business operations. Stripe imposes high revenue thresholds for firearms-industry clients that are not yet met. Authorize.net is the payment provider at launch.

Stripe (Future)

When revenue thresholds are met, Stripe can replace or supplement Authorize.net. The BillingProvider trait interface makes this a provider swap, not an architectural change. Stripe’s subscription management, usage metering, and webhook system are more mature for recurring billing at scale.

Provider Trait Interface

trait BillingProvider: Send + Sync {
    fn create_subscription(&self, org_id: &OrgId, plan: &Plan) -> Result<Subscription>;
    fn cancel_subscription(&self, org_id: &OrgId) -> Result<()>;
    fn check_subscription(&self, org_id: &OrgId) -> SubscriptionStatus;
    fn process_event_payment(&self, payment: &EventPaymentRequest) -> Result<PaymentResult>;
    fn handle_webhook(&self, payload: &[u8]) -> Result<()>;
}

AuthorizeNetBilling now, StripeBilling later — same interface, swapped via config.yaml.

Billing Model

Org Subscriptions (Pro)

Organizations pay a flat subscription for Pro features. No per-seat charges, no storage limits, no usage metering. A shooter’s data is always accessible regardless of subscription status — downgrading or canceling restricts access to Pro features, never to data.

Orgs manage their subscription through the dashboard. Tier upgrades, payment method changes, and invoices are all web-based.

Individual User Upgrades

Free-tier users can upgrade to Pro for additional features. No storage limits on any tier — personal score history is always retained and accessible.

Event Enrollment Payments

Instructors can charge for event enrollment — paying to join a class, course, or training event. This is a real-world service (attending a physical training event), not a digital good, so it can be processed in-app without app store payment systems or revenue cuts.

Where Payment Happens

Subscriptions — Web Only

Users manage Pro subscriptions through the dashboard or marketing site. Mobile apps display subscription status (read from the server) but never initiate subscription payments.

This avoids:

  • App store revenue cut (15-30%)
  • App store billing dependency — if deplatformed, billing continues via the web
  • Platform-specific billing code — no StoreKit (iOS) or Google Play Billing to maintain

In-app subscription flow:

  1. User taps “Upgrade to Pro” in the mobile app
  2. App opens a link to the RDP website (dashboard or marketing site)
  3. User pays via Authorize.net on the web
  4. User returns to the app
  5. App checks subscription status from the server — Pro features unlock

The app never touches payment for subscriptions. It reads subscription state from the server.

Event Enrollment — In-App or Web

Event enrollment payments can be processed in-app because they are payments for real-world services (like Eventbrite, Uber, or Airbnb). Apple and Google allow third-party payment processors for real-world services — no app store cut applies.

In-app event enrollment flow:

  1. Shooter views an event with a fee
  2. Taps “Enroll” — payment form appears in-app
  3. Payment processed via Authorize.net
  4. Server confirms payment, adds shooter to roster
  5. Shooter receives confirmation

Server Integration

Billing is an isolated module in the server crate:

apps/server/
  src/
    auth/
    billing/            # BillingProvider trait, Authorize.net implementation, subscription state
    handlers/
    ...

Key Flows

Org subscribes (web):

  1. Org admin clicks “Subscribe” in the dashboard
  2. Dashboard presents Authorize.net payment form (hosted or embedded)
  3. Authorize.net processes payment, notifies our server
  4. Server updates the org’s subscription status in Postgres
  5. Features unlock based on subscription tier

Event enrollment (in-app or web):

  1. Shooter selects an event with a fee
  2. Payment processed via Authorize.net
  3. Server confirms payment, adds shooter to event roster
  4. Instructor sees enrollment in their dashboard

Subscription state check:

  1. API handlers check the org’s or user’s subscription status before allowing tier-gated operations
  2. Subscription status is cached in Postgres — not checked against the payment provider on every request

Data Model Additions

OrgSubscription {
  id
  org_id              → Org
  provider            (authorize_net | stripe)
  provider_customer_id
  provider_subscription_id
  status              (active | past_due | canceled | trialing)
  plan_tier
  current_period_start
  current_period_end
  created_at
  updated_at
}

UserSubscription {
  id
  user_id             → User
  provider            (authorize_net | stripe)
  provider_customer_id
  provider_subscription_id
  status              (free | active | past_due | canceled)
  created_at
  updated_at
}

EventPayment {
  id
  event_id            → Event
  user_id             → User
  provider            (authorize_net | stripe)
  provider_transaction_id
  amount_cents
  status              (pending | completed | refunded | failed)
  created_at
}

The provider field and provider_* IDs allow the same schema to work with Authorize.net now and Stripe later. No migration needed on provider swap.

White-Label / On-Prem

White-label deployments do not use payment processing. These customers pay T.REX ARMS via enterprise contracts (invoices, purchase orders). The billing module uses NoOpBilling — all features are unlocked by default. Event enrollment is free (no payment flow).

App Store Independence

Billing is designed to survive deplatforming:

  • Subscriptions are web-only — billing continues regardless of app store status
  • Event enrollment uses third-party payment (Authorize.net), not app store IAP
  • The PWA provides app-like access independent of app stores
  • Billing revenue is never routed through app store payment systems