Tip
๐ Ship your next Rails app 10x faster! I've built RailsFast, a production-ready Rails boilerplate template that comes with everything you need to launch a software business in days, not weeks. Go check it out!
profitable allows you to calculate the MRR, ARR, churn, LTV, ARPU, total revenue & estimated valuation of your pay-powered Rails SaaS app, and display them in a simple dashboard. It also provides handy methods you can use independently if you don't want the full dashboard.
pay is the easiest way of handling payments in your Rails application. Think of profitable as the complement to pay that calculates business SaaS metrics like MRR, ARR, churn, total revenue & estimated valuation directly within your Rails application.
Usually, you would look into your Stripe Dashboard or query the Stripe API to know your MRR / ARR / churn โ but when you're using pay, you already have that data available and auto synced to your own database. So we can leverage it to make handy, composable ActiveRecord queries that you can reuse in any part of your Rails app (dashboards, internal pages, reports, status messages, etc.)
Think doing something like: "Current MRR: $#{Profitable.mrr}" or "Your app is worth $#{Profitable.valuation_estimate("3x")} at a 3x valuation"
Add this line to your application's Gemfile:
gem 'profitable'Then run bundle install.
Provided you have a valid pay installation (Pay::Customer, Pay::Subscription, Pay::Charge, etc.) everything is already set up and you can just start using Profitable methods right away.
Current MRR processor coverage is verified for stripe, braintree, paddle_billing, and paddle_classic.
For Stripe, metered subscription items are intentionally excluded from fixed run-rate metrics like mrr, arr, new_mrr, and churned_mrr.
Important
profitable does not yet normalize MRR for every processor that pay supports.
If a subscription comes from an unsupported processor such as lemon_squeezy, it will currently contribute 0 to processor-adapter-dependent metrics until an adapter is added.
Verified processor-adapter coverage today:
stripebraintreepaddle_billingpaddle_classic
Metrics that depend on processor-specific subscription amount parsing include mrr, arr, new_mrr, churned_mrr, mrr_growth, mrr_growth_rate, lifetime_value, time_to_next_mrr_milestone, and MRR-derived fields in summaries.
Metrics based primarily on Pay::Charge and generic subscription lifecycle fields are much more portable across processors, including all_time_revenue, revenue_in_period, ttm_revenue, revenue_run_rate, customer counts, subscriber counts, and churn calculations.
profitable also provides a simple dashboard to see your main business metrics.
In your config/routes.rb file, mount the profitable engine:
mount Profitable::Engine => '/profitable'It's a good idea to make sure you're adding some sort of authentication to the /profitable route to avoid exposing sensitive information:
authenticate :user, ->(user) { user.admin? } do
mount Profitable::Engine => '/profitable'
endYou can now navigate to /profitable to see your app's business metrics like MRR, ARR, churn, etc.
All methods return numbers that can be converted to a nicely-formatted, human-readable string using the to_readable method.
Profitable.mrr: Monthly Recurring Revenue (MRR) from subscriptions that are billable right nowProfitable.arr: Annual Recurring Revenue (ARR), calculated as currentmrr * 12, not trailing revenueProfitable.ttm: Founder-friendly shorthand alias forttm_revenueProfitable.ttm_revenue: Trailing twelve-month revenue, net of refunds whenamount_refundedis presentProfitable.revenue_run_rate(in_the_last: 30.days): Recent revenue annualized (useful for TrustMRR-style revenue multiples)Profitable.all_time_revenue: Net revenue since launchProfitable.revenue_in_period(in_the_last: 30.days): Net revenue (recurring and non-recurring) in the specified periodProfitable.recurring_revenue_in_period(in_the_last: 30.days): Only recurring revenue in the specified periodProfitable.recurring_revenue_percentage(in_the_last: 30.days): Percentage of revenue that is recurring in the specified periodProfitable.new_mrr(in_the_last: 30.days): Full MRR from subscriptions that first became billable in the specified periodProfitable.churned_mrr(in_the_last: 30.days): MRR lost due to churn in the specified periodProfitable.average_revenue_per_customer: Average revenue per customer (ARPC)Profitable.lifetime_value: Estimated customer lifetime value (LTV)Profitable.estimated_valuation(at: "3x"): ARR-based valuation heuristicProfitable.estimated_arr_valuation(at: "3x"): Explicit ARR-based valuation heuristicProfitable.estimated_ttm_revenue_valuation(at: "2x"): TTM revenue-based valuation heuristicProfitable.estimated_revenue_run_rate_valuation(at: "2x", in_the_last: 30.days): Recent revenue run-rate valuation heuristic
Profitable.total_customers: Total number of customers who have ever monetized through a paid charge or a paid subscription stateProfitable.total_subscribers: Total number of customers who have ever reached a paid subscription state (trial-only subscriptions do not count)Profitable.active_subscribers: Number of customers with subscriptions that are billable right nowProfitable.new_customers(in_the_last: 30.days): Number of first-time customers added in the period based on first monetization date, not signup dateProfitable.new_subscribers(in_the_last: 30.days): Number of customers whose subscriptions first became billable in the specified periodProfitable.churned_customers(in_the_last: 30.days): Number of customers who churned in the specified period
Profitable.churn(in_the_last: 30.days): Churn rate for the specified periodProfitable.mrr_growth_rate(in_the_last: 30.days): MRR growth rate for the specified periodProfitable.time_to_next_mrr_milestone: Estimated time to reach the next MRR milestone
Profitable.mrr_growth(in_the_last: 30.days): Calculates the absolute MRR growth over the specified periodProfitable.mrr_growth_rate(in_the_last: 30.days): Calculates the MRR growth rate (as a percentage) over the specified period
Profitable.time_to_next_mrr_milestone: Estimates the time to reach the next MRR milestone
# Get the current MRR
Profitable.mrr.to_readable # => "$1,234"
# Get the number of new customers in the last 60 days
Profitable.new_customers(in_the_last: 60.days).to_readable # => "42"
# Get the churn rate for the last quarter
Profitable.churn(in_the_last: 3.months).to_readable # => "12%"
# You can specify the precision of the output number (no decimals by default)
Profitable.new_mrr(in_the_last: 24.hours).to_readable(2) # => "$123.45"
# Get the estimated valuation at 5x ARR (defaults to 3x if no multiple is specified)
Profitable.estimated_arr_valuation(multiple: 5).to_readable # => "$500,000"
# Get trailing twelve-month revenue
Profitable.ttm_revenue.to_readable # => "$123,456"
# Founder-friendly shorthand for trailing twelve-month revenue
Profitable.ttm.to_readable # => "$123,456"
# Get recent revenue annualized (useful for TrustMRR-style revenue multiples)
Profitable.revenue_run_rate(in_the_last: 30.days).to_readable # => "$96,000"
# `estimated_valuation` remains as a backwards-compatible alias of `estimated_arr_valuation`
Profitable.estimated_valuation(at: "4.5x").to_readable # => "$450,000"
# Be explicit about the denominator when comparing marketplace comps
Profitable.estimated_ttm_revenue_valuation(2).to_readable
Profitable.estimated_revenue_run_rate_valuation(2.7, in_the_last: 30.days).to_readable
# Get the time to next MRR milestone
Profitable.time_to_next_mrr_milestone.to_readable # => "26 days left to $10,000 MRR"All time-based methods default to a 30-day period if no time range is specified.
Numeric values are returned in the same currency as your pay configuration. The to_readable method returns a human-readable format:
- Currency values are prefixed with "$" and formatted as currency.
- Percentage values are suffixed with "%" and formatted as percentages.
- Integer values are formatted with thousands separators but without currency symbols.
For more precise calculations, you can access the raw numeric value:
# Returns the raw MRR integer value in cents (123456 equals $1.234,56)
Profitable.mrr # => 123456Revenue methods are net of refunds when amount_refunded is present on pay_charges.
mrr_growth_rate: This calculation compares the MRR at the start and end of the specified period. It assumes a linear growth rate over the period, which may not reflect short-term fluctuations. For more accurate results, consider using shorter periods or implementing a more sophisticated growth calculation method if needed.time_to_next_mrr_milestone: This estimation is based on the current MRR and the recent growth rate. It assumes a constant growth rate, which may not reflect real-world conditions. The calculation may be inaccurate for very new businesses or those with irregular growth patterns.
profitable exposes both standard recurring revenue metrics (MRR, ARR) and trailing actuals (TTM revenue) on purpose.
These metrics are related, but they are not interchangeable:
| Metric | What it means | Best for | What it is not |
|---|---|---|---|
MRR |
Monthly Recurring Revenue from subscriptions that are billable right now | Operating cadence, near-term momentum, tracking upgrades/downgrades | It's not monthly cash collected from all sources |
ARR |
Annual Recurring Revenue, calculated as the current recurring base annualized | Forecasting recurring scale, board/investor reporting, recurring-revenue quality | It's not a historical last-12-month revenue |
MRR * 12 |
Simple annualization of the current monthly recurring base | Fast ARR approximation when the base is normalized monthly | It's not TTM revenue or TTM profit |
TTM revenue |
Actual revenue collected over the last 12 months | Buyer-facing historical actuals, smoothing seasonality, sanity-checking ARR | It's not a forward recurring run-rate |
TTM profit |
Actual profit over the last 12 months | Small bootstrapped SaaS exits, ROI-minded buyers, earnings-based multiples | It's not something profitable can derive from pay alone |
ARRis a run-rate metric. Stripe describes it as revenue you "expect to earn in a year" and notes thatARR = MRR ร 12.TTMis a trailing metric. CFI defines it as the "most recent 12-month period" and uses it for reported actuals such as revenue and EBITDA.TTM revenuetells you what customers actually paid over the last year.TTM profittells you what the business actually kept after costs. This is often what smaller acquisition buyers care about most, but it requires cost data outsidepay.- In acquire-style market reports,
TTMcan refer to bothTTM profitandTTM revenuedepending on the chart. The denominator must always be stated explicitly. - In
profitable, the shorthand methodttmis defined to meanttm_revenuebecause the gem does not yet model costs or profit.
In other words:
ARRanswers: "What is my current recurring run-rate? What do I expect to earn in a year?"TTM revenueanswers: "What did I actually collect over the last year?"
Profitable.mrr: Monthly Recurring Revenue (MRR) from subscriptions that are billable right nowProfitable.arr: Annual Recurring Revenue (ARR), calculated from current MRRProfitable.ttm: shorthand alias forttm_revenueProfitable.ttm_revenue: trailing 12-month revenue, net of refunds whenamount_refundedis presentProfitable.revenue_run_rate: recent revenue annualized to a yearly run-rateProfitable.estimated_valuation: ARR-multiple heuristicProfitable.estimated_ttm_revenue_valuation: TTM revenue heuristicProfitable.estimated_revenue_run_rate_valuation: recent revenue run-rate heuristic
profitable does not calculate TTM profit, because payroll, contractor spend, hosting, support, software tools, taxes, and owner add-backs do not live inside pay.
- If you're operating the business week to week:
MRRis usually the best pulse metric. - If you want to understand your current subscription run-rate:
ARRis the right metric. - If you're preparing buyer materials for a bootstrapped SaaS exit: add
TTM revenueand your ownTTM profit. - If your business has meaningful one-time revenue, services, setup fees, or seasonal swings:
TTM revenuematters more thanARR. - If you are speaking to serious SaaS buyers about revenue quality: pair
ARRwith churn, growth, concentration, and margins.
These are short excerpts from current market and finance sources, followed by why they matter for profitable.
-
Acquire.com Biannual Multiples Report (Jan 2026): "anchor valuation on profit" Acquire says the January 2026 report is focused "entirely on profit multiples," which is highly relevant for smaller bootstrapped SaaS exits. In the same report, some visual breakdowns segment businesses by
TTM revenuebands, so it is important not to assume one bareTTMlabel means the same thing everywhere. -
Acquire.com Biannual Multiples Report, January 2024: "4.3x TTM profit" The earlier report gives a concrete historical benchmark for how these profit multiples looked on the marketplace.
-
Acquire.com 2025 webinar recap: "$100k-$1M in TTM revenue" Acquire says that cohort averaged
4.35x, which is a useful live-market anchor for micro-SaaS exits. -
Acquire.com SaaS valuation multiples guide: "5x to 15x ARR" Stronger recurring SaaS businesses are also routinely discussed in ARR-multiple terms, especially when growth and retention are strong.
-
Stripe on ARR: "ARR = ยฃ50,000 x 12 = ยฃ600,000" This is the clearest shorthand for why
ARRis a run-rate, not a trailing actual. -
CFI on TTM: "most recent 12-month period" This is why
ttm_revenuebelongs besidearr: it measures trailing actuals, not a projection. -
Quiet Light on selling SaaS: "EBITDA or SDE" Quiet Light explicitly says smaller SaaS businesses are often valued on earnings, not revenue, which is why
TTM profitmatters. -
Quiet Light on larger SaaS: "ARR of $1M or more" The same source says larger SaaS businesses may qualify for revenue multiples, which is why
ARRbecomes more important as the business scales. -
Software Equity Group, 3Q25 SaaS M&A: "5.4x" SEG reported average SaaS M&A valuations of
5.4xrevenue in 3Q25, which is useful context for larger, more institutional software transactions. -
TrustMRR live listing example: "$164,819 TTM revenue" Live marketplaces increasingly show
TTM revenue,TTM profit, andARRside by side, which matches how buyers actually compare deals. -
TrustMRR FAQ: "asking price divided by annualized revenue" TrustMRR explicitly defines its marketplace multiple as asking price divided by
last 30 days revenue ร 12, so its multiple is a revenue run-rate multiple, not an ARR multiple. -
TrustMRR FAQ: "Only aggregate revenue metrics" TrustMRR says it only pulls revenue-level aggregates from payment providers, which is another reason its native multiple is revenue-based rather than profit-based.
-
TrustMRR FAQ: "profit margin for the last 30 days" TrustMRR asks sellers to provide profit margin separately when listing for sale, which reinforces that profit-based heuristics need cost inputs outside the payment provider.
estimated_valuationis intentionally simple. It is kept as a backwards-compatible ARR heuristic. Preferestimated_arr_valuationin new code when you want the denominator to be explicit.- Do not compare an ARR multiple and a TTM profit multiple as if they were the same kind of number. They are based on different denominators.
- A
4x TTM profitdeal, a2x TTM revenuedeal, and an8x ARRdeal can all describe reasonable SaaS outcomes in different buyer segments. - If two businesses both have
$300k ARR, the one with lower churn, better margins, lower concentration, and cleaner growth usually deserves the higher multiple. - If two businesses both have
$300k TTM revenue, the one with stronger profit and more recurring revenue usually deserves the higher price.
These are rough, source-backed heuristics. They are not interchangeable.
| SaaS profile | Common denominator | Rough multiple | Source |
|---|---|---|---|
| Smaller profitable SaaS on Acquire.com (2024-2025 confirmed transactions) | TTM profit |
3.9x median |
Acquire.com Jan 2026 report |
Micro-SaaS under $100k TTM revenue |
TTM profit |
3.55x average |
Acquire.com webinar recap |
Micro-SaaS with $100k-$1M TTM revenue |
TTM profit |
4.35x average |
Acquire.com webinar recap |
| TrustMRR marketplace listings | Annualized last 30d revenue |
often roughly 0.6x-5.5x ask multiples |
TrustMRR homepage snapshot |
| Mid-6-figure ARR SaaS | TTM revenue |
2x-4x revenue |
Acquire.com founder-driven acquisition recap |
| Older Acquire.com SaaS baseline | TTM revenue or TTM profit |
2-3x revenue or 5x profit |
Acquire.com 7-8 figures webinar recap |
| Strong recurring SaaS with high growth and retention | ARR |
5x-15x ARR |
Acquire.com SaaS valuation multiples guide |
How to read this table:
- Smaller bootstrapped SaaS buyers on Acquire-style marketplaces often underwrite on
TTM profit. - If profit is low or intentionally reinvested, buyers may fall back to
TTM revenue. - TrustMRR listing multiples are a secondary comparison set: they are based on recent revenue run-rate, specifically
last 30 days revenue ร 12. - Higher-quality SaaS with real scale, low churn, and strong growth is more likely to be discussed in
ARRterms.
You can only multiply a metric by a multiple if the denominator matches.
This is already built into the gem:
# Example: 6x ARR
Profitable.estimated_arr_valuation(multiple: 6).to_readableUse this when:
- your business is strongly recurring,
- churn and retention are solid,
- and you want a run-rate-based heuristic.
This is useful when buyers care more about trailing actuals than annualized run-rate:
ttm_revenue_cents = Profitable.ttm_revenue.to_i
low_estimate_cents = (ttm_revenue_cents * 2.0).round
high_estimate_cents = (ttm_revenue_cents * 4.0).roundUse this when:
- the business has meaningful one-time revenue,
- profit is thin because you are reinvesting,
- or the buyer is thinking in revenue-band terms.
This is the closest match to TrustMRR-style marketplace multiples:
# Default: annualized last-30-days revenue
Profitable.revenue_run_rate.to_readable
Profitable.estimated_revenue_run_rate_valuation(2.7).to_readableUse this when:
- you're comparing against TrustMRR listings,
- the market is quoting a multiple on recent revenue rather than ARR,
- and you want the denominator to match the marketplace comp.
profitable cannot calculate this yet because it does not know your costs.
# You need to compute this outside of profitable:
ttm_profit_cents = your_ttm_profit_cents
low_estimate_cents = (ttm_profit_cents * 3.5).round
high_estimate_cents = (ttm_profit_cents * 4.35).roundUse this when:
- the business is a smaller profitable micro-SaaS,
- the buyer is focused on ROI and cash flow,
- or you're comparing yourself to Acquire.com-style marketplace comps.
After checking out the repo, install dependencies:
bundle installThe gem includes a Minitest test suite. Run it with:
# Run all tests
bundle exec rake testThis gem uses Appraisal to test against multiple versions of the Pay gem, ensuring compatibility across Pay 7.x through 11.x.
Generate appraisal gemfiles:
bundle exec appraisal installRun tests against a specific Pay version:
# Test against Pay 10.x
bundle exec appraisal pay-10.0 rake test
# Test against Pay 11.x
bundle exec appraisal pay-11.0 rake testRun tests against all Pay versions:
bundle exec appraisal rake testTests run on SQLite by default, but the gem supports:
- PostgreSQL (9.3+)
- MySQL (5.7.9+)
- MariaDB (10.2.7+)
- SQLite (3.9.0+)
The gem automatically detects your database adapter and uses the appropriate JSON query syntax.
To install this gem onto your local machine, run bundle exec rake install.
- Calculate split by plan / add support for multiple plans (churn by plan, MRR by plan, etc) โ not just aggregated
- Calculate MRR expansion (plan upgrades), contraction (plan downgrades), etc. like Stripe does
- Add active customers (not just total customers)
- Add % of change over last period (this period vs last period)
- Calculate total period revenue vs period recurring revenue (started, but not sure if accurate)
- Add revenue last month to dashboard (not just past 30d, like previous month)
- Support other currencies other than USD (convert currencies)
- Make sure other payment processors other than Stripe work as intended (Paddle, Braintree, etc. โ I've never used them)
- Add a way to input monthly costs (maybe via config file?) so that we can calculate a profit margin %
- Allow dashboard configuration via config file (which metrics to show, etc.)
- Return a JSON in the dashboard endpoint with main metrics (for monitoring / downstream consumption)
Bug reports and pull requests are welcome on GitHub at https://github.com/rameerez/profitable. Our code of conduct is: just be nice and make your mom proud of what you do and post online.
The gem is available as open source under the terms of the MIT License.
