Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Tests

on:
push:
pull_request:

jobs:
phpunit:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php-version:
- '7.4'
- '8.0'
- '8.1'
- '8.2'
- '8.3'
- '8.4'
- '8.5'

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none

- name: Validate Composer files
run: composer validate --strict

- name: Install dependencies
run: composer install --no-interaction --no-progress --prefer-dist

- name: Run PHPUnit
run: vendor/bin/phpunit
159 changes: 113 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,136 @@
# Robokassa SDK (PHP)
# Robokassa SDK для PHP

SDK для интеграции с платёжной системой **Robokassa** на PHP.
Позволяет отправлять платёжные запросы (включая JWT), проверять статус платежа и получать доступные методы оплаты.
SDK для интеграции с платёжной системой Robokassa на PHP.

## 📦 Установка
Текущий основной способ создания платёжной ссылки — `payment()->sendJwt()`. Старый метод `payment()->sendCurl()` сохранён только для обратной совместимости.

Установите SDK через **Composer**:
## Установка

```sh
composer require robokassa/sdk-php
````
```

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

## 🚀 Доступные методы
```php
<?php

| Метод | Описание | Документация |
|-------------------------------------------------|-------------------------------------------------------------------------------| ------------------------------------------------------------------------------------------- |
| `payment()->sendJwt(array $params): string` | ✅ Рекомендуемый способ. Создаёт ссылку на оплату через JWT-интерфейс | [docs.robokassa.ru/ru/invoice-api](https://docs.robokassa.ru/ru/invoice-api) |
| `payment()->sendCurl(array $params): string` | Создаёт ссылку на оплату через стандартный интерфейс | — |
| `webService()->getPaymentMethods(string $lang = 'ru'): array` | Получает список доступных методов оплаты | [docs.robokassa.ru/xml-interfaces/#currency](https://docs.robokassa.ru/xml-interfaces/#currency) |
| `webService()->opState(int $invoiceID): array` | Получает статус оплаты по `InvoiceID` | [docs.robokassa.ru/xml-interfaces/#account](https://docs.robokassa.ru/xml-interfaces/#account) |
| `status()->getInvoiceInformationList(array $filters): array` | Получает список выставленных счетов с возможностью фильтрации по статусу, дате, сумме и т.д.| [docs.robokassa.ru/invoiceapi/#status](https://docs.robokassa.ru/invoiceapi/#status) |
| `receipt()->sendSecondCheck(array $payload): string` | Отправляет запрос на формирование второго чека и возвращает ответ | [docs.robokassa.ru/second-check/#request](https://docs.robokassa.ru/second-check/#request) |
| `receipt()->getCheckStatus(array $payload): array` | Отправляет запрос на получение статуса фискального чека | [docs.robokassa.ru/second-check/#status](https://docs.robokassa.ru/second-check/#status) |
use Robokassa\Client\HttpClient;
use Robokassa\Robokassa;

$robokassa = new Robokassa(
[
'login' => getenv('ROBOKASSA_LOGIN') ?: '',
'password1' => getenv('ROBOKASSA_PASSWORD1') ?: '',
'password2' => getenv('ROBOKASSA_PASSWORD2') ?: '',
'hashType' => 'md5',
],
new HttpClient()
);
```

## ⚙️ Настройка окружения
Поддерживаемые алгоритмы подписи:

SDK не зависит от дополнительных библиотек для работы с конфигурацией: передавайте логин и пароли так, как это принято в вашем проекте (Laravel, Symfony, Docker, чистый PHP и т.д.). В SDK данные попадают в массив настроек при создании клиента, поэтому вы можете использовать любую существующую систему управления секретами.
```text
md5, ripemd160, sha1, sha256, sha384, sha512
```

### Минимальная настройка для примеров
Если передан неизвестный алгоритм, SDK выбросит `Robokassa\Exception\RobokassaException`.

1. Скопируйте файл `.env.example` в `.env`.
2. Заполните переменные `ROBOKASSA_LOGIN`, `ROBOKASSA_PASSWORD1`, `ROBOKASSA_PASSWORD2`.
3. Запустите нужный файл из папки `examples/`. Файл [`examples/bootstrap.php`](./examples/bootstrap.php) автоматически считывает `.env` и загружает значения в `$_ENV`.
## Доступные методы

### Использование в собственном приложении
| Метод | Описание | Документация |
| --- | --- | --- |
| `payment()->sendJwt(array $params): string` | Рекомендуемый способ. Создаёт ссылку на оплату через JWT-интерфейс. | [Invoice API](https://docs.robokassa.ru/ru/invoice-api) |
| `status()->getInvoiceInformationList(array $filters): array` | Получает список выставленных счетов по фильтрам. | [Invoice API](https://docs.robokassa.ru/ru/invoice-api) |
| `webService()->getPaymentMethods(string $lang = 'en'): array` | Получает список доступных способов оплаты. | [XML-интерфейсы](https://docs.robokassa.ru/ru/xml-interfaces) |
| `webService()->opState(int $invoiceID): array` | Получает статус оплаты по `InvoiceID`. | [XML-интерфейсы](https://docs.robokassa.ru/ru/xml-interfaces) |
| `receipt()->sendSecondCheck(array $payload): string` | Отправляет запрос на формирование второго чека. | [Второй чек](https://docs.robokassa.ru/ru/second-receipt.html) |
| `receipt()->getCheckStatus(array $payload): array` | Получает статус фискального чека. | [Второй чек](https://docs.robokassa.ru/ru/second-receipt.html) |

* **Фреймворки (Laravel, Symfony и др.)** — используйте штатные механизмы конфигурации и передавайте значения при создании `Robokassa`.
* **Чистый PHP или Docker** — задайте переменные окружения (например, через `export` или `docker run -e`) либо заполните `$_ENV` любым удобным способом.
## Создание ссылки на оплату через JWT

```php
$robokassa = new Robokassa(
[
'login' => getenv('ROBOKASSA_LOGIN') ?: '',
'password1' => getenv('ROBOKASSA_PASSWORD1') ?: '',
'password2' => getenv('ROBOKASSA_PASSWORD2') ?: '',
'hashType' => 'md5',
],
new HttpClient()
);
$url = $robokassa->payment()->sendJwt([
'OutSum' => 100.00,
'InvId' => 123456,
'Description' => 'Оплата заказа #123456',
'Culture' => 'ru',
]);
```

## 📂 Примеры использования
Метод возвращает строку со ссылкой на оплату.

Полные примеры использования SDK находятся в папке [`examples/`](./examples):
## Получение статуса счетов

* [`send_payment_jwt.php`](./examples/send_payment_jwt.php) — создание ссылки на оплату через **JWT** (рекомендуется)
* [`send_payment_curl.php`](./examples/send_payment_curl.php) — создание ссылки на оплату через стандартный CURL-интерфейс
* [`get_payment_methods.php`](./examples/get_payment_methods.php) — получение доступных способов оплаты
* [`get_invoice_status.php`](./examples/get_invoice_status.php) — проверка статуса счёта
* [`send_second_check.php`](./examples/send_second_check.php) — отправка второго чека
* [`get_check_status.php`](./examples/get_check_status.php) — проверка статуса чека
* [`get_invoice_information.php`](./examples/get_invoice_information.php) — запрос статуса созданного счета/ссылки
```php
$result = $robokassa->status()->getInvoiceInformationList([
'CurrentPage' => 1,
'PageSize' => 10,
'InvoiceStatuses' => ['paid', 'expired', 'notpaid'],
'DateFrom' => '2024-01-01',
'DateTo' => '2024-01-31',
'InvoiceTypes' => ['onetime', 'reusable'],
]);
```

## 📌 Дополнительно
Прямое создание сервиса статусов остаётся рабочим для старого кода:

* Метод `payment()->sendJwt()` — предпочтительный способ и рекомендуется к использованию.
* Официальная документация: [docs.robokassa.ru](https://docs.robokassa.ru/)
```php
use Robokassa\Service\StatusService;

$status = new StatusService($httpClient, $login, $password1);
```

## XML-интерфейсы

```php
$methods = $robokassa->webService()->getPaymentMethods('ru');
$state = $robokassa->webService()->opState(123456);
```

## Второй чек

```php
$result = $robokassa->receipt()->sendSecondCheck($payload);
$status = $robokassa->receipt()->getCheckStatus([
'merchantId' => 'merchant',
'id' => '123456',
]);
```

## Обратная совместимость: sendCurl()

`payment()->sendCurl(array $params): string` помечен как `@deprecated`, будет удалён в следующей major версии. Используйте `payment()->sendJwt()`.

Метод оставлен без runtime warning, чтобы не ломать существующие интеграции.

```php
$url = $robokassa->payment()->sendCurl([
'OutSum' => 100.00,
'InvoiceID' => 123456,
'Description' => 'Оплата заказа #123456',
]);
```

## Примеры

Основные примеры находятся в папке [`examples/`](./examples):

* [`send_payment_jwt.php`](./examples/send_payment_jwt.php) — создание ссылки на оплату через JWT.
* [`get_invoice_information.php`](./examples/get_invoice_information.php) — получение списка счетов через `$robokassa->status()`.
* [`get_payment_methods.php`](./examples/get_payment_methods.php) — получение доступных способов оплаты.
* [`get_invoice_status.php`](./examples/get_invoice_status.php) — проверка статуса оплаты через XML-интерфейс.
* [`send_second_check.php`](./examples/send_second_check.php) — отправка второго чека.
* [`get_check_status.php`](./examples/get_check_status.php) — проверка статуса чека.

Устаревший пример для обратной совместимости:

* [`send_payment_curl.php`](./examples/send_payment_curl.php) — старый способ создания ссылки через `sendCurl()`.

## Проверка

```sh
composer validate --strict
vendor/bin/phpunit
```
31 changes: 13 additions & 18 deletions examples/get_invoice_information.php
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
<?php
require_once __DIR__ . '/bootstrap.php';

use Robokassa\Client\HttpClient;
use Robokassa\Service\StatusService;

$http = new HttpClient();
$status = new StatusService($http, $_ENV['ROBOKASSA_LOGIN'] ?? '', $_ENV['ROBOKASSA_PASSWORD1'] ?? '');

try {
$result = $status->getInvoiceInformationList([
'MerchantLogin' => $_ENV['ROBOKASSA_LOGIN'] ?? '',
'CurrentPage' => 1,
'PageSize' => 10,
'InvoiceStatuses' => ['paid','expired','notpaid'],
'DateFrom' => '2023-01-01',
'DateTo' => '2025-09-05',
'IsAscending' => true,
'InvoiceTypes' => ['onetime','reusable'],
'PaymentAliases' => ['BankCard'],
'SumFrom' => 1,
'SumTo' => 10000,
$robokassa = createRobokassa();

$result = $robokassa->status()->getInvoiceInformationList([
'CurrentPage' => 1,
'PageSize' => 10,
'InvoiceStatuses' => ['paid','expired','notpaid'],
'DateFrom' => '2023-01-01',
'DateTo' => '2025-09-05',
'IsAscending' => true,
'InvoiceTypes' => ['onetime','reusable'],
'PaymentAliases' => ['BankCard'],
'SumFrom' => 1,
'SumTo' => 10000,
]);

print_r($result);
Expand Down
2 changes: 2 additions & 0 deletions examples/send_payment_curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

/**
* Пример использования метода payment()->sendCurl()
*
* @deprecated Метод будет удалён в следующей major версии. Используйте payment()->sendJwt().
* Создаёт платёжную ссылку через обычный POST-запрос (не JWT)
*/

Expand Down
Binary file removed src/.DS_Store
Binary file not shown.
69 changes: 34 additions & 35 deletions src/Client/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,9 @@
namespace Robokassa\Client;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;

final class Response {
/** @var string */
public $body;

/** @var int */
public $status;

public function __construct(string $body, int $status) {
$this->body = $body;
$this->status = $status;
}
}

interface HttpClientInterface {
public function get(string $url, array $headers = []): Response;
public function post(string $url, string $body, array $headers = []): Response;
}
use Robokassa\Exception\RobokassaException;

final class HttpClient implements HttpClientInterface {
/** @var Client */
Expand All @@ -31,29 +15,44 @@ public function __construct(?Client $client = null) {
}

public function get(string $url, array $headers = []): Response {
try {
$r = $this->client->get($url, ['headers' => $headers]);
return new Response((string)$r->getBody(), $r->getStatusCode());
} catch (RequestException $e) {
$resp = $e->getResponse();
$msg = $resp ? (string)$resp->getBody() : $e->getMessage();

throw new \Exception('Ошибка HTTP GET: ' . $msg, 0, $e);
}
return $this->request('get', $url, null, $headers);
}

public function post(string $url, string $body, array $headers = []): Response {
return $this->request('post', $url, $body, $headers);
}

/**
* Выполняет HTTP-запрос и заворачивает сетевые ошибки в исключение SDK.
*
* @param string $method
* @param string $url
* @param string|null $body
* @param array $headers
* @return Response
* @throws RobokassaException
*/
private function request(string $method, string $url, ?string $body, array $headers): Response {
$options = array(
'headers' => $headers,
);
if ($body !== null) {
$options['body'] = $body;
}

try {
$r = $this->client->post($url, [
'body' => $body,
'headers' => $headers,
]);
$r = $this->client->{$method}($url, $options);
return new Response((string)$r->getBody(), $r->getStatusCode());
} catch (RequestException $e) {
$resp = $e->getResponse();
$msg = $resp ? (string)$resp->getBody() : $e->getMessage();

throw new \Exception('Ошибка HTTP POST: ' . $msg, 0, $e);
$response = $e->getResponse();
if ($response !== null) {
throw new RobokassaException(
'Ошибка HTTP ' . strtoupper($method) . ': HTTP Status: ' . $response->getStatusCode()
);
}
throw new RobokassaException('Сетевая ошибка HTTP ' . strtoupper($method));
} catch (GuzzleException $e) {
throw new RobokassaException('Сетевая ошибка HTTP ' . strtoupper($method));
}
}
}
8 changes: 8 additions & 0 deletions src/Client/HttpClientInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace Robokassa\Client;

interface HttpClientInterface {
public function get(string $url, array $headers = []): Response;

public function post(string $url, string $body, array $headers = []): Response;
}
15 changes: 15 additions & 0 deletions src/Client/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace Robokassa\Client;

final class Response {
/** @var string */
public $body;

/** @var int */
public $status;

public function __construct(string $body, int $status) {
$this->body = $body;
$this->status = $status;
}
}
Loading
Loading