fix: invoice.paid reactivating manually deactivated relays #10

Merged
hodlbod merged 1 commits from userAdityaa/caravel:invoice-activation into master 2026-04-14 22:10:41 +00:00
Contributor

Problem

Currently, the Stripe invoice.paid webhook reactivates all inactive relays, including manually deactivated ones. This recovery should only apply to relays suspended due to billing issues.

Solution

Introduce a delinquent status to distinguish billing suspension from manual deactivation:

  • Three relay statuses: active | inactive | delinquent
  • delinquent: Active relay suspended due to unpaid invoice or subscription cancellation
  • inactive: Manually deactivated by user (remains inactive; not recovered by billing events)
  • active: Operational relay

Implementation

  1. Billing Suspension Path: handle_invoice_overdue() and handle_subscription_updated() call mark_relay_delinquent() to set status = delinquent
  2. Recovery Logic: should_reactivate_after_payment() checks status == RELAY_STATUS_DELINQUENT && plan != "free"
  3. Manual Deactivation: deactivate_relay() sets status = inactive; protected from billing recovery
  4. Infra Sync: Both delinquent and inactive treated as "inactive" for zooid API JSON
  5. No Schema Changes: Uses existing status column; no migration

Closes #8

#### Problem Currently, the Stripe invoice.paid webhook reactivates all inactive relays, including manually deactivated ones. This recovery should only apply to relays suspended due to billing issues. ### Solution Introduce a delinquent status to distinguish billing suspension from manual deactivation: * Three relay statuses: active | inactive | delinquent * delinquent: Active relay suspended due to unpaid invoice or subscription cancellation * inactive: Manually deactivated by user (remains inactive; not recovered by billing events) * active: Operational relay ### Implementation 1. **Billing Suspension Path**: handle_invoice_overdue() and handle_subscription_updated() call mark_relay_delinquent() to set status = delinquent 2. **Recovery Logic**: should_reactivate_after_payment() checks status == RELAY_STATUS_DELINQUENT && plan != "free" 3. **Manual Deactivation**: deactivate_relay() sets status = inactive; protected from billing recovery 4. **Infra Sync**: Both delinquent and inactive treated as "inactive" for zooid API JSON 5. **No Schema Changes**: Uses existing status column; no migration Closes #8
userAdityaa marked the pull request as work in progress 2026-04-14 18:34:31 +00:00
Owner

What would you think about adding a new "delinquent" status instead of deactivation_source? It would keep the code simpler and would be more adaptive to future behavior (it would make sense to do some kind of soft-deactivation with zooid in the future, where it goes into a read-only state or something like that).

What would you think about adding a new "delinquent" status instead of deactivation_source? It would keep the code simpler and would be more adaptive to future behavior (it would make sense to do some kind of soft-deactivation with zooid in the future, where it goes into a read-only state or something like that).
Author
Contributor

What would you think about adding a new "delinquent" status instead of deactivation_source? It would keep the code simpler and would be more adaptive to future behavior (it would make sense to do some kind of soft-deactivation with zooid in the future, where it goes into a read-only state or something like that).

agreed, just adding the "delinquent" status feels more cleaner and robust for long-term.
(and) I also feel that the current solution feels like tagging metadata onto inactive, which can be messy in long-term.

Moving forward with the suggestion and pushing the PR soon.

> What would you think about adding a new "delinquent" status instead of deactivation_source? It would keep the code simpler and would be more adaptive to future behavior (it would make sense to do some kind of soft-deactivation with zooid in the future, where it goes into a read-only state or something like that). agreed, just adding the "delinquent" status feels more cleaner and robust for long-term. (and) I also feel that the current solution feels like tagging metadata onto inactive, which can be messy in long-term. Moving forward with the suggestion and pushing the PR soon.
userAdityaa added 1 commit 2026-04-14 22:01:15 +00:00
userAdityaa force-pushed invoice-activation from 43ab49b0bd to fd8d6d257b 2026-04-14 22:01:15 +00:00 Compare
userAdityaa marked the pull request as ready for review 2026-04-14 22:07:33 +00:00
hodlbod merged commit 1d4034340b into master 2026-04-14 22:10:41 +00:00
userAdityaa deleted branch invoice-activation 2026-04-14 22:11:57 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: coracle/caravel#10