PHPackages                             sozidatel/drdengi-php-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. sozidatel/drdengi-php-sdk

ActiveLibrary[API Development](/categories/api)

sozidatel/drdengi-php-sdk
=========================

PHP SDK for the Drebedengi SOAP API.

v0.7.0(2w ago)018—0%UnlicensePHPPHP &gt;=8.2CI passing

Since Jun 14Pushed 2w agoCompare

[ Source](https://github.com/sozidatel/drdengi-php-sdk)[ Packagist](https://packagist.org/packages/sozidatel/drdengi-php-sdk)[ RSS](/packages/sozidatel-drdengi-php-sdk/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (5)Versions (13)Used By (0)

drdengi-php-sdk
===============

[](#drdengi-php-sdk)

PHP SDK для SOAP API Дребеденег.

SDK намеренно не генерирует PHP-классы из WSDL: в WSDL почти все полезные ответы описаны как `anyType`, поэтому публичный слой здесь доменный и типизированный, а legacy-массивы остаются на границе транспорта.

Изменения релизов перечислены в [CHANGELOG.md](CHANGELOG.md), а важные шаги миграции — в [UPGRADING.md](UPGRADING.md). Политика совместимости описана в [docs/versioning.md](docs/versioning.md).

Установка
---------

[](#установка)

```
composer require sozidatel/drdengi-php-sdk
```

Для локальной разработки в этом репозитории:

```
composer install
composer test:unit
composer analyse
```

Настройка
---------

[](#настройка)

```
use Soz\Drebedengi\Credentials;
use Soz\Drebedengi\ClientOptions;
use Soz\Drebedengi\DrebedengiClient;
use Soz\Drebedengi\Endpoint;
use Soz\Drebedengi\WsdlCache;

$credentials = new Credentials(
    apiId: getenv('DREB_API_ID'),
    login: getenv('DREB_LOGIN'),
    password: getenv('DREB_PASSWORD'),
);

$client = DrebedengiClient::fromCredentials(
    $credentials,
    options: new ClientOptions(
        timezone: new DateTimeZone('Europe/Podgorica'),
        connectTimeout: 10,
        readTimeout: 30.0,
        wsdlCache: WsdlCache::Memory,
    ),
);
```

По умолчанию SDK обращается только к основному SaaS-серверу `Endpoint::RU_BASE_URI`. Сервер можно передать как base URL или как объект `Endpoint`:

```
// Официальное ME-зеркало.
$meClient = DrebedengiClient::fromCredentials($credentials, Endpoint::ME_BASE_URI);

// Self-hosted установка с путём. SDK добавит /soap/dd.wsdl и /soap/.
$selfHostedClient = DrebedengiClient::fromCredentials(
    $credentials,
    'https://money.example.com/drebedengi',
);
```

SDK не добавляет скрытые fallback-серверы. Чтобы включить failover, передайте два или больше base URL в нужном порядке:

```
$client = DrebedengiClient::fromCredentials(
    $credentials,
    [Endpoint::RU_BASE_URI, Endpoint::ME_BASE_URI],
);
```

Успешно ответивший endpoint становится активным, пока инфраструктурный сбой не заставит SDK выбрать другой. Read-only вызов можно повторить на следующем endpoint. Если до первой операции записи endpoint ещё не выбран чтением, SDK сначала выполняет безопасный `getAccessStatus`. Саму запись он отправляет только один раз: неоднозначный сбой после отправки приводит к `AmbiguousMutationException` и не вызывает автоматический повтор. Credentials передаются каждому endpoint из явно заданного списка, поэтому добавляйте только доверенные серверы.

SDK переопределяет SOAP `location`, потому что официальный WSDL может указывать другой адрес сервиса. Параметры `location` и `exceptions` зарезервированы транспортом: переданные через `soapOptions`значения для них игнорируются, чтобы credentials не ушли на другой endpoint, а `SoapFault`всегда проходил через классификацию исключений SDK. Остальные SOAP options остаются настраиваемыми и имеют приоритет над соответствующими полями `ClientOptions`. Передача `null` в `connectTimeout` или `readTimeout` отключает типизированное значение.

Дребеденьги передают даты операций как `YYYY-MM-DD HH:MM:SS` без timezone. SDK не нашел timezone в SOAP-методах аккаунта, поэтому timezone аккаунта нужно задавать явно через `ClientOptions`. Если не задать, будет использована `date_default_timezone_get()`.

Чтение данных
-------------

[](#чтение-данных)

```
$places = $client->places()->list();
$categories = $client->categories()->list();
$sources = $client->sources()->list();
$currencies = $client->currencies()->list();
$tags = $client->tags()->list();
$balance = $client->balance()->list();
```

У всех пяти финансовых справочников — счетов, категорий, источников, валют и тегов — одинаковые точечные методы:

```
$categories = $client->categories()->byIds(['10', '20']);
$category = $client->categories()->find('10');    // Category|null
$category = $client->categories()->require('10'); // Category или InvalidArgumentException
```

`byIds()` проверяет положительные ID, удаляет повторы и при пустом списке не делает SOAP-вызов. Неизвестные ID сервер просто не включает в ответ.

Остатки на выбранную дату и дополнительные опции:

```
use Soz\Drebedengi\Model\BalanceQuery;

$balance = $client->balance()->list(
    BalanceQuery::at(new DateTimeImmutable('2026-07-14'))
        ->includeHidden()
        ->includeZero()
        ->subtractAccumulations()
        ->subtractDebts(),
);
```

`restDate` форматируется в timezone из `ClientOptions`. `subtractAccumulations()` вычитает зарезервированные накопления, `subtractDebts()` — долги, а `includeHidden()` и `includeZero()`добавляют скрытые и нулевые счета. Legacy-массив параметров для `balance()->list()` пока поддерживается для обратной совместимости.

Места хранения:

```
$places = $client->places()->list();      // счета и папки, отсортированы по sort
$accounts = $client->places()->accounts(); // только type=4, можно использовать в операциях
$folders = $client->places()->folders();   // только type=9
$placeTree = $client->places()->tree(includeHidden: false);

foreach ($placeTree as $node) {
    echo $node->place->name;
    echo $node->place->canHaveTransactions() ? " можно использовать в операциях\n" : " только папка\n";
}
```

`type=4` — реальный счет, `type=9` — папка для дерева. Отрицательные `parent_id`, например `-3`, сохраняются как `systemParentId`, а не как обычная папка. Для `-3` счет остается видимым и принимает операции, но в UI попадает в группу `Скрытые суммы`, а его баланс исключается из `Итого`; это можно проверить через `$place->isExcludedFromTotal()`.

Плоский список категорий уже отсортирован по `sort`. Для дерева:

```
$tree = $client->categories()->tree(includeHidden: false);

foreach ($tree as $node) {
    echo $node->category->name . "\n";
    foreach ($node->children as $child) {
        echo "  " . $child->category->name . "\n";
    }
}
```

Для ``:

```
foreach ($client->categories()->options(includeHidden: false) as $option) {
    echo "{$option->label}";
}
```

CRUD финансовых справочников
----------------------------

[](#crud-финансовых-справочников)

Создание и частичное обновление возвращают уже перечитанный типизированный DTO, а не сырой SOAP-массив:

```
use Soz\Drebedengi\Model\ReferenceWriteToken;

$writeToken = ReferenceWriteToken::generate();
$category = $client->categories()->create(
    name: 'Кафе',
    parentId: null, // null означает корневую категорию
    hidden: false,
    sort: 10,
    description: 'Еда вне дома',
    writeToken: $writeToken,
);

$category = $client->categories()->update($category->id, [
    'name' => 'Кафе и рестораны',
    'is_hidden' => false,
]);

$account = $client->places()->createAccount(
    name: 'Наличные для поездки',
    hidden: true,
);
$account = $client->places()->update($account->id, [
    'name' => 'Основная карта',
    'description' => 'Повседневные расходы',
]);
```

`createAccount()` намеренно создаёт только обычный `type=4` счёт. SOAP не даёт безопасного контракта создания папок, кредитных карт или системных долговых счетов. Обновление также отклоняет такие объекты, потому что legacy `setPlaceList` может потерять их server-managed состояние.

`places()->delete()` ещё строже: он удаляет только обычный пустой счёт и перед `deleteObject()` проверяет весь журнал, включая плановые операции. Папки, долговые, credit-card, purse-owned и auto-hide счета отклоняются. Legacy-сервер не выполняет это удаление транзакционно, поэтому во время удаления нельзя параллельно добавлять операции в тот же счёт.

Источники доходов и теги поддерживают тот же CRUD:

```
$sources = $client->sources()->list(); // плоский список, отсортирован по sort
$sourceTree = $client->sources()->tree(includeHidden: false);
$sourceOptions = $client->sources()->options(includeHidden: false);

$source = $client->sources()->create('Возвраты');
$source = $client->sources()->update($source->id, ['name' => 'Возвраты и компенсации']);

$tag = $client->tags()->create('Командировка', hidden: true);
$tag = $client->tags()->update($tag->id, ['is_hidden' => false]);
$tagOptions = $client->tags()->options(includeHidden: false);
```

Список тегов может включать семейные теги других пользователей. `tags()->update()` и `tags()->delete()` намеренно работают только с тегами, чей `userId` совпадает с текущим пользователем: update через legacy `setTagList` иначе переписал бы владельца, а delete удалил бы чужой видимый семейный тег. Если владелец не пришёл в SOAP-ответе, SDK также отклоняет mutation.

При удалении тега Дребеденьги могут убрать первое вхождение `[ИмяТега]` из комментариев связанных операций, поэтому автоматическое удаление безопаснее использовать для новых или заведомо неиспользуемых тегов.

Валюты имеют отдельные методы для безопасного изменения курса и default:

```
$previousDefault = $client->currencies()->default();

$currency = $client->currencies()->create(
    name: 'Тестовая валюта', // name и code: максимум 16 символов
    course: '1.25',
    code: 'TST',
    hidden: true,
);

$currency = $client->currencies()->update($currency->id, [
    'course' => '1.30',
]);

// Менять default безопасно только если прежнюю валюту потом можно записать
// обратно через legacy setCurrencyList.
if (
    $previousDefault !== null
    && $previousDefault->ratio === 1
    && !$previousDefault->investing
) {
    $currency = $client->currencies()->setDefault($currency->id);
    $client->currencies()->setDefault($previousDefault->id);
}
$client->currencies()->delete($currency->id);
```

Legacy `setCurrencyList` принудительно записывает `ratio=1` и `is_investing=false`. Поэтому SDK разрешает update/default только для обычных неинвестиционных валют с `ratio=1` и отклоняет опасную запись crypto/investing валют до SOAP-вызова. Непосредственно перед типизированной mutation старый каталог валют инвалидируется, а после успешного ответа перечитывается. Если ответ потерян или не подтверждает целевой ID, следующий доступ снова обратится к серверу, а не вернёт потенциально устаревший снимок.

Перед типизированными `update()` и `delete()`, а также `currencies()->setDefault()`, SDK проверяет полный доступ. Update читает существующий объект и дополняет patch обязательными legacy-полями. Delete сначала убеждается, что ID принадлежит именно этому справочнику: серверный тип `object` общий для категорий, источников и счетов. Неизвестный ID возвращает `false`, не выполняя удаление. Для нестандартных будущих полей у каждого сервиса сохранён `savePayloads()` как raw escape hatch. Он намеренно обходит типизированные guards и read-back; после `currencies()->savePayloads()` вызови `currencies()->refresh()`, если общий каталог должен сразу увидеть изменения.

Удаление родительской категории сохраняет legacy cascade-семантику и может удалить всё поддерево. Источники используют тот же иерархический `deleteObject(..., 'object')`. SDK не добавляет leaf-only guard, поэтому перед удалением категории или источника проверь детей через `list()` / `tree()`, если каскад не задуман.

`ReferenceWriteToken` позволяет немедленно повторить создание того же справочного объекта с прежним `client_id`. Как и `RecordWriteToken`, это не долговечный idempotency key: повтор должен идти с совершенно тем же payload до следующей успешной записи справочника того же типа. При failover повторяй только на endpoint из `AmbiguousMutationException`.

Аккаунт и подписка:

```
$userId = $client->account()->userId();
$hasAccess = $client->account()->hasAccess();
$expireDate = $client->account()->expireDate();
```

Операции:

```
use Soz\Drebedengi\Model\RecordQuery;
use Soz\Drebedengi\Model\OperationType;

$records = $client->records()->list(
    RecordQuery::forDateRange(
        new DateTimeImmutable('2026-01-01'),
        new DateTimeImmutable('2026-01-31'),
    )
        ->operationType(OperationType::Expense)
        ->onlyPlaces(['11416426'])
        ->onlyCategories(['CATEGORY_ID'])
        ->withBalanceAfter()
);

foreach ($records as $record) {
    echo $record->balanceAfter?->toDecimalString();
}
```

Периоды и остальные фильтры detail-журнала:

```
$records = $client->records()->list(
    (new RecordQuery())
        ->thisMonth() // также today(), lastMonth(), thisQuarter(), thisYear(), lastYear(), allTime(), last20()
        ->relativeTo(new DateTimeImmutable('2026-07-14'))
        ->operationType(OperationType::Expense)
        ->includePlanned()
        ->includeDebts(false)
        ->forUser('USER_ID')
        ->exceptPlaces(['PLACE_ID'])
        ->onlyTags(['TAG_ID'])
        ->exceptCategories(['CATEGORY_ID']),
);
```

`relativeTo()` задаёт опорную дату для именованного периода и форматируется в timezone аккаунта. `forAllUsers()` возвращает query к семейной выборке. Для счетов, тегов и категорий доступны `only...()`, `except...()` и возврат к полной выборке через `all...()`.

`records()->list()` без query и пустой `new RecordQuery()` одинаково возвращают последние 20 операций. Detail-журнал поддерживает только оригинальную валюту каждой операции: для пересчитанных финансовых сумм используйте `ReportQuery::convertedToCurrency()`.

При `includePlanned()` в `Record` заполняются `planned`, `plannedRepeatId`, `plannedPeriodId`и `plannedInitialDate`. Плановые операции нельзя сочетать с `withBalanceAfter()`: SOAP не отдаёт готовый прогнозный остаток, а вычислять его из фактического остатка было бы неоднозначно.

Фильтры по категориям и тегам допустимы только для расходов или доходов, поэтому перед ними нужно явно выбрать `OperationType::Expense` или `OperationType::Income`. Несовместимое сочетание SDK отклоняет до SOAP-вызова.

По умолчанию `RecordQuery` использует `r_currency=0`, то есть оригинальную валюту операции. Обычное чтение использует безопасный detail report (`is_report=true`, `r_how=1`). В legacy SOAP режиме `is_report=false` сервер считает запрос первоначальной синхронизацией и сбрасывает служебные соответствия `client_id` / `server_id`, поэтому SDK не использует его для журнала.

`withBalanceAfter()` добавляет в каждый `Record` поле `balanceAfter` с остатком на счёте сразу после операции. Расчёт доступен только для оригинальной валюты и может выполнить дополнительный `getRecordList`, если исходный запрос содержит фильтры, а также один `getBalance`.

Агрегированные отчёты
---------------------

[](#агрегированные-отчёты)

`ReportService` отделён от журнала операций, потому что SOAP возвращает для агрегатов дерево категорий или источников, а не обычные `Record`:

```
use Soz\Drebedengi\Model\ReportQuery;

$query = ReportQuery::forDateRange(
    new DateTimeImmutable('2026-01-01'),
    new DateTimeImmutable('2026-01-31'),
)
    ->convertedToCurrency('CURRENCY_ID')
    ->onlyPlaces(['PLACE_ID'])
    ->averageDaily(); // также averageWeekly(), averageMonthly(), withoutAveraging()

$expenses = $client->reports()->expensesByCategory($query);
$income = $client->reports()->incomeBySource($query);

foreach ($expenses as $row) {
    echo $row->name . ': ' . $row->amount->toDecimalString();
}
```

Без query отчёт строится за текущий месяц в оригинальных валютах и без усреднения. `ReportQuery`поддерживает те же периоды, пользователей, планы, долги и only/except-фильтры, что и `RecordQuery`. `ReportRow::amount` имеет тип `DecimalMoneyAmount`: при валютном пересчёте сервер может вернуть дробное количество minor units, и SDK сохраняет его без округления.

Создание операций
-----------------

[](#создание-операций)

Суммы в SDK представлены как integer minor units, то есть в мельчайших единицах конкретной валюты. Для обычных валют это исторически 2 знака после запятой, а для криптовалют точность может быть выше.

```
use Soz\Drebedengi\Model\ExpenseGroupItem;
use Soz\Drebedengi\Model\RecordWriteToken;

$currency = $client->currencies()->require('CURRENCY_ID');

$result = $client->records()->createExpense(
    placeId: 'PLACE_ID',
    categoryId: 'CATEGORY_ID',
    amount: $currency->amount('12.34'),
    currencyId: $currency->id,
    date: new DateTimeImmutable(),
    comment: 'Обед',
);

$recordId = $result->firstServerId();
$rawResponse = $result->raw;

$client->records()->createIncome(
    placeId: 'PLACE_ID',
    sourceId: 'SOURCE_ID',
    amount: $currency->amount('100.00'),
    currencyId: $currency->id,
    date: new DateTimeImmutable(),
    comment: 'Возврат',
);

$client->records()->createTransfer(
    fromPlaceId: 'FROM_PLACE_ID',
    toPlaceId: 'TO_PLACE_ID',
    amount: $currency->amount('50.00'),
    currencyId: $currency->id,
    date: new DateTimeImmutable(),
    comment: 'Перенос между счетами',
);
```

Группа расходов, например строки одного чека:

```
$client->records()->createExpenseGroup(
    placeId: 'PLACE_ID',
    items: [
        new ExpenseGroupItem('CATEGORY_ID_1', $currency->amount('12.34'), 'Кофе'),
        ['categoryId' => 'CATEGORY_ID_2', 'amount' => $currency->amount('56.78'), 'comment' => 'Продукты'],
    ],
    currencyId: $currency->id,
    date: new DateTimeImmutable(),
);
```

Для нескольких строк SDK отправляет один `setRecordList` с общим `group_id`, который связывает все позиции чека в одну группу.

Все методы `records()->create*()` возвращают `WriteResult`: в нём доступны `serverIds`, `clientIds`, `firstServerId()`, `serverIdForClientId()` и исходные строки `raw`. Объект также поддерживает `count()`, `foreach` и чтение `$result[0]`. Низкоуровневый `savePayloads()` сохранён для кода, которому нужен именно сырой SOAP-массив.

Если ответ на запись потерян, заранее созданный токен позволяет немедленно повторить тот же payload с тем же `client_id`:

```
$writeToken = RecordWriteToken::generate();
$operationDate = new DateTimeImmutable();

$result = $client->records()->createExpense(
    placeId: 'PLACE_ID',
    categoryId: 'CATEGORY_ID',
    amount: $currency->amount('12.34'),
    currencyId: $currency->id,
    date: $operationDate,
    comment: 'Обед',
    writeToken: $writeToken,
);
```

Токен фиксирует только идентификаторы: при повторе нужно передать совершенно те же аргументы, включая тот же `$operationDate`, суммы и комментарий. Legacy-дедупликация ограничена: следующий успешный `setRecordList` для того же API ID заменяет сохранённые соответствия. Повторяй запрос до другой записи и до `sync()->initialRecords()`. При failover нельзя повторять вызов на том же failover-клиенте: создай клиент с одним endpoint из `AmbiguousMutationException::$endpoint` либо сначала сверь результат чтением. Автоматического повтора mutations SDK не делает.

`Currency::amount()` — рекомендуемый способ создавать суммы: он сам применяет точность из `ratio` и связывает `MoneyAmount` с ID валюты. Валюту можно найти без ручного перебора:

```
$btc = $client->currencies()->requireByCode('BTC');

$amount = $btc->amount('0.00001234');
```

Прежний `MoneyAmount::fromDecimalString()` остаётся совместимым для валют с двумя знаками. Перед записью SDK загружает каталог валют и проверяет scale, а у суммы от `Currency::amount()` — ещё и совпадение `currencyId`. Поэтому потенциально неверная сумма отклоняется до SOAP-вызова. Отдельный аргумент `currencyId` в методах `create*()` пока сохранён ради обратной совместимости; передавай в него ID той же `Currency`, которая создала сумму. В следующей major-версии этот дубль можно будет убрать в пользу обязательной currency-bound суммы.

При чтении через `records()` и `balance()` суммы уже имеют правильный `scale` и связанный `currencyId`. `withScale()` и `fromFloat()` объявлены устаревшими. Для точного изменения scale с сохранением суммы используй `rescale()`; намеренная переинтерпретация тех же minor units называется `reinterpretScale()`. Каталог валют лениво кэшируется в пределах экземпляра `DrebedengiClient`; для принудительного обновления есть `$client->currencies()->refresh()`.

`createTransfer()` и `createExchange()` сами создают парные записи и связывают их через `client_move_id` / `client_change_id`. Перевод на тот же самый счёт SDK отклоняет до SOAP-вызова.

Обновление и удаление
---------------------

[](#обновление-и-удаление)

```
use Soz\Drebedengi\Model\RecordPatch;

$record = $client->records()->byIds(['RECORD_ID'])[0];
$result = $client->records()->update($record, new RecordPatch(
    amount: $currency->amount('25.00'),
    comment: 'Исправленный комментарий',
));

$client->records()->delete('RECORD_ID', $record->operationType);
```

`RecordPatch` поддерживает `placeId`, `budgetObjectId`, `amount`, `operationDate`, `comment`, `currencyId` и `duty`. Сумма трактуется как абсолютная, а тип расхода или дохода определяет её направление. Исходный `Record` остаётся неизменным. Перемещения и обмены состоят из двух связанных строк; одиночный `update()` отклоняет их до SOAP-вызова, чтобы не отправлять неполную пару. При смене `currencyId` одновременно передай `amount`, созданный через новую `Currency`; иначе currency-bound сумма исходной операции не пройдёт проверку согласованности.

DTO сохраняют исходный SOAP-массив в поле `raw`, чтобы можно было разбирать неизвестные legacy-поля без потери данных. Методы `delete()` принимают только положительные целочисленные server ID и проверяют их до SOAP-вызова.

Обработка ошибок
----------------

[](#обработка-ошибок)

```
use Soz\Drebedengi\Exception\AmbiguousMutationException;
use Soz\Drebedengi\Exception\EndpointUnavailableException;
use Soz\Drebedengi\Exception\SoapFaultException;

try {
    $client->records()->createExpense(/* ... */);
} catch (AmbiguousMutationException $exception) {
    // Запись могла сохраниться. Не повторяй её с новым client_id.
} catch (EndpointUnavailableException $exception) {
    // retrySafe=true означает, что повтор не создаст дубль.
} catch (SoapFaultException $exception) {
    // Сервер ответил business/API fault.
}
```

У транспортных исключений доступны свойства `method`, `endpoint`, `retrySafe` и `faultCode`. `AmbiguousMutationException` наследует `EndpointUnavailableException`, поэтому ставь более узкий catch первым.

Синхронизация по revision
-------------------------

[](#синхронизация-по-revision)

```
$current = $client->sync()->currentRevision();
$changes = $client->sync()->changesSince($lastSavedRevision);
```

Потребитель SDK должен сохранять последнюю успешно обработанную revision сам. Для cron-синхронизаций важно сохранять progress инкрементально после каждой обработанной revision, а не только в конце пачки.

Для настоящей первоначальной синхронизации можно получить полный набор записей в legacy sync/export-формате:

```
$initialRecords = $client->sync()->initialRecords();
```

Этот вызов намеренно использует `is_report=false`. Сервер Дребеденег очищает при нём служебную таблицу дедупликации `client_id` / `server_id` для текущего API ID. Не используй `initialRecords()` для обычного чтения журнала или проверки результата записи. Перед этим state-changing запросом SDK загружает каталог валют; суммы первоначальной синхронизации получают тот же правильный `scale` и `currencyId`, что и обычный журнал.

Прямой доступ к SOAP
--------------------

[](#прямой-доступ-к-soap)

Не все методы WSDL покрыты доменным API SDK. Для редких методов есть прямой вызов с автоматическим добавлением credentials:

```
$result = $client->raw()->call('getAccessStatus');
$accums = $client->raw()->call('getAccumList', [[]]);
```

Live-тесты
----------

[](#live-тесты)

Скопируй `.env.example` в `.env` и заполни тестовый аккаунт:

```
DREB_TEST_BASE_URI=https://www.drebedengi.ru
DREB_TEST_API_ID=
DREB_TEST_LOGIN=
DREB_TEST_PASSWORD=
DREB_TEST_TIMEZONE=UTC
DREB_RUN_LIVE_READ_TESTS=1
DREB_RUN_LIVE_WRITE_TESTS=0
DREB_BROWSER_CHECKS=0
```

Read-only проверка настоящего API:

```
composer test:integration:read
```

Записывающие тесты отделены и требуют явного флага `DREB_RUN_LIVE_WRITE_TESTS=1`:

```
composer test:integration:write
```

Для обратной совместимости старый `DREB_RUN_LIVE_TESTS=1` пока включает только read-only тесты и никогда не разрешает запись. Записывающие тесты создают только объекты с уникальными тестовыми именами или комментариями, покрывают операции и CRUD справочников, восстанавливают исходную default-валюту и удаляют fixtures двумя проходами. После каждого теста выполняется повторное чтение всех типов и проверка нулевого остатка. `deleteAll` в автоматических тестах не используется.

Если аккаунт отвечает `No payment` на `setRecordList`, тест записи будет пропущен. Для полной проверки создания/удаления операций нужен тестовый аккаунт с активным доступом к API-записи.

Текущие ограничения
-------------------

[](#текущие-ограничения)

- Основной SDK работает только с SOAP API.
- Credential-shaped методы покупок и чеков можно вызвать через `raw()`, но они пока не типизированы. Регистрация пользователя и некоторые внутренние методы сервера имеют другую сигнатуру credentials и намеренно не проксируются этим клиентом. Статус подписки уже доступен через `account()`.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance98

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

12

Last Release

14d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b92bb94198941d2c2a0d4d652bd1a7d15f8aace2b96179e2788afd109d139ca?d=identicon)[sozidatel](/maintainers/sozidatel)

---

Top Contributors

[![sozidatel](https://avatars.githubusercontent.com/u/1482409?v=4)](https://github.com/sozidatel "sozidatel (19 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sozidatel-drdengi-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/sozidatel-drdengi-php-sdk/health.svg)](https://phpackages.com/packages/sozidatel-drdengi-php-sdk)
```

###  Alternatives

[exsyst/swagger

A php library to manipulate Swagger specifications

35916.4M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24016.2M20](/packages/hubspot-api-client)[pocketmine/bedrock-protocol

An implementation of the Minecraft: Bedrock Edition protocol in PHP

172445.0k18](/packages/pocketmine-bedrock-protocol)[botman/driver-telegram

Telegram driver for BotMan

93459.5k6](/packages/botman-driver-telegram)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
