# Расширение OpenCart 4

> Как установить расширение Qut Pay в магазин на OpenCart 4, настроить ключ и вебхук, выбрать статусы заказа, сделать возврат и что проверять при типовых сбоях.

## Коротко

Расширение добавляет в OpenCart 4 способ «Оплата через Kaspi (QR)»: покупатель подтверждает заказ → попадает на страницу оплаты Qut Pay (QR и ссылка Kaspi) → платит в приложении Kaspi.kz. Статус заказа обновляется автоматически по вебхуку, деньги приходят напрямую на ваш Kaspi Pay.

Загрузка: **https://api.qut.kz/downloads/qutpay.ocmod.zip**

Имя архива важно: OpenCart берёт код расширения (`qutpay`) из имени файла. Не переименовывайте его.

## Требования

| Что | Значение |
|---|---|
| OpenCart | 4.0.2.0 и новее |
| PHP | 8.0 и новее с расширениями `curl` и `json` |
| Валюта магазина | **KZT (₸)**. При другой валюте способ оплаты не показывается |
| Сайт | должен быть доступен из интернета по https, иначе в проде не дойдёт вебхук |
| Qut Pay | организация с подключённым кассиром Kaspi (для песочницы кассир не нужен) |

## Шаг 1. Установка

1. Админка → **Extensions → Installer** → **Upload** → выберите `qutpay.ocmod.zip`. После загрузки нажмите зелёную кнопку **Install** напротив «Qut Pay (Kaspi QR)».
2. **Extensions → Extensions → Payments** → найдите в списке «Qut Pay (Kaspi QR)» и нажмите зелёный **плюс**. На этом шаге создаётся таблица `oc_qutpay_invoice`.
3. Там же нажмите **Edit** и переходите к настройкам.

## Шаг 2. Ключ и вебхук

Кабинет: https://qut.kz/app

1. **Интеграции → API-ключи** → назовите ключ именем магазина → **Создать**. Ключ показывается один раз — скопируйте сразу.
2. Поле **«Адрес webhook»** в настройках расширения доступно только для чтения — скопируйте его. Выглядит оно так:

   ```
   https://ВАШ-МАГАЗИН/index.php?route=extension/qutpay/payment/qutpay.webhook
   ```

3. Кабинет → **Интеграции → Вебхуки** → добавьте этот адрес, в списке «Ключ» выберите созданный ключ → **Создать**. Показанный секрет скопируйте в поле «Секрет webhook» расширения.

**Без секрета заказы не будут отмечаться оплаченными автоматически** — расширение вернёт 503, а мы будем повторять доставку.

## Шаг 3. Настройка

| Поле | Что указать |
|---|---|
| API-ключ | Ключ из шага 2. Кнопка **«Проверить ключ»** рядом делает реальный запрос к API и проверяет его |
| Адрес webhook | Только для чтения. Скопируйте в кабинет |
| Секрет webhook | Значение, показанное один раз при добавлении адреса |
| Адрес API | `https://api.qut.kz` — не менять |
| Ожидание оплаты | Статус сразу после подтверждения заказа, когда покупатель ушёл на страницу оплаты |
| Оплачен | Статус после подтверждения оплаты. Для цифровых товаров обычно «Выполнен» |
| Счёт истёк / Счёт отменён / Возврат | Соответствующие статусы. Можно выбрать «не менять статус заказа» |
| Геозона, Статус, Порядок сортировки | Как у остальных способов оплаты |
| Лог отладки | Включите на время настройки: записи идут в `storage/logs/qutpay.log` |

Включите переключатель **Статус** и сохраните.

Значения статусов по умолчанию соответствуют стандартной установке OpenCart (1 Pending, 2 Processing, 14 Expired, 7 Canceled, 11 Refunded). Если статусы в магазине переопределены, выберите нужные вручную.

## Как меняются статусы заказа

1. **На шаге подтверждения** покупатель нажимает кнопку. Расширение вызывает `POST /api/v1/invoices`: сумма, описание «Название магазина — заказ №N», `externalOrderId` — номер заказа, `customer` (телефон нормализуется до `7XXXXXXXXXX`), в `metadata` — метки магазина и заказа, заголовок `Idempotency-Key` вида `oc-{заказ}-…`.
2. Счёт сохраняется в `oc_qutpay_invoice`, заказу ставится «Ожидание оплаты», покупателя перебрасывает на `payUrl`. Если покупатель вернулся и нажал кнопку снова, а прежний счёт ещё открыт и сумма не менялась, переиспользуется тот же `payUrl`.
3. **Вебхук** проверяется по сырому телу запроса, отметки времени старше 5 минут отклоняются. Обработка идемпотентна по паре (счёт, статус).

| Событие | Что происходит |
|---|---|
| `paid` | Статус «Оплачен», в историю пишется номер и ссылка чека. Если сумма счёта не совпала с суммой заказа — статус не меняется, в историю добавляется предупреждение |
| `expired` / `cancelled` | Соответствующий статус, но только если менеджер ещё не перевёл заказ в processing/complete |
| `refunded` | Статус «Возврат» |
| `partially_refunded` | Только запись в историю |
| Поздняя оплата (`late: true`) | Заказ отмечается оплаченным даже по истёкшему счёту |

4. **Когда покупатель возвращается**, расширение запрашивает `GET /api/v1/invoices/{id}?live=1`. Если счёт оплачен — отправляет на стандартную страницу `checkout/success` (корзина очищается). Иначе показывает страницу ожидания с кнопками «Проверить ещё раз» (автообновление каждые 8 секунд), «Открыть страницу оплаты Kaspi» и «Вернуться к оформлению».

Ответы обработчика вебхука: `200` — принято, `401` — плохая подпись, `400` — битое тело, `503` — расширение выключено или не задан секрет.

## Возвраты

**Из админки OpenCart возврат не делается.** Возврат оформляйте в кабинете Qut Pay: **Счета → откройте счёт → Возврат**. После этого придёт событие `invoice.refunded`, и заказ в OpenCart перейдёт в выбранный вами статус «Возврат».

При частичном возврате статус заказа не меняется — добавляется только запись в историю. Подробнее: [API возвратов](/kb/ru/refunds-api).

## Проверка

Сделайте заказ с ключом песочницы (`qp_test_…`). На странице оплаты есть кнопка «[Sandbox] оплатить», которая имитирует платёж — вебхук при этом уходит по-настоящему, так что переходы статусов проверяются целиком.

Переходя в боевой режим, вставьте ключ `qp_live_…` и проведите один заказ на небольшую сумму.

## Частые проблемы

| Симптом | Причина и решение |
|---|---|
| Способ не появляется в кассе | Валюта не KZT; расширение не установлено в списке Payments; выключен Статус; не совпадает геозона |
| Заказ зависает в «Ожидание оплаты» | Вебхук не зарегистрирован, неверный адрес или не совпадает секрет. Смотрите журнал доставок в кабинете |
| В журнале 503 | Расширение выключено либо пустое поле «Секрет webhook» |
| В журнале 401 | Неверный секрет. Добавьте адрес заново и вставьте новый секрет |
| Вебхук вообще не приходит | Магазин в режиме Maintenance — он блокирует входящие запросы. После выхода из режима статус подтянется и при возврате покупателя |
| «Проверить ключ» выдаёт ошибку | Ключ скопирован неверно или не тот режим: для песочницы `qp_test_…`, для боевого `qp_live_…` |
| Адрес вебхука неверный | Адрес строится из `HTTP_CATALOG` админки. Если магазин за прокси или на другом домене, поправьте адрес вручную и зарегистрируйте в кабинете правильный |
| Расширения нет в Installer | Архив переименовали. Файл должен называться `qutpay.ocmod.zip` |

Полная диагностика: [Плагин не работает](/kb/ru/plugin-not-working).

## Вопросы и ответы

**Заработает ли на OpenCart 3?** Нет. Расширение написано под OpenCart 4.0.2.x. На третьей версии можно обращаться к API напрямую: [PHP SDK](/kb/ru/sdk-php).

**Можно ли вести два магазина на одном API-ключе?** Да: расширение хранит метку заказа в `metadata` счёта и сверяет её в вебхуке, поэтому события чужого магазина игнорируются. Но удобнее выдать каждому магазину свой ключ.

**Где смотреть лог?** При включённом «Логе отладки» — `storage/logs/qutpay.log`.

**Что будет, если покупатель ушёл, не оплатив?** Счёт в свой срок станет `expired`, заказ перейдёт в выбранный вами статус. Покупатель может открыть заказ заново и получить новый счёт.

**А что есть для WordPress?** Плагин для WooCommerce: [Плагин WooCommerce](/kb/ru/woocommerce).
