111 lines
3.6 KiB
PHP
111 lines
3.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace paygw_moneta\local\receipt;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
/**
|
|
* Покупатель в чеке — объект `client` из `kassaspecification.pdf`: обязательно
|
|
* хотя бы одно из `email`/`phone`; при обоих чек уходит только на e-mail.
|
|
* `name` — ФИО покупателя, передаётся только вместе с контактом.
|
|
*
|
|
* @package paygw_moneta
|
|
* @copyright 2026 Moneta Labs {@link https://moneta.ru/}
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
final class Client
|
|
{
|
|
public const NAME_MAX_LENGTH = 256;
|
|
public const EMAIL_MAX_LENGTH = 64;
|
|
|
|
/** По `kassaspecification.pdf` (принимающая сторона); JSON-схема Check URL даёт 13. */
|
|
public const PHONE_MAX_LENGTH = 19;
|
|
|
|
private function __construct(
|
|
public readonly ?string $name,
|
|
public readonly ?string $email,
|
|
public readonly ?string $phone,
|
|
) {}
|
|
|
|
/**
|
|
* Строит покупателя из «сырых» данных профиля; невалидные значения
|
|
* отбрасываются. Если у покупателя нет ни e-mail, ни телефона, подставляется
|
|
* `$fallbackEmail` — настройка «Email для чеков» (чек уходит продавцу, ФИО
|
|
* покупателя остаётся); без неё покупателя нет (`null`).
|
|
*/
|
|
public static function fromContacts(
|
|
?string $email,
|
|
?string $phone,
|
|
?string $name = null,
|
|
?string $fallbackEmail = null,
|
|
): ?self {
|
|
$email = self::normalizeEmail($email);
|
|
$phone = self::normalizePhone($phone);
|
|
if ($email === null && $phone === null) {
|
|
$email = self::normalizeEmail($fallbackEmail);
|
|
}
|
|
if ($email === null && $phone === null) {
|
|
return null;
|
|
}
|
|
|
|
return new self(self::normalizeName($name), $email, $phone);
|
|
}
|
|
|
|
/**
|
|
* @param array{name?: ?string, email?: ?string, phone?: ?string} $data
|
|
*/
|
|
public static function fromArray(array $data): self
|
|
{
|
|
$client = self::fromContacts($data['email'] ?? null, $data['phone'] ?? null, $data['name'] ?? null);
|
|
if ($client === null) {
|
|
throw new InvalidArgumentException('У покупателя нет ни e-mail, ни телефона.');
|
|
}
|
|
|
|
return $client;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, string>
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return array_filter(
|
|
['name' => $this->name, 'email' => $this->email, 'phone' => $this->phone],
|
|
static fn(?string $value): bool => $value !== null,
|
|
);
|
|
}
|
|
|
|
public static function normalizeEmail(?string $email): ?string
|
|
{
|
|
$email = trim((string) $email);
|
|
if (
|
|
$email === ''
|
|
|| strlen($email) > self::EMAIL_MAX_LENGTH
|
|
|| filter_var($email, FILTER_VALIDATE_EMAIL) === false
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return $email;
|
|
}
|
|
|
|
public static function normalizePhone(?string $phone): ?string
|
|
{
|
|
$digits = preg_replace('/[^0-9]/', '', (string) $phone) ?? '';
|
|
if ($digits === '' || strlen($digits) + 1 > self::PHONE_MAX_LENGTH) {
|
|
return null;
|
|
}
|
|
|
|
return '+' . $digits;
|
|
}
|
|
|
|
private static function normalizeName(?string $name): ?string
|
|
{
|
|
$name = ReceiptText::validate((string) $name, self::NAME_MAX_LENGTH);
|
|
|
|
return $name === '' ? null : $name;
|
|
}
|
|
}
|