## Look at the row

```bash
bin/kamal app exec --interactive --reuse 'bin/rails console'
```

```ruby
account = User.find_by(email_address: "them@example.com").account
account.subscription&.attributes
```

`entitled?` is true when the status is `active` or `trialing`. Anything else, including a missing
row entirely, means the gate closes.

## Then check whether the webhook arrived

Stripe's dashboard has a delivery log per endpoint showing every attempt and the response your app
returned. Three outcomes and three different fixes:

- **No delivery at all.** The event type is not enabled on the endpoint. See
  [Stripe Setup](/docs/billing-entitlements/stripe-setup).
- **Delivered, 400.** Signature verification failed, so the handler never ran.
- **Delivered, 200, row still wrong.** The handler ran against a different account than you expect.

## The account mismatch

This is the one that looks impossible and is common. Billing belongs to an `Account`, not to a
`User`, which is what lets a team share one subscription later. If someone paid while signed in with
one email and is now signed in with another, they have two accounts, and only one of them is paying.

```ruby
User.where(email_address: ["them@example.com", "them@work.example.com"]).map { |u| u.account_id }
```

Two different ids is your answer.

## Fixing it by hand

Stripe is the source of truth for whether money moved. Once you have confirmed it did, activating
the local row is safe and idempotent:

```ruby
Subscription.activate!(account, plan: :pro)
```

That is the same call the webhook makes, so running it twice changes nothing.

## Stop it happening again

If the webhook was the problem, fix the endpoint rather than the row. Stripe retries failed
deliveries for three days, so a corrected signing secret usually replays the backlog on its own and
the next customer never sees the paywall.
