- MODX 3
- PHP 8.2
- miniShop3
Интеграция mspYandexPay
Нужны только шаги установки? Откройте Быстрый старт. Ниже сопоставлены методы Merchant API и классы mspYandexPay для MiniShop3.
API ↔ код
| Метод / точка | Класс / файл | Назначение |
|---|---|---|
POST /orders | YandexPayClient::createOrder(), YandexPay*Payment::send(), PaymentService | Создание заказа, получение paymentUrl |
GET /orders/{id} | YandexPayClient::getOrder() | Статус заказа в API |
| Capture | YandexPayClient::captureOrder(), processor capture | Списание после AUTHORIZED |
| Cancel / VOID | YandexPayClient::cancelOrder(), processor cancel | Отмена холда |
| Refund (v2) | YandexPayClient::refundOrder(), processor refund | Полный или частичный возврат |
| Webhook JWT | webhook.php, JwtVerifier, WebhookService | Уведомления, смена статуса MS3 |
| Cash Register / QR | CashRegisterClient, YandexPayQrPayment | Заказ с orderSource=QR |
| Кнопка на сайте | сниппет mspYandexPayButton, connector payment/create | Создание paymentUrl с витрины |
Webhook
URL на сайте:
https://ваш-домен.ru/assets/components/mspyandexpay/webhook.phpОфициально: Webhook Merchant API.
Обработка:
- Тело запроса — JWT (
Content-Type: application/octet-stream). JwtVerifierпроверяет ES256 по JWKS текущей среды иmerchantId/exp.WebhookServiceобрабатывает события и пишет идемпотентность вmsp_yandex_pay_webhook_events(UNIQUEevent_key).- Повтор того же события → HTTP 200 без повторной смены статуса.
| Событие | Действие |
|---|---|
ORDER_STATUS_UPDATED | маппинг paymentStatus → статус MS3 |
OPERATION_STATUS_UPDATED + SUCCESS | CAPTURE → CAPTURED, REFUND → REFUNDED, VOID/CANCEL → VOIDED |
| Статус Yandex | Действие MS3 |
|---|---|
CAPTURED, CONFIRMED | ms3_status_paid |
FAILED, VOIDED | ms3_status_canceled |
REFUNDED | mspyandexpay_refunded_status_id или ms3_status_canceled |
PENDING, AUTHORIZED, PARTIALLY_REFUNDED | без смены статуса |
Заказ не помечается оплаченным при получении paymentUrl. AUTHORIZED (холд) тоже не paid.
IP whitelist не заменяет проверку JWT.
WebhookController
Класс WebhookController — точка входа HTTP для webhook JWT. Метод handleRawJwt(string $jwt):
- Вызывает
JwtVerifier::verify($jwt)→ декодирует payload. - Передаёт payload в
WebhookService::handle()→ идемпотентная обработка события. - Возвращает HTTP-ответ:
| Код | Тело | Когда |
|---|---|---|
| 200 | {"success": true, "duplicate": false, "message": "..."} | Успешная обработка |
| 200 | {"success": true, "duplicate": true, "message": "..."} | Повтор события (идемпотентность) |
| 401 | {"success": false, "message": "unauthorized"} | Ошибка JWT (подпись, merchantId, exp) |
| 400 | {"success": false, "message": "bad request"} | Ошибка обработки payload |
| 500 | {"success": false, "message": "internal error"} | Неожиданная ошибка |
JwtVerifier
Класс JwtVerifier проверяет подпись JWT алгоритмом ES256 по публичным ключам JWKS Яндекс Пэй.
Процесс верификации:
- Извлекает JWKS из URL среды:
https://sandbox.pay.yandex.ru/api/jwksилиhttps://pay.yandex.ru/api/jwks. - Кэширует JWKS на 10 минут (
jwksCacheTtl), чтобы не дергать API на каждый webhook. - Проверяет подпись JWT и claims:
merchantIdдолжен совпадать сmspyandexpay_merchant_idexp(expiration) не истёк (leeway 60 секунд)
- Возвращает декодированный payload или бросает
YandexPayAuthenticationException.
Ошибки верификации:
Empty JWT— пустое тело запросаInvalid JWT signature or claims— подпись не совпадает, истёкexp, неверныйmerchantIdUnable to load JWKS— не удалось получить публичные ключи ЯндексJWT merchantId mismatch— merchantId в JWT не совпадает с настройкой MODX
JWKS кэш: статический self::$jwksCache живёт 10 минут. Для отладки можно вызвать JwtVerifier::clearCache().
Поток одностадийной оплаты
- Покупатель оформляет заказ и выбирает Яндекс Пэй.
YandexPayPayment::send()собирает сумму изmsOrder(OrderBuilder) и вызываетPOST /orders.- Компонент сохраняет транзакцию в
msp_yandex_pay_transactionsи отдаёт redirect наpaymentUrl. - Покупатель платит в форме Яндекс Пэй.
- Webhook приносит
CAPTURED/CONFIRMED. OrderStatusUpdaterвыставляетms3_status_paid.
Двухстадийная схема
- В кабинете Яндекс Пэй включите двухстадийные платежи.
- Используйте способ «Яндекс Пэй (двухстадийная)» (
YandexPayTwoStagePayment). - После оплаты webhook приносит
AUTHORIZED. Заказ в MS3 ещё не paid. - Спишите или отмените холд через connector (нужна mgr-сессия):
assets/components/mspyandexpay/connector.php?action=capture&order_id=<id>&ctx=mgr
assets/components/mspyandexpay/connector.php?action=cancel&order_id=<id>&reason=...&ctx=mgrОпционально передайте amount для partial capture. Финальный статус также приходит webhook’ом (CAPTURE / VOID).
Возврат
После CAPTURED / CONFIRMED (и при PARTIALLY_REFUNDED для следующего частичного):
assets/components/mspyandexpay/connector.php?action=refund&order_id=<id>&ctx=mgr
assets/components/mspyandexpay/connector.php?action=refund&order_id=<id>&amount=10.00&ctx=mgrЧастичный возврат собирает targetCart. Полный — статус из mspyandexpay_refunded_status_id.
Split
- Глобально:
mspyandexpay_payment_methods=CARD,SPLIT(или толькоSPLIT) и при необходимостиpreferred_payment_method=SPLIT. - Или способ «Яндекс Пэй + Сплит»: методы
CARD+SPLIT, preferredSPLIT. mspyandexpay_is_prepayment=falseтипичен для оплаты при получении.
QR / Cash Register
- Включите
mspyandexpay_qr_enabled. - Активируйте способ «Яндекс Пэй QR» и привяжите к доставкам.
YandexPayQrPayment::send()создаёт заказ сorderSource=QRи channel в metadata.
Кнопка на витрине
Сниппет mspYandexPayButton регистрирует button.js и кнопку, которая дергает connector payment/create и редиректит на paymentUrl.
Параметры:
| Параметр | Описание |
|---|---|
order_id | ID заказа MS3. Если пусто, берётся msorder.id или заказ по ?msorder=<uuid>. |
text | Текст кнопки (по умолчанию «Оплатить Яндекс Пэй»). |
class | CSS-класс (по умолчанию ms3yp-button). |
{'!mspYandexPayButton' | snippet : [
'order_id' => $id,
'text' => 'Оплатить Яндекс Пэй',
]}[[!mspYandexPayButton?
&order_id=`[[+id]]`
&text=`Оплатить Яндекс Пэй`
]]Показывайте кнопку только для заказов, которые ещё нужно оплатить. Повторный send при существующей транзакции переиспользует payment_url (idempotency).
Checkout (MVP)
Endpoints для сценария Yandex Checkout по уже существующему заказу MS3 (ms3-{id} / cart.externalId):
https://ваш-домен.ru/assets/components/mspyandexpay/checkout/render.php
https://ваш-домен.ru/assets/components/mspyandexpay/checkout/create.phpАвторизация: JWT в Authorization: Bearer …. Полная доставка Yandex Logistics в пакет не входит.
checkout/render.php
Назначение: отдать данные заказа для отображения в форме Checkout Yandex.
Запрос:
POST /assets/components/mspyandexpay/checkout/render.php
Content-Type: application/json
Authorization: Bearer <JWT>
{
"cart": {
"externalId": "ms3-123"
}
}Тело запроса:
| Поле | Тип | Описание |
|---|---|---|
cart.externalId | string | ID заказа MS3 в формате ms3-{id} |
Ответ 200 OK:
{
"success": true,
"data": {
"cart": {
"externalId": "ms3-123",
"total": "1000.00",
"currency": "RUB",
"items": [...]
}
}
}Ошибки:
| Код | Тело | Когда |
|---|---|---|
| 400 | {"success": false, "message": "Invalid JSON"} | Неверный JSON |
| 401 | {"success": false, "message": "unauthorized"} | JWT не прошёл верификацию |
| 400 | {"success": false, "message": "..."} | Ошибка обработки (заказ не найден) |
| 500 | {"success": false, "message": "internal error"} | Неожиданная ошибка |
checkout/create.php
Назначение: создать платёж в Yandex для существующего заказа MS3.
Запрос:
POST /assets/components/mspyandexpay/checkout/create.php
Content-Type: application/json
Authorization: Bearer <JWT>
{
"cart": {
"externalId": "ms3-123"
},
"paymentMethod": "CARD"
}Тело запроса:
| Поле | Тип | Описание |
|---|---|---|
cart.externalId | string | ID заказа MS3 ms3-{id} |
paymentMethod | string | Опционально: CARD, SPLIT |
Ответ 200 OK:
{
"success": true,
"paymentUrl": "https://pay.yandex.ru/l/...",
"orderId": "yandex-order-id"
}Ошибки: те же что у render.php + ошибки API Yandex (недостаточно средств, недоступен метод оплаты).
Авторизация JWT
Оба endpoint требуют JWT в заголовке Authorization: Bearer <JWT>. JWT должен быть подписан вашим приватным ключом и содержать claims:
merchantId— ваш Merchant IDexp— время истечения (не больше 1 часа)iat— время выпуска
Яндекс Пэй проверяет JWT через свой JWKS. Компонент проверяет тот же JWT на входе через JwtVerifier.
Процессоры (Connector)
Компонент предоставляет connector.php с действиями capture, cancel, refund. Каждый процессор вызывается через GET/POST запрос к assets/components/mspyandexpay/connector.php с параметром action и order_id. Для refund и capture может передаваться дополнительный параметр amount (частичный capture/refund) и lines (JSON‑массив позиций для частичного возврата).
capture
assets/components/mspyandexpay/connector.php?action=capture&order_id=<id>&ctx=mgrorder_id– ID записи в таблице{prefix}msp_yandex_pay_transactions.ctx=mgr– обязательный контекст менеджера, иначе процессор отклонит запрос (требуется сессия менеджера).
Поведение:
- Загружает объект
OrderизTransactionRepository. - Вызывает
OrderOperationService::capture($order, $payload). - Если передан
amount, формируетorderAmountв запросе к Yandex API. - При успехе возвращает JSON
{ "success": true }, иначе{ "success": false, "message": "…" }.
cancel
assets/components/mspyandexpay/connector.php?action=cancel&order_id=<id>&reason=...&ctx=mgrreason– произвольный текст причины отмены (по умолчаниюMerchant cancel).
Поведение:
- Загружает
Order. - Вызывает
OrderOperationService::cancel($order, $reason). - Делает запрос
POST /orders/{orderId}/cancelк Yandex API. - Возвращает JSON‑ответ аналогично
capture.
refund
assets/components/mspyandexpay/connector.php?action=refund&order_id=<id>&ctx=mgr
# Частичный возврат
assets/components/mspyandexpay/connector.php?action=refund&order_id=<id>&amount=10.00&ctx=mgr
# Возврат по линиям
assets/components/mspyandexpay/connector.php?action=refund&order_id=<id>&lines={"items":[...]}&ctx=mgramount– опциональная сумма для частичного возврата.lines— JSON‑строка или массив позиций, которые нужно вернуть (используется API Yandexrefundсlines).
Поведение:
- Загружает
Order. - Формирует массив
$optionsизamountи/илиlines. - Вызывает
OrderOperationService::refund($order, $options)→ запрос к Yandex APIPOST /orders/{orderId}/refund. - Возвращает JSON‑ответ аналогично
capture.
Хранение транзакций
| Таблица | Назначение |
|---|---|
{prefix}msp_yandex_pay_transactions | order_id, UNIQUE yandex_order_id, payment_url, статусы, сумма, environment |
{prefix}msp_yandex_pay_webhook_events | идемпотентность webhook (UNIQUE event_key) |
В msOrder.properties пишутся yandex_pay_order_id, yandex_pay_payment_url, статусы после webhook / операций.
Безопасность
- JWT обязателен. IP whitelist не заменяет подпись.
- Сумма заказа берётся с сервера из
msOrder, не из request покупателя. - API key не попадает во frontend и маскируется в логах.
- Capture / cancel / refund через connector требуют сессию mgr.
- Default environment — sandbox.
Ограничения
- Фискализация чеков настраивается в кабинете Yandex или своим ОФД, не в payload компонента.
- Two-stage и часть sandbox-сценариев могут требовать включения у поддержки Yandex.
- Checkout — MVP без логистики Yandex.
- Ядро MiniShop3 пакет не меняет.
Что дальше
- Системные настройки
- FAQ
- Официально: план интеграции, фискализация
