All posts
Integrator8 min read

Odoo subscription billing: recurring invoices, MRR tracking, and payment provider setup

Odoo Subscriptions handles recurring revenue with automatic invoice generation, MRR/ARR tracking, and churn analytics. Wiring it to a payment provider (Stripe, Adyen) for online payments requires specific configuration that the basic setup guide omits.

Odoo Subscriptions (the sale_subscription module) automates recurring revenue: invoice generation on schedule, MRR/ARR reporting, and churn tracking. The module works for annual contracts invoiced monthly, monthly SaaS plans, and maintenance agreements. Getting the payment provider integration right - especially for online self-service portals - is where most implementations stall.

Installing the subscriptions module#

Install via Apps: search "Subscriptions" → install sale_subscription.

This adds:

  • Subscription products (products with a recurring billing policy).
  • Subscription templates (recurring billing intervals).
  • MRR/ARR reporting.
  • Customer portal subscription management.

Subscription product configuration#

Products that appear on subscriptions must be configured with the correct billing type.

Product → Sales tab:

  • Create Recurring Invoices: enabled.
  • Subscription Template: select the default billing interval (monthly, quarterly, annual).

Product → Invoice Policy: "Ordered Quantities" for subscription services (bill on subscription creation, not on delivery).

Subscription templates#

Subscriptions → Configuration → Recurring Plans → New:

FieldExample value
NameMonthly Plan
Billing Period1 Month
Duration12 Months (for annual commitments billed monthly)
Payment ModePayment Provider / Manual
Auto-close OverdueAfter 15 days
Closing ReasonNon-payment

Billing Period determines the invoice frequency. Duration caps the total contract length. Leave Duration empty for indefinite subscriptions.

Creating a subscription#

Subscriptions → New:

  1. Select customer.
  2. Select Recurring Plan (billing interval).
  3. Add subscription lines (products from catalog).
  4. Start Date → sets first billing date.
  5. Confirm (or set to In Progress).

On confirmation, Odoo schedules the first invoice generation based on the billing period.

Automatic invoice generation#

A cron job (sale.subscription.cron.invoice) runs daily. It checks all active subscriptions whose next invoice date has passed and generates invoices.

Invoice generation flow:

  1. Cron finds subscriptions where next_invoice_date <= today.
  2. Creates a draft invoice for each subscription.
  3. Posts the invoice (auto-confirm = True on the plan).
  4. Sends the invoice to the customer by email (if email send = True on the plan).
  5. Advances next_invoice_date by one billing period.

For auto-collected plans (Stripe/Adyen), the cron also triggers payment collection via the stored payment token.

Stripe integration for recurring payments#

Step 1: Install payment provider

Apps → install payment_stripe.

Step 2: Configure Stripe

Accounting → Configuration → Payment Providers → Stripe:

  • Set Stripe Secret Key and Publishable Key.
  • Set State to "Enabled."
  • Enable "Save Credit Cards" (required for recurring collection).
  • Set Capture Mode to "Automatic."

Step 3: Configure the subscription template

Recurring Plan → Payment Mode: "Payment Provider."

Select Stripe as the payment provider.

Step 4: Customer onboarding flow

When a customer subscribes via the portal:

  1. They see the subscription summary.
  2. They click "Pay" → Stripe Checkout or inline card form (depending on configuration).
  3. They enter card details. Odoo tokenizes the card via Stripe's payment intent API.
  4. The token stores in payment.token linked to the customer.

For subsequent billing periods, the cron charges the stored token automatically via Stripe's payment_intents or charges API (depending on your Stripe configuration - SCA-compliant flow uses payment intents).

Step 5: Handle failed payments

When Stripe declines a charge:

  • Odoo marks the invoice as "In Payment Attempt Failed."
  • The subscription moves to "Past Due" state after the configured grace period.
  • The customer receives an email with a payment link.
  • If unpaid after the auto-close period, the subscription closes.

Configure dunning email templates in: Subscriptions → Configuration → Settings → Overdue Invoices → Email Template.

MRR and ARR tracking#

Odoo calculates MRR (Monthly Recurring Revenue) from active subscriptions:

MRR = sum of (monthly equivalent of all active subscription line amounts)

For annual plans: MRR = annual amount / 12.

View in Subscriptions → Reporting → MRR Analysis.

Key metrics tracked:

  • MRR: Current monthly recurring revenue.
  • Net New MRR: MRR added minus MRR lost (churn + downgrades).
  • Churn Rate: % of MRR lost per month.
  • Customer Lifetime Value (CLV): Average revenue per customer over their subscription lifetime.

Churn tracking#

When a subscription closes (cancelled or expired), Odoo records the close reason and the MRR lost. The churn cohort report shows which signup month has the highest churn rate - useful for identifying onboarding problems.

Close reasons are configured in: Subscriptions → Configuration → Close Reasons. Common reasons: Non-payment, Competitor, Feature Gap, End of Contract. Add custom reasons and assign them when closing subscriptions to get actionable churn data.

Trial periods#

Some plans offer a free trial before billing starts. Configure on the subscription:

  • Set a trial end date.
  • Generate the first invoice only after the trial end date.
  • During the trial, subscription status shows "Trial."

Implement by setting next_invoice_date to the trial end date when the subscription is created. The cron will not generate an invoice until that date arrives.

Portal self-service#

Customers can manage subscriptions from the portal (portal.customer.com/my/subscriptions):

  • View active subscriptions.
  • Download invoices.
  • Update payment method.
  • Cancel (if self-cancellation is permitted on the plan).

Enable self-cancellation: Recurring Plan → Customer Can Close = True.

Disable self-upgrades if pricing changes require approval: Customer Can Upgrade = False.

Try ERPeek on your own Odoo module - ask questions, scaffold tests, and explore your codebase in plain language.

Get started free