update to provisioner code
This commit is contained in:
@@ -3,33 +3,74 @@
|
||||
namespace App\Services\Accounts;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
|
||||
final class PurchasedAccountProvisioner
|
||||
{
|
||||
public function ensure(?string $email, ?string $name = null, bool $sendSetupLink = true): ?User
|
||||
{
|
||||
public function ensure(
|
||||
?string $email,
|
||||
?string $name = null,
|
||||
bool $sendSetupLink = true
|
||||
): ?User {
|
||||
$email = strtolower(trim((string) $email));
|
||||
|
||||
if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$user = User::query()->whereRaw('lower(email) = ?', [$email])->first();
|
||||
/*
|
||||
* Fast path for the normal case.
|
||||
*
|
||||
* Paddle can deliver several related webhook events concurrently, so
|
||||
* this lookup alone cannot guarantee that another request will not
|
||||
* create the user between this SELECT and our INSERT.
|
||||
*/
|
||||
$user = $this->findByEmail($email);
|
||||
|
||||
if ($user) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
$user = User::create([
|
||||
'name' => $this->displayName($name, $email),
|
||||
'email' => $email,
|
||||
'email_verified_at' => now(),
|
||||
'password' => Str::random(64),
|
||||
'is_admin' => false,
|
||||
]);
|
||||
$created = false;
|
||||
|
||||
if ($sendSetupLink) {
|
||||
try {
|
||||
$user = User::create([
|
||||
'name' => $this->displayName($name, $email),
|
||||
'email' => $email,
|
||||
'email_verified_at' => now(),
|
||||
'password' => Str::random(64),
|
||||
'is_admin' => false,
|
||||
]);
|
||||
|
||||
$created = true;
|
||||
} catch (UniqueConstraintViolationException $exception) {
|
||||
/*
|
||||
* Another webhook may have created this account after our SELECT
|
||||
* but before our INSERT. In that case the unique email constraint
|
||||
* correctly rejects our INSERT; simply reuse the account that won
|
||||
* the race.
|
||||
*/
|
||||
$user = $this->findByEmail($email);
|
||||
|
||||
if (! $user) {
|
||||
/*
|
||||
* The unique violation was not caused by the email account
|
||||
* appearing concurrently, so preserve the original failure.
|
||||
*/
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the request that actually created the account should send the
|
||||
* account setup/reset link. Concurrent Paddle events must not result
|
||||
* in several setup emails being sent to the customer.
|
||||
*/
|
||||
if ($created && $sendSetupLink) {
|
||||
DB::afterCommit(static function () use ($email): void {
|
||||
Password::sendResetLink(['email' => $email]);
|
||||
});
|
||||
@@ -38,9 +79,17 @@ final class PurchasedAccountProvisioner
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function findByEmail(string $email): ?User
|
||||
{
|
||||
return User::query()
|
||||
->whereRaw('LOWER(email) = ?', [$email])
|
||||
->first();
|
||||
}
|
||||
|
||||
private function displayName(?string $name, string $email): string
|
||||
{
|
||||
$name = trim((string) $name);
|
||||
|
||||
if ($name !== '') {
|
||||
return mb_substr($name, 0, 120);
|
||||
}
|
||||
@@ -48,6 +97,12 @@ final class PurchasedAccountProvisioner
|
||||
$localPart = strstr($email, '@', true) ?: 'SonarDesk Customer';
|
||||
$localPart = trim(str_replace(['.', '_', '-'], ' ', $localPart));
|
||||
|
||||
return mb_substr($localPart !== '' ? ucwords($localPart) : 'SonarDesk Customer', 0, 120);
|
||||
return mb_substr(
|
||||
$localPart !== ''
|
||||
? ucwords($localPart)
|
||||
: 'SonarDesk Customer',
|
||||
0,
|
||||
120
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user