16 Commits

Author SHA1 Message Date
Anatoliy Poloz
5a0d6ac03a fix(sbp): production url 2026-06-03 13:24:38 +03:00
Anatoliy Poloz
95203efbad fix(oauth): after saloon upgrade 2026-06-03 10:07:00 +03:00
c763fe6c71 fix(statements/transactions) 2026-06-02 21:00:44 +03:00
a412af3e07 fix(transactions) 2026-06-02 19:46:51 +03:00
9b62a59705 saloon update 2026-04-25 13:59:25 +03:00
Anatoliy Poloz
d4db68f2e7 refactor(error): handling 2026-04-07 18:47:53 +03:00
Anatoliy Poloz
ea374d2696 doc(test-payin) added 2026-04-07 18:44:27 +03:00
Anatoliy Poloz
13c91ad108 bump versions 2026-04-05 09:37:54 +03:00
Anatoliy Poloz
3cf15b7913 refactor(qr) 2026-04-02 16:45:41 +03:00
Anatoliy Poloz
a05a6506e8 fix(sign) 2026-03-21 07:10:16 +03:00
Anatoliy Poloz
4f52a189c7 add Identification 2026-03-19 19:38:21 +03:00
Anatoliy Poloz
b7b2a8fd61 refactor(Sbp) 2026-03-17 12:26:30 +03:00
Anatoliy Poloz
e2adc8d530 fix(OpenSslCmsSigner) 2026-03-10 22:29:35 +03:00
Anatoliy Poloz
c070297211 fix(sign) 2026-03-08 18:35:02 +03:00
Anatoliy Poloz
706fcb01b2 fix 2026-03-08 18:03:43 +03:00
7a6b13b12d update docs cross links 2026-02-15 08:09:32 +03:00
89 changed files with 1633 additions and 514 deletions

17
_docs/alfabank/README.md Normal file
View File

@@ -0,0 +1,17 @@
# Alfa Bank SDK Documentation
Добро пожаловать в структурированную документацию Alfa API.
## Основные разделы
### 1. [Pay-in & KYC (Alfa API / Alfa ID)](payin_and_kyc/README.md)
Документация по приему платежей, идентификации пользователей через Alfa ID и управлению номинальными счетами.
### 2. [QR Payout (СБП C2B)](qr_payout/0_full.md)
Инструкции по интеграции Системы Быстрых Платежей (СБП) для QR-оплаты.
## Быстрая навигация
- [Карта всех эндпоинтов](endpoints.md)
- [Как подключиться](payin_and_kyc/Подключение_к_Alfa_API/how_to_connect.md)
- [Песочница](payin_and_kyc/Песочница/sandbox_intro.md)
- [Чек-лист готовности](payin_and_kyc/Общая_информация/check-list.md)

View File

@@ -0,0 +1,71 @@
# Карта эндпоинтов Alfa API
| Метод | Путь | Файл | Описание |
| :--- | :--- | :--- | :--- |
| `GET` | `/api/endpoint` | [Управление API Key](payin_and_kyc/Портал_разработчика/api_key_managment.md) | |
| `GET` | `/api/endpoint` | [TLS-сертификат](payin_and_kyc/Подключение_к_Alfa_API/tls-cert.md) | |
| `POST` | `/api/gd/v1/crypto-service/certificates` | [Добавление нового сертификата](payin_and_kyc/Транспортная_подпись_и_шифрование/managing-certificates/post-certificates.md) | |
| `GET` | `/api/gd/v1/crypto-service/certificates` | [Получение сертификатов](payin_and_kyc/Транспортная_подпись_и_шифрование/managing-certificates/get-certificates.md) | |
| `PATCH` | `/api/gd/v1/crypto-service/certificates/{id}` | [Обновление сертификата](payin_and_kyc/Транспортная_подпись_и_шифрование/managing-certificates/patch-certificates.md) | |
| `POST` | `/api/na/jp/v1/beneficiaries` | [Создание запроса на регистрацию бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/add-beneficiary.md) | |
| `GET` | `/api/na/jp/v1/beneficiaries/requests/{requestId}/status` | [Получение статуса запроса](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/get-request-status.md) | |
| `GET` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}` | [Получение информации о бенефициаре `H2H`](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/get-beneficiary.md) | |
| `PUT` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}` | [Создание запроса на изменение информации о бенефициаре](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/change-beneficiary.md) | |
| `GET` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/balance` | [Получение информации о балансе бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/get-balance.md) | |
| `POST` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/requisites` | [Добавление платёжных реквизитов бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/add-requisites.md) | |
| `GET` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/requisites` | [Получение платёжных реквизитов бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/get-requisites.md) | |
| `PATCH` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/requisites/{requisiteId}` | [Изменение признаков платёжных реквизитов бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/change-requisites.md) | |
| `POST` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/scoring` | [Создание запроса на проверку бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/beneficiary-scoring.md) | |
| `GET` | `/api/na/jp/v1/beneficiaries/{beneficiaryId}/transactions` | [Получение списка денежных операций бенефициара](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Бенефициары/get-transactions.md) | |
| `POST` | `/api/na/jp/v1/deals` | [Регистрация сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/add-deal.md) | |
| `PATCH` | `/api/na/jp/v1/deals/action` | [Выполнение действия по сделке](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/deal-actions.md) | |
| `POST` | `/api/na/jp/v1/deals/steps` | [Выполнение действия по этапам сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/add-step.md) | |
| `PATCH` | `/api/na/jp/v1/deals/steps/contractors` | [Выполнение действия по участникам этапа сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/step-contractors.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}` | [Получение информации о сделке](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-deal.md) | |
| `PATCH` | `/api/na/jp/v1/deals/{dealId}/is-valid` | [Проверка возможности осуществления выплат по сделке](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/deal-is-valid.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}/payoffs` | [Получение списка платежей по сделке](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-deal-payoffs.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}/requests/{requestId}/status` | [Получение статуса запроса на завершение этапа сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-deal-request-status.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}/steps` | [Получение списка этапов сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-steps.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}/steps/{stepId}` | [Получение информации об этапе сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-step.md) | |
| `GET` | `/api/na/jp/v1/deals/{dealId}/steps/{stepId}/contractors` | [Получение информации об участниках этапа сделки](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/get-contractors.md) | |
| `GET` | `/api/na/jp/v1/nominal-accounts` | [Получение списка номинальных счетов](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/get-nominal-accounts.md) | |
| `POST` | `/api/na/jp/v1/payoffs` | [Создание запроса на выплату с номинального счёта](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Выплаты/payoff.md) | |
| `GET` | `/api/na/jp/v1/unidentified-transactions` | [Получение списка неидентифицированных пополнений номинального счёта](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/get-unidentified-transactions.md) | |
| `POST` | `/api/na/jp/v1/unidentified-transactions/{id}/identify` | [Создание запроса на идентификацию пополнения номинального счёта](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/identify.md) | |
| `GET` | `/api/na/jp/v1/unidentified-transactions/{id}/identify/{identifyId}` | [Получение статуса запроса на идентификацию пополнения номинального счёта](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/get-identify-request-status.md) | |
| `POST` | `/api/na/jp/v1/unidentified-transactions/{id}/refunds` | [Создание запроса на возврат неидентифицированного пополнения](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/transaction-refund.md) | |
| `GET` | `/api/na/jp/v1/unidentified-transactions/{id}/refunds/{refundId}` | [Получение статуса запроса на возврат неидентифицированного пополнения](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/get-transaction-refund-request-status.md) | |
| `POST` | `/api/na/jp/v1/universal-recipients` | [Создание запроса на регистрацию универсального реципиента](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/add-universal_recipient.md) | |
| `GET` | `/api/na/jp/v1/universal-recipients` | [Получение списка универсальных реципиентов номинального счёта](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/get-universal_recipients_list.md) | |
| `GET` | `/api/na/jp/v1/universal-recipients/{universalRecipientId}` | [Получение информации об универсальном реципиенте](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/get-universal_recipient.md) | |
| `POST` | `/api/na/jp/v1/universal-recipients/{universalRecipientId}/requisites` | [Добавление платёжных реквизитов универсального реципиента](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/add-requisites.md) | |
| `GET` | `/api/na/jp/v1/universal-recipients/{universalRecipientId}/requisites` | [Получение платёжных реквизитов универсального реципиента](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/get-requisites.md) | |
| `PATCH` | `/api/na/jp/v1/universal-recipients/{universalRecipientId}/requisites/{requisiteId}` | [Изменение признаков платёжных реквизитов универсального реципиента](payin_and_kyc/продукты_и_сервисы/Номинальные_счета/Контрагенты/Универсальные_реципиенты/change-requisites.md) | |
| `POST` | `/api/signature-encryption-tests/json` | [Формат исходного сообщения JSON](payin_and_kyc/Песочница/signature-encryption-tests/jws_POST.md) | |
| `POST` | `/api/signature-encryption-tests/text` | [Формат исходного сообщения TXT](payin_and_kyc/Песочница/signature-encryption-tests/text_POST.md) | |
| `POST` | `/api/signature-encryption-tests/xml` | [Формат исходного сообщения XML](payin_and_kyc/Песочница/signature-encryption-tests/xmldsig_POST.md) | |
| `GET` | `/api/v1/endpoint` | [Начало работы в песочнице](payin_and_kyc/Песочница/sandbox_intro.md) | |
| `POST` | `/api/v1/webhook-events` | [Партнёрский метод для получения вебхуков](payin_and_kyc/Вебхуки/partner-webhook.md) | |
| `POST` | `/api/v1/webhooks` | [Создание вебхука](payin_and_kyc/Вебхуки/create-webhook.md) | |
| `POST` | `/api/v1/webhooks-events` | [Проверка работы вебхука](payin_and_kyc/Песочница/check-webhook/checking_the_webhook_eg_transactions.md) | |
| `DELETE` | `/api/v1/webhooks/f9f8c674-7a2a-4297-bdb8-ddd3013de3a3?object=ul_transaction_default` | [Удаление вебхука](payin_and_kyc/Вебхуки/delete-webhook.md) | |
| `DELETE` | `/api/v1/webhooks/{id}` | [Удаление вебхука](payin_and_kyc/Вебхуки/delete-webhook.md) | |
| `PUT` | `/api/v2/webhooks/f9f8c674-7a2a-4297-bdb8-ddd3013de3a3` | [Редактирование вебхука](payin_and_kyc/Вебхуки/edit-webhook.md) | |
| `GET` | `/api/v2/webhooks/f9f8c674-7a2a-4297-bdb8-ddd3013de3a3?object=ul_transaction_default` | [Получение вебхука](payin_and_kyc/Вебхуки/get-webhook.md) | |
| `GET` | `/api/v2/webhooks/{id}` | [Получение вебхука](payin_and_kyc/Вебхуки/get-webhook.md) | |
| `PUT` | `/api/v2/webhooks/{id}` | [Редактирование вебхука](payin_and_kyc/Вебхуки/edit-webhook.md) | |
| `GET` | `/api/v2/webhooks?offset=20&limit=10&object=ul_transaction_default` | [Получение списка вебхуков](payin_and_kyc/Вебхуки/get-webhooks-list.md) | |
| `PUT` | `/filebox/secure-file-transfer.incoming-files.to-processing/12345678-90AB-CDEF-1234-1234567890AB?X-Amz-Date=20230608T152952Z&X-Amz-Expires=3600&X-Amz-Credential=secure-file-transfer%2F20230608%2Fbaas%2Fs3%2Faws4_request&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=36ef1ccf28bdf6e54710fc3c1dd235c8f383275c2bbb0c9db365da272d3c24a8` | [Загрузка файла](payin_and_kyc/Обмен_файлами/upload.md) | |
| `GET` | `/filebox/secure-file-transfer.incoming-files.to-processing/12345678-90ab-cdef-1234-1234567890ab?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20230608T152952Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Credential=secure-file-transfer%2F20230608%2Fdev%2Fs3%2Faws4_request&X-Amz-Signature=e78c051fcff1017a68795620c20e50db33126ab8374c2ee9644ef4743b3625c6` | [Скачивание файла](payin_and_kyc/Обмен_файлами/download.md) | |
| `GET` | `/oidc/authorize?response_type=code&client_id=0cee0683-85ae-49f2-a63d-29f97aad1911&redirect_uri=https%3A%2F%2Fwwww.partner.ru%2Fcode&scope=openid&state=abcdef` | [Получение кода авторизации](payin_and_kyc/Alfa_ID/Authorization_Code_Flow/obtaining_an_authorization_code_GET.md) | |
| `GET` | `/oidc/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}&state={state}` | [Получение кода авторизации](payin_and_kyc/Alfa_ID/Authorization_Code_Flow/obtaining_an_authorization_code_GET.md) | |
| `GET` | `/oidc/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}&state={state}` | [Получение кода авторизации](payin_and_kyc/Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/obtaining_an_authorization_code.md) | |
| `GET` | `/oidc/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}&state={state}` | [Получение кода авторизации](payin_and_kyc/Alfa_ID/Authorization_Code_Flow_for_Holding_Companies/getting_authorization_token.md) | |
| `GET` | `/oidc/client-consents` | [Данные по согласиям пользователей](payin_and_kyc/Alfa_ID/expansion/client-consents_GET.md) | |
| `POST` | `/oidc/clients/{clientId}/client-secret` | [Client secret](payin_and_kyc/Подключение_к_Alfa_API/client_secret.md) | |
| `POST` | `/oidc/revoke` | [Отзыв токена доступа](payin_and_kyc/Alfa_ID/expansion/revoke-token_POST.md) | |
| `POST` | `/oidc/token` | [Обновление токена доступа](payin_and_kyc/Alfa_ID/Authorization_Code_Flow/access_token_refresh_POST.md) | |
| `POST` | `/oidc/token` | [Как использовать CCF (Client Credentials Flow)](payin_and_kyc/Alfa_ID/client_credentials_flow/how_to_use_ccf.md) | |
| `POST` | `/oidc/token` | [Обновление токена доступа](payin_and_kyc/Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/refreshing_access_token_POST.md) | |
| `POST` | `/oidc/token` | [Получение токена доступа `H2H` `POST`](payin_and_kyc/Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/geting_access_token_POST.md) | |
| `GET` | `/oidc/userinfo` | [Получение профиля представителя пользователя `B2B` `B2C`](payin_and_kyc/Alfa_ID/Authorization_Code_Flow/obtaining_a_user_representative_profile.md) | |

View File

@@ -0,0 +1,30 @@
# Pay-in & KYC (Alfa API / Alfa ID)
Этот раздел содержит документацию по интеграции с банковскими сервисами Alfa-Bank для B2B, B2C и H2H сегментов.
## Навигация по разделам
### 🚀 [Начало работы](Подключение_к_Alfa_API/how_to_connect.md)
- [Как подключиться](Подключение_к_Alfa_API/how_to_connect.md)
- [Подписание договора](Подключение_к_Alfa_API/contract_signing.md)
- [Чек-лист готовности](Общая_информация/check-list.md)
### 🆔 [Alfa ID](Alfa_ID/what_is_alfa_id.md)
- [Что такое Alfa ID](Alfa_ID/what_is_alfa_id.md)
- [Authorization Code Flow](Alfa_ID/Authorization_Code_Flow/how_to_use_ACF.md)
- [H2H Аутентификация](Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md)
### 💳 [Номинальные счета](продукты_и_сервисы/Номинальные_счета/intro.md)
- [Введение](продукты_и_сервисы/Номинальные_счета/intro.md)
- [Выплаты](продукты_и_сервисы/Номинальные_счета/Выплаты/intro.md)
- [Безопасные сделки](продукты_и_сервисы/Номинальные_счета/Безопасные_сделки/intro.md)
- [Идентификация пополнений](продукты_и_сервисы/Номинальные_счета/Идентификация_пополнений/how_it_works_intro.md)
### 🛠 [Инструменты и Безопасность](Общая_информация/authorization.md)
- [Авторизация (API Key / Alfa ID)](Общая_информация/authorization.md)
- [Вебхуки](Вебхуки/webhooks_intro.md)
- [Транспортная подпись и шифрование](Транспортная_подпись_и_шифрование/signature-encryption-intro.md)
- [Песочница](Песочница/sandbox_intro.md)
---
[Все эндпоинты](../endpoints.md)

View File

@@ -1,7 +1,6 @@
Ниже представлен сконвертированный из HTML текст документации в формате Markdown. Все ссылки сохранены, структура таблиц и примеров кода приведена к удобному для чтения виду.
---
# Удаление вебхука
**Теги:** `B2B` `B2C` `H2H`
@@ -14,7 +13,7 @@
| **Пром** | `DELETE https://baas.alfabank.ru/api/v1/webhooks/{id}` |
### Scope
`scope` события, на которое подписываетесь, с допустимыми значениями можно посмотреть [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/webhooks/webhooks).
`scope` события, на которое подписываетесь, с допустимыми значениями можно посмотреть [здесь](webhooks_intro.md).
---
@@ -23,7 +22,7 @@
### HEADERS
| Атрибут | Тип | Обязательность | Описание |
| :--- | :--- | :--- | :--- |
| **Authorization** | string | **required** | Bearer {access_token}, полученный по [Authorization Code Flow](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf/articles/intro/intro) / [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | string | **required** | Bearer {access_token}, полученный по [Authorization Code Flow](../Alfa_ID/Authorization_Code_Flow/how_to_use_ACF.md) / [Authorization Code Flow H2H](../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | string | **required** | `application/json` |
| **Content-Type** | string | optional | `application/json` |
@@ -95,4 +94,4 @@ curl -X DELETE https://baas.alfabank.ru/api/v1/webhooks/f9f8c674-7a2a-4297-bdb8-
---
**Навигация:**
[← Редактирование вебхука](https://developers.alfabank.ru/products/alfa-api/documentation/articles/webhooks/articles/edit-webhook/v2/edit-webhook) | [Получение вебхука →](https://developers.alfabank.ru/products/alfa-api/documentation/articles/webhooks/articles/get-webhook/v1/get-webhook)
[← Редактирование вебхука](edit-webhook.md) | [Получение вебхука →](get-webhook.md)

View File

@@ -23,10 +23,10 @@
Это упрощённый способ авторизации запросов без аутентификации сотрудников вашей организации, который не требует регулярного обновления ключа доступа. Подходит для выполнения операций по своей организации. Контроль безопасности обеспечивается за счёт того, что запросы принимаются только с ваших доверенных серверов.
Доступные с помощью этого способа авторизации продукты и сервисы можно посмотреть [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/selfservice/articles/api-key/api-key).
Доступные с помощью этого способа авторизации продукты и сервисы можно посмотреть [здесь](../API_Key/api-key.md).
Использование упрощённого способа авторизации будет оптимальным вариантом, если у вас H2H-интеграция, использующая продукты и сервисы, доступные с помощью `API Key` , и вам не требуется аутентификация сотрудников для доступа к продуктам и сервисам Alfa API.
Подробнее о способах авторизации можно прочитать в статьях ниже.
[Alfa ID](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/intro/intro)[API Key](https://developers.alfabank.ru/products/alfa-api/documentation/articles/api-key/api-key)
[Alfa ID](../Alfa_ID/what_is_alfa_id.md) [API Key](../API_Key/api-key.md)

View File

@@ -12,6 +12,6 @@
Шифрование канала происходит исходя из выбора метода шифрования. Это может быть:
•шифрование с применением сертификата RSA (как применить RSA-сертификат смотрите в разделе [Требования к сертификату](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/tls/tls));•шифрование с применением сертификатов ГОСТ (раздел в подготовке).Выбор того или иного метода шифрования канала определяется критичностью данных:
•шифрование с применением сертификата RSA (как применить RSA-сертификат смотрите в разделе [Требования к сертификату](../Подключение_к_Alfa_API/tls-cert.md));•шифрование с применением сертификатов ГОСТ (раздел в подготовке).Выбор того или иного метода шифрования канала определяется критичностью данных:
•если персональные данные не передаются, то TLS RSA будет самым простым, дешёвым и гибким инструментом интеграции;•если персональные данные передаются, то обязательно использование сертификата ГОСТ. Партнёр API может выбирать, что использовать - TLS ГОСТ или вариант с ФПСУ ГОСТ.

View File

@@ -1,16 +1,16 @@
## Чек-лист готовности к тестовой интеграции
1. **Заключите соглашение и договор**
Заключите соглашение о конфиденциальности и неразглашении информации, договор на информационно-технологическое взаимодействие с банком и подпишите приложения к нему. Подробнее в шаге 1 [инструкции по подключению](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/instruction/instruction).
Заключите соглашение о конфиденциальности и неразглашении информации, договор на информационно-технологическое взаимодействие с банком и подпишите приложения к нему. Подробнее в шаге 1 [инструкции по подключению](../Подключение_к_Alfa_API/how_to_connect.md).
2. **Получите тестовые client_id и scope**
Подробнее в шаге 2 [инструкции по подготовке к работе с песочницей](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/instruction/instruction).
Подробнее в шаге 2 [инструкции по подготовке к работе с песочницей](../Подключение_к_Alfa_API/how_to_connect.md).
3. **Установите тестовый TLS-сертификат, сертификаты Минцифры и проверьте доступ до Swagger UI**
Подробнее в шагах 3-5 [инструкции по подготовке к работе с песочницей](https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/sandbox).
Подробнее в шагах 3-5 [инструкции по подготовке к работе с песочницей](../Песочница/sandbox_intro.md).
4. **Получите тестовый client_secret**
Подробнее [на странице Client secret](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/clientsecret/clientsecret).
Подробнее [на странице Client secret](../Подключение_к_Alfa_API/client_secret.md).
5. **Реализуйте и протестируйте интеграцию Alfa ID и Alfa API в песочнице**
@@ -21,12 +21,12 @@
2. **Создайте интеграцию в проме на Портале разработчика**
Подробнее [на странице Создание интеграции в проме](https://developers.alfabank.ru/products/alfa-api/documentation/articles/selfservice/articles/prod/prod)
Подробнее [на странице Создание интеграции в проме](../Портал_разработчика/creating_prod_integration.md)
3. **Выпустите TLS-сертификат**
Подробнее [на странице TLS-сертификат](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/tls/tls)
Подробнее [на странице TLS-сертификат](../Подключение_к_Alfa_API/tls-cert.md)
4. **Получите client_secret**
Подробнее [на странице Client secret](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/clientsecret/clientsecret)
Подробнее [на странице Client secret](../Подключение_к_Alfa_API/client_secret.md)
5. **Начните использовать Alfa ID и Alfa API**

View File

@@ -8,9 +8,9 @@
Перед началом работы рекомендуем внимательно ознакомиться с нашей документацией, которая содержит всю необходимую информацию для успешной интеграции.
[Подключение к Alfa API](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/connection)
[Подключение к Alfa API](../Подключение_к_Alfa_API/how_to_connect.md)
[Песочница](https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/sandbox)
[Песочница](../Песочница/sandbox_intro.md)
## Преимущества

View File

@@ -82,4 +82,4 @@ invalid_request
Сортировка в пагинации происходит следующим образом: вверху списка возвращаются новые записи, внизу - старые записи.
---
[Версионирование](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/version/version) | [Термины и определения](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/terms/terms)
[Версионирование](vensioning.md) | [Термины и определения](terms_and_definitions.md)

View File

@@ -61,5 +61,5 @@
**Scopes** – это общий набор операций и/или клэймов.
---
[Пагинация](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/pagination/pagination) | [Что это и для чего нужно](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/intro/intro)
[Пагинация](pagination.md) | [Что это и для чего нужно](Сертификаты_Минцифры/what_it_is_and_what_it_is_for.md)

View File

@@ -57,4 +57,4 @@ B2BB2CH2H# Сертификаты для Android
Очистите кэш вашего браузераОчистите кэш браузера на устройстве, с которого осуществляется доступ. Это необходимо для корректной работы с ресурсами, защищёнными сертификатами безопасности Минцифры.
[Что это и для чего нужно](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/intro/intro)[Сертификаты для iOS](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/ios/ios)
[Что это и для чего нужно](what_it_is_and_what_it_is_for.md) [Сертификаты для iOS](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/ios/ios)

View File

@@ -78,4 +78,4 @@ B2BB2CH2H# Сертификаты для Linux
Очистите кэш вашего браузераОчистите кэш браузера на устройстве, с которого осуществляется доступ. Это необходимо для корректной работы с ресурсами, защищёнными сертификатами безопасности Минцифры.
[Сертификаты для MacOS](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/macos/macos)[Как подключить](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/instruction/instruction)
[Сертификаты для MacOS](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/macos/macos) [Как подключить](../../Подключение_к_Alfa_API/how_to_connect.md)

View File

@@ -19,4 +19,4 @@
Ответы на часто задаваемые вопросы можно посмотреть здесь.
---
[Термины и определения](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/terms/terms) | [Сертификаты для Android](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/android/android)
[Термины и определения](../terms_and_definitions.md) | [Сертификаты для Android](for_android.md)

View File

@@ -12,8 +12,8 @@
---
### Описание процесса
* **RSA-сертификат:** Процесс формирования и подписания описан [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature-encryption/articles/signature/articles/text-rsa/text-rsa), а шифрования — [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature-encryption/articles/encryption/articles/text-rsa/text-rsa).
* **ГОСТ-сертификат:** Процесс формирования и подписания описан [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature-encryption/articles/signature/articles/text-gost/text-gost), а шифрования — [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature-encryption/articles/encryption/articles/text-gost/text-gost).
* **RSA-сертификат:** Процесс формирования и подписания описан [здесь](../../Транспортная_подпись_и_шифрование/transport-signature/json-rsa.md), а шифрования — [здесь](../../Транспортная_подпись_и_шифрование/encription/json-rsa.md).
* **ГОСТ-сертификат:** Процесс формирования и подписания описан [здесь](../../Транспортная_подпись_и_шифрование/transport-signature/json-ghost.md), а шифрования — [здесь](../../Транспортная_подпись_и_шифрование/encription/json-ghost.md).
* **Тестирование:** Подписание запроса и дешифрование ответа происходит с использованием закрытого ключа. Для тестов используйте [закрытый ключ](https://developers.alfabank.ru/assets/developers-portal.docs.alfa-api/articles/sandbox/articles/signature/articles/text/v1/files/sandbox_alfa_test_jwt_2026.zip) (пароль уточняйте у персонального менеджера).
> **⚠️ Внимание**

View File

@@ -8,19 +8,19 @@
> Если вы ещё не являетесь клиентом Альфа-Банка, то для подключения к Alfa API вам необходимо открыть [счёт](https://alfabank.ru/sme/quick/docstariffs/), оформить [расчётно-кассовое обслуживание](https://alfabank.ru/sme/) и подключить [Альфа-Бизнес](https://alfabank.ru/corporate/rko/internet-bank/#form).
1. **Подпишите договор**
[Подпишите договор](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/onboarding/onboarding).
[Подпишите договор](contract_signing.md).
Комиссия за подключение и использование не взимается. Вопросы по заключению договора можно направлять на почту [alfa_api@alfabank.ru](https://developers.alfabank.rumailto:alfa_api@alfabank.ru).
Комиссия за подключение и использование не взимается. Вопросы по заключению договора можно направлять на почту [alfa_api@alfabank.ru](mailto:alfa_api@alfabank.ru).
2. **Проверьте тестовую интеграцию**
Получите тестовые учётные данные и настройте тестовую интеграцию [на Портале разработчика](https://developers.alfabank.ru/products/alfa-api/documentation/articles/selfservice/articles/test/test), реализуйте и протестируйте аутентификацию в Alfa ID и вызов методов Alfa API [в песочнице](https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/sandbox).
Получите тестовые учётные данные и настройте тестовую интеграцию [на Портале разработчика](../Портал_разработчика/testing.md), реализуйте и протестируйте аутентификацию в Alfa ID и вызов методов Alfa API [в песочнице](../Песочница/sandbox_intro.md).
3. **Подготовьтесь к промышленной интеграции**
Создайте интеграцию в проме, выпустите TLS-сертификат и `client_secret` . Подробнее [в инструкции](https://developers.alfabank.ru/products/alfa-api/documentation/articles/selfservice/articles/prod/prod).
Создайте интеграцию в проме, выпустите TLS-сертификат и `client_secret` . Подробнее [в инструкции](../Портал_разработчика/creating_prod_integration.md).
4. **Используйте Alfa API**
Если вы ещё не являетесь клиентом Альфа-Банка, то для подключения к Alfa API вам необходимо открыть [счёт](https://alfabank.ru/sme/quick/docstariffs/), оформить [расчётно-кассовое обслуживание](https://alfabank.ru/sme/) и подключить [Альфа-Бизнес](https://alfabank.ru/corporate/rko/internet-bank/#form).
---
[← Сертификаты для Linux](https://developers.alfabank.ru/products/alfa-api/documentation/articles/specification/articles/certificate/articles/linux/linux) [← Подписание договора](https://developers.alfabank.ru/products/alfa-api/documentation/articles/connection/articles/onboarding/onboarding)
[← Сертификаты для Linux](../Общая_информация/Сертификаты_Минцифры/for_linux.md) [← Подписание договора](contract_signing.md)

View File

@@ -0,0 +1,137 @@
какой email технической поддержки ?
10:36
Доставлено
Для связи с технической поддержкой используйте электронную почту alfa\_api@alfabank.ru.
10:36
как происходит flow регистрации бенифициара ?
10:53
Доставлено
Для регистрации бенефициара необходимо выполнить следующие шаги:
1. 1.
**Создать запрос на регистрацию бенефициара** с помощью метода:
- •
**Песочница**: `POST https://sandbox.alfabank.ru/api/na/jp/v1/beneficiaries`
- •
**Пром**: `POST https://baas.alfabank.ru/api/na/jp/v1/beneficiaries`
При этом нужно учитывать, что в зависимости от типа бенефициара поля запроса заполняются по-разному.
2. 2.
3. 3.
При успешной регистрации бенефициару присваивается уникальный идентификатор — `beneficiaryId`.
**Важно:** учёт денежных средств на виртуальных счетах бенефициаров будет вестись корректно только при условии, что владелец номинального счёта не использует другие каналы вывода средств с номинального счёта (например, банк-клиент, платёжные поручения на бумажном носителе, сервисы Alfa API для осуществления переводов и другие).
Ссылки на документацию:
- •
- •
- •
10:53
а как же socring ?
10:55
Доставлено
Для проверки актуальности ранее зарегистрированных данных о бенефициаре используются следующие методы:
1. 1.
**«Создание запроса на проверку бенефициара»** — инициирует процесс скоринга бенефициара.
2. 2.
**«Получение статуса запроса»** — позволяет отслеживать статус ранее созданного запроса на проверку.
**Ссылки на документацию:**
- •
- •
10:55
каким образом можно пополнить номинальный счёт компании ?
22:24
Доставлено
Депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства. Для этого владельцу счёта необходимо выполнить следующие шаги:
1. 1.
2. 2.
3. 3.
22:25
каким образом можно пополнить номинальный счёт компании ?
22:25
Доставлено
Депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства. Для этого владельцу счёта необходимо выполнить следующие шаги:
1. 1.
2. 2.
3. 3.
**Важно:** для работы с сервисом необходимо использовать электронную подпись. Допускается использование сертификата, выпущенного с помощью [сервиса «Электронная подпись»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature/articles/intro/intro), или сертификата УКЭП, выпущенного в любом аккредитованном УЦ.
Ссылка на статью с подробным описанием процесса: [Идентификация пополнений номинального счёта](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/intro/intro).
22:25
а что это значит "любым удобным способом"? каким например ?
22:25
Доставлено
В предоставленной документации нет конкретного перечня способов пополнения номинального счёта. Однако указано, что депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства с помощью специальных методов API.
Для идентификации пополнений владелец счёта может использовать следующие методы:
1. 1.
2. 2.
3. 3.
Для более детальной информации о возможных способах пополнения номинального счёта рекомендуется обратиться на почту alfa\_api@alfabank.ru.
22:25

View File

@@ -0,0 +1,153 @@
---
title: "Интеграция с Альфа-Банком: API, документация, примеры кода"
source: "https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/articles/nominal-accounts/articles/intro/intro"
author:
published:
created: 2026-04-07
description: "Описания API-методов, примеры запросов, инструкции по настройке интеграции для работы с продуктами и сервисами Альфа-Банка"
tags:
- "clippings"
---
#### Инструкция по тестированию
Рекомендации по тестированию [номинальных счетов](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/intro/intro).
##### Идентификация пополнений
Для тестирования методов для идентификации пополнений номинального счёта предварительно необходимо создать тестовое неидентифицированное пополнение с помощью метода [«Создание тестового неидентифицированного пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/articles/nominal-accounts/articles/unidentified-transaction/v1/unidentified-transaction).
В результате успешного запроса будет создано тестовое неидентифицированное пополнение c идентификатором `id`, который далее необходимо будет передать в методе [«Создание запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/identify/v1/identify).
[Создание тестового неидентифицированного пополнения номинального счёта](https://developers.alfabank.ru/products/alfa-api/documentation/articles/sandbox/articles/nominal-accounts/articles/unidentified-transaction/v1/unidentified-transaction)
API Helper
Beta
---
какой email технической поддержки?
10:36
Доставлено
Для связи с технической поддержкой используйте электронную почту alfa\_api@alfabank.ru.
10:36
как происходит flow регистрации бенифициара?
10:53
Доставлено
Для регистрации бенефициара необходимо выполнить следующие шаги:
1. 1.
**Создать запрос на регистрацию бенефициара** с помощью метода:
- •
**Песочница**: `POST https://sandbox.alfabank.ru/api/na/jp/v1/beneficiaries`
- •
**Пром**: `POST https://baas.alfabank.ru/api/na/jp/v1/beneficiaries`
При этом нужно учитывать, что в зависимости от типа бенефициара поля запроса заполняются по-разному.
2. 2.
**Получить статус запроса** с помощью метода [«Получение статуса запроса»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/get-request-status/v1/get-request-status).
3. 3.
**После регистрации бенефициара необходимо указать его платёжные реквизиты** с помощью метода [«Добавление платёжных реквизитов бенефициара»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/add-requisites/v1/add-requisites).
При успешной регистрации бенефициару присваивается уникальный идентификатор — `beneficiaryId`.
**Важно:** учёт денежных средств на виртуальных счетах бенефициаров будет вестись корректно только при условии, что владелец номинального счёта не использует другие каналы вывода средств с номинального счёта (например, банк-клиент, платёжные поручения на бумажном носителе, сервисы Alfa API для осуществления переводов и другие).
Ссылки на документацию:
- •
[Создание запроса на регистрацию бенефициара](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/add-beneficiary/v1/add-beneficiary)
- •
[Получение статуса запроса](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/get-request-status/v1/get-request-status)
- •
[Добавление платёжных реквизитов бенефициара](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/add-requisites/v1/add-requisites)
10:53
а как же socring?
10:55
Доставлено
Для проверки актуальности ранее зарегистрированных данных о бенефициаре используются следующие методы:
1. 1.
**«Создание запроса на проверку бенефициара»** — инициирует процесс скоринга бенефициара.
2. 2.
**«Получение статуса запроса»** — позволяет отслеживать статус ранее созданного запроса на проверку.
**Ссылки на документацию:**
- •
[Создание запроса на проверку бенефициара](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/beneficiary-scoring/v1/beneficiary-scoring)
- •
[Получение статуса запроса](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/get-request-status/v1/get-request-status)
10:55
каким образом можно пополнить номинальный счёт компании?
22:24
Доставлено
Депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства. Для этого владельцу счёта необходимо выполнить следующие шаги:
1. 1.
**Получить информацию обо всех неидентифицированных пополнениях** с помощью метода [«Получение списка неидентифицированных пополнений номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-unidentified-transactions/v1/get-unidentified-transactions).
2. 2.
**Создать запрос на идентификацию денежных средств** с помощью метода [«Создание запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/identify/v1/identify).
3. 3.
**Получить статус запроса на идентификацию денежных средств** с помощью метода [«Получение статуса запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-identify-request-status/v1/get-identify-request-status).
22:25
каким образом можно пополнить номинальный счёт компании?
22:25
Доставлено
Депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства. Для этого владельцу счёта необходимо выполнить следующие шаги:
1. 1.
Получить информацию обо всех неидентифицированных пополнениях с помощью метода [«Получение списка неидентифицированных пополнений номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-unidentified-transactions/v1/get-unidentified-transactions).
2. 2.
Создать запрос на идентификацию денежных средств с помощью метода [«Создание запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/identify/v1/identify).
3. 3.
Получить статус запроса на идентификацию денежных средств с помощью метода [«Получение статуса запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-identify-request-status/v1/get-identify-request-status).
**Важно:** для работы с сервисом необходимо использовать электронную подпись. Допускается использование сертификата, выпущенного с помощью [сервиса «Электронная подпись»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/signature/articles/intro/intro), или сертификата УКЭП, выпущенного в любом аккредитованном УЦ.
Ссылка на статью с подробным описанием процесса: [Идентификация пополнений номинального счёта](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/intro/intro).
22:25
а что это значит "любым удобным способом"? каким например?
22:25
Доставлено
В предоставленной документации нет конкретного перечня способов пополнения номинального счёта. Однако указано, что депонент (бенефициар) может пополнить номинальный счёт удобным ему способом. После этого владелец номинального счёта должен идентифицировать поступившие денежные средства с помощью специальных методов API.
Для идентификации пополнений владелец счёта может использовать следующие методы:
1. 1.
Получить информацию обо всех неидентифицированных пополнениях: [«Получение списка неидентифицированных пополнений номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-unidentified-transactions/v1/get-unidentified-transactions).
2. 2.
Создать запрос на идентификацию денежных средств: [«Создание запроса на идентификацию пополнения номинального счёта»](https://developers.alfa-bank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/identify/v1/identify).
3. 3.
Получить статус запроса на идентификацию денежных средств: [«Получение статуса запроса на идентификацию пополнения номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-identify-request-status/v1/get-identify-request-status).
Для более детальной информации о возможных способах пополнения номинального счёта рекомендуется обратиться на почту alfa\_api@alfabank.ru.
22:25

View File

@@ -0,0 +1,355 @@
H2H
Песочница
POST https://sandbox.alfabank.ru/api/na/jp/v1/unidentified-transactions
##### Scope
`beneficiary-payments`
[](https://developers.alfabank.ru/assets/developers-portal.docs.alfa-api/articles/sandbox/articles/nominal-accounts/articles/unidentified-transaction/v1/files/unidentified-transaction.yaml)
##### Описание
Метод позволяет создать тестовое неидентифицированное пополнение номинального счёта.
###### Параметры запроса
Атрибут
Описание
Authorization
Accept
stringrequired
application/json
Content-Type
stringrequired
application/json
Атрибут
Описание
documentNumber
stringrequired
Номер платёжного поручения
date
stringrequired
Дата и время пополнения по МСК
amount
stringrequired
Сумма пополнения в рублях
purpose
stringrequired
Назначение платежа
accountNumber
stringrequired
Номер номинального счёта
objectrequired
Информация о плательщике
inn
stringoptional
ИНН плательщика
kpp
stringoptional
КПП плательщика
accountNumber
stringoptional
Номер счёта плательщика
name
stringoptional
Имя плательщика
bankAccount
stringoptional
Номер счёта банка плательщика
bankBik
stringoptional
БИК банка плательщика
bankName
stringoptional
Наименования банка плательщика
```bash
1curl --location 'https://baas.alfabank.ru/api/na/jp/v1/unidentified-transactions' \
2--header 'Authorization: Bearer {access-token}' \
3--header 'Content-Type: application/json' \
4--header 'Accept: application/json' \
5--data '{
6 "documentNumber": "3",
7 "date": "2026-04-03T13:23:24.439Z",
8 "amount": 200.9,
9 "purpose": "Входящая транзакция созданная вручную для тестирования на песочнице",
10 "accountNumber": "40817810306050000000",
11 "payer": {
12 "inn": "221284672706",
13 "kpp": "641914992",
14 "accountNumber": "87149728127096890679",
15 "name": "Имя плательщика",
16 "bankAccount": "3932755239052037086628",
17 "bankBik": "108997635",
18 "bankName": "Наименования банка плательщика"
19 }
20}'
```
###### Параметры ответа
```json
1{
2 "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6\"",
3 "documentNumber": "3",
4 "date": "2025-04-08T10:47:17.927Z",
5 "amount": 200.9,
6 "purpose": "Входящая транзакция созданная вручную для тестирования на песочнице",
7 "accountNumber": "40817810306050000000",
8 "payer": {
9 "inn": "221284672706",
10 "kpp": "641914992",
11 "accountNumber": "87149728127096890679",
12 "name": "Имя плательщика",
13 "bankAccount": "3932755239052037086628",
14 "bankBik": "108997635",
15 "bankName": "Наименования банка плательщика"
16 }
17}
```
атрибут
описание
id
stringrequired
Идентификатор операции пополнения
documentNumber
stringrequired
Номер платёжного поручения
date
stringrequired
Дата и время пополнения по МСК
amount
stringrequired
Сумма пополнения в рублях
purpose
stringrequired
Назначение платежа
accountNumber
stringrequired
Номер номинального счёта
objectrequired
Информация о плательщике
inn
stringoptional
ИНН плательщика
kpp
stringoptional
КПП плательщика
accountNumber
stringoptional
Номер счёта плательщика
name
stringoptional
Имя плательщика
bankAccount
stringoptional
Номер счёта банка плательщика
bankBik
stringoptional
БИК банка плательщика
bankName
stringoptional
Наименования банка плательщика
атрибут
описание
error
stringrequired
Код ошибки. Принимает возможные значения:
- •
`invalid_request` — неверные входные параметры запроса
error\_description
stringoptional
Описание ошибки
атрибут
описание
error
stringrequired
Код ошибки. Принимает возможные значения:
- •
`invalid_token` — предоставленный токен просрочен, отозван, имеет неправильный формат или недействителен по другим причинам
error\_description
stringoptional
Описание ошибки
атрибут
описание
error
stringrequired
Код ошибки. Принимает возможные значения:
- •
`insufficient_scope` — у пользователя нет прав на использование запрашиваемого сервиса, доступ к которому не предусмотрен настройками scope
error\_description
stringoptional
Описание ошибки
атрибут
описание
error
stringrequired
Код ошибки. Принимает возможные значения:
- •
`unknown_endpoint` — эндпоинт не найден или неактивен
error\_description
stringoptional
Описание ошибки
Нет тела ответа
атрибут
описание
error
stringrequired
Код ошибки. Принимает возможные значения:
- •
`internal_error` — внутренняя ошибка сервиса при выполнении обработки запроса
error\_description
stringoptional
Описание ошибки
Нет тела ответа

View File

@@ -22,7 +22,7 @@
| Атрибут | Тип | Обязательность | Описание |
| :---------------- | :----- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Authorization** | string | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | string | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](../../../../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | string | required | application/json |
### PATH (Путь)

View File

@@ -2,22 +2,22 @@
_Номинальные счета / Выплаты / Как это работает_
Для осуществления выплат с номинального счёта предварительно необходимо [зарегистрировать контрагента](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/intro/intro), участвующего в выплате.
Для осуществления выплат с номинального счёта предварительно необходимо [зарегистрировать контрагента](../Контрагенты/intro.md), участвующего в выплате.
## Общий механизм выплат с номинального счёта
1. **Создайте запрос на выплату**
Метод [«Создание запроса на выплату с номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/payoff/v1/payoff)
Метод [«Создание запроса на выплату с номинального счёта»](payoff.md)
2. **Получите статус запроса на выплату**
Метод [«Получение статуса запроса на выплату с номинального счёта»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/get-payoff-request-status/v1/get-payoff-request-status)
Метод [«Получение статуса запроса на выплату с номинального счёта»](get-payoff-request-status.md)
3. **Подтвердите или отклоните обработку запроса-дубля на выплату**
В случае, если статус запроса на выплату уже был создан ранее, он примет статус `MANUAL_ACCEPTANCE` и его нужно будет подтвердить или отклонить.
Метод [«Подтвердить или отклонить запрос-дубль на выплату»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/payoff-accept/v1/payoff-accept)
Метод [«Подтвердить или отклонить запрос-дубль на выплату»](payoff-accept.md)
4. **Получите информацию о платеже по его идентификатору**
Метод [«Получение информации о платеже»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/get-payoff/v1/get-payoff)
Метод [«Получение информации о платеже»](get-payoff.md)
---
@@ -33,13 +33,13 @@ _Номинальные счета / Выплаты / Как это работа
Необходимо учитывать:
- На номинальном счёте должен быть положительный баланс.
- Если выплата производится в пользу универсального реципиента, то для него предварительно необходимо зарегистрировать платёжные реквизиты с помощью метода [«Добавление платёжных реквизитов универсального реципиента»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/universal-recipients/articles/add-requisites/v1/add-requisites).
- Если выплата производится в пользу универсального реципиента, то для него предварительно необходимо зарегистрировать платёжные реквизиты с помощью метода [«Добавление платёжных реквизитов универсального реципиента»](../Контрагенты/Универсальные_реципиенты/add-requisites.md).
В качестве платёжных реквизитов получателя могут быть указаны:
- реквизиты счёта;
- номер телефона (СБП);
- зашифрованный номер карты (как зашифровать номер карты, описано [здесь](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/cardtoken/cardtoken));
- зашифрованный номер карты (как зашифровать номер карты, описано [здесь](../how-to-encrypt-your-card-number-eg-cardtoken.md));
- идентификатор платёжных реквизитов контрагента.
## Выплаты с биллингом на стороне Банка
@@ -55,11 +55,11 @@ _Номинальные счета / Выплаты / Как это работа
### Механизм возврата денежных средств с налогового виртуального кошелька
1. **Создайте запрос на возврат денежных средств с налогового виртуального кошелька**
Метод [«Создание запроса на возврат денежных средств с налогового виртуального кошелька»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/tax-refund/v1/tax-refund)
Метод [«Создание запроса на возврат денежных средств с налогового виртуального кошелька»](tax-refund.md)
2. **Получите статус запроса на возврат налоговых денежных средств**
Метод [«Получение статуса запроса на возврат налоговых денежных средств»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/get-tax-refund-request-status/v1/get-tax-refund-request-status)
Метод [«Получение статуса запроса на возврат налоговых денежных средств»](get-tax-refund-request-status.md)
3. **Подтвердите или отклоните обработку запроса-дубля на возврат денежных средств с налогового виртуального кошелька**
В случае, если статус запроса на выплату уже был создан ранее, он примет статус `MANUAL_ACCEPTANCE` и его нужно будет подтвердить или отклонить.
Метод [«Подтверждение или отклонение запроса-дубля на возврат денежных средств с налогового виртуального кошелька»](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/tax-refund-accept/v1/tax-refund-accept)
Метод [«Подтверждение или отклонение запроса-дубля на возврат денежных средств с налогового виртуального кошелька»](tax-refund-accept.md)

View File

@@ -27,7 +27,7 @@
| Атрибут | Тип | Обязательность | Описание |
| :---------------- | :----- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Authorization** | string | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | string | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](../../../../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | string | required | `application/json` |
### PATH (Путь)
@@ -90,5 +90,5 @@ curl --location --globoff --request PATCH \
## Навигация
- **Предыдущий шаг:** [Получение статуса запроса на возврат налоговых денежных средств](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/na-payments/articles/get-tax-refund-request-status/v1/get-tax-refund-request-status)
- **Предыдущий шаг:** [Получение статуса запроса на возврат налоговых денежных средств](get-tax-refund-request-status.md)
- **Раздел:** Номинальные счета / Выплаты

View File

@@ -1,7 +1,6 @@
Вот очищенная и структурированная версия документации в формате Markdown:
# Создание запроса на идентификацию пополнения номинального счёта
**Тип интеграции:** H2H
## Эндпоинты
@@ -27,7 +26,7 @@
### HEADERS
| Атрибут | Тип | Обяз. | Описание |
| :--- | :--- | :--- | :--- |
| **Authorization** | string | Да | Bearer {access_token}, полученный по [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | string | Да | Bearer {access_token}, полученный по [Authorization Code Flow H2H](../../../../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | string | Да | application/json |
| **Content-Type** | string | Да | application/json |
@@ -104,5 +103,5 @@ curl --location --globoff 'https://baas.alfabank.ru/api/na/jp/v1/unidentified-tr
---
**Навигация:**
* [Предыдущая: Получение списка неидентифицированных пополнений](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-unidentified-transactions/v1/get-unidentified-transactions)
* [Следующая: Получение статуса запроса на идентификацию](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/identify-transactions/articles/get-identify-request-status/v1/get-identify-request-status)
* [Предыдущая: Получение списка неидентифицированных пополнений](get-unidentified-transactions.md)
* [Следующая: Получение статуса запроса на идентификацию](get-identify-request-status.md)

View File

@@ -28,7 +28,7 @@
| Атрибут | Тип | Обязательность | Описание |
| :---------------- | :------- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Authorization** | `string` | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | `string` | required | Bearer {access_token}, полученный по [Authorization Code Flow H2H](../../../../../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | `string` | required | `application/json` |
### PATH
@@ -86,5 +86,5 @@ curl --location --globoff --request POST 'https://baas.alfabank.ru/api/na/jp/v1/
## Навигация
- **Предыдущий метод:** [Создание запроса на изменение информации о бенефициаре](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/change-beneficiary/v1/change-beneficiary)
- **Следующий метод:** [Добавление платёжных реквизитов бенефициара](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/add-requisites/v1/add-requisites)
- **Предыдущий метод:** [Создание запроса на изменение информации о бенефициаре](change-beneficiary.md)
- **Следующий метод:** [Добавление платёжных реквизитов бенефициара](add-requisites.md)

View File

@@ -24,7 +24,7 @@
### HEADERS
| Атрибут | Тип | Обяз. | Описание |
| :--- | :--- | :--- | :--- |
| **Authorization** | string | Да | Bearer {access_token}, полученный по [Authorization Code Flow H2H](https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/intro/intro) |
| **Authorization** | string | Да | Bearer {access_token}, полученный по [Authorization Code Flow H2H](../../../../../Alfa_ID/Authorization_Code_Flow_for_H2H_Integrations/How_to_use_ACF_for_H2H.md) |
| **Accept** | string | Да | `application/json` |
| **Content-Type** | string | Да | `application/json` |
@@ -99,5 +99,5 @@ curl --location 'https://baas.alfabank.ru/api/na/jp/v1/universal-recipients' \
---
**Навигация:**
* Предыдущая статья: [Получение списка денежных операций бенефициара](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/beneficiaries/articles/get-transactions/v1/get-transactions)
* Следующая статья: [Получение списка универсальных реципиентов номинального счёта](https://developers.alfabank.ru/products/alfa-api/documentation/articles/nominal-accounts/articles/counterparties/articles/universal-recipients/articles/get-urs/v1/get-urs)
* Предыдущая статья: [Получение списка денежных операций бенефициара](../Бенефициары/get-transactions.md)
* Следующая статья: [Получение списка универсальных реципиентов номинального счёта](get-universal_recipients_list.md)

View File

@@ -1,8 +1,20 @@
## Главная
### Инструкция по интеграции СБП C2B (Альфа-Банк)
## Содержание
- [Обзор API](#1-обзор-api)
- [Процесс аутентификации](#2-процесс-аутентификации)
- [Тестирование](#3-тестирование)
- [Как провести тестовую оплату](#4-как-провести-тестовую-оплату)
- [Получение статуса платежа](#5-получение-статуса-платежа)
- [Переход на промышленную среду](#6-переход-на-промышленную-среду)
- [Статический QR-код](#статический-qr-код-сбп)
- [Динамический QR-код](#интеграция-динамических-qr-кодов-сбп)
- [Кассовая платёжная ссылка](#кассовая-платёжная-ссылка-сбп-техническая-документация)
Добро пожаловать в руководство по прямой интеграции с платежным шлюзом (ПШ) Альфа-Банка для реализации сервиса СБП C2B.
---
### ⚠️ Важное объявление (Изменение адресов)

View File

@@ -3,8 +3,8 @@ title: Available property mappers
weight: 19
---
In previous sections we've already seen how to [create](/docs/laravel-data/v4/as-a-data-transfer-object/mapping-property-names) data objects where the keys of the
payload differ from the property names of the data object. It is also possible to [transform](/docs/laravel-data/v4/as-a-resource/mapping-property-names) data objects to an array/json/... where the keys of the payload differ
In previous sections we've already seen how to [create](../as-a-data-transfer-object/mapping-property-names.md) data objects where the keys of the
payload differ from the property names of the data object. It is also possible to [transform](../as-a-resource/mapping-property-names.md) data objects to an array/json/... where the keys of the payload differ
from the property names of the data object.
These mappings can be set manually put the package also provide a set of mappers that can be used to automatically map

View File

@@ -16,7 +16,7 @@ interface Cast
A cast receives the following:
- **property** a `DataProperty` object which represents the property for which the value is cast. You can read more about the internal structures of the package [here](/docs/laravel-data/v4/advanced-usage/internal-structures)
- **property** a `DataProperty` object which represents the property for which the value is cast. You can read more about the internal structures of the package [here](../advanced-usage/internal-structures.md)
- **value** the value that should be cast
- **properties** an array of the current properties that will be used to create the data object
- **creationContext** the context in which the data object is being created you'll find the following info here:

View File

@@ -14,7 +14,7 @@ interface RuleInferrer
}
```
A collection of previous inferred rules is given, and a `DataProperty` object which represents the property for which the value is transformed. You can read more about the internal structures of the package [here](/docs/laravel-data/v4/advanced-usage/internal-structures).
A collection of previous inferred rules is given, and a `DataProperty` object which represents the property for which the value is transformed. You can read more about the internal structures of the package [here](../advanced-usage/internal-structures.md).
The `ValidationContext` is also injected, this contains the following info:

View File

@@ -16,7 +16,7 @@ interface Transformer
The following parameters are provided:
- **property**: a `DataProperty` object which represents the property for which the value is transformed. You can read more about the internal structures of the package [here](/docs/laravel-data/v4/advanced-usage/internal-structures)
- **property**: a `DataProperty` object which represents the property for which the value is transformed. You can read more about the internal structures of the package [here](../advanced-usage/internal-structures.md)
- **value**: the value that should be transformed, this will never be `null`
- **context**: a `TransformationContext` object which contains the current transformation context with the following properties:
- **transformValues** indicates if values should be transformed or not

View File

@@ -57,7 +57,7 @@ The `handle` method has several arguments:
- **payload** the non normalized payload
- **class** the `DataClass` object for the data
object [more info](/docs/laravel-data/v4/advanced-usage/internal-structures)
object [more info](../advanced-usage/internal-structures.md)
- **properties** the key-value properties which will be used to construct the data object
- **creationContext** the context in which the data object is being created you'll find the following info here:
- **dataClass** the data class which is being created

View File

@@ -118,7 +118,7 @@ Tip: we can also remove the `EnumCast` since the package will automatically cast
## Creating your own casts
It is possible to create your casts. You can read more about this in the [advanced chapter](/docs/laravel-data/v4/advanced-usage/creating-a-cast).
It is possible to create your casts. You can read more about this in the [advanced chapter](../advanced-usage/creating-a-cast.md).
## Casting arrays or collections of non-data types

View File

@@ -162,7 +162,7 @@ will try to create itself from the following types:
- An *array*
This list can be extended using extra normalizers, find more about
it [here](/docs/laravel-data/v4/advanced-usage/normalizers).
it [here](../advanced-usage/normalizers.md).
When a data object cannot be created using magical methods or the default methods, a `CannotCreateData`
exception will be thrown.
@@ -190,7 +190,7 @@ SongData::factory()->withoutMagicalCreation()->from($song);
## Advanced creation using factories
It is possible to configure how a data object is created, whether it will be validated, which casts to use and more. You
can read more about it [here](/docs/laravel-data/v4/as-a-data-transfer-object/factories).
can read more about it [here](../as-a-data-transfer-object/factories.md).
## DTO classes

View File

@@ -22,7 +22,7 @@ SongData::factory()->collect(Song::all())
## Disable property name mapping
We saw [earlier](/docs/laravel-data/v4/as-a-data-transfer-object/mapping-property-names) that it is possible to map
We saw [earlier](../as-a-data-transfer-object/mapping-property-names.md) that it is possible to map
property names when creating a data object from an array. This can be disabled when using a factory:
```php
@@ -47,7 +47,7 @@ SongData::factory()->withoutValidation()->from(['title' => 'Never gonna give you
## Disabling magic methods
A data object can be created
using [magic methods](/docs/laravel-data/v4/as-a-data-transfer-object/creating-a-data-object.md#magical-creation) , this can be disabled
using [magic methods](../as-a-data-transfer-object/creating-a-data-object.md.md#magical-creation) , this can be disabled
when using a factory:
```php
@@ -136,4 +136,4 @@ class SongData extends Data
}
```
You can read more about creation contexts [here](/docs/laravel-data/v4/advanced-usage/pipeline).
You can read more about creation contexts [here](../advanced-usage/pipeline.md).

View File

@@ -37,7 +37,7 @@ class ContractData extends Data
}
```
You can also use the `MapName` attribute when you want to combine input (see [transforming data objects](/docs/laravel-data/v4/as-a-resource/mapping-property-names)) and output property name mapping:
You can also use the `MapName` attribute when you want to combine input (see [transforming data objects](../as-a-resource/mapping-property-names.md)) and output property name mapping:
```php
#[MapName(SnakeCaseMapper::class)]
@@ -91,4 +91,4 @@ SongData::from([
]);
```
The package has a set of default mappers available, you can find them [here](/docs/laravel-data/v4/advanced-usage/available-property-mappers).
The package has a set of default mappers available, you can find them [here](../advanced-usage/available-property-mappers.md).

View File

@@ -68,4 +68,4 @@ SongData::factory()
->from(['title' => 'Never gonna give you up']); // artist will `null` instead of `Optional`
```
You can read more about this [here](/docs/laravel-data/v4/as-a-data-transfer-object/factories#disabling-optional-values).
You can read more about this [here](../as-a-data-transfer-object/factories.md#disabling-optional-values).

View File

@@ -63,7 +63,7 @@ class UpdateSongController
}
```
We have a complete section within these docs dedicated to validation, you can find it [here](/docs/laravel-data/v4/validation/introduction).
We have a complete section within these docs dedicated to validation, you can find it [here](../validation/introduction.md).
## Getting the data object filled with request data from anywhere

View File

@@ -18,7 +18,7 @@ Which will output the following array:
]
```
By default, calling `toArray` on a data object will recursively transform all properties to an array. This means that nested data objects and collections of data objects will also be transformed to arrays. Other complex types like `Carbon`, `DateTime`, `Enums`, etc... will be transformed into a string. We'll see in the [transformers](/docs/laravel-data/v4/as-a-resource/transformers) section how to configure and customize this behavior.
By default, calling `toArray` on a data object will recursively transform all properties to an array. This means that nested data objects and collections of data objects will also be transformed to arrays. Other complex types like `Carbon`, `DateTime`, `Enums`, etc... will be transformed into a string. We'll see in the [transformers](../as-a-resource/transformers.md) section how to configure and customize this behavior.
If you only want to transform a data object to an array without transforming the properties, you can call the `all` method:

View File

@@ -81,4 +81,4 @@ And a transformed version of the data object will look like this:
]
```
The package has a set of default mappers available, you can find them [here](/docs/laravel-data/v4/advanced-usage/available-property-mappers).
The package has a set of default mappers available, you can find them [here](../advanced-usage/available-property-mappers.md).

View File

@@ -43,7 +43,7 @@ class ArtistData extends Data{
Next to a `DateTimeInterfaceTransformer` the package also ships with an `ArrayableTransformer` that transforms an `Arrayable` object to an array.
It is possible to create transformers for your specific types. You can find more info [here](/docs/laravel-data/v4/advanced-usage/creating-a-transformer).
It is possible to create transformers for your specific types. You can find more info [here](../advanced-usage/creating-a-transformer.md).
## Global transformers
@@ -114,7 +114,7 @@ Outputting the following array:
]
```
The [mapping of property names](/docs/laravel-data/v4/as-a-resource/mapping-property-names) can also be disabled:
The [mapping of property names](../as-a-resource/mapping-property-names.md) can also be disabled:
```php
ArtistData::from($artist)->transform(
@@ -122,7 +122,7 @@ ArtistData::from($artist)->transform(
);
```
It is possible to enable [wrapping](/docs/laravel-data/v4/as-a-resource/wrapping-data) the data object:
It is possible to enable [wrapping](../as-a-resource/wrapping-data.md) the data object:
```php
use Spatie\LaravelData\Support\Wrapping\WrapExecutionType;

View File

@@ -5,7 +5,19 @@ weight: 1
In this quickstart, we'll guide you through the most important functionalities of the package and how to use them.
First, you should [install the package](/docs/laravel-data/v4/installation-setup).
## Table of Contents
- [Using requests](#using-requests)
- [Casting data](#casting-data)
- [Customizing the creation of a data object](#customizing-the-creation-of-a-data-object)
- [Nesting data objects and arrays of data objects](#nesting-data-objects-and-arrays-of-data-objects)
- [Usage in controllers](#usage-in-controllers)
- [Using transformers](#using-transformers)
- [Generating a blueprint](#generating-a-blueprint)
- [Lazy properties](#lazy-properties)
- [Conclusion](#conclusion)
First, you should [install the package](../installation-setup.md).
We will create a blog with different posts, so let's start with the `PostData` object. A post has a title, some content, a status and a date when it was published:
@@ -189,7 +201,7 @@ As you can see, we're missing the `date` rule on the `published_at` property. By
- `array` when a property type is `array`
- `enum:*` when a property type is a native enum
You can read more about the process of automated rule generation [here](/docs/laravel-data/v4/as-a-data-transfer-object/request-to-data-object#content-automatically-inferring-rules-for-properties-1).
You can read more about the process of automated rule generation [here](../as-a-data-transfer-object/request-to-data-object.md#content-automatically-inferring-rules-for-properties-1).
We can easily add the date rule by using an attribute to our data object:
@@ -232,7 +244,7 @@ array:4 [
]
```
There are [tons](/docs/laravel-data/v4/advanced-usage/validation-attributes) of validation rule attributes you can add to data properties. There's still much more you can do with validating data objects. Read more about it [here](/docs/laravel-data/v4/as-a-data-transfer-object/request-to-data-object#validating-a-request).
There are [tons](../advanced-usage/validation-attributes.md) of validation rule attributes you can add to data properties. There's still much more you can do with validating data objects. Read more about it [here](../as-a-data-transfer-object/request-to-data-object.md#validating-a-request).
Tip: By default, when creating a data object in a non request context, no validation is executed:
@@ -275,7 +287,7 @@ It is possible to define casts within the `data.php` config file. By default, th
This code means that if a class property is of type `DateTime`, `Carbon`, `CarbonImmutable`, ... it will be automatically cast.
You can create your own casts; read more about it [here](/docs/laravel-data/v4/advanced-usage/creating-a-cast).
You can create your own casts; read more about it [here](../advanced-usage/creating-a-cast.md).
### Local casts
@@ -356,7 +368,7 @@ class PostData extends Data
}
```
You can read more about casting [here](/docs/laravel-data/v4/as-a-data-transfer-object/casts).
You can read more about casting [here](../as-a-data-transfer-object/casts.md).
## Customizing the creation of a data object
@@ -406,7 +418,7 @@ class PostData extends Data
```
Magic creation methods allow you to create data objects from any type by passing them to the `from` method of a data
object, you can read more about it [here](/docs/laravel-data/v4/as-a-data-transfer-object/creating-a-data-object#content-magical-creation).
object, you can read more about it [here](../as-a-data-transfer-object/creating-a-data-object.md#content-magical-creation).
It can be convenient to transform more complex models than our `Post` into data objects because you can decide how a model
would be mapped onto a data object.
@@ -764,7 +776,7 @@ Which leads to the following JSON:
}
```
You can read more about transformers [here](/docs/laravel-data/v4/as-a-resource/transformers).
You can read more about transformers [here](../as-a-resource/transformers.md).
## Generating a blueprint
@@ -1010,19 +1022,19 @@ You can do quite a lot with lazy properties like including them:
- when they are requested in the URL query
- by default, with an option to exclude them
And a lot more. You can read all about it [here](/docs/laravel-data/v4/as-a-resource/lazy-properties).
And a lot more. You can read all about it [here](../as-a-resource/lazy-properties.md).
## Conclusion
So that's it, a quick overview of this package. We barely scratched the surface of what's possible with the package. There's still a lot more you can do with data objects like:
- [casting](/docs/laravel-data/v4/advanced-usage/eloquent-casting) them into Eloquent models
- [transforming](/docs/laravel-data/v4/advanced-usage/typescript) the structure to typescript
- [working](/docs/laravel-data/v4/as-a-data-transfer-object/collections) with `DataCollections`
- [optional properties](/docs/laravel-data/v4/as-a-data-transfer-object/optional-properties) not always required when creating a data object
- [wrapping](/docs/laravel-data/v4/as-a-resource/wrapping) transformed data into keys
- [mapping](/docs/laravel-data/v4/as-a-data-transfer-object/mapping-property-names) property names when creating or transforming a data object
- [appending](/docs/laravel-data/v4/as-a-resource/appending-properties) extra data
- [including](/docs/laravel-data/v4/as-a-resource/lazy-properties#content-using-query-strings) properties using the URL query string
- [casting](../advanced-usage/eloquent-casting.md) them into Eloquent models
- [transforming](../advanced-usage/typescript.md) the structure to typescript
- [working](../as-a-data-transfer-object/collections.md) with `DataCollections`
- [optional properties](../as-a-data-transfer-object/optional-properties.md) not always required when creating a data object
- [wrapping](../as-a-resource/wrapping.md) transformed data into keys
- [mapping](../as-a-data-transfer-object/mapping-property-names.md) property names when creating or transforming a data object
- [appending](../as-a-resource/appending-properties.md) extra data
- [including](../as-a-resource/lazy-properties.md#content-using-query-strings) properties using the URL query string
- [inertia](https://spatie.be/docs/laravel-data/v4/advanced-usage/use-with-inertia) support for lazy properties
- and so much more ... you'll find all the information here in the docs

View File

@@ -57,4 +57,4 @@ By default, five rule inferrers are enabled:
- A `array` type will add the `array` rule
- **AttributesRuleInferrer** will make sure that rule attributes we described above will also add their rules
It is possible to write your rule inferrers. You can find more information [here](/docs/laravel-data/v4/advanced-usage/creating-a-rule-inferrer).
It is possible to write your rule inferrers. You can find more information [here](../advanced-usage/creating-a-rule-inferrer.md).

View File

@@ -56,7 +56,7 @@ Completely disabling validation can be done by setting the `validation_strategy`
'validation_strategy' => \Spatie\LaravelData\Support\Creation\ValidationStrategy::Disabled->value,
```
If you require a more fine-grained control over when validation should happen, you can use [data factories](/docs/laravel-data/v4/as-a-data-transfer-object/factories) to manually specify the validation strategy.
If you require a more fine-grained control over when validation should happen, you can use [data factories](../as-a-data-transfer-object/factories.md) to manually specify the validation strategy.
## A quick glance at the validation functionality
@@ -87,7 +87,7 @@ The package will generate the following validation rules:
]
```
The package follows an algorithm to infer rules from the data object. You can read more about it [here](/docs/laravel-data/v4/validation/auto-rule-inferring).
The package follows an algorithm to infer rules from the data object. You can read more about it [here](../validation/auto-rule-inferring.md).
### Validation attributes
@@ -107,7 +107,7 @@ class SongData extends Data
When you provide an artist with a length of more than 20 characters, the validation will fail.
There's a complete [chapter](/docs/laravel-data/v4/validation/using-validation-attributes) dedicated to validation attributes.
There's a complete [chapter](../validation/using-validation-attributes.md) dedicated to validation attributes.
### Manual rules
@@ -132,7 +132,7 @@ class SongData extends Data
}
```
You can read more about manual rules in its [dedicated chapter](/docs/laravel-data/v4/validation/manual-rules).
You can read more about manual rules in its [dedicated chapter](../validation/manual-rules.md).
### Using the container
@@ -147,7 +147,7 @@ If the request contains data that is not compatible with the data object, a vali
### Working with the validator
We provide a few points where you can hook into the validation process. You can read more about it in the [dedicated chapter](/docs/laravel-data/v4/validation/working-with-the-validator).
We provide a few points where you can hook into the validation process. You can read more about it in the [dedicated chapter](../validation/working-with-the-validator.md).
It is for example to:
@@ -207,7 +207,7 @@ The validation rules for this class will be:
]
```
There are a few quirky things to keep in mind when working with nested data objects, you can read all about it [here](/docs/laravel-data/v4/validation/nesting-data).
There are a few quirky things to keep in mind when working with nested data objects, you can read all about it [here](../validation/nesting-data.md).
## Validation of nested data collections
@@ -241,7 +241,7 @@ In this case the validation rules for `AlbumData` would look like this:
]
```
More info about nested data collections can be found [here](/docs/laravel-data/v4/validation/nesting-data).
More info about nested data collections can be found [here](../validation/nesting-data.md).
## Default values

View File

@@ -5,7 +5,7 @@ weight: 6
A data object can contain other data objects or collections of data objects. The package will make sure that also for these data objects validation rules will be generated.
Let's take a look again at the data object from the [nesting](/docs/laravel-data/v4/as-a-data-transfer-object/nesting) section:
Let's take a look again at the data object from the [nesting](../as-a-data-transfer-object/nesting.md) section:
```php
class AlbumData extends Data

View File

@@ -63,4 +63,4 @@ Now the validation rules will look like this:
## Skipping validation for all properties
By using [data factories](/docs/laravel-data/v4/as-a-data-transfer-object/factories) or setting the `validation_strategy` in the `data.php` config you can skip validation for all properties of a data class.
By using [data factories](../as-a-data-transfer-object/factories.md) or setting the `validation_strategy` in the `data.php` config you can skip validation for all properties of a data class.

View File

@@ -31,7 +31,7 @@ above data object will look like this:
```
For each Laravel validation rule we've got a matching validation attribute, you can find a list of
them [here](/docs/laravel-data/v4/advanced-usage/validation-attributes).
them [here](../advanced-usage/validation-attributes.md).
## Referencing route parameters

View File

@@ -1,49 +1,56 @@
{
"name": "apoloz/alfa-bank-sdk",
"description": "Unofficial Alfa Bank API SDK for PHP using Saloon and Laravel Data",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "apoloz",
"email": "anatoliy.poloz@gmail.com"
}
],
"require": {
"php": "^8.2",
"saloonphp/saloon": "^3.0",
"saloonphp/laravel-plugin": "^3.0",
"saloonphp/pagination-plugin": "^2.0",
"spatie/laravel-data": "^4.0",
"guzzlehttp/guzzle": "^7.0",
"phpseclib/phpseclib": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"pestphp/pest": "^2.0",
"phpstan/phpstan": "^2.1"
},
"autoload": {
"psr-4": {
"Apoloz\\AlfaBank\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Apoloz\\AlfaBank\\Laravel\\AlfaBankServiceProvider"
]
}
},
"minimum-stability": "stable",
"prefer-stable": true,
"scripts": {
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage"
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
"name": "apoloz/alfa-bank-sdk",
"description": "Unofficial Alfa Bank API SDK for PHP using Saloon and Laravel Data",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "apoloz",
"email": "anatoliy.poloz@gmail.com"
}
],
"require": {
"php": "^8.4",
"saloonphp/saloon": "^4.0",
"saloonphp/laravel-plugin": "^4.0",
"saloonphp/pagination-plugin": "^2.0",
"spatie/laravel-data": "^4.0",
"guzzlehttp/guzzle": "^7.0",
"phpseclib/phpseclib": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"pestphp/pest": "^2.0",
"phpstan/phpstan": "^2.1"
},
"autoload": {
"psr-4": {
"Apoloz\\AlfaBank\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Apoloz\\AlfaBank\\Laravel\\AlfaBankServiceProvider"
]
}
},
"minimum-stability": "stable",
"prefer-stable": true,
"scripts": {
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage"
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
},
"audit": {
"ignore": {
"PKSA-xnj5-w74d-6wmz": "true",
"PKSA-rnpm-45mg-w6ht": "true",
"PKSA-5szq-gvrg-ttfq": "true"
}
}
}
}

View File

@@ -8,11 +8,17 @@ namespace Apoloz\AlfaBank\Core;
// <BELIEF_STATE "Saloon's plugin architecture requires importing traits for features like retries and timeouts. Local contracts (SignerInterface) are imported for dependency injection.">
use Apoloz\AlfaBank\Contracts\SignerInterface;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Http\Middleware\LoggingRequestMiddleware;
use Apoloz\AlfaBank\Http\Middleware\LoggingResponseMiddleware;
use Apoloz\AlfaBank\Http\Middleware\RequestIdMiddleware;
use Apoloz\AlfaBank\Http\Middleware\VerifyJwsResponseMiddleware;
use Apoloz\AlfaBank\Http\Pagination\AlfaOffsetPaginator;
use Apoloz\AlfaBank\Traits\HasAlfaErrorHandling;
use Psr\Log\LoggerInterface;
use Saloon\Helpers\OAuth2\OAuthConfig;
use Saloon\Http\Connector;
use Saloon\Http\OAuth2\GetClientCredentialsTokenRequest;
use Saloon\Http\PendingRequest;
use Saloon\Http\Request;
use Saloon\PaginationPlugin\Contracts\HasPagination;
use Saloon\PaginationPlugin\OffsetPaginator;
@@ -42,7 +48,7 @@ class AlfaBankConnector extends Connector implements HasPagination
return $this->alfaConfig->baseUrl;
}
public function boot(\Saloon\Http\PendingRequest $pendingRequest): void
public function boot(PendingRequest $pendingRequest): void
{
//
}
@@ -69,7 +75,7 @@ class AlfaBankConnector extends Connector implements HasPagination
throw new \RuntimeException('Client ID/Secret not configured.');
}
$request = new \Saloon\Http\OAuth2\GetClientCredentialsTokenRequest($oauthConfig, $scopes);
$request = new GetClientCredentialsTokenRequest($oauthConfig, $scopes);
$response = $this->send($request);
@@ -97,10 +103,10 @@ class AlfaBankConnector extends Connector implements HasPagination
// <SECTION name="logging_mutator" intent="Allow attaching a PSR-3 logger to the connector for debugging.">
// <BELIEF_STATE "Logging should be an optional, opt-in feature. This method provides a fluent interface to attach a logging middleware, which will automatically sanitize sensitive data before output.">
public function withLogging(\Psr\Log\LoggerInterface $logger): static
public function withLogging(LoggerInterface $logger): static
{
$this->middleware()->onRequest(new \Apoloz\AlfaBank\Http\Middleware\LoggingRequestMiddleware($logger));
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\LoggingResponseMiddleware($logger));
$this->middleware()->onRequest(new LoggingRequestMiddleware($logger));
$this->middleware()->onResponse(new LoggingResponseMiddleware($logger));
return $this;
}
@@ -110,7 +116,7 @@ class AlfaBankConnector extends Connector implements HasPagination
*/
public function withJwsVerification(string $publicKeyPem): static
{
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\VerifyJwsResponseMiddleware($publicKeyPem));
$this->middleware()->onResponse(new VerifyJwsResponseMiddleware($publicKeyPem));
return $this;
}
@@ -125,8 +131,9 @@ class AlfaBankConnector extends Connector implements HasPagination
->setClientId($this->alfaConfig->clientId ?? '')
->setClientSecret($this->alfaConfig->clientSecret ?? '')
->setRedirectUri($this->alfaConfig->redirectUri ?? '')
->setAuthorizeEndpoint($idUrl . '/oidc/authorize')
->setTokenEndpoint('/oidc/token');
->setAuthorizeEndpoint($idUrl.'/oidc/authorize')
->setTokenEndpoint('/oidc/token')
->setAllowBaseUrlOverride();
}
protected function defaultConfig(): array

View File

@@ -36,6 +36,7 @@ class CreateBeneficiaryRequestData extends Data
public string $countryOfTaxResidence,
#[DataCollectionOf(AddressData::class)]
public DataCollection $addresses,
public ?bool $isBeneficiary = null,
#[DataCollectionOf(DigestSignatureData::class)]
public ?DataCollection $digestSignatures = null,
public ?string $accountNumber = null,

View File

@@ -29,6 +29,7 @@ class UnidentifiedTransactionData extends Data
public ?string $documentNumber = null,
public ?string $date = null,
public ?string $purpose = null,
public ?string $accountNumber = null,
public ?PayerData $payer = null, // Nested DTO for payer details
) {}
// </SECTION>

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Data\Statement;
use Spatie\LaravelData\Data;
class CartInfoData extends Data
{
public function __construct(
public ?string $documentCode = null,
public ?string $documentContent = null,
public ?string $documentDate = null,
public ?string $documentNumber = null,
public ?string $paymentNumber = null,
public ?string $restAmount = null,
) {}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Data\Statement;
use Spatie\LaravelData\Data;
class CurTransferData extends Data
{
public function __construct(
public ?string $bankOperationCode = null,
public ?string $beneficiaryBankAccount = null,
public ?string $beneficiaryBankName = null,
public ?string $beneficiaryBankOption = null,
public ?string $beneficiaryCustomerAccount = null,
public ?string $beneficiaryCustomerName = null,
public ?string $detailsOfCharges = null,
public ?string $exchangeRate = null,
public ?string $instructedAmount = null,
public ?string $instructionCode = null,
public ?string $intermediaryBankAccount = null,
public ?string $intermediaryBankName = null,
public ?string $intermediaryBankOption = null,
public ?string $orderingCustomerAccount = null,
public ?string $orderingCustomerName = null,
public ?string $payeeBankBic = null,
public ?string $payeeBankCorrAccount = null,
public ?string $payeeBankName = null,
public ?string $payerInn = null,
public ?string $receiverCorrespondentAccount = null,
public ?string $receiverCorrespondentName = null,
public ?string $regulatoryReporting = null,
public ?string $senderCorrespondentAccount = null,
public ?string $senderCorrespondentName = null,
public ?string $senderCorrespondentOption = null,
public ?string $senderToReceiverInformation = null,
public ?string $transactionRelatedReference = null,
public ?string $valueDateCurrencyInterbankSettledAmount = null,
) {}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Data\Statement;
use Spatie\LaravelData\Data;
class DepartmentalInfoData extends Data
{
public function __construct(
public ?string $uip = null,
public ?string $drawerStatus101 = null,
public ?string $kbk = null,
public ?string $oktmo = null,
public ?string $reasonCode106 = null,
public ?string $taxPeriod107 = null,
public ?string $docNumber108 = null,
public ?string $docDate109 = null,
public ?string $paymentKind110 = null,
) {}
}

View File

@@ -1,29 +1,17 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Defines the data structure for detailed Russian Ruble (RUR) transfer information within a transaction.
// RESPONSIBILITY: To hold and type-safely represent comprehensive details about the payer and payee in an RUR transfer.
// INPUTS: Raw JSON object from the API response.
// OUTPUTS: A structured object containing RUR transfer details.
// LINKS: TransactionData.php
*/
namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for Russian Ruble transfer details.">
// <BELIEF_STATE "This DTO provides a clear, type-safe contract for highly detailed RUR transfer information, which is critical for financial reconciliation and compliance with Russian banking standards.">
class RurTransferData extends Data
{
// <SECTION name="constructor_properties" intent="Define the comprehensive properties of an RUR transfer.">
// <BELIEF_STATE "The constructor includes a wide array of optional fields covering full payee and payer details (account, bank BIC, correspondent account, INN, KPP, name), as well as specific payment attributes like `paymentKind110`, `priority`, `receiptDate`, and `valueDate`. All are optional as not all fields may be present or relevant in every transaction record.">
public function __construct(
public ?CartInfoData $cartInfo = null,
public ?string $deliveryKind = null,
public ?DepartmentalInfoData $departmentalInfo = null,
public ?string $payeeAccount = null,
public ?string $payeeBankBic = null,
public ?string $payeeBankCorrAccount = null,
@@ -38,10 +26,10 @@ class RurTransferData extends Data
public ?string $payerInn = null,
public ?string $payerKpp = null,
public ?string $payerName = null,
public ?string $payingCondition = null,
public ?string $paymentKind110 = null,
public ?string $priority = null,
public ?string $purposeCode = null,
public ?string $receiptDate = null,
public ?string $valueDate = null,
) {}
// </SECTION>
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Data\Statement;
use Spatie\LaravelData\Data;
class SwiftTransferData extends Data
{
public function __construct(
public ?string $bankOperationCode = null,
public ?string $beneficiaryBankAccount = null,
public ?string $beneficiaryBankName = null,
public ?string $beneficiaryBankOption = null,
public ?string $beneficiaryCustomerAccount = null,
public ?string $beneficiaryCustomerName = null,
public ?string $detailsOfCharges = null,
public ?string $exchangeRate = null,
public ?string $instructedAmount = null,
public ?string $instructionCode = null,
public ?string $intermediaryBankAccount = null,
public ?string $intermediaryBankName = null,
public ?string $intermediaryBankOption = null,
public ?string $orderingCustomerAccount = null,
public ?string $orderingCustomerName = null,
public ?string $payeeBankCorrAccount = null,
public ?string $payeeBankName = null,
public ?string $payeeBankBic = null,
public ?string $payerInn = null,
public ?string $receiverCorrespondentAccount = null,
public ?string $receiverCorrespondentName = null,
public ?string $regulatoryReporting = null,
public ?string $senderCorrespondentAccount = null,
public ?string $senderCorrespondentName = null,
public ?string $senderCorrespondentOption = null,
public ?string $senderToReceiverInformation = null,
public ?string $transactionRelatedReference = null,
public ?string $valueDateCurrencyInterbankSettledAmount = null,
) {}
}

View File

@@ -1,35 +1,18 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Defines the data structure for a paginated collection of TransactionData objects.
// RESPONSIBILITY: To provide a structured, type-safe container for lists of account transactions returned by the API, including pagination metadata.
// INPUTS: Raw JSON array from the API response.
// OUTPUTS: An object containing pagination details and a collection of `TransactionData`.
// LINKS: Requests/Statement/GetTransactionsRequest.php
*/
namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import necessary DTOs and Spatie Laravel Data components.">
// <BELIEF_STATE "This collection DTO relies on `PaginationData` for metadata and `TransactionData` for the individual items. `DataCollection` and `DataCollectionOf` are Spatie Data features for handling typed arrays of DTOs.">
use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of account transactions.">
// <BELIEF_STATE "A dedicated DTO for a collection ensures that the structure of paginated lists is consistent and type-safe, providing a reliable contract for consumers to manage financial statements.">
class TransactionCollectionData extends Data
{
// <SECTION name="constructor_properties" intent="Define the properties of the transaction collection.">
// <BELIEF_STATE "The collection structure includes pagination metadata (`pagination`) and the actual list of transactions (`transactions`). The `#[DataCollectionOf(TransactionData::class)]` attribute is crucial for ensuring that each item in the `transactions` array is automatically cast to a `TransactionData` DTO.">
public function __construct(
public PaginationData $pagination,
#[DataCollectionOf(TransactionData::class)]
public DataCollection $transactions
public DataCollection $transactions,
public ?array $_links = null,
) {}
// </SECTION>
}

View File

@@ -1,39 +1,32 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Defines the data structure for a single transaction record.
// RESPONSIBILITY: To hold and type-safely represent the details of an individual account transaction.
// INPUTS: Raw JSON object from the API response.
// OUTPUTS: A structured object containing transaction details.
// LINKS: TransactionCollectionData.php
*/
namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import necessary DTOs and Spatie Laravel Data components.">
// <BELIEF_STATE "This DTO includes nested `AmountData` for monetary values and `RurTransferData` for detailed transfer information.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single account transaction.">
// <BELIEF_STATE "This DTO provides a comprehensive, type-safe representation of a transaction, essential for consistent data handling and financial reporting.">
class TransactionData extends Data
{
// <SECTION name="constructor_properties" intent="Define all properties that describe an account transaction.">
// <BELIEF_STATE "The constructor includes key identifiers like `uuid`, `transactionId`, `operationDate`, `number`, and `direction` (DEBIT/CREDIT). Crucially, `amount` and `amountRub` use the nested `AmountData` DTO to ensure monetary values are correctly associated with their currencies. `paymentPurpose` and `rurTransfer` provide additional context, with `rurTransfer` being another nested DTO for specific RUR details.">
public function __construct(
public string $uuid,
public string $transactionId,
public string $operationDate,
public string $number,
public string $direction, // DEBIT, CREDIT
public AmountData $amount, // Nested DTO for amount
public AmountData $amountRub, // Nested DTO for amount in RUB
public string $direction,
public AmountData $amount,
public AmountData $amountRub,
public ?string $documentDate = null,
public ?string $filial = null,
public ?string $operationCode = null,
public ?string $paymentPurpose = null,
public ?RurTransferData $rurTransfer = null, // Nested DTO for RUR specific transfer details
public ?string $priority = null,
public ?string $revaln = null,
public ?string $correspondingAccount = null,
public ?string $debtorCode = null,
public ?string $extendedDebtorCode = null,
public ?RurTransferData $rurTransfer = null,
public ?SwiftTransferData $swiftTransfer = null,
public ?CurTransferData $curTransfer = null,
) {}
// </SECTION>
}

View File

@@ -23,7 +23,7 @@ enum Environment: string
// <SECTION name="sbp_environments" intent="Define base URLs for SBP API environments.">
// <BELIEF_STATE "These cases provide the base URLs for the SBP (Faster Payments System) API, distinguishing between production and sandbox endpoints. The sandbox URL specifically includes a port, which is common for testing environments requiring specific network configurations.">
case SBP_PRODUCTION = 'https://pay.alfabank.ru/fsCryptoProxy';
case SBP_PRODUCTION = 'https://217.12.103.146/fsCryptoProxy';
case SBP_SANDBOX = 'https://217.12.103.132:2443/fsCryptoProxy';
// </SECTION>

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Requests\NominalAccounts\Identification;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\UnidentifiedTransactionData;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
class CreateUnidentifiedTransactionRequest extends Request implements HasBody
{
use HasJsonBody;
protected Method $method = Method::POST;
public function __construct(
protected string $documentNumber,
protected string $date,
protected float $amount,
protected string $purpose,
protected string $accountNumber,
protected ?array $payer = null,
) {}
public function resolveEndpoint(): string
{
return '/api/na/jp/v1/unidentified-transactions';
}
public function createDtoFromResponse(Response $response): UnidentifiedTransactionData
{
return UnidentifiedTransactionData::from($response->json());
}
protected function defaultBody(): array
{
return array_filter([
'documentNumber' => $this->documentNumber,
'date' => $this->date,
'amount' => $this->amount,
'purpose' => $this->purpose,
'accountNumber' => $this->accountNumber,
'payer' => $this->payer ?? $this->getDefaultPayer(),
], fn ($value) => $value !== null);
}
protected function getDefaultPayer(): array
{
return [
'inn' => '221284672706',
'kpp' => '641914992',
'accountNumber' => '87149728127096890679',
'name' => 'Тестовый плательщик',
'bankAccount' => '3932755239052037086628',
'bankBik' => '108997635',
'bankName' => 'АО "Альфа-Банк"',
];
}
}

View File

@@ -1,49 +1,30 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Represents the API call to retrieve a list of account transactions.
// RESPONSIBILITY: To define the HTTP method, endpoint, and query parameters for fetching statement data, and to map the response to a structured collection DTO.
// INPUTS: Account number, date range, and pagination parameters.
// OUTPUTS: TransactionCollectionData (a DTO containing a list of transactions).
// LINKS: AlfaBankConnector.php#paginate
*/
namespace Apoloz\AlfaBank\Requests\Statement;
// <SECTION name="module_imports" intent="Import the response DTO and Saloon framework components.">
// <BELIEF_STATE "This request requires the `TransactionCollectionData` DTO to structure its response. It's a GET request, so it doesn't need body-related traits.">
use Apoloz\AlfaBank\Data\Statement\TransactionCollectionData;
use Apoloz\AlfaBank\Traits\HasAlfaPagination;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
class GetTransactionsRequest extends Request
{
use HasAlfaPagination;
protected Method $method = Method::GET;
public function __construct(
protected string $accountNumber,
protected string $executeDateFrom,
protected string $executeDateTo,
?int $limit = 1000,
?int $offset = 0
) {
$this->limit = $limit;
$this->offset = $offset;
}
protected string $statementDate,
protected int $page = 1,
protected ?string $curFormat = null,
) {}
public function resolveEndpoint(): string
{
return '/api/jp/v1/statement/transactions';
}
// </SECTION>
// <SECTION name="response_dto_caster" intent="Transform the raw API JSON response into a structured collection DTO.">
// <BELIEF_STATE "The API returns a list of transactions. Casting this directly to a `TransactionCollectionData` DTO provides a type-safe, iterable object for the end-user, which is a significant improvement over dealing with raw arrays.">
public function createDtoFromResponse(Response $response): TransactionCollectionData
{
return TransactionCollectionData::from($response->json());
@@ -51,11 +32,11 @@ class GetTransactionsRequest extends Request
protected function defaultQuery(): array
{
return array_merge([
return array_filter([
'accountNumber' => $this->accountNumber,
'executeDateFrom' => $this->executeDateFrom,
'executeDateTo' => $this->executeDateTo,
], $this->getPaginationQuery());
'statementDate' => $this->statementDate,
'page' => $this->page,
'curFormat' => $this->curFormat,
], fn ($v) => $v !== null);
}
// </SECTION>
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Defines the data structure for the response when generating an SBP QR Code.
// RESPONSIBILITY: To provide a strongly-typed structure for the API's response to QR code generation requests, including status, potential error messages, and the generated QR code data (image, payload, ID).
// INPUTS: Raw array data from the SBP QR Code generation API response.
// OUTPUTS: An instantiated DTO with properties mapped from the input array.
// LINKS: Sbp/Requests/GetQrCodeRequest.php, README.md#sbp-api
*/
namespace Apoloz\AlfaBank\Sbp\Data;
// <SECTION name="module_imports" intent="Import the base Spatie\LaravelData class.">
// <BELIEF_STATE "This class extends `Spatie\LaravelData\Data` to leverage its robust data mapping and type safety, ensuring consistent and predictable handling of SBP QR Code response payloads.">
use Spatie\LaravelData\Data;
// </SECTION>
/**
*
* "queryData":
{
"notificationUrl":"https://sbp-payments",
"SenderFIO":"Иванов|Иван|Иванович",
"SenderID":"0079123456789",
"SenderBankBIC":"44525593"
}
*
* */
// <SECTION name="class_definition" intent="Define the QR Code response data structure.">
// <BELIEF_STATE "The `QrCodeResponseData` DTO encapsulates all possible fields returned by the SBP QR Code generation API. Fields are explicitly typed as nullable to accommodate variations in API responses, particularly for error scenarios where certain data might be absent.">
class GetQRCdQueryData extends Data
{
public function __construct(
public string $notificationUrl,
public string $SenderFIO,
public string $SenderID
) {}
}

View File

@@ -54,8 +54,8 @@ class QrCodeRequestData extends Data
public ?string $width = null,
public ?string $height = null,
// <BELIEF_STATE "The `notificationUrl` specifies where the bank should send webhook notifications about payment status changes.">
public ?string $notificationUrl = null,
// <BELIEF_STATE "The `queryData` specifies where the bank should send webhook notifications about payment status changes.">
public GetQRCdQueryData $queryData,
// Dynamic QR specific fields
// <BELIEF_STATE "The `OrderNumber` is specific to dynamic QRs, providing a unique identifier for the order in the merchant's system.">
@@ -68,17 +68,6 @@ class QrCodeRequestData extends Data
// <BELIEF_STATE "Subscription service identifiers are used for recurring payment scenarios with SBP.">
public ?string $subscriptionServiceId = null,
public ?string $subscriptionServiceName = null,
// Optional user data for Dynamic QR (often part of sender identification)
// <BELIEF_STATE "Sender details are crucial for identifying the payer in certain SBP scenarios. `#[MapName]` ensures these follow API conventions.">
#[MapName('SenderFIO')]
public ?string $senderFio = null,
#[MapName('SenderID')]
public ?string $senderId = null,
#[MapName('SenderBankBIC')]
public ?string $senderBankBic = null,
) {}
// </SECTION>
}

View File

@@ -13,11 +13,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import DTO and Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request requires the `ActivateCashQrRequestData` DTO for its JSON body and utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\CashLink\ActivateCashQrRequestData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -25,7 +26,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for activating a CashLink QR Code, a key management operation in SBP payment systems.">
class ActivateCashQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Activating a resource is an action that changes the system state, so POST is the correct verb.">
@@ -56,9 +57,9 @@ class ActivateCashQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Fields from the DTO are mapped, including conditional parameters for order number, payment purpose, QR TTL, and notification URLs.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'ActivateCashQRc',
'TermNo' => $this->data->TermNo,
'qrcId' => $this->data->qrcId,
@@ -67,22 +68,22 @@ class ActivateCashQrRequest extends Request implements HasBody
];
if ($this->data->OrderNumber !== null) {
$body['OrderNumber'] = $this->data->OrderNumber;
$bodyArray['OrderNumber'] = $this->data->OrderNumber;
}
if ($this->data->paymentPurpose !== null) {
$body['paymentPurpose'] = $this->data->paymentPurpose;
$bodyArray['paymentPurpose'] = $this->data->paymentPurpose;
}
if ($this->data->qrTtl !== null) {
$body['qrTtl'] = $this->data->qrTtl;
$bodyArray['qrTtl'] = $this->data->qrTtl;
}
if ($this->data->redirectUrl !== null) {
$body['redirectUrl'] = $this->data->redirectUrl;
$bodyArray['redirectUrl'] = $this->data->redirectUrl;
}
if ($this->data->notificationUrl !== null) {
$body['queryData'] = ['notificationUrl' => $this->data->notificationUrl];
$bodyArray['queryData'] = ['notificationUrl' => $this->data->notificationUrl];
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,20 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to change the account for a CashLink QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for modifying a CashLink QR Code, which is a key management operation.">
class ChangeAccountForCashQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Modifying a resource via an action is correctly represented by a POST request in the SBP API context.">
@@ -53,7 +53,7 @@ class ChangeAccountForCashQrRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Return the raw JSON response as an array.">
// <BELIEF_STATE "For this specific request, the response is currently returned as a raw array. A dedicated DTO could be created if a structured object is required.">
public function createDtoFromResponse(\Saloon\Http\Response $response): array
public function createDtoFromResponse(Response $response): array
{
return $response->json();
}
@@ -61,9 +61,9 @@ class ChangeAccountForCashQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Fields are conditionally added based on constructor parameters, ensuring the payload precisely matches the API specification.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'ChangeAccForCashQRc',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
@@ -71,16 +71,16 @@ class ChangeAccountForCashQrRequest extends Request implements HasBody
];
if ($this->NewTermNo !== null) {
$body['NewTermNo'] = $this->NewTermNo;
$bodyArray['NewTermNo'] = $this->NewTermNo;
}
if ($this->NewMerchantId !== null) {
$body['NewMerchantId'] = $this->NewMerchantId;
$bodyArray['NewMerchantId'] = $this->NewMerchantId;
}
if ($this->redirectUrl !== null) {
$body['redirectUrl'] = $this->redirectUrl;
$bodyArray['redirectUrl'] = $this->redirectUrl;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,20 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to change the terminal number for a CashLink QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for modifying a CashLink QR Code, specifically its associated terminal, which is a key management operation.">
class ChangeTermNoForCashQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Modifying a resource via an action is correctly represented by a POST request in the SBP API context.">
@@ -49,7 +49,7 @@ class ChangeTermNoForCashQrRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Return the raw JSON response as an array.">
// <BELIEF_STATE "For this specific request, the response is currently returned as a raw array. A dedicated DTO could be created if a structured object is required.">
public function createDtoFromResponse(\Saloon\Http\Response $response): array
public function createDtoFromResponse(Response $response): array
{
return $response->json();
}
@@ -57,13 +57,15 @@ class ChangeTermNoForCashQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` and `qrcId` are included in the body to identify the target.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'ChangeTermNoForCashQRc',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,20 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to delete a CashLink QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for deleting a CashLink QR Code, which is a critical management operation.">
class DeleteCashQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Deleting a resource via an action is correctly represented by a POST request in the SBP API context.">
@@ -49,7 +49,7 @@ class DeleteCashQrRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Return the raw JSON response as an array.">
// <BELIEF_STATE "For this specific request, the response is currently returned as a raw array. A dedicated DTO could be created if a structured object is required.">
public function createDtoFromResponse(\Saloon\Http\Response $response): array
public function createDtoFromResponse(Response $response): array
{
return $response->json();
}
@@ -57,13 +57,15 @@ class DeleteCashQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` and `qrcId` are included in the body to identify the target.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'DelCashQRc',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,20 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to get the history of a CashLink QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for querying the history of a CashLink QR Code, which is useful for auditing and reconciliation.">
class GetCashQrHistoryRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP history requests are often performed via POST requests with a JSON body.">
@@ -51,7 +51,7 @@ class GetCashQrHistoryRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Return the raw JSON response as an array.">
// <BELIEF_STATE "For this specific request, the response is currently returned as a raw array. A dedicated DTO could be created if a structured object is required.">
public function createDtoFromResponse(\Saloon\Http\Response $response): array
public function createDtoFromResponse(Response $response): array
{
return $response->json();
}
@@ -59,15 +59,17 @@ class GetCashQrHistoryRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. All constructor properties are directly mapped to the request body to identify the QR Code and the date range.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'GetCashQrcId',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
'dateFrom' => $this->dateFrom,
'dateTo' => $this->dateTo,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,20 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to get the status of a CashLink QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for querying the status of a CashLink QR Code, which is crucial for tracking payment flows.">
class GetCashQrStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -49,7 +49,7 @@ class GetCashQrStatusRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Return the raw JSON response as an array.">
// <BELIEF_STATE "For this specific request, the response is currently returned as a raw array. A dedicated DTO could be created if a structured object is required.">
public function createDtoFromResponse(\Saloon\Http\Response $response): array
public function createDtoFromResponse(Response $response): array
{
return $response->json();
}
@@ -57,13 +57,15 @@ class GetCashQrStatusRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` and `qrcId` identify the target.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'GetCashQRCstatus',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -14,11 +14,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\CashLink;
// <BELIEF_STATE "This POST request requires specific DTOs for its JSON body and its response, along with Saloon's body-handling traits.">
use Apoloz\AlfaBank\Sbp\Data\CashLink\RegCashQrRequestData;
use Apoloz\AlfaBank\Sbp\Data\CashLink\RegCashQrResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -26,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for generating a CashLink QR Code, a key feature in SBP payment systems.">
class RegCashQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Creating a new QR Code resource or initiating a generation action is correctly represented by a POST request.">
@@ -57,30 +58,30 @@ class RegCashQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Fields from the DTO are mapped, including conditional parameters for media type, dimensions, redirect URL, and QR Code ID.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'RegCashQRc',
'TermNo' => $this->data->TermNo,
];
if ($this->data->mediaType !== null) {
$body['mediaType'] = $this->data->mediaType;
$bodyArray['mediaType'] = $this->data->mediaType;
}
if ($this->data->width !== null) {
$body['width'] = $this->data->width;
$bodyArray['width'] = $this->data->width;
}
if ($this->data->height !== null) {
$body['height'] = $this->data->height;
$bodyArray['height'] = $this->data->height;
}
if ($this->data->redirectUrl !== null) {
$body['redirectUrl'] = $this->data->redirectUrl;
$bodyArray['redirectUrl'] = $this->data->redirectUrl;
}
if ($this->data->qrcId !== null) {
$body['qrcId'] = $this->data->qrcId;
$bodyArray['qrcId'] = $this->data->qrcId;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -18,7 +18,6 @@ use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
// </SECTION>
@@ -26,7 +25,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for modifying a Digital Ruble CashLink QR Code, which is a key management operation.">
class ChangeAccForCashUniQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Modifying a resource via an action is correctly represented by a POST request in the SBP API context.">
@@ -57,42 +56,42 @@ class ChangeAccForCashUniQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Fields from the DTO are mapped, including conditional parameters for the new account, merchant, redirect URL, etc.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'ChangeAccForCashUniQr',
'TermNo' => $this->data->TermNo,
];
if ($this->data->messageID !== null) {
$body['messageID'] = $this->data->messageID;
$bodyArray['messageID'] = $this->data->messageID;
}
if ($this->data->qrcId !== null) {
$body['qrcId'] = $this->data->qrcId;
$bodyArray['qrcId'] = $this->data->qrcId;
}
if ($this->data->account !== null) {
$body['account'] = $this->data->account;
$bodyArray['account'] = $this->data->account;
}
if ($this->data->accountDR !== null) {
$body['accountDR'] = $this->data->accountDR;
$bodyArray['accountDR'] = $this->data->accountDR;
}
if ($this->data->NewTermNo !== null) {
$body['NewTermNo'] = $this->data->NewTermNo;
$bodyArray['NewTermNo'] = $this->data->NewTermNo;
}
if ($this->data->NewMerchantId !== null) {
$body['NewMerchantId'] = $this->data->NewMerchantId;
$bodyArray['NewMerchantId'] = $this->data->NewMerchantId;
}
if ($this->data->redirectUrl !== null) {
$body['redirectUrl'] = $this->data->redirectUrl;
$bodyArray['redirectUrl'] = $this->data->redirectUrl;
}
if ($this->data->paymentPageUrl !== null) {
$body['paymentPageUrl'] = $this->data->paymentPageUrl;
$bodyArray['paymentPageUrl'] = $this->data->paymentPageUrl;
}
if ($this->data->fraudScore !== null) {
$body['fraudScore'] = $this->data->fraudScore;
$bodyArray['fraudScore'] = $this->data->fraudScore;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -18,7 +18,6 @@ use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
// </SECTION>
@@ -26,7 +25,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for generating a Digital Ruble CashLink QR Code, a key feature in the SBP Digital Ruble operations.">
class CreateCashUniQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Creating a new QR Code resource or initiating a generation action is correctly represented by a POST request.">
@@ -57,40 +56,40 @@ class CreateCashUniQrRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Fields from the DTO are mapped, including conditional and nested `queryData` for notification URLs.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'ReqCashUniQr',
'TermNo' => $this->data->TermNo,
];
if ($this->data->messageID !== null) {
$body['messageID'] = $this->data->messageID;
$bodyArray['messageID'] = $this->data->messageID;
}
if ($this->data->redirectUrl !== null) {
$body['redirectUrl'] = $this->data->redirectUrl;
$bodyArray['redirectUrl'] = $this->data->redirectUrl;
}
if ($this->data->paymentPageUrl !== null) {
$body['paymentPageUrl'] = $this->data->paymentPageUrl;
$bodyArray['paymentPageUrl'] = $this->data->paymentPageUrl;
}
if ($this->data->mediaType !== null) {
$body['mediaType'] = $this->data->mediaType;
$bodyArray['mediaType'] = $this->data->mediaType;
}
if ($this->data->width !== null) {
$body['width'] = $this->data->width;
$bodyArray['width'] = $this->data->width;
}
if ($this->data->height !== null) {
$body['height'] = $this->data->height;
$bodyArray['height'] = $this->data->height;
}
if ($this->data->qrcId !== null) {
$body['qrcId'] = $this->data->qrcId;
$bodyArray['qrcId'] = $this->data->qrcId;
}
if ($this->data->notificationUrl !== null) {
$body['queryData'] = ['notificationUrl' => $this->data->notificationUrl];
$bodyArray['queryData'] = ['notificationUrl' => $this->data->notificationUrl];
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -18,7 +18,6 @@ use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
// </SECTION>
@@ -26,7 +25,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for querying the status of a Digital Ruble CashLink QR Code, which is crucial for tracking payment flows.">
class GetCashUniQrStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -57,9 +56,9 @@ class GetCashUniQrStatusRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo`, `uniQrId`, and `paramsId` identify the target. Optional fields like `messageID` and `payrrn` provide additional context.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'GetCashUniQrStatus',
'TermNo' => $this->data->TermNo,
'uniQrId' => $this->data->uniQrId,
@@ -67,13 +66,13 @@ class GetCashUniQrStatusRequest extends Request implements HasBody
];
if ($this->data->messageID !== null) {
$body['messageID'] = $this->data->messageID;
$bodyArray['messageID'] = $this->data->messageID;
}
if ($this->data->payrrn !== null) {
$body['payrrn'] = $this->data->payrrn;
$bodyArray['payrrn'] = $this->data->payrrn;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -6,15 +6,16 @@ namespace Apoloz\AlfaBank\Sbp\Requests\DigitalRuble;
use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CreateUniQrRequestData;
use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\UniQrResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
class CreateUniQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
protected Method $method = Method::POST;
@@ -32,40 +33,40 @@ class CreateUniQrRequest extends Request implements HasBody
return UniQrResponseData::from($response->json());
}
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'ReqUniQr',
'TermNo' => $this->data->TermNo,
'uniQrType' => $this->data->uniQrType,
];
if ($this->data->amount !== null) {
$body['amount'] = $this->data->amount;
$bodyArray['amount'] = $this->data->amount;
}
if ($this->data->paymentPurpose !== null) {
$body['paymentPurpose'] = $this->data->paymentPurpose;
$bodyArray['paymentPurpose'] = $this->data->paymentPurpose;
}
if ($this->data->redirectUrl !== null) {
$body['redirectUrl'] = $this->data->redirectUrl;
$bodyArray['redirectUrl'] = $this->data->redirectUrl;
}
if ($this->data->paymentPageUrl !== null) {
$body['paymentPageUrl'] = $this->data->paymentPageUrl;
$bodyArray['paymentPageUrl'] = $this->data->paymentPageUrl;
}
if ($this->data->mediaType !== null) {
$body['mediaType'] = $this->data->mediaType;
$bodyArray['mediaType'] = $this->data->mediaType;
}
if ($this->data->width !== null) {
$body['width'] = $this->data->width;
$bodyArray['width'] = $this->data->width;
}
if ($this->data->height !== null) {
$body['height'] = $this->data->height;
$bodyArray['height'] = $this->data->height;
}
if ($this->data->qrTtl !== null) {
$body['qrTtl'] = $this->data->qrTtl;
$bodyArray['qrTtl'] = $this->data->qrTtl;
}
if ($this->data->messageID !== null) {
$body['messageID'] = $this->data->messageID;
$bodyArray['messageID'] = $this->data->messageID;
}
$queryData = [];
@@ -83,9 +84,9 @@ class CreateUniQrRequest extends Request implements HasBody
}
if (! empty($queryData)) {
$body['queryData'] = $queryData;
$bodyArray['queryData'] = $queryData;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
}

View File

@@ -12,20 +12,21 @@ namespace Apoloz\AlfaBank\Sbp\Requests\DigitalRuble;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\GetUniQrStatusResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to get the status of Universal QR Codes.">
// <BELIEF_STATE "This class encapsulates the logic for querying the status of multiple Uni QR Codes, which is part of managing digital ruble transactions.">
class GetUniQrStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -49,21 +50,23 @@ class GetUniQrStatusRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `GetUniQrStatusResponseData` DTO provides a type-safe and structured object for the status of Universal QR Codes.">
public function createDtoFromResponse(\Saloon\Http\Response $response): \Apoloz\AlfaBank\Sbp\Data\DigitalRuble\GetUniQrStatusResponseData
public function createDtoFromResponse(Response $response): GetUniQrStatusResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\DigitalRuble\GetUniQrStatusResponseData::from($response->json());
return GetUniQrStatusResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` and `uniQrIds` are included in the body to identify the targets.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'GetUniQrStatus',
'TermNo' => $this->TermNo,
'uniQrIds' => $this->uniQrIds,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -13,20 +13,21 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Management;
// <SECTION name="module_imports" intent="Import DTO and Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request requires the `BlockQrRequestData` DTO for its JSON body and utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Management\BlockQrRequestData;
use Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to block a QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for blocking an SBP QR Code, which is a critical management operation.">
class BlockQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Blocking a resource is an action that changes the system state, so POST is the correct verb.">
@@ -49,20 +50,22 @@ class BlockQrRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `SbpManagementResponseData` DTO provides a structured object for the result of the block operation.">
public function createDtoFromResponse(\Saloon\Http\Response $response): \Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData
public function createDtoFromResponse(Response $response): SbpManagementResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData::from($response->json());
return SbpManagementResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Using the DTO's `toArray` method ensures the payload is correctly structured.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'BlockQRcId'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,21 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Management;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Management\GetBlockedQrListResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to get a list of blocked QR Codes.">
// <BELIEF_STATE "This class encapsulates the logic for querying blocked QR Codes, which is part of managing payment terminals.">
class GetBlockedQrListRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP list requests are often performed via POST requests with a JSON body.">
@@ -48,20 +49,22 @@ class GetBlockedQrListRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `GetBlockedQrListResponseData` DTO provides a type-safe object for the list of blocked QR Codes.">
public function createDtoFromResponse(\Saloon\Http\Response $response): \Apoloz\AlfaBank\Sbp\Data\Management\GetBlockedQrListResponseData
public function createDtoFromResponse(Response $response): GetBlockedQrListResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Management\GetBlockedQrListResponseData::from($response->json());
return GetBlockedQrListResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` is included in the body to identify the terminal.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'GetBlockedQRcIdList',
'TermNo' => $this->TermNo,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -13,20 +13,21 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Management;
// <SECTION name="module_imports" intent="Import DTO and Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request requires the `BlockQrRequestData` DTO for its JSON body and utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Management\BlockQrRequestData;
use Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to unblock a QR Code.">
// <BELIEF_STATE "This class encapsulates the logic for unblocking an SBP QR Code, which is a critical management operation.">
class UnblockQrRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Unblocking a resource is an action that changes the system state, so POST is the correct verb.">
@@ -49,20 +50,22 @@ class UnblockQrRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `SbpManagementResponseData` DTO provides a structured object for the result of the unblock operation.">
public function createDtoFromResponse(\Saloon\Http\Response $response): \Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData
public function createDtoFromResponse(Response $response): SbpManagementResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Management\SbpManagementResponseData::from($response->json());
return SbpManagementResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Using the DTO's `toArray` method ensures the payload is correctly structured.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'UnblockQRcId'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -13,11 +13,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Registry;
// <SECTION name="module_imports" intent="Import DTO and Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request requires the `GetBankListResponseData` DTO for its response and utilizes `HasBody` and `HasJsonBody` for proper payload serialization.">
use Apoloz\AlfaBank\Sbp\Data\Registry\GetBankListResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -25,7 +26,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for querying the list of SBP participating banks, which is essential for routing payments.">
class GetBankListRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP list requests are often performed via POST requests with a JSON body.">
@@ -57,20 +58,20 @@ class GetBankListRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Optional fields are conditionally added to the body.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'GetBankList',
];
if ($this->messageID !== null) {
$body['messageID'] = $this->messageID;
$bodyArray['messageID'] = $this->messageID;
}
if ($this->BankMemberIdFilter !== null) {
$body['BankMemberIdFilter'] = $this->BankMemberIdFilter;
$bodyArray['BankMemberIdFilter'] = $this->BankMemberIdFilter;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -14,11 +14,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Reversal;
// <BELIEF_STATE "This POST request requires specific DTOs for its JSON body and its response, along with Saloon's body-handling traits.">
use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalDataRequestData;
use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalDataResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -26,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for querying detailed SBP reversal information, which is crucial for auditing and reconciliation.">
class GetQrcReversalDataRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP data requests are often performed via POST requests with a JSON body.">
@@ -57,12 +58,14 @@ class GetQrcReversalDataRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Other fields from the DTO are conditionally added, ensuring the payload precisely matches the API specification.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'GetQRCreversalData'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -5,15 +5,16 @@ declare(strict_types=1);
namespace Apoloz\AlfaBank\Sbp\Requests\Reversal;
use Apoloz\AlfaBank\Sbp\Data\Reversal\ReversalHistoryResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
class GetQrcReversalHistoryRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
protected Method $method = Method::POST;
@@ -32,12 +33,14 @@ class GetQrcReversalHistoryRequest extends Request implements HasBody
return ReversalHistoryResponseData::from($response->json());
}
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'GetQRCreversalHistory',
'TermNo' => $this->TermNo,
'payrrn' => $this->payrrn,
];
return CmsSigner::canonicalize($bodyArray);
}
}

View File

@@ -14,11 +14,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Reversal;
// <BELIEF_STATE "This POST request requires specific DTOs for its JSON body and its response, along with Saloon's body-handling traits.">
use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalStatusRequestData;
use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalStatusResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -26,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for querying the status of an SBP reversal, which is a critical operation in payment systems.">
class GetQrcReversalStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -57,12 +58,14 @@ class GetQrcReversalStatusRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Other fields from the DTO are conditionally added, ensuring the payload precisely matches the API specification.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'GetQRCreversalStatus'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -14,11 +14,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Reversal;
// <BELIEF_STATE "This POST request requires specific DTOs for its JSON body and its response, along with Saloon's body-handling traits.">
use Apoloz\AlfaBank\Sbp\Data\Reversal\QrcReversalRequestData;
use Apoloz\AlfaBank\Sbp\Data\Reversal\QrcReversalResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -26,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for initiating an SBP reversal, which is a critical operation in payment systems.">
class QrcReversalRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Initiating a new transaction or action that changes the system state (like a reversal) is correctly represented by a POST request.">
@@ -57,12 +58,14 @@ class QrcReversalRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Other fields from the DTO are conditionally added, ensuring the payload precisely matches the API specification.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'QRCreversal'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -14,11 +14,12 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Status;
// <BELIEF_STATE "This POST request requires specific DTOs for its JSON body and its response, along with Saloon's body-handling traits.">
use Apoloz\AlfaBank\Sbp\Data\Status\GetQrcStatusRequestData;
use Apoloz\AlfaBank\Sbp\Data\Status\GetQrcStatusResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -26,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for querying the status of an SBP QR Code, which is crucial for tracking payment flows.">
class GetQrcStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -57,12 +58,14 @@ class GetQrcStatusRequest extends Request implements HasBody
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. The `TermNo` is mandatory, and `qrcId`, `payrrn`, and `messageID` are optional filters to pinpoint the transaction.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'GetQRCstatus'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,20 +12,21 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Subscription;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Subscription\GetQrSubStatusResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasStringBody;
// <BELIEF_STATE "The `Response` object is fully qualified in `createDtoFromResponse` as it is not explicitly imported with a `use` statement at the top. This is acceptable but less explicit.">
// use Saloon\Http\Response; // Explicit import would be cleaner
// </SECTION>
// <SECTION name="class_definition" intent="Define the request to get the status of a QR Code subscription.">
// <BELIEF_STATE "This class encapsulates the logic for querying the status of an SBP subscription, which is crucial for managing recurring payments.">
class GetQrSubStatusRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Even though it retrieves data, SBP status checks are often performed via POST requests with a JSON body.">
@@ -50,27 +51,27 @@ class GetQrSubStatusRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `GetQrSubStatusResponseData` DTO provides a structured object for the status of the subscription.">
public function createDtoFromResponse(\Saloon\Http\Response $response): \Apoloz\AlfaBank\Sbp\Data\Subscription\GetQrSubStatusResponseData
public function createDtoFromResponse(Response $response): GetQrSubStatusResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Subscription\GetQrSubStatusResponseData::from($response->json());
return GetQrSubStatusResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Other fields are conditionally added, ensuring the payload precisely matches the API specification.">
protected function defaultBody(): array
protected function defaultBody(): string
{
$body = [
$bodyArray = [
'command' => 'GetQRCSubStatus',
'TermNo' => $this->TermNo,
'qrcId' => $this->qrcId,
];
if ($this->RQperiod !== null) {
$body['RQperiod'] = $this->RQperiod;
$bodyArray['RQperiod'] = $this->RQperiod;
}
return $body;
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -12,11 +12,13 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Subscription;
// <SECTION name="module_imports" intent="Import Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Subscription\QrcSubPayPetitionResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -24,7 +26,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for initiating a payment through an SBP subscription, which is a key part of recurring payment flows.">
class QrcSubPayPetitionRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Initiating a payment action that changes the system state is correctly represented by a POST request.">
@@ -50,23 +52,25 @@ class QrcSubPayPetitionRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `QrcSubPayPetitionResponseData` DTO provides a structured object for the result of the payment petition.">
public function createDtoFromResponse(Response $response): \Apoloz\AlfaBank\Sbp\Data\Subscription\QrcSubPayPetitionResponseData
public function createDtoFromResponse(Response $response): QrcSubPayPetitionResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Subscription\QrcSubPayPetitionResponseData::from($response->json());
return QrcSubPayPetitionResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. All constructor properties are directly mapped to the request body.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return [
$bodyArray = [
'command' => 'QRCSubPayPetition',
'TermNo' => $this->TermNo,
'subscriptionToken' => $this->subscriptionToken,
'SenderMemberId' => $this->SenderMemberId,
'qrcId' => $this->qrcId,
];
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -13,11 +13,13 @@ namespace Apoloz\AlfaBank\Sbp\Requests\Subscription;
// <SECTION name="module_imports" intent="Import DTO and Saloon components for a JSON body request.">
// <BELIEF_STATE "This POST request requires the `RegQrSubRequestData` DTO for its JSON body and utilizes `HasBody` and `HasJsonBody` for proper payload serialization. It currently returns a raw array response.">
use Apoloz\AlfaBank\Sbp\Data\Subscription\RegQrSubRequestData;
use Apoloz\AlfaBank\Sbp\Data\Subscription\RegQrSubResponseData;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasJsonBody;
use Saloon\Traits\Body\HasStringBody;
// </SECTION>
@@ -25,7 +27,7 @@ use Saloon\Traits\Body\HasJsonBody;
// <BELIEF_STATE "This class encapsulates the logic for initiating an SBP subscription, which is crucial for recurring payment models.">
class RegQrSubRequest extends Request implements HasBody
{
use HasJsonBody;
use HasStringBody;
// <SECTION name="http_method" intent="Declare the HTTP verb.">
// <BELIEF_STATE "Registering a new resource or initiating an action that changes the system state is correctly represented by a POST request.">
@@ -48,20 +50,22 @@ class RegQrSubRequest extends Request implements HasBody
// <SECTION name="response_caster" intent="Map the JSON response to a DTO.">
// <BELIEF_STATE "Casting the response to a `RegQrSubResponseData` DTO provides a structured object for the result of the subscription registration.">
public function createDtoFromResponse(Response $response): \Apoloz\AlfaBank\Sbp\Data\Subscription\RegQrSubResponseData
public function createDtoFromResponse(Response $response): RegQrSubResponseData
{
return \Apoloz\AlfaBank\Sbp\Data\Subscription\RegQrSubResponseData::from($response->json());
return RegQrSubResponseData::from($response->json());
}
// </SECTION>
// <SECTION name="body_builder" intent="Construct the JSON body for the request.">
// <BELIEF_STATE "The `command` field specifies the operation to the SBP API. Using the DTO's `toArray` method ensures the payload is correctly structured.">
protected function defaultBody(): array
protected function defaultBody(): string
{
return array_merge(
$bodyArray = array_merge(
['command' => 'RegQRcSub'],
$this->data->toArray()
);
return CmsSigner::canonicalize($bodyArray);
}
// </SECTION>
}

View File

@@ -1,47 +1,33 @@
<?php
declare(strict_types=1);
/*
// MODULE_CONTRACT: Implements `SignerInterface` using OpenSSL for PKCS#7 (CMS) data signing.
// RESPONSIBILITY: To provide a concrete implementation of digital signing that takes a raw data string (e.g., canonicalized JSON), signs it using the configured mTLS certificate and private key, and returns a Base64 encoded PKCS#7 detached signature, adhering to Alfa Bank's security requirements.
// INPUTS: Data string to be signed. Certificate path and private key path are provided during instantiation.
// OUTPUTS: Base64 encoded PKCS#7 detached signature string.
// LINKS: Contracts/SecurityContracts.php, Utils/CmsSigner.php, README.md#security
*/
namespace Apoloz\AlfaBank\Services;
// <SECTION name="module_imports" intent="Import necessary classes for the signer contract.">
// <BELIEF_STATE "The `SignerInterface` is imported to ensure that `OpenSslCmsSigner` adheres to a predefined contract for digital signing, promoting interoperability and testability within the SDK.">
use Apoloz\AlfaBank\Contracts\SignerInterface;
use Apoloz\AlfaBank\Exceptions\SignerException;
// </SECTION>
// <SECTION name="class_definition" intent="Define the OpenSSL-based CMS signer service.">
// <BELIEF_STATE "The `OpenSslCmsSigner` class provides a robust and compliant solution for PKCS#7 CMS signing. By implementing `SignerInterface` and utilizing OpenSSL functions, it ensures that data integrity and authenticity can be cryptographically verified, which is critical for secure communication with financial institutions like Alfa Bank. The class takes certificate and private key paths during construction, allowing for flexible configuration.">
class OpenSslCmsSigner implements SignerInterface
{
// <SECTION name="constructor_definition" intent="Initialize the signer with certificate and private key paths.">
// <BELIEF_STATE "The constructor receives the file paths to the mTLS certificate and private key. These paths are stored as protected properties, ensuring that the signer instance is configured with the necessary credentials to perform digital signing operations, aligning with the principle of dependency injection for external resources.">
public function __construct(
protected string $certificatePath,
protected string $privateKeyPath
) {}
// </SECTION>
// <SECTION name="sign_method" intent="Sign a given data string using PKCS#7 CMS.">
// <BELIEF_STATE "The `sign` method performs the digital signature process as required by Alfa Bank. It reads the certificate and private key from the configured paths, then uses `openssl_pkcs7_sign` with `PKCS7_BINARY | PKCS7_DETACHED` flags to create a detached CMS signature. Error handling is included for robust operation, and the final signature is Base64 encoded after stripping unnecessary headers, ensuring compatibility with API expectations.">
public function sign(string $data): string
{
$privateKey = openssl_pkey_get_private(file_get_contents($this->privateKeyPath)); // assuming no passphrase for simplicity here
if (! $privateKey) {
throw new SignerException("Could not read private key for signing from {$this->privateKeyPath}.");
if (!file_exists($this->privateKeyPath)) {
throw new SignerException("Private key file not found: {$this->privateKeyPath}");
}
if (!file_exists($this->certificatePath)) {
throw new SignerException("Certificate file not found: {$this->certificatePath}");
}
$privateKey = openssl_pkey_get_private(file_get_contents($this->privateKeyPath));
$cert = openssl_x509_read(file_get_contents($this->certificatePath));
if (! $cert) {
throw new SignerException("Could not read certificate for signing from {$this->certificatePath}.");
if (!$privateKey || !$cert) {
throw new SignerException("Could not read private key or certificate for signing.");
}
$inFile = tempnam(sys_get_temp_dir(), 'alfa_in');
@@ -49,6 +35,7 @@ class OpenSslCmsSigner implements SignerInterface
file_put_contents($inFile, $data);
// Используем PKCS7_DETACHED согласно документации Альфа-Банка для digestSignatures
$status = openssl_pkcs7_sign(
$inFile,
$outFile,
@@ -58,25 +45,27 @@ class OpenSslCmsSigner implements SignerInterface
PKCS7_BINARY | PKCS7_DETACHED
);
if (! $status) {
if (file_exists($inFile)) {
unlink($inFile);
}
if (file_exists($outFile)) {
unlink($outFile);
}
throw new SignerException('Signing failed: ' . openssl_error_string());
if (!$status) {
@unlink($inFile);
@unlink($outFile);
throw new SignerException("OpenSSL sign failed: " . openssl_error_string());
}
$signedData = file_get_contents($outFile);
unlink($inFile);
unlink($outFile);
@unlink($inFile);
@unlink($outFile);
// Надежно извлекаем только Base64 блок подписи из S/MIME или PEM структуры
$parts = explode("\n\n", $signedData);
$signature = end($parts);
foreach ($parts as $part) {
// Очищаем часть от пробелов и переносов
$cleaned = str_replace(["\n", "\r", ' '], '', $part);
// Ищем блок, который выглядит как длинный Base64 (больше 128 символов) и не содержит S/MIME заголовков
if (preg_match('/^[a-zA-Z0-9\/+]+=*$/', $cleaned) && strlen($cleaned) > 128) {
return $cleaned;
}
}
return str_replace(["\n", "\r"], '', $signature);
throw new SignerException("Failed to extract signature body from CMS container. Output: " . substr($signedData, 0, 100));
}
// </SECTION>
}
// </SECTION>

View File

@@ -43,7 +43,8 @@ trait HasAlfaErrorHandling
if (str_contains($contentType, 'application/json')) {
$data = $response->json();
$errorCode = $data['error'] ?? $data['ErrorCode'] ?? 'HTTP_' . $response->status();
$errorDescription = $data['error_description'] ?? $data['message'] ?? 'Request failed';
$rawDescription = $data['error_description'] ?? $data['message'] ?? 'Request failed';
$errorDescription = $this->decodeBankMessage($rawDescription);
} else {
$errorCode = 'HTTP_' . $response->status();
$errorDescription = 'Request failed with status ' . $response->status();
@@ -55,4 +56,15 @@ trait HasAlfaErrorHandling
"({$requestId}) {$errorDescription}"
);
}
private function decodeBankMessage(string $message): string
{
$decoded = preg_replace_callback(
'/%u([0-9A-Fa-f]{4})/',
fn ($m) => mb_convert_encoding(pack('H*', $m[1]), 'UTF-8', 'UCS-2BE'),
$message
);
return urldecode($decoded);
}
}

View File

@@ -12,37 +12,47 @@ trait SignableData
{
public function sign(SignerInterface $signer): self
{
// 1. Create a clone to represent the data-to-be-signed (without existing signatures)
$clone = clone $this;
// 1. Get raw data as array
$data = $this->toArray();
// We explicitly set digestSignatures to null or empty for the signature calculation
// assuming the bank expects the "business payload" only.
// If the bank expects the field "digestSignatures": [] to be present in signed string,
// we should initialize it as empty collection.
// Based on common practices, we exclude it or keep it null.
// Let's assume we exclude it (set to null if the property is nullable).
$clone->digestSignatures = null;
// 2. Remove existing signatures if any
unset($data['digestSignatures']);
// 2. Serialize to JSON
// We use JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES to match typical API expectations
$jsonToSign = $clone->toJson(JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// 3. Canonicalize (RFC 8785 - simple version: sort keys, remove nulls, no spaces)
$canonicalData = $this->canonicalizeArray($data);
// 3. Sign
$jsonToSign = json_encode($canonicalData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// 4. Sign
$signature = $signer->sign($jsonToSign);
// 4. Attach signature to the original object
// 5. Attach signature to the original object
if ($this->digestSignatures === null) {
$this->digestSignatures = new DataCollection(DigestSignatureData::class, []);
}
// We can't push to DataCollection directly if it's not array-access compatible in all versions,
// but Spatie DataCollection is usually iterable.
// Safest way is to reconstruct it.
$current = $this->digestSignatures->toCollection()->all();
$current[] = new DigestSignatureData($signature, 'cms');
$current = array_values($this->digestSignatures->toCollection()->all());
$current[] = new DigestSignatureData($signature, 'RSA');
$this->digestSignatures = new DataCollection(DigestSignatureData::class, $current);
return $this;
}
private function canonicalizeArray(array $array): array
{
// Remove nulls and empty strings
$array = array_filter($array, fn ($v) => $v !== null);
// Sort keys lexically
ksort($array);
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = $this->canonicalizeArray($value);
}
}
return $array;
}
}

View File

@@ -7,22 +7,66 @@ use Saloon\Http\Faking\MockResponse;
test('can get transactions statement', function (): void {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTransactionsRequest::class => MockResponse::make([
'pagination' => [
'totalCount' => 1,
'limit' => 10,
'offset' => 0,
'_links' => [
['rel' => 'prev', 'href' => '?accountNumber=40702810500006103990&statementDate=2018-03-15&page=1'],
['rel' => 'next', 'href' => '?accountNumber=40702810500006103990&statementDate=2018-03-15&page=3'],
],
'transactions' => [
[
'uuid' => 'uuid-123',
'transactionId' => 'tx-123',
'operationDate' => '2023-01-01',
'number' => '1',
'uuid' => '55daccdf-de87-3879-976c-8b8415c8caf9',
'transactionId' => '1211206MOCO#DS0000017',
'operationDate' => '2018-12-31T00:00:00Z',
'number' => '1843',
'direction' => 'CREDIT',
'amount' => ['amount' => 100.00, 'currencyName' => 'RUB'],
'amountRub' => ['amount' => 100.00, 'currencyName' => 'RUB'],
'paymentPurpose' => 'Test',
'correspondingAccount' => '408...',
'amountRub' => ['amount' => 100.00, 'currencyName' => 'RUR'],
'documentDate' => '2021-10-07',
'filial' => 'АО "АЛЬФА-БАНК"',
'operationCode' => '01',
'paymentPurpose' => 'НДС не облагается',
'priority' => '5',
'revaln' => 'ПК',
'correspondingAccount' => '30101810400000000225',
'debtorCode' => '0',
'extendedDebtorCode' => '50012008',
'rurTransfer' => [
'cartInfo' => [
'documentCode' => 'doc-code',
'documentNumber' => 'doc-num',
],
'deliveryKind' => 'электронно',
'departmentalInfo' => [
'kbk' => '39210202010061000160',
'oktmo' => '11605000',
],
'payeeAccount' => '40802810401300015422',
'payeeBankBic' => '044525593',
'payeeInn' => '7728168971',
'payeeName' => 'Наименование получателя',
'payerAccount' => '40802810701300000761',
'payerBankBic' => '044525593',
'payerInn' => '7728168971',
'payerName' => 'Гаврилов Добрыня Петрович',
'payingCondition' => 'payingCondition',
'paymentKind110' => '1',
'purposeCode' => '1',
'receiptDate' => '2018-12-31',
'valueDate' => '2018-12-31',
],
'swiftTransfer' => [
'bankOperationCode' => 'CRED',
'beneficiaryBankAccount' => 'LOYDGB21323',
'beneficiaryBankName' => 'АО "АЛЬФА-БАНК"',
'exchangeRate' => '67,74',
'instructedAmount' => 'USD70,00',
],
'curTransfer' => [
'bankOperationCode' => 'CRED',
'intermediaryBankOption' => 'D',
'intermediaryBankName' => 'АО "АЛЬФА-БАНК"',
'orderingCustomerName' => 'ООО Радуга',
'exchangeRate' => '67,74',
],
],
],
], 200, ['Content-Type' => 'application/json']),
@@ -32,10 +76,69 @@ test('can get transactions statement', function (): void {
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$request = new GetTransactionsRequest('408...', '2023-01-01', '2023-01-02');
$request = new GetTransactionsRequest(
accountNumber: '40702810500006103990',
statementDate: '2018-03-15',
page: 2,
curFormat: 'swiftTransfer',
);
$response = $connector->send($request);
expect($response->status())->toBe(200)
->and($response->dto()->transactions[0]->transactionId)->toBe('tx-123')
->and($response->dto()->transactions[0]->amount->amount)->toBe(100.00);
$dto = $response->dto();
expect($response->status())->toBe(200);
expect($dto->_links)->toBeArray()->toHaveCount(2);
expect($dto->transactions)->toHaveCount(1);
$tx = $dto->transactions[0];
expect($tx->transactionId)->toBe('1211206MOCO#DS0000017')
->and($tx->direction)->toBe('CREDIT')
->and($tx->amount->amount)->toBe(100.00)
->and($tx->amountRub->currencyName)->toBe('RUR')
->and($tx->documentDate)->toBe('2021-10-07')
->and($tx->filial)->toBe('АО "АЛЬФА-БАНК"')
->and($tx->operationCode)->toBe('01')
->and($tx->priority)->toBe('5')
->and($tx->revaln)->toBe('ПК')
->and($tx->debtorCode)->toBe('0')
->and($tx->extendedDebtorCode)->toBe('50012008');
expect($tx->rurTransfer)->not->toBeNull();
expect($tx->rurTransfer->cartInfo)->not->toBeNull();
expect($tx->rurTransfer->cartInfo->documentCode)->toBe('doc-code');
expect($tx->rurTransfer->deliveryKind)->toBe('электронно');
expect($tx->rurTransfer->departmentalInfo)->not->toBeNull();
expect($tx->rurTransfer->departmentalInfo->kbk)->toBe('39210202010061000160');
expect($tx->rurTransfer->payingCondition)->toBe('payingCondition');
expect($tx->rurTransfer->purposeCode)->toBe('1');
expect($tx->rurTransfer->receiptDate)->toBe('2018-12-31');
expect($tx->swiftTransfer)->not->toBeNull();
expect($tx->swiftTransfer->bankOperationCode)->toBe('CRED');
expect($tx->swiftTransfer->instructedAmount)->toBe('USD70,00');
expect($tx->curTransfer)->not->toBeNull();
expect($tx->curTransfer->orderingCustomerName)->toBe('ООО Радуга');
});
test('can get transactions with default page', function (): void {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTransactionsRequest::class => MockResponse::make([
'_links' => [],
'transactions' => [],
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$request = new GetTransactionsRequest(
accountNumber: '40702810500006103990',
statementDate: '2018-03-15',
);
$response = $connector->send($request);
expect($response->status())->toBe(200);
expect($response->dto()->transactions)->toHaveCount(0);
});