Commit Graph

7 Commits

Author SHA1 Message Date
Juan José Mata
5b0ddd06a4 Add post-trial inactive Family cleanup with data archival (#1199)
* Add post-trial inactive family cleanup with data archival

Families that expire their trial without subscribing now get cleaned up
daily. Empty families (no accounts) are destroyed immediately after a
14-day grace period. Families with meaningful data (12+ transactions,
some recent) get their data exported as NDJSON/ZIP to an ArchivedExport
record before deletion, downloadable via a token-based URL for 90 days.

- Add InactiveFamilyCleanerJob (scheduled daily at 4 AM, managed mode only)
- Add ArchivedExport model with token-based downloads
- Add inactive_trial_for_cleanup scope and requires_data_archive? to Family
- Extend DataCleanerJob to purge expired archived exports
- Add ArchivedExportsController for unauthenticated token downloads

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Fix Brakeman redirect warning in ArchivedExportsController

Use rails_blob_path instead of redirecting directly to the ActiveStorage
attachment, which avoids the allow_other_host: true open redirect.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Update schema.rb with archived_exports table

Add the archived_exports table definition to schema.rb to match
the pending migration, unblocking CI tests.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Fix broken CI tests for ArchivedExports and InactiveFamilyCleaner

- ArchivedExportsController 404 test: use assert_response :not_found
  instead of assert_raises since Rails rescues RecordNotFound in
  integration tests and returns a 404 response.
- InactiveFamilyCleanerJob test: remove assert_no_difference on
  Family.count since the inactive_trial fixture gets cleaned up by
  the job. The test intent is to verify the active family survives,
  which is checked by assert Family.exists?.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Wrap ArchivedExport creation in a transaction

Ensure the ArchivedExport record and its file attachment succeed
atomically. If the attach fails, the transaction rolls back so no
orphaned record is left without an export file.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Store only a digest of the download token for ArchivedExport

Replace plaintext download_token column with download_token_digest
(SHA-256 hex). The raw token is generated via SecureRandom on create,
exposed transiently via attr_reader for use in emails/logs, and only
its digest is persisted. Lookup uses find_by_download_token! which
digests the incoming token before querying.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Remove raw download token from cleanup job logs

Log a truncated digest prefix instead of the raw token, which is the
sole credential for the unauthenticated download endpoint.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

* Fix empty assert_no_difference block in cleaner job test

Wrap the perform_now call with both assertions so the
ArchivedExport.count check actually exercises the job.

https://claude.ai/code/session_01LR3Vo83R5s5SczYe6T33dQ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-14 20:14:18 +01:00
Juan José Mata
6b5a5b1877 fix: Show cancellation message when subscription is pending cancellation (#752)
* fix: Show cancellation message when subscription is pending cancellation

When a subscription is cancelled via Stripe, the UI incorrectly showed
"Your contribution continues on..." instead of reflecting the cancellation
status. This fix adds tracking of `cancel_at_period_end` from Stripe webhooks
and displays "Your contribution ends on..." when a subscription has been
cancelled but is still active until the billing period ends.

https://claude.ai/code/session_01Y8ELTdK1k9o315iSq43TRN

* chore: Update schema.rb with cancel_at_period_end column

https://claude.ai/code/session_01Y8ELTdK1k9o315iSq43TRN

* Schema version

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-23 18:55:51 +01:00
Juan José Mata
e0fb585bda Hide contribution payments from demo user(s) (#738)
* Hide payment contribution options from demo and manually created users

Demo data users and manually created users don't have stripe_customer_id
set on their family, so they should not see payment/contribution options.

Changes:
- Add can_manage_subscription? method to Family::Subscribeable that checks
  for presence of stripe_customer_id
- Guard Settings::PaymentsController to return 403 for users without
  stripe_customer_id
- Guard SubscriptionsController#show action (Stripe portal redirect) for
  users without stripe_customer_id
- Update settings navigation to hide the payment link when
  stripe_customer_id is not present
- Add tests for the new behavior

* Fix broken test

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-23 12:35:49 +01:00
Juan José Mata
8e36c8e736 Rename billing to payment throughout the codebase (#726)
* Rename billing to payment throughout the codebase

This change updates terminology from "billing" to "payment" to better
reflect that these are contributions/payments rather than bills.

Changes include:
- Rename BillingsController to PaymentsController
- Rename billing_email to payment_email
- Rename next_billing_date to next_payment_date
- Rename create_billing_portal_session_url to create_payment_portal_session_url
- Update routes from billing to payment
- Update all 12 locale files with new terminology
- Update views, helpers, and tests

* Update app/views/subscriptions/upgrade.html.erb

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Juan José Mata <jjmata@jjmata.com>

---------

Signed-off-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-21 19:06:00 +01:00
Zach Gollwitzer
10dd9e061a Improve account sync performance, handle concurrent market data syncing (#2236)
* PlaidConnectable concern

* Remove bad abstraction

* Put sync implementations in own concerns

* Sync strategies

* Move sync orchestration to Sync class

* Clean up sync class, add state machine

* Basic market data sync cron

* Fix price sync

* Improve sync window column names, add timestamps

* 30 day syncs by default

* Clean up market data methods

* Report high duplicate sync counts to Sentry

* Add sync states throughout app

* account tab session

* Persistent account tab selections

* Remove manual sleep

* Add migration to clear stale syncs on self hosted apps

* Tweak sync states

* Sync completion event broadcasts

* Fix timezones in tests

* Cleanup

* More cleanup

* Plaid item UI broadcasts for sync

* Fix account ID namespace conflict

* Sync broadcasters

* Smoother account sync refreshes

* Remove test sync delay
2025-05-15 10:19:56 -04:00
Zach Gollwitzer
c26a7dd2dd Handle super admins for billing emails 2025-05-06 14:14:57 -04:00
Zach Gollwitzer
5da4bb6dc3 Subscription tests and domain (#2209)
* Save work

* Subscriptions and trials domain

* Store family ID on customer

* Remove indirection of stripe calls

* Test simplifications

* Update brakeman

* Fix stripe tests in CI

* Update billing page to show subscription details

* Remove legacy columns

* Complete billing settings page

* Fix hardcoded plan name

* Handle subscriptions for self hosting mode

* Lint fixes
2025-05-06 14:05:21 -04:00