• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kobotoolbox / kpi / 30021995917
82%
master: 76%

Build:
Build:
LAST BUILD BRANCH: hugo/dev-2080-transfer-details-admin
DEFAULT BRANCH: master
Ran 23 Jul 2026 03:49PM UTC
Jobs 10
Files 896
Run time 3min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

23 Jul 2026 03:45PM UTC coverage: 81.817% (-0.02%) from 81.833%
30021995917

push

github

web-flow
feat(stripe): add webhook handling to update status of unpaid subscriptions DEV-2399 (#7254)

### đŸ“Ŗ Summary
Update our Stripe billing integration so that certain subscription plans
(like Teams) can remain in an "unpaid" status indefinitely instead of
automatically canceling after payment retries fail.

### 💭 Notes
- I have updated the Teams plans in the Stripe test sandbox to include a
product metadata key `'preserve_unpaid_status': true`.
- After this pr is approved, the Stripe production environment will need
to be updated with this key as well as changing the Subscriptions'
status behavior from canceling to marking as unpaid.

### 👀 Preview steps

1. â„šī¸ Have a an active `djstripe` webhook handling
2. In the kpi shell:
3. 🔴 [on main] Run a mock customer.subscription.updated signal in the
shell for an unpaid subscription
4. Notice that all unpaid behaviors fallback to our default Stripe
configuration which is to move them to cancelled
5. đŸŸĸ [on PR] Run the following test block to test our new webhook
signal:
```
from unittest.mock import MagicMock
from djstripe.models import Subscription
from kobo.apps.stripe.signals import handle_unpaid_subscription

# Grab any local subscription to use for the target
sub = Subscription.objects.first()
if not sub:
    print("âš ī¸ No subscriptions found. Please create one to run this preview.")
else:
    original_cancel = sub.cancel
    sub.cancel = MagicMock()

    # 1. Simulate 'preserve_unpaid_status': 'true'
    mock_event_preserved = MagicMock()
    mock_event_preserved.data = {
        "object": {
            "id": sub.id,
            "status": "unpaid",
            "items": {"data": [{"price": {"product": {"metadata": {"preserve_unpaid_status": "true"}}}}]}
        }
    }
    sub.cancel.reset_mock()
    handle_unpaid_subscription(sender=None, event=mock_event_preserved, instance=sub)

    # đŸŸĸ Notice that this did NOT trigger the cancel method
    print("Preserve Status Checked:", "PASS (Subscription not canceled... (continued)

9480 of 12794 branches covered (74.1%)

5 of 49 new or added lines in 1 file covered. (10.2%)

30705 of 37529 relevant lines covered (81.82%)

5.51 hits per line

Uncovered Changes

Lines Coverage ∆ File
44
20.55
20.55% kobo/apps/stripe/signals.py
Jobs
ID Job ID Ran Files Coverage
1 30021995917.1 23 Jul 2026 03:49PM UTC 892
49.35
2 30021995917.2 23 Jul 2026 03:50PM UTC 892
52.8
3 30021995917.3 23 Jul 2026 03:50PM UTC 892
44.24
4 30021995917.4 23 Jul 2026 03:51PM UTC 894
50.69
5 30021995917.5 23 Jul 2026 03:51PM UTC 894
54.06
6 30021995917.6 23 Jul 2026 03:52PM UTC 894
67.02
7 30021995917.7 23 Jul 2026 03:53PM UTC 894
45.6
8 30021995917.8 23 Jul 2026 03:53PM UTC 892
58.99
9 30021995917.9 23 Jul 2026 03:54PM UTC 894
60.15
10 30021995917.10 23 Jul 2026 03:56PM UTC 896
69.38
Source Files on build 30021995917
  • Tree
  • List 896
  • Changed 3
  • Source Changed 0
  • Coverage Changed 3
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • 3f66d2af on github
  • Prev Build on main (#30008784565)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc