Add account self-delete and subscription lifecycle sync (#34) (#39)
Deploy Beta / unit-tests (push) Successful in 10s
Unit Tests / test (push) Successful in 10s
Deploy Beta / docker (push) Successful in 26s
Deploy Beta / deploy-beta (push) Successful in 6m46s

## Summary
- Closes [#34](#34)
- Companion for [chat_web_app#75](ai_ml_operations/chat_web_app#75) (portal cancel/change local sync)
- Soft-delete `DELETE /api/user/` for the authenticated user only: `deleted=True`, `is_active=False`, hide conversations, blacklist outstanding refresh tokens; staff self-delete rejected
- Stripe `customer.subscription.updated` / `deleted` webhooks sync plan status, `cancel_at_period_end`, and `current_period_end`; checkout assigns plan from `metadata.plan_slug`
- **UserAuthEvent audit**: `account_deleted`, `subscription_started` (first active plan), `subscription_updated` (plan/status/cancel changes) — visible on user admin
- Document FE contract in README (endpoint, response, post-delete logout)

## Test plan
- [ ] `uv run python manage.py test chat_backend.tests.test_views_users.CustomUserSelfDeleteTestCase finance.tests`
- [ ] Authenticated `DELETE /api/user/` soft-deletes self, hides conversations, blocks re-login, writes `account_deleted` auth event
- [ ] Checkout / Backer assign writes `subscription_started`; portal cancel/change writes `subscription_updated`
- [ ] Anonymous / staff self-delete rejected; body cannot target another user
- [ ] After portal cancel, webhook sets `cancel_at_period_end` / `canceled` on `GET /finance/subscription/`Reviewed-on: #39
This commit was merged in pull request #39.
This commit is contained in:
2026-08-01 12:24:17 -07:00
parent cc45ae5808
commit eedc842b08
14 changed files with 740 additions and 10 deletions
+4 -1
View File
@@ -82,7 +82,7 @@ class CustomUser(AbstractUser):
class UserAuthEvent(models.Model):
"""Audit trail for password reset / set actions, shown on user admin."""
"""Audit trail for auth / account / subscription actions (user admin)."""
class EventType(models.TextChoices):
PASSWORD_RESET_REQUESTED = (
@@ -91,6 +91,9 @@ class UserAuthEvent(models.Model):
)
PASSWORD_SET = ("password_set", "Password set")
INVITE_SENT = ("invite_sent", "Invite sent")
ACCOUNT_DELETED = ("account_deleted", "Account deleted")
SUBSCRIPTION_STARTED = ("subscription_started", "Subscription started")
SUBSCRIPTION_UPDATED = ("subscription_updated", "Subscription updated")
user = models.ForeignKey(
CustomUser,