fix(encryption): guard billed users independently of the subscriptions flag

This commit is contained in:
Víctor Falcón 2026-07-17 10:25:31 +02:00
parent 808d41eee2
commit f140f5c785
3 changed files with 27 additions and 5 deletions

View File

@ -5,6 +5,7 @@ namespace App\Console\Commands\Concerns;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Laravel\Cashier\Subscription;
trait FindsUsersWithLegacyEncryption
{
@ -33,11 +34,32 @@ trait FindsUsersWithLegacyEncryption
* Drop users who are still being billed. These accounts must never be emailed a
* deletion warning nor deleted while a subscription or trial is active.
*
* Unlike {@see User::hasActiveSubscriptionOrTrial()}, this check is intentionally
* independent of the `subscriptions.enabled` feature flag: that flag controls whether
* the paywall is enforced, not whether real Stripe subscriptions exist. A destructive
* command must never delete a paying customer just because the flag is off in the
* runtime that happens to execute it.
*
* @param Collection<int, User> $users
* @return Collection<int, User>
*/
protected function excludeBilledUsers(Collection $users): Collection
{
return $users->reject(fn (User $user): bool => $user->hasActiveSubscriptionOrTrial())->values();
return $users->reject(fn (User $user): bool => $this->isBilled($user))->values();
}
/**
* Whether the user is on a generic trial or holds a subscription that is still
* valid and not merely coasting through its grace period (it will not renew).
*/
private function isBilled(User $user): bool
{
if ($user->onGenericTrial()) {
return true;
}
return $user->subscriptions->contains(
fn (Subscription $subscription): bool => $subscription->valid() && ! $subscription->onGracePeriod()
);
}
}

View File

@ -28,8 +28,8 @@ test('it soft-deletes users with encrypted data who did not sign in within the g
expect(User::whereKey($clean->id)->exists())->toBeTrue();
});
test('it never deletes a subscribed user', function () {
config(['subscriptions.enabled' => true]);
test('it never deletes a subscribed user even when the subscriptions flag is off', function () {
config(['subscriptions.enabled' => false]);
$subscribed = User::factory()->create(['last_active_at' => now()->subDays(30)]);
Transaction::factory()->for($subscribed)->create();

View File

@ -28,8 +28,8 @@ test('it queues a warning email for users with encrypted transactions or account
Queue::assertNotPushed(SendUpdateEmailJob::class, fn (SendUpdateEmailJob $job) => $job->user->is($clean));
});
test('it never warns a subscribed user', function () {
config(['subscriptions.enabled' => true]);
test('it never warns a subscribed user even when the subscriptions flag is off', function () {
config(['subscriptions.enabled' => false]);
$subscribed = User::factory()->create();
Transaction::factory()->for($subscribed)->create();