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
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
# Переименуйте в ".env" и заполните своими данными
ROBOKASSA_LOGIN=
ROBOKASSA_PASSWORD1=
ROBOKASSA_PASSWORD2=
ROBOKASSA_PASSWORD2=

# Параметры примера hold_payment.php
ROBOKASSA_HOLD_ACTION=create
ROBOKASSA_HOLD_INVOICE_ID=
ROBOKASSA_HOLD_OUT_SUM=
ROBOKASSA_HOLD_RESULT_URL2=
73 changes: 64 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ md5, ripemd160, sha1, sha256, sha384, sha512

## Доступные методы

| Метод | Описание | Документация |
| --- | --- | --- |
| `payment()->sendJwt(array $params): string` | Рекомендуемый способ. Создаёт ссылку на оплату через JWT-интерфейс. | [Invoice API](https://docs.robokassa.ru/ru/invoice-api) |
| Метод | Описание | Документация |
| --- |------------------------------------------------------------------------------| --- |
| `payment()->sendJwt(array $params): string` | Рекомендуемый способ. Создаёт ссылку на оплату через JWT-интерфейс. | [Invoice API](https://docs.robokassa.ru/ru/invoice-api) |
| `payment()->sendSavedCard(array $params): string` | Создаёт счёт для оплаты по сохранённой банковской карте через JWT-интерфейс. | [Оплата по сохраненной карте](https://docs.robokassa.ru/ru/saving) |
| `payment()->sendRecurring(array $params): string` | Создаёт дочерний рекуррентный платёж по оплаченной материнской операции. | [Периодические платежи](https://docs.robokassa.ru/ru/recurring-payments) |
| `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) |
| `payment()->sendHold(array $params): string` | Создаёт счёт для двухстадийной оплаты. | [Холдирование](https://docs.robokassa.ru/ru/holding.html) |
| `payment()->confirmHold(int $invoiceID, string $outSum, ?array $receipt = null): bool` | Подтверждает списание удержанных средств. | [Холдирование](https://docs.robokassa.ru/ru/holding.html#request) |
| `payment()->cancelHold(int $invoiceID, string $outSum): bool` | Отменяет холдирование. | [Холдирование](https://docs.robokassa.ru/ru/holding.html#cancel) |
| `payment()->sendRecurring(array $params): string` | Создаёт дочерний рекуррентный платёж по оплаченной материнской операции. | [Периодические платежи](https://docs.robokassa.ru/ru/recurring-payments) |
| `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) |

## Создание ссылки на оплату через JWT

Expand Down Expand Up @@ -89,6 +92,57 @@ SDK передаст токен в поле `Token` внутри массива

Если `AdditionalParameters` уже содержит другие значения, они сохранятся. `Token` нельзя совмещать с `Recurring` и `StepByStep` в одном счёте.

## Холдирование

Опция должна быть предварительно подключена для магазина и работает только с платежами банковскими картами. Для создания двухстадийного платежа используйте `sendHold()`:

```php
$url = $robokassa->payment()->sendHold([
'InvId' => 400001,
'OutSum' => '100.00',
'Description' => 'Оплата заказа #400001',
'AdditionalParameters' => [
'ResultURL2' => 'https://example.com/robokassa/result2',
],
]);
```

SDK создаст одноразовый счёт и самостоятельно добавит строковый параметр:

```php
'AdditionalParameters' => [
'StepByStep' => 'true',
]
```

`StepByStep` нельзя совмещать с `Recurring` и `Token`. Уведомление о переходе операции в `HOLD` поступает на `ResultURL2`; подпись входящего JWS необходимо проверить до изменения состояния заказа.

После получения состояния `HOLD` подтвердите списание:

```php
$accepted = $robokassa->payment()->confirmHold(400001, '100.00');
```

При необходимости в третьем аргументе можно передать обновлённый чек. Сумму и состав корзины разрешено изменять только в меньшую сторону:

```php
$accepted = $robokassa->payment()->confirmHold(400001, '90.00', $updatedReceipt);
```

Для отмены холда:

```php
$accepted = $robokassa->payment()->cancelHold(400001, '100.00');
```

Возвращаемое значение показывает только, принят ли запрос Robokassa. Оно не является конечным статусом операции. После Confirm или Cancel проверьте состояние через `webService()->opState()`:

```php
$state = $robokassa->webService()->opState(400001);
```

Основные коды состояния холда: `20` — средства удержаны, `50` — операция обрабатывается, `60` — холд отменён, `100` — списание подтверждено. При `false` или сетевой ошибке не повторяйте Confirm/Cancel автоматически: сначала запросите состояние операции. Confirm и Cancel не поддерживают тестовый режим.

## Рекуррентные платежи

Для материнского платежа создайте обычный счёт через `sendJwt()` и передайте `Recurring=true` в `AdditionalParameters`:
Expand Down Expand Up @@ -177,6 +231,7 @@ $url = $robokassa->payment()->sendCurl([

* [`send_payment_jwt.php`](./examples/send_payment_jwt.php) — создание ссылки на оплату через JWT.
* [`send_saved_card_payment.php`](./examples/send_saved_card_payment.php) — создание счёта для оплаты по сохранённой карте.
* [`hold_payment.php`](./examples/hold_payment.php) — создание, подтверждение, отмена и проверка двухстадийного платежа.
* [`send_recurring_payment.php`](./examples/send_recurring_payment.php) — создание дочернего рекуррентного платежа по оплаченной материнской операции.
* [`get_invoice_information.php`](./examples/get_invoice_information.php) — получение списка счетов через `$robokassa->status()`.
* [`get_payment_methods.php`](./examples/get_payment_methods.php) — получение доступных способов оплаты.
Expand Down
67 changes: 67 additions & 0 deletions examples/hold_payment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

require_once __DIR__ . '/bootstrap.php';

/**
* Пример жизненного цикла двухстадийного платежа.
*
* Перед запуском задайте:
* ROBOKASSA_HOLD_ACTION — create, confirm, cancel или status
* ROBOKASSA_HOLD_INVOICE_ID — InvoiceID операции
* ROBOKASSA_HOLD_OUT_SUM — сумма операции
* ROBOKASSA_HOLD_RESULT_URL2 — серверный callback для действия create
*
* Confirm и Cancel работают только в основном режиме и не должны повторяться
* автоматически при неоднозначном результате.
*/

try {
$action = strtolower($_ENV['ROBOKASSA_HOLD_ACTION'] ?? 'create');
$invoiceID = (int)($_ENV['ROBOKASSA_HOLD_INVOICE_ID'] ?? 0);
if ($invoiceID <= 0) {
throw new InvalidArgumentException('Укажите положительный ROBOKASSA_HOLD_INVOICE_ID.');
}
$outSum = $_ENV['ROBOKASSA_HOLD_OUT_SUM'] ?? '10.00';
$robokassa = createRobokassa();

switch ($action) {
case 'create':
$resultUrl2 = $_ENV['ROBOKASSA_HOLD_RESULT_URL2'] ?? '';
if ($resultUrl2 === '') {
throw new InvalidArgumentException('Укажите ROBOKASSA_HOLD_RESULT_URL2.');
}
$url = $robokassa->payment()->sendHold([
'InvId' => $invoiceID,
'OutSum' => $outSum,
'Description' => 'Двухстадийная оплата заказа #' . $invoiceID,
'AdditionalParameters' => [
'ResultURL2' => $resultUrl2,
],
]);
echo "Ссылка на оплату с холдированием: $url\n";
break;

case 'confirm':
$accepted = $robokassa->payment()->confirmHold($invoiceID, $outSum);
echo $accepted
? "Запрос подтверждения принят. Проверьте итоговый статус операции.\n"
: "Запрос подтверждения отклонён. Не повторяйте его автоматически; проверьте статус.\n";
break;

case 'cancel':
$accepted = $robokassa->payment()->cancelHold($invoiceID, $outSum);
echo $accepted
? "Запрос отмены принят. Проверьте итоговый статус операции.\n"
: "Запрос отмены отклонён. Не повторяйте его автоматически; проверьте статус.\n";
break;

case 'status':
print_r($robokassa->webService()->opState($invoiceID));
break;

default:
throw new InvalidArgumentException('ROBOKASSA_HOLD_ACTION должен быть create, confirm, cancel или status.');
}
} catch (Throwable $e) {
echo 'Ошибка: ' . $e->getMessage() . "\n";
}
6 changes: 5 additions & 1 deletion src/Robokassa.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class Robokassa {
private string $paymentCurl = 'https://auth.robokassa.ru/Merchant/Indexjson.aspx';
private string $jwtApiUrl = 'https://services.robokassa.ru/InvoiceServiceWebApi/api/CreateInvoice';
private string $recurringUrl = 'https://auth.robokassa.ru/Merchant/Recurring';
private string $holdConfirmUrl = 'https://auth.robokassa.ru/Merchant/Payment/Confirm';
private string $holdCancelUrl = 'https://auth.robokassa.ru/Merchant/Payment/Cancel';
private string $webServiceUrl = 'https://auth.robokassa.ru/Merchant/WebService/Service.asmx';

private bool $is_test = false;
Expand Down Expand Up @@ -129,7 +131,9 @@ private function createPaymentService(): PaymentService {
$this->paymentCurl,
$this->jwtApiUrl,
$this->hashType,
$this->recurringUrl
$this->recurringUrl,
$this->holdConfirmUrl,
$this->holdCancelUrl
);
}

Expand Down
Loading
Loading