Skip to content

Architecture: Mobayilo

For Mobayilo, the source of truth is:

  • Rails 8.1 monolith
  • Hotwire (Turbo/Stimulus) views
  • Server-rendered pages + small JSON endpoints where needed

Mobayilo is a mobile-first web app that lets users place international calls from their browser (WebRTC) to real phone numbers (PSTN) using Twilio, funded by a pay-as-you-go credit wallet charged via Stripe.

  • Rails 8.1 (monolith)
  • Hotwire (Turbo + Stimulus) for SPA-like UX without a JS-heavy frontend
  • Tailwind CSS for mobile-first responsive UI
  • PostgreSQL for users, balances, call logs, and ledger transactions
  • Twilio Programmable Voice for PSTN + browser WebRTC
  • Stripe for one-time credit purchases
Browser (Hotwire + Twilio Voice JS)
↕ HTTPS
Rails 8.1 (HTML + JSON endpoints + webhooks)
PostgreSQL
↕ ↕
Stripe webhooks Twilio webhooks/TwiML
  • User: authentication + settings.
  • Account wallet: cached_balance_cents is the operational balance cache; Transaction ledger is the source of truth.
  • Transaction (ledger): every balance mutation (purchase, call charge, refund, bonus).
  • Call: one row per call attempt; stores Twilio SIDs, status, duration, computed cost.
  • CallRate: price per minute by destination (country/prefix-based).
  • Token issuance: generate Twilio capability/access tokens for the browser SDK after checking user status + balance.
  • Call orchestration: accept a “call initiate” action and hand off to Twilio with the destination number.
  • TwiML generation: return TwiML that instructs Twilio how to connect calls.
  • Webhook processing:
    • Twilio status callbacks update Call state and final duration.
    • Stripe events create/complete a credit purchase transaction.
  • Billing: compute final call cost and create a ledger entry to deduct credits exactly once (idempotent).
  • Dialer is the primary screen: large tap targets, bottom-aligned controls, minimal text.
  • Use a bottom nav for 3–5 main areas (Dial, History, Credits, Settings).
  • Optimize for flaky networks: optimistic UI + clear retry states.
  • Avoid heavy client state; rely on Turbo and server truth.
  • Auth: sessions, registrations, passwords (Devise)
  • Dialer: GET /call, POST /calls (initiate)
  • Calls: GET /history, GET /calls/:id
  • Credits: GET /credits, POST /credits/purchase, GET /transactions
  • Rates: GET /rates, GET /rates/calculate?phone_number=...
  • Webhooks:
    • POST /api/webhooks/twilio/voice (TwiML)
    • POST /api/webhooks/twilio/status (status updates)
    • POST /api/webhooks/stripe (payment events)
  • Post-process Twilio call completion to compute/charge cost (idempotent).
  • Periodic sync/update of Twilio pricing (optional) and refreshing CallRate.
  1. Rails app skeleton (Tailwind + Hotwire) + Devise auth.
  2. Wallet + ledger transactions (DB + UI).
  3. Stripe top-ups (PaymentIntent + webhook crediting).
  4. Twilio Voice JS in dialer (token issuance + basic call connect/disconnect).
  5. Twilio status webhook → call logs → cost deduction.
  6. History + rates calculator UI; basic admin tools.
  • Billing rounding (MVP): per-started-minute.
  • JS bundling: esbuild (recommended) vs importmap.
  • Call status UI: Turbo Streams only vs add Action Cable for richer events.
  • Remove any dev-only SMTP CRL bypass (SMTP_DISABLE_CRL_CHECK / VERIFY_NONE) before launch.