| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Invoicing (account) |
| Lines of code | 830 |
| Technical Name |
gb_subscription_dunning_failed_payment_re |
| License | OPL-1 |
| Website | https://gencbaris.com/odoo_plugins/ |
| Versions | 18.0 19.0 |
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Invoicing (account) |
| Lines of code | 830 |
| Technical Name |
gb_subscription_dunning_failed_payment_re |
| License | OPL-1 |
| Website | https://gencbaris.com/odoo_plugins/ |
| Versions | 18.0 19.0 |
Subscription Dunning & Fa
Smart retry, card-expiry nudges and involuntary-churn recovery for failed renewals (BYOK PSP)
Failed card renewals are silent revenue leaks: a declined charge quietly cancels a subscription and the customer churns without ever meaning to. Chasing those failures by hand is slow and most teams simply lose the revenue. This module adds a full dunning engine on top of Odoo invoicing for SaaS and subscription businesses, with configurable retry back-off, soft/hard decline routing, card-expiry nudges and escalating recovery campaigns that suspend or cancel only when every attempt is exhausted. A cron drives it hands-free, and it runs on your own payment-provider keys (BYOK) so no card data is ever stored or proxied in Odoo.
Key Features
Configurable retry plans with back-off
gb.dunning.plan sets max_attempts, grace_days and ordered gb.dunning.plan.step rows with per-step delay_hours. get_delay_for_attempt returns the wait before each retry, falling back to an exponential 6h/24h/72h/168h schedule when no explicit steps are defined.
Soft vs hard decline routing
classify_reason matches the PSP reason code against the plan's soft_decline_reasons (insufficient funds, issuer unavailable) and hard_decline_reasons (lost/stolen card, do-not-honor). Soft declines are rescheduled while a hard decline aborts retries immediately, so effort goes where it can succeed.
Card-expiry nudges
gb.dunning.campaign stores card_last4, card_exp_month and card_exp_year. _is_card_expiring compares against the plan's expiry_nudge_days, and _send_expiry_nudge emails the customer before the token expires, setting card_expiry_warned so the reminder is sent once.
Per-invoice recovery campaigns
Each failed account.move out_invoice drives a campaign through draft, active, recovered, suspended and cancelled states on mail.thread. run_retry charges, logs the outcome and either marks recovered or schedules the next_attempt_date until max_attempts or the grace_deadline is reached.
Configurable exhaustion action
When all retries fail, _finalise_exhausted applies the plan's final_action: suspend the campaign (when auto_suspend is on), cancel it, or schedule a mail todo activity for manual review. Recovery never silently drops a customer without the action you chose.
Full attempt log and KPIs
Every cycle writes a gb.dunning.attempt with attempt_number, success/failed state, reason_code and charged amount; _compute_reason_class tags each as success/soft/hard. Campaigns roll up attempt_count, failed_count and recovered_amount so you can measure revenue saved.
Hands-free cron processing
cron_process_due_campaigns runs run_retry on every active campaign whose next_attempt_date is due, keeping the batch alive past individual errors. cron_expiry_nudges sweeps active campaigns for soon-to-expire cards, so the whole recovery flow runs unattended.
BYOK PSP, no card data stored
The _simulate_charge hook is overridden to call the customer's own PSP (Stripe, Adyen, Braintree, GoCardless) using the stored psp_token_ref; no PAN is ever held in Odoo, only last-4 and expiry for display. An account.move action_open_dunning button and start_dunning_for_overdue helper launch campaigns from invoices.
Use Cases
Screenshots
Retry Attempts
Dunning Campaigns
Dunning Plans
Run Dunning Retries
Why Choose This Module
Failed card renewals are silent revenue leaks. This module adds a full dunning engine on top of Odoo invoicing and subscriptions: smart retry schedules, card-expiry reminders before the next charge, and escalating recovery campaigns that suspend or cancel only when every attempt is exhausted. It runs on your own payment-provider keys (BYOK) — we never store or proxy card data.
Specifications
- Compatible: Odoo 18.0 / 19.0
- License: LGPL-3
- Languages: 35+
- Author: Baris Genc
- Dependencies: account, mail
- Support: odoo@gencbaris.com
Odoo Proprietary License v1.0 This software and associated files (the "Software") may only be used (executed, modified, executed after modifications) if you have purchased a valid license from the authors, typically via Odoo Apps, or if you have received a written agreement from the authors of the Software (see the COPYRIGHT file). You may develop Odoo modules that use the Software as a library (typically by depending on it, importing it and using its resources), but without copying any source code or material from the Software. You may distribute those modules under the license of your choice, provided that this license is compatible with the terms of the Odoo Proprietary License (For example: LGPL, MIT, or proprietary licenses similar to this one). It is forbidden to publish, distribute, sublicense, or sell copies of the Software or modified copies of the Software. The above copyright notice and this permission notice must be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Please log in to comment on this module