Mar 13, 2026 8 min read 1494260guide

Mastering Recurring Revenue: How We Engineered a Mollie Recurring Payments Module for Odoo

A technical deep-dive into building a robust Mollie recurring payments module for Odoo, covering API challenges, webhook implementation, tokenization, subscription logic, and business value.

Mastering Recurring Revenue: How We Engineered a Mollie Recurring Payments Module for Odoo

Introduction: The Recurring Revenue Gap in Odoo

Odoo's modular architecture has revolutionized business operations, offering integrated solutions for CRM, sales, inventory, accounting, and HR through its web-based administration platform. However, when it comes to subscription-based business models—a growing trend across SaaS, e-commerce, and service industries—Odoo's native payment ecosystem presented a significant gap. The existing Mollie payment module, while excellent for one-time transactions, lacked the sophisticated recurring billing capabilities that modern subscription businesses require.
At AtomixWeb, we specialize in deploying and managing Odoo for businesses scaling their operations. We consistently encountered clients struggling with manual subscription management, missed renewals, and payment failures that disrupted their recurring revenue streams. This case study details how we engineered a comprehensive Mollie recurring payments module that transforms Odoo into a subscription management powerhouse.

The Problem: Why Odoo Needed Recurring Payments

Odoo's flexible API and multi-company support make it ideal for businesses operating across different markets and currencies. Yet, its payment infrastructure remained transaction-focused rather than relationship-focused. The existing Mollie module handled individual payments efficiently but couldn't manage:
  • Automated subscription renewals
  • Payment method updates for expired cards
  • Prorated billing for mid-cycle upgrades
  • Failed payment retry logic
  • Subscription lifecycle management
Without these capabilities, businesses using Odoo for subscription services faced manual workarounds, increased churn, and revenue leakage—problems that directly contradicted Odoo's promise of automated workflows for business operations.

Requirements Analysis: Building for Scale

Before writing a single line of code, we analyzed the requirements through the lens of Odoo's core strengths and our clients' business needs:

Core Functional Requirements

  1. Seamless Integration: The module must integrate with Odoo's existing sales order and invoice workflows
  2. Tokenization Security: Store payment methods securely without handling raw card data
  3. Webhook Reliability: Handle asynchronous payment notifications with 100% reliability
  4. Subscription Flexibility: Support multiple billing cycles (monthly, quarterly, annual)
  5. Failed Payment Management: Implement intelligent retry logic and dunning procedures
  6. Multi-currency Support: Align with Odoo's native multi-currency capabilities
  7. Reporting Integration: Feed subscription metrics into Odoo's robust reporting and analytics

Technical Requirements

  • Leverage Odoo's modular architecture for easy installation
  • Utilize Odoo's ORM for data consistency
  • Implement proper error handling and logging
  • Ensure production-ready scaling for high-availability deployments
  • Maintain backward compatibility with existing Mollie transactions

API Challenges: Navigating Mollie's Recurring Payments Ecosystem

Mollie's API documentation for recurring payments presented several challenges that required careful architectural decisions:

Challenge 1: Tokenization vs. Customer Objects

Mollie offers two approaches for recurring payments: simple tokenization and full customer objects with stored payment methods. We chose the customer object approach for its superior flexibility:
# Creating a Mollie customer for recurring payments
customer_data = {
    'name': partner.name,
    'email': partner.email,
    'locale': partner.lang or 'en_US',
    'metadata': {
        'odoo_partner_id': partner.id,
        'company_id': company.id
    }
}

response = mollie_client.customers.create(customer_data)
# Store customer_id in Odoo partner record
partner.mollie_customer_id = response['id']

Challenge 2: Webhook Security

Mollie webhooks require proper signature verification to prevent spoofing. We implemented a robust verification system:
import hashlib
import hmac

def verify_webhook_signature(request_body, mollie_signature, webhook_key):
    """Verify Mollie webhook signature"""
    calculated_signature = hmac.new(
        webhook_key.encode('utf-8'),
        request_body.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(calculated_signature, mollie_signature)

Challenge 3: Idempotency Requirements

Mollie's API requires idempotent requests for payment creation. We implemented a unique idempotency key system:
import uuid

def create_idempotency_key(invoice_id, attempt=0):
    """Generate unique idempotency key for Mollie API"""
    return f"odoo-invoice-{invoice_id}-attempt-{attempt}-{uuid.uuid4()}"

Webhook Implementation: The Heartbeat of Recurring Payments

Webhooks are critical for recurring payment systems. They provide real-time notifications about payment status changes. Our implementation needed to handle:

Webhook Endpoint Design

We created a dedicated webhook controller that:
  1. Validates incoming requests
  2. Processes payment status updates
  3. Triggers appropriate Odoo workflows
  4. Implements retry logic for failed webhook processing
from odoo import http
import json

class MollieWebhookController(http.Controller):
    @http.route('/mollie/webhook', type='json', auth='public', csrf=False)
    def mollie_webhook(self, **kwargs):
        """Main webhook endpoint for Mollie notifications"""
        # 1. Verify webhook signature
        if not self._verify_signature(http.request):
            return {'status': 'error', 'message': 'Invalid signature'}
        
        # 2. Parse and validate webhook data
        webhook_data = json.loads(http.request.httprequest.data)
        payment_id = webhook_data.get('id')
        
        # 3. Process based on payment status
        status = webhook_data.get('status')
        if status == 'paid':
            self._process_paid_payment(payment_id, webhook_data)
        elif status == 'failed':
            self._process_failed_payment(payment_id, webhook_data)
        elif status == 'canceled':
            self._process_canceled_payment(payment_id, webhook_data)
        
        return {'status': 'processed'}

Webhook Reliability Patterns

To ensure no webhook notifications were lost:
  1. Immediate Acknowledgment: Return 200 OK immediately upon receipt
  2. Asynchronous Processing: Queue webhook processing for background jobs
  3. Retry Mechanism: Implement exponential backoff for failed processing
  4. Dead Letter Queue: Store unprocessable webhooks for manual review

Tokenization Strategy: Secure Payment Storage

Tokenization is fundamental to recurring payments. We needed to store payment methods securely while maintaining PCI compliance:

Payment Method Storage Architecture

class MolliePaymentMethod(models.Model):
    _name = 'mollie.payment.method'
    _description = 'Mollie Payment Method'
    
    partner_id = fields.Many2one('res.partner', required=True)
    mollie_customer_id = fields.Char('Mollie Customer ID')
    payment_method_id = fields.Char('Mollie Payment Method ID')
    method_type = fields.Selection([
        ('creditcard', 'Credit Card'),
        ('directdebit', 'Direct Debit'),
        ('paypal', 'PayPal')
    ])
    is_default = fields.Boolean('Default Payment Method')
    expiry_date = fields.Date('Expiry Date')
    last_used = fields.Datetime('Last Used')
    
    # Security: Never store full card numbers
    # Only store Mollie's tokenized references

Payment Method Lifecycle Management

  1. Initial Capture: During first payment, create customer and store payment method
  2. Default Management: Allow customers to set default payment methods
  3. Expiry Handling: Monitor and notify about expiring payment methods
  4. Failover Logic: Automatically try backup payment methods

Subscription Logic: The Business Engine

The subscription logic needed to integrate seamlessly with Odoo's sales and accounting modules:

Subscription Model Design

class MollieSubscription(models.Model):
    _name = 'mollie.subscription'
    _description = 'Mollie Subscription'
    
    name = fields.Char('Subscription Reference')
    partner_id = fields.Many2one('res.partner', required=True)
    product_id = fields.Many2one('product.product', required=True)
    interval = fields.Selection([
        ('1 month', 'Monthly'),
        ('3 months', 'Quarterly'),
        ('12 months', 'Annual')
    ], default='1 month')
    amount = fields.Monetary('Amount', currency_field='currency_id')
    currency_id = fields.Many2one('res.currency', required=True)
    start_date = fields.Date('Start Date')
    next_payment_date = fields.Date('Next Payment Date')
    status = fields.Selection([
        ('active', 'Active'),
        ('suspended', 'Suspended'),
        ('canceled', 'Canceled'),
        ('completed', 'Completed')
    ], default='active')
    
    # Integration with Odoo Sales
    sale_order_ids = fields.One2many('sale.order', 'subscription_id')
    invoice_ids = fields.One2many('account.move', 'subscription_id')
    
    def _create_next_payment(self):
        """Create next payment in subscription cycle"""
        # 1. Create sales order
        sale_order = self.env['sale.order'].create({
            'partner_id': self.partner_id.id,
            'subscription_id': self.id,
            'order_line': [(0, 0, {
                'product_id': self.product_id.id,
                'product_uom_qty': 1,
                'price_unit': self.amount
            })]
        })
        
        # 2. Confirm order and create invoice
        sale_order.action_confirm()
        invoice = sale_order._create_invoices()
        
        # 3. Process payment through Mollie
        payment_result = self._process_mollie_payment(invoice)
        
        # 4. Update subscription dates
        self.next_payment_date = self._calculate_next_date()
        
        return invoice

Subscription Lifecycle Management

  1. Trial Periods: Support free trials with automatic conversion to paid
  2. Upgrades/Downgrades: Handle prorated billing for plan changes
  3. Pause/Resume: Allow temporary subscription pauses
  4. Cancellation Flow: Implement graceful cancellation with end-of-term completion

Integration with Odoo's Core Modules

Sales Module Integration

# Extend sale.order model to support subscriptions
class SaleOrder(models.Model):
    _inherit = 'sale.order'
    
    subscription_id = fields.Many2one('mollie.subscription', 'Subscription')
    is_recurring = fields.Boolean('Recurring Order', compute='_compute_is_recurring')
    
    def _compute_is_recurring(self):
        for order in self:
            order.is_recurring = bool(order.subscription_id)

Accounting Module Integration

# Extend account.move for subscription invoices
class AccountMove(models.Model):
    _inherit = 'account.move'
    
    subscription_id = fields.Many2one('mollie.subscription', 'Subscription')
    mollie_payment_id = fields.Char('Mollie Payment ID')
    
    def action_post(self):
        """Override to handle subscription invoice posting"""
        res = super().action_post()
        
        # If this is a subscription invoice, trigger payment
        if self.subscription_id and self.move_type == 'out_invoice':
            self.subscription_id._process_payment(self)
        
        return res

Scalability Analysis: Production-Ready Architecture

At AtomixWeb, we design all Odoo solutions with scalability in mind. The Mollie recurring payments module incorporates several scalability patterns:

Horizontal Scaling Considerations

  1. Stateless Webhooks: Webhook processing is stateless, allowing multiple workers
  2. Database Partitioning: Subscription data partitioned by company for multi-tenant deployments
  3. Queue-Based Processing: Payment processing uses Odoo's queue jobs for load distribution

Performance Optimizations

# Batch processing for subscription renewals
class SubscriptionBatchProcessor(models.Model):
    _name = 'subscription.batch.processor'
    
    def process_due_subscriptions(self, batch_size=100):
        """Process subscriptions due for renewal in batches"""
        due_date = fields.Date.today()
        
        # Find subscriptions due for renewal
        due_subscriptions = self.env['mollie.subscription'].search([
            ('next_payment_date', '<=', due_date),
            ('status', '=', 'active')
        ], limit=batch_size)
        
        # Process in parallel using Odoo's queue jobs
        for subscription in due_subscriptions:
            subscription.with_delay()._create_next_payment()
        
        return len(due_subscriptions)

High-Availability Deployment

  1. Redundant Webhook Endpoints: Deploy multiple webhook endpoints across regions
  2. Payment Method Caching: Cache frequently accessed payment methods
  3. Graceful Degradation: Continue operating with reduced functionality during API outages

Business Value: Transforming Odoo into a Subscription Platform

The Mollie recurring payments module delivers significant business value by leveraging Odoo's existing strengths:

Automated Revenue Operations

  • Reduced Manual Work: Eliminates manual invoice creation and payment tracking
  • Improved Cash Flow: Predictable, automated payment collection
  • Lower Churn: Automated payment retry logic reduces involuntary churn

Enhanced Customer Experience

  • Self-Service Portal: Customers can update payment methods and manage subscriptions
  • Transparent Billing: Clear subscription details and billing history
  • Flexible Plans: Easy upgrades, downgrades, and cancellations

Operational Efficiency

  • Integrated Reporting: Subscription metrics feed directly into Odoo's analytics
  • Multi-Company Support: Manage subscriptions across different business units
  • Audit Trail: Complete history of subscription changes and payments

Implementation Steps: Getting Started

Step 1: Installation and Configuration

# Install the module
odoo-bin -c odoo.conf -i mollie_recurring

# Configure Mollie API credentials
# Settings → Mollie → Configuration

Step 2: Webhook Setup

  1. Configure your Mollie dashboard webhook URL: https://your-odoo-domain.com/mollie/webhook
  2. Generate and store webhook signing key
  3. Test webhook delivery

Step 3: Product Configuration

  1. Create subscription products in Odoo
  2. Configure billing intervals and pricing
  3. Set up trial periods if needed

Step 4: Customer Onboarding

  1. Enable recurring payments for customer portals
  2. Configure payment method collection
  3. Set up email notifications

Conclusion: The Future of Recurring Payments in Odoo

Building a Mollie recurring payments module for Odoo required deep understanding of both platforms' capabilities and limitations. By leveraging Odoo's flexible API, modular architecture, and robust business logic framework, we created a solution that transforms Odoo from a transactional system into a comprehensive subscription management platform.
At AtomixWeb, we've deployed this module for clients across various industries, from SaaS companies to subscription box services. The results have been transformative: reduced administrative overhead, improved cash flow predictability, and enhanced customer satisfaction.
The module demonstrates how Odoo's extensible platform, combined with thoughtful integration design, can solve complex business problems. As subscription models continue to dominate the business landscape, having robust recurring payment capabilities within Odoo's integrated ecosystem provides a significant competitive advantage.
For businesses considering implementing recurring payments in Odoo, the key takeaways are:
  1. Start with clear requirements aligned with your business model
  2. Design for scalability from day one
  3. Leverage Odoo's existing workflows rather than reinventing them
  4. Implement robust error handling and monitoring
  5. Continuously iterate based on customer feedback and usage patterns
With the right approach and expertise, Odoo can become the central nervous system for your subscription business, driving growth through automated, reliable recurring revenue operations.
Technical Support

Stuck on Implementation?

If you're facing issues deploying this tool or need a managed setup on Hostinger, our engineers are here to help. We also specialize in developing high-performance custom web applications and designing end-to-end automation workflows.

Engineering trusted by teams at

Managed Setup & Infra

Production-ready deployment on Hostinger, AWS, or Private VPS.

Custom Web Applications

We build bespoke tools and web dashboards from scratch.

Workflow Automation

End-to-end automated pipelines and technical process scaling.

Faster ImplementationRapid Deployment
100% Free Audit & ReviewTechnical Analysis