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
- Seamless Integration: The module must integrate with Odoo's existing sales order and invoice workflows
- Tokenization Security: Store payment methods securely without handling raw card data
- Webhook Reliability: Handle asynchronous payment notifications with 100% reliability
- Subscription Flexibility: Support multiple billing cycles (monthly, quarterly, annual)
- Failed Payment Management: Implement intelligent retry logic and dunning procedures
- Multi-currency Support: Align with Odoo's native multi-currency capabilities
- 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:
- Validates incoming requests
- Processes payment status updates
- Triggers appropriate Odoo workflows
- 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:
- Immediate Acknowledgment: Return 200 OK immediately upon receipt
- Asynchronous Processing: Queue webhook processing for background jobs
- Retry Mechanism: Implement exponential backoff for failed processing
- 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 referencesPayment Method Lifecycle Management
- Initial Capture: During first payment, create customer and store payment method
- Default Management: Allow customers to set default payment methods
- Expiry Handling: Monitor and notify about expiring payment methods
- 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 invoiceSubscription Lifecycle Management
- Trial Periods: Support free trials with automatic conversion to paid
- Upgrades/Downgrades: Handle prorated billing for plan changes
- Pause/Resume: Allow temporary subscription pauses
- 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 resScalability 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
- Stateless Webhooks: Webhook processing is stateless, allowing multiple workers
- Database Partitioning: Subscription data partitioned by company for multi-tenant deployments
- 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
- Redundant Webhook Endpoints: Deploy multiple webhook endpoints across regions
- Payment Method Caching: Cache frequently accessed payment methods
- 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 → ConfigurationStep 2: Webhook Setup
- Generate and store webhook signing key
- Test webhook delivery
Step 3: Product Configuration
- Create subscription products in Odoo
- Configure billing intervals and pricing
- Set up trial periods if needed
Step 4: Customer Onboarding
- Enable recurring payments for customer portals
- Configure payment method collection
- 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:
- Start with clear requirements aligned with your business model
- Design for scalability from day one
- Leverage Odoo's existing workflows rather than reinventing them
- Implement robust error handling and monitoring
- 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.