Ведите своих клиентов и их кредиты

PHP

Создайте клиента, пополните и спишите его кредиты, прочитайте баланс и выписку, запустите флоу, который оплачивает клиент, и заблокируйте клиента.

7 мин чтения

Клиент — пользователь вашего приложения, который платит за то, что Slen выполняет для него, кредитами — вашей собственной единицей, без валюты. Ваше приложение создаёт клиента, пополняет его кредиты и называет клиента в каждом запуске; Slen берёт кредиты за вызовы моделей в запуске. Вызовам этой страницы нужен ключ с customers:write, а чтению — с customers:read.

Создать клиента

PHP
// The external user id is your own opaque id of the customer — not an email address or a name.
$customer = $slen->customers()->create('user_8421');

echo $customer->externalUserId, ' ', $customer->status->value, PHP_EOL;
  • Внешний идентификатор — ваш собственный идентификатор клиента. Держите его непрозрачным — не адрес почты и не имя: он виден в консоли и в каждой выписке.

  • customers.create() гарантирует, что клиент существует: второй вызов отвечает тем же клиентом. Он не двигает кредиты, и у нового клиента нет счетов.

  • У клиента по одному счёту на каждый тип кредитов: default, если вы не назвали другой.

Пополнить и списать

PHP
use Slen\Sdk\Modules\Customers\Options\MovementOptions;

$account = $slen->customers()->topUp('user_8421', '100.0000', new MovementOptions(note: 'Invoice 7781', idempotencyKey: 'invoice-7781:topup'));

echo $account->balance, PHP_EOL;
  • customers.topUp() добавляет кредиты. Он создаёт счёт этого типа кредитов, а также клиента, когда их ещё нет.

  • Примечание записывается в строку выписки: номер счёта на оплату, причина.

  • Ключ идемпотентности называет движение: под тем же ключом кредиты движутся один раз — Ключи идемпотентности.

Тип кредитов, примечание и ключ движения — именованные аргументы MovementOptions, последнего аргумента вызова. Остальные вызовы этой страницы принимают свои опции так же — CustomerListOptions, AccountOptions, BlockOptions, EntryListOptions, PriceListOptions, — а RequestOptions там, где больше ничего не принимают.

PHP
// A service of your own application, paid for in the customer's credits.
$charged = $slen->customers()->charge('user_8421', '5.0000', new MovementOptions(note: 'PDF export', idempotencyKey: 'export-991:charge'));

echo $charged->balance, PHP_EOL;
  • customers.charge() берёт кредиты за услугу вашего собственного приложения, которую Slen не выполняет: экспорт, отчёт, место в команде. Выписка показывает его отдельным списанием, а не тратой вызова модели.

  • Списание, которое счёт не может покрыть, отклоняется с insufficient_credits, если счёту нельзя уходить ниже нуля. Списание ничего не создаёт: списание для клиента, которого Slen не знает, отклоняется с customer_not_found.

  • customers.adjust() вручную корректирует баланс, со знаком: положительная сумма добавляет кредиты, отрицательная — забирает.

  • Кредиты — десятичная строка не больше чем с четырьмя знаками после точки: '5', '5.25'. Любое другое значение, ноль и отрицательное пополнение или списание отклоняются до отправки запроса, с кодом invalid_request.

Прочитать баланс и выписку

PHP
$customer = $slen->customers()->get('user_8421');

foreach ($customer->accounts as $held) {
    echo $held->creditType, ' ', $held->available, PHP_EOL;
}

customers.get() отвечает клиентом со всеми его счетами. У счёта три суммы:

  • balance — сколько на счёте;

  • reserved — сколько на нём держат идущие запуски;

  • available — balance минус reserved: сколько может взять новый запуск.

PHP
use Slen\Sdk\Modules\Customers\Options\EntryListOptions;

$statement = $slen->customers()->entries('user_8421', new EntryListOptions(limit: 2));

foreach ($statement->data as $entry) {
    echo $entry->entryType->value, ' ', $entry->credits, ' ', $entry->note, PHP_EOL;
}

customers.entries() читает выписку: каждое движение кредитов клиента, сначала самые новые, постранично. Её можно сузить до одного типа кредитов и одного вида записи.

ВидКто пишетЧто делает
topupcustomers.topUp()Добавляет к балансу
chargecustomers.charge()Забирает из баланса
adjustcustomers.adjust()Двигает баланс в любую сторону
holdВызов модели в запускеЗабирает цену из доступного до вызова модели
commitТот же вызов, когда он удалсяЗабирает цену из баланса
releaseТот же вызов, когда он не удалсяВозвращает цену в доступное

Страница несёт курсор следующей: передайте его в следующий вызов и остановитесь, когда он равен null. customers.list() так же читает клиентов проекта с их счетами. Он находит их по части внешнего идентификатора, по статусу, по типу кредитов или по lowBalance — доступно меньше кредитов, чем число, которое вы передали.

Запуск, который оплачивает клиент

Назовите клиента в запуске, а тип кредитов — когда он не default, — Отправить запрос.

  • Когда у проекта есть цена в кредитах для операции, каждый вызов модели в запуске резервирует эту цену на счёте клиента до вызова модели. Кредиты забираются, когда вызов удался, и возвращаются, когда он не удался.

  • Операция без цены в кредитах кредитов не берёт. Цены задают в консоли, а customers.prices() их читает.

  • Запуск, который счёт не может оплатить, отклоняется до вызова модели, с insufficient_credits.

  • Платный запуск для клиента, которого Slen не знает, отклоняется с customer_not_found. Сначала создайте клиента и пополните его кредиты.

PHP
use Slen\Sdk\Exceptions\InsufficientCreditsException;
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;

try {
    $slen->operations()->execute(
        'generate_email',
        ['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email.']]],
        new ExecuteOptions(customer: 'user_9310', idempotencyKey: 'order-45:email'),
    );
} catch (InsufficientCreditsException $refused) {
    // The customer's account cannot pay for the run: nothing ran, nothing was held.
    echo $refused->errorCode, PHP_EOL;
}

insufficient_credits — один из отказов в трате, и оба SDK выбрасывают его как тип этого семейства — Когда трата отклонена.

Настройки счёта

customers.updateAccount() задаёт две настройки одного счёта:

  • может ли баланс уходить ниже нуля — по умолчанию нет;

  • порог, ниже которого движение отправляет вебхук customer.balance_low, — '0', значение по умолчанию, его выключает.

Вызов задаёт обе сразу: настройка, которую вы не передали, возвращается к значению по умолчанию. Он создаёт клиента и счёт, когда их нет. Вебхук низкого баланса отправляется один раз — когда движение опускает доступные кредиты ниже порога, — Вебхуки.

Заблокировать клиента

PHP
use Slen\Sdk\Modules\Customers\Options\BlockOptions;

// New runs for the customer are refused from now on; the balance can still be settled.
$blocked = $slen->customers()->block('user_8421', new BlockOptions(reason: 'chargeback'));

echo $blocked->status->value, ' ', $blocked->blockReason, PHP_EOL;
  • Новые запуски заблокированного клиента отклоняются с customer_blocked. Запуск, который стоял в очереди или ждал до блокировки, отклоняется на следующем вызове модели, который берёт кредиты. Оба SDK выбрасывают этот код как отказ политики.

  • Пополнения, списания, корректировки и выписка остаются открытыми, чтобы вы могли рассчитаться с клиентом.

  • customers.unblock() снимает блокировку.

  • customers.delete() удаляет клиента без истории: без движений и без баланса. Удаление клиента с историей отклоняется с 409 и кодом customer_has_history — заблокируйте его вместо этого.

Что читать дальше