Skip to content

Instantly share code, notes, and snippets.

Last active November 8, 2022 12:54
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Calculating MRR with C# and the Stripe API
// This is the source for the blog post here:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Stripe;
public static class StripeExtensions
public static decimal CalculateSubscriptionMonthlyRevenue(Subscription subscription)
decimal revenue = 0;
foreach (var item in subscription.Items)
var multiplier = item.Plan.Interval switch
"day" => 30M,
"week" => 4M,
"month" => 1M,
"year" => 1M / 12M,
_ => throw new UnreachableException($"Unexpected plan interval: {item.Plan.Interval}.")
revenue += multiplier * item.Quantity * item.Price.UnitAmountDecimal.GetValueOrDefault();
return revenue / 100M; // The UnitAmount is in cents.
public static decimal CalculateCustomerMonthlyRevenue(Customer customer)
var subscriptions = customer.Subscriptions;
var revenue = 0M;
foreach (var subscription in subscriptions)
revenue += CalculateSubscriptionMonthlyRevenue(subscription);
// Apply the coupon, if any. We only look at % off coupons.
// We can ignore the amount off discount. That's a one time discount and doesn't affect ongoing MRR.
if (customer.Discount is { Coupon.PercentOff: { } percentOff })
revenue *= 1 - percentOff / 100M;
return revenue;
public static async Task<decimal> CalculateMonthlyRecurringRevenue()
string? lastId = null;
var customerClient = new CustomerService();
decimal revenue = 0M;
bool hasMore = true;
while (hasMore)
var customers = await customerClient.ListAsync(
new CustomerListOptions
Limit = 100, /* Max Limit is 100 */
Expand = new List<string> { "data.subscriptions" },
StartingAfter = lastId
revenue += customers.Sum(CalculateCustomerMonthlyRevenue);
hasMore = customers.HasMore;
if (hasMore)
lastId = customers.LastOrDefault()?.Id;
if (lastId is null)
throw new InvalidOperationException("API reports more customers but no last id was returned.");
return revenue;
Copy link

TAzmir commented Oct 14, 2022

I haven't gone through the complete code, but I would like to give you some suggestions here. :)

(1) I would rename this CalculateSubscriptionMonthlyRevenue to CalculateSubscriptionMonthlyRevenueInCents. This would remove your comment inside the "UnitAmount is in cents" method.

(2) I would not throw ex _ => throw new UnreachableException($"Unexpected plan interval: {item.Plan.Interval}.") instead I would return 0M. This way you can say that your CalculateSubscriptionMonthlyRevenueInCents function only supports day/week/month and year calculations. Other calculations you would simply ignore with 0M instead of "breaking" your application. I hope it makes sense to you. :)

(3) I would pass only the subscription items instead of passing the entire subscription. I'm still refering to CalculateSubscriptionMonthlyRevenueInCents.

(4) If there are no subscription items, I would immediately return 0. Again, I'm still refering CalculateSubscriptionMonthlyRevenueInCents.

I hope these suggestions make sense for you. :)


Copy link

"week" => 4M,

You should probably make this 52M / 12M which gives you 4.333 weeks per month.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment