# Customer Account Hooks

https://dev.wisecp.com/es/customer-account-hooks

The 103 hooks about the customer account itself: signing up, signing in, verifying, blocking, sub-accounts and affiliates.

## Overview

This domain carries the account **itself**: its opening, its sign-in, its verification, its blocking, its deletion. The screens a customer sees are a domain of their own.

Some of these hooks fire **in the management panel**, because an operator can change an account too. So when writing a listener, the answer to "who did this" is not always the customer.

## Reference

### The account's life

- **action:user.created**: An account was opened. The return is ignored.
- **action:user.updated**: Account details changed. The return is ignored.
- **action:user.status_changed**: The account status changed. The return is ignored.
- **action:user.account_blocked**: The account was blocked, or unblocked. The return is ignored.
- **action:user.deleted**: The account was deleted. The return is ignored.
- **action:user.deleted_dormant**: A long-dormant account was deleted on its own. The return is ignored.

### Sign-in and security

- **action:user.logged_in**: A sign-in happened. The return is ignored.
- **action:user.login_failed**: A sign-in attempt failed. The return is ignored.
- **action:user.logged_in_as**: A staff member entered a customer account. The return is ignored.
- **action:user.password_changed**: The password changed. The return is ignored.
- **action:user.two_factor_changed**: Two-step verification was switched on or off. The return is ignored; by here the change is already saved.
- **action:user.session_revoked**: A session was ended. The return is ignored.
- **action:user.account_switched**: The customer switched to another account. The return is ignored; the session is already written.

### Gates

Eighteen gates stand in front of every weighty account action. They share one contract: a non-empty string stops the action. An empty return — `null` or `''` — lets it through.

- **gate:user.create**: Opening an account. A filled return stops it, and that text becomes the error.
- **gate:user.login**: Signing in — after the password checked out. A filled return stops it; a string and `['message' => '…']` are both taken.
- **gate:user.login_as**: A staff member entering a customer account. A filled return stops it.
- **gate:user.delete**: Deleting an account. A filled return stops it, and the delete answers `false`.
- **gate:user.block**: Blocking an account. A filled return stops it.
- **gate:user.add_funds**: Adding funds — before the invoice is issued. A filled return stops it, and the text reaches the customer.
- **gate:user.email_change**: Changing the e-mail address. A filled return stops it.
- **gate:user.two_factor_disable**: Switching two-step verification off. A filled return stops it; a string and `['message' => '…']` are both taken.
- **gate:user.affiliate_enroll**: Joining the affiliate programme. A filled return stops it, and no record is opened.
- **gate:user.affiliate_withdrawal**: An affiliate payout request. A filled return stops it.
- **gate:user.gdpr_process**: Processing a data request. A filled return stops it.
- **gate:user.subuser_invite**: Inviting a sub-account. A filled return stops it, and the text reaches the customer.

### Filters

These eight do **not** share one contract. Three shapes stand side by side, so read the return of the one you are writing for.

- **filter:user.required_fields**: The required fields on signing up and on the profile. Return an array; every listener's return is merged in.
- **filter:user.login_resolve**: Which account the entered identity resolves to. The value changes by reference; the return is not used.
- **filter:user.password_verify**: The password check. Returning `true` counts the password as good; every other return is ignored.
- **filter:user.login_redirect**: Where a sign-in lands. The value changes by reference; the return is not used.
- **filter:user.add_funds_amount**: The amount being added. The value changes by reference; the return is not used.
- **filter:custom_field.save_value**: The value a custom field saves. The value changes by reference; the return is not used.
- **filter:custom_field.load_value**: The value a custom field loads. The value changes by reference; the return is not used.
- **filter:user.verification_fields**: The fields on a verification document. The fields change by reference; the return is ignored.

### Example

```php
// A GATE: no account from a blocked country.
Hook::add('gate:user.create', 10, function ($data) {
    $cc = strtoupper((string) ($data['country'] ?? ''));
    if (Acme::blockedCountry($cc)) return 'We take no sign-ups from there.';
    return null;
});

// AN EVENT: put the new account on the marketing list.
Hook::add('action:user.created', 10, function ($user_id, $data) {
    Crm::subscribe((int) $user_id, (string) ($data['email'] ?? ''));
});
```

## Pitfalls

> **Both the customer and an operator change an account**
> 
> Most hooks here fire from **two places**: the customer's own screen and the management panel. Writing a gate to say "the customer cannot do this" stops **the operator** as well. Where you need to tell them apart, read the actor the hook hands you.

> **The sign-in gate does not check the password**
> 
> The sign-in gate runs **after** the credentials check. The work there is not checking a password but stopping for another reason. To touch the password check itself, a filter of its own exists.

> **Sub-accounts and switching blur the identity**
> 
> A customer can enter another account as a **sub-user**. The person signed in and the account being worked on are then **different**. Use the account id the hook hands you; an id read from the session points at the wrong person after a switch.

> **The delete hook is a point of no return**
> 
> Deleting an account takes its records with it. By the time the delete **event** fires the work is done; gather what you need at **the gate** instead. The gate is earlier, and it can stop the action where that is wanted.

## Related Articles

- [Customer Site Data and Gates](https://dev.wisecp.com/en/customer-site-data-and-gates)
- [Service Lifecycle Hooks](https://dev.wisecp.com/en/service-lifecycle-hooks)
- [Writing a Hook Listener](https://dev.wisecp.com/en/writing-a-hook-listener)
