msp3PaymentSkeleton — шаблон для создания платёжных дополнений MiniShop3
Сделал открытый шаблон для разработки платёжных дополнений MiniShop3 под MODX 3 github.com/Ibochkarev/msp3PaymentSkeleton
Если вы хотя бы раз писали интеграцию с платёжным API, то знаете этот сценарий.
Сначала нужно разобраться с API провайдера.
Потом появляются webhook, подписи, попытки оплаты, внешний ID, возвраты, статусы, обработка ошибок, 54-ФЗ, настройки, вкладка в заказе, resolver, сборка…
И только после этого можно наконец заняться самой интеграцией с платёжной системой.
При этом значительная часть этой работы каждый раз практически одинаковая.
Поэтому я собрал msp3PaymentSkeleton — готовую основу, от которой можно начинать разработку нового payment Extra для MiniShop3.
msp3PaymentSkeleton — это не готовая интеграция с каким-то банком или агрегатором.
Это developer skeleton для создания таких интеграций.
Внутри уже подготовлены:
Почему вообще понадобился skeleton
В MiniShop3 постепенно появляется всё больше отдельных платёжных дополнений.
У каждого провайдера своё API:
Например:
И нет особого смысла реализовывать этот слой заново в каждом дополнении.
Главное изменение — lifecycle
В основе skeleton используется новый payment lifecycle MiniShop3.
Поэтому платёжное дополнение не должно самостоятельно управлять статусом заказа:
Вместо этого Extra сообщает lifecycle о событии:
А MiniShop3 уже сам применяет соответствующую бизнес-логику.
Например:
Это важный момент.
Платёжный Extra отвечает за интеграцию с провайдером, а MiniShop3 — за жизненный цикл платежа и связанный с ним заказ.
Такой подход значительно проще поддерживать, когда количество платёжных интеграций растёт.
Что реально нужно написать под нового провайдера
После клонирования skeleton основная работа находится в нескольких местах.
В проекте они специально отмечены:
В первую очередь:
ApiClient — API провайдера.
Здесь находятся:
Если у провайдера HMAC-SHA256 — уже есть базовый вариант.
Если другая схема — меняется этот слой.
WebhookParser — перевод событий провайдера в события MiniShop3.
Например:
SkeletonPayment — адаптация самого платежа к контракту MiniShop3.
В частности, здесь собирается запрос:
А send() возвращает:
Быстрый старт
Например, нужно сделать msp3MyPay.
Клонируем skeleton:
Запускаем генератор:
И получаем уже свой проект.
Дополнительные части можно оставить через --keep:
Есть также:
--no-encrypt — для локальной сборки без шифрования.
--self-destruct — удалить bin/init.php после успешной инициализации.
Webhook — уже не «прикрутим потом»
В платёжных интеграциях webhook часто становится самым проблемным местом.
Платёж создан — это только начало.
Дальше провайдер может:
Стандартный endpoint:
Дальше:
При этом verifyWebhook() должен действительно проверять подпись.
Заглушка: return true; не является реализацией webhook security.
Идемпотентность
Повтор webhook — обычная ситуация.
Например:
Повторная обработка одного и того же providerEventId не должна ломать заказ.
Поэтому идемпотентность вынесена в общий сценарий lifecycle, а при разработке конкретного провайдера необходимо корректно сопоставить его идентификатор события.
А если у провайдера только один webhook URL?
Такое тоже встречается.
Некоторые API дают один webhook URL на весь магазин, а некоторые отправляют вообще не JSON, а form POST.
Для этого в skeleton предусмотрен отдельный: webhook.php
Он позволяет адаптировать нестандартный входящий webhook, не ломая основной payment API MiniShop3.
Возвраты
Возврат также проходит через lifecycle.
Вместо:
используется:
И учитывается состояние payment attempt.
Например, нельзя считать заказ оплаченным только потому, что был создан платёж.
Сначала:
А не:
Это особенно важно для API, где создание платежа и фактическое списание денег — разные операции.
Дополнительные ID провайдера
У разных платёжных систем могут быть разные идентификаторы:
MiniShop3 использует external_id, но иногда одного идентификатора недостаточно для последующих API-вызовов.
Поэтому skeleton предусматривает сохранение дополнительных данных через:
Это позволяет не потерять идентификатор, который понадобится для refund, capture, sync или других операций.
Вкладка платежа в заказе
Ещё одна вещь, которую обычно приходится делать отдельно в каждом Extra, — интерфейс менеджера.
В skeleton уже подготовлена интеграция через: MS3OrderTabsRegistry
Вкладка может содержать:
То есть здесь тоже не нужно начинать с исследования внутренностей менеджера.
54-ФЗ
Если провайдер работает с чеками, skeleton уже содержит необходимую основу.
Предусмотрены:
Конкретный формат данных, конечно, адаптируется под API провайдера.
Безопасность
Секреты можно хранить через: msPayment.properties
Поддерживаются:
При этом секреты не должны попадать в payload событий.
В частности:
не должны сохраняться как данные платёжной попытки.
Документация внутри самого skeleton
Помимо кода, в репозитории есть отдельная документация:
Потому что «платёж создаётся» и «платёжное дополнение готово к публикации» — немного разные вещи.
Чек-лист перед релизом
Перед публикацией проверяем:
Получается довольно простая модель.
Было:
Стало:
Именно последняя часть и должна отличаться между платёжными дополнениями.
Кому это пригодится
Если вы собираетесь сделать новое платёжное дополнение для MiniShop3 — попробуйте начать с skeleton.
Особенно если это:
Что дальше
Это не попытка сделать ещё один «универсальный платёжный модуль».
Наоборот.
Идея в том, чтобы сделать небольшую и понятную стандартную основу для отдельных платёжных Extras.
Тогда:
Каждое дополнение остаётся самостоятельным и адаптируется под API своего провайдера, но общая архитектура становится гораздо более предсказуемой.
Репозиторий
→ GitHub: Ibochkarev/msp3PaymentSkeleton
Если делаете платёжный Extra для MiniShop3 — можно не начинать с нуля.
Если в процессе использования skeleton обнаружится сценарий, который приходится реализовывать одинаково в нескольких платёжных дополнениях, это как раз хороший кандидат для следующей версии шаблона.
Если вы хотя бы раз писали интеграцию с платёжным API, то знаете этот сценарий.
Сначала нужно разобраться с API провайдера.
Потом появляются webhook, подписи, попытки оплаты, внешний ID, возвраты, статусы, обработка ошибок, 54-ФЗ, настройки, вкладка в заказе, resolver, сборка…
И только после этого можно наконец заняться самой интеграцией с платёжной системой.
При этом значительная часть этой работы каждый раз практически одинаковая.
Поэтому я собрал msp3PaymentSkeleton — готовую основу, от которой можно начинать разработку нового payment Extra для MiniShop3.
Идея простая: не писать инфраструктуру платёжного дополнения заново, а сразу работать с API конкретного провайдера.Что это
msp3PaymentSkeleton — это не готовая интеграция с каким-то банком или агрегатором.
Это developer skeleton для создания таких интеграций.
Внутри уже подготовлены:
- Payment handler;
- API client;
- подписи запросов;
- webhook;
- payment lifecycle;
- payment attempts;
- payment link;
- refund;
- cancel;
- вкладка платежа в заказе;
- 54-ФЗ;
- настройки;
- логи;
- тестовый и production режимы;
- resolver;
- тесты;
- сборка пакета;
- документация;
- чек-лист перед релизом.
Почему вообще понадобился skeleton
В MiniShop3 постепенно появляется всё больше отдельных платёжных дополнений.
У каждого провайдера своё API:
- свои endpoint;
- свои способы авторизации;
- свои подписи;
- свои статусы;
- свой формат webhook;
- свои идентификаторы платежей;
- свои правила возврата.
Например:
Заказ
↓
Payment
↓
Payment Attempt
↓
Provider API
↓
Webhook
↓
Payment Lifecycle
↓
Статус платежа
↓
Статус заказаИ нет особого смысла реализовывать этот слой заново в каждом дополнении.
Главное изменение — lifecycle
В основе skeleton используется новый payment lifecycle MiniShop3.
Поэтому платёжное дополнение не должно самостоятельно управлять статусом заказа:
$order->set('status_id', 3);
$order->save();Вместо этого Extra сообщает lifecycle о событии:
paid
failed
cancelled
refunded
partially_refundedА MiniShop3 уже сам применяет соответствующую бизнес-логику.
Например:
paid
↓
ms3_status_paid
failed
↓
ms3_payment_on_failed_status
refunded
↓
ms3_payment_on_refunded_statusЭто важный момент.
Платёжный Extra отвечает за интеграцию с провайдером, а MiniShop3 — за жизненный цикл платежа и связанный с ним заказ.
Такой подход значительно проще поддерживать, когда количество платёжных интеграций растёт.
Что реально нужно написать под нового провайдера
После клонирования skeleton основная работа находится в нескольких местах.
В проекте они специально отмечены:
// PROVIDER:В первую очередь:
src/Api/ApiClient.php
src/Api/Signature.php
src/Api/WebhookParser.php
src/Payment/SkeletonPayment.php
src/Service/Settings.phpApiClient — API провайдера.
Здесь находятся:
- host;
- endpoint создания платежа;
- refund;
- cancel;
- HTTP-запросы;
- headers;
- body;
- обработка ответа.
Если у провайдера HMAC-SHA256 — уже есть базовый вариант.
Если другая схема — меняется этот слой.
WebhookParser — перевод событий провайдера в события MiniShop3.
Например:
SUCCESS
↓
paid
FAIL
↓
failed
REFUND
↓
refundedSkeletonPayment — адаптация самого платежа к контракту MiniShop3.
В частности, здесь собирается запрос:
buildPaymentRequest()А send() возвращает:
payment_link
payment_id
external_id
currencyБыстрый старт
Например, нужно сделать msp3MyPay.
Клонируем skeleton:
cp -R msp3PaymentSkeleton ../msp3MyPay
cd ../msp3MyPayЗапускаем генератор:
php bin/init.php \
--name=msp3MyPay \
--provider=MyPayИ получаем уже свой проект.
Дополнительные части можно оставить через --keep:
php bin/init.php \
--name=msp3MyPay \
--provider=MyPay \
--keep=second-method,bindings,receipts-extraЕсть также:
--no-encrypt — для локальной сборки без шифрования.
--self-destruct — удалить bin/init.php после успешной инициализации.
Webhook — уже не «прикрутим потом»
В платёжных интеграциях webhook часто становится самым проблемным местом.
Платёж создан — это только начало.
Дальше провайдер может:
- прислать подтверждение оплаты;
- прислать ошибку;
- прислать отмену;
- прислать возврат;
- повторить одно и то же событие несколько раз.
Стандартный endpoint:
POST /assets/components/minishop3/api.php/api/v1/payment/webhook/{payment_method_id}Дальше:
raw body
↓
verifyWebhook()
↓
parseWebhook()
↓
PaymentWebhookEvent
↓
ms3_payment_lifecycle
↓
payment attemptПри этом verifyWebhook() должен действительно проверять подпись.
Заглушка: return true; не является реализацией webhook security.
Идемпотентность
Повтор webhook — обычная ситуация.
Например:
Provider
│
├── webhook #123
│
├── timeout
│
└── webhook #123 повторноПовторная обработка одного и того же providerEventId не должна ломать заказ.
Поэтому идемпотентность вынесена в общий сценарий lifecycle, а при разработке конкретного провайдера необходимо корректно сопоставить его идентификатор события.
А если у провайдера только один webhook URL?
Такое тоже встречается.
Некоторые API дают один webhook URL на весь магазин, а некоторые отправляют вообще не JSON, а form POST.
Для этого в skeleton предусмотрен отдельный: webhook.php
Он позволяет адаптировать нестандартный входящий webhook, не ломая основной payment API MiniShop3.
Возвраты
Возврат также проходит через lifecycle.
Вместо:
$order->set('status_id', ...);используется:
lifecycle->refund()И учитывается состояние payment attempt.
Например, нельзя считать заказ оплаченным только потому, что был создан платёж.
Сначала:
pending
↓
paid
↓
refundА не:
payment created
↓
refundЭто особенно важно для API, где создание платежа и фактическое списание денег — разные операции.
Дополнительные ID провайдера
У разных платёжных систем могут быть разные идентификаторы:
operationId
mdOrder
invoice_id
paymentId
transactionIdMiniShop3 использует external_id, но иногда одного идентификатора недостаточно для последующих API-вызовов.
Поэтому skeleton предусматривает сохранение дополнительных данных через:
lifecycle->initiate()Это позволяет не потерять идентификатор, который понадобится для refund, capture, sync или других операций.
Вкладка платежа в заказе
Ещё одна вещь, которую обычно приходится делать отдельно в каждом Extra, — интерфейс менеджера.
В skeleton уже подготовлена интеграция через: MS3OrderTabsRegistry
Вкладка может содержать:
- статус платежа;
- external ID;
- сумму;
- информацию от провайдера;
- операции возврата;
- отмену;
- синхронизацию.
То есть здесь тоже не нужно начинать с исследования внутренностей менеджера.
54-ФЗ
Если провайдер работает с чеками, skeleton уже содержит необходимую основу.
Предусмотрены:
- включение/отключение чека;
- НДС;
- признак способа расчёта;
- предмет расчёта доставки.
Конкретный формат данных, конечно, адаптируется под API провайдера.
Безопасность
Секреты можно хранить через: msPayment.properties
Поддерживаются:
secret
secret_key
webhook_secretПри этом секреты не должны попадать в payload событий.
В частности:
secret
token
api_key
passwordне должны сохраняться как данные платёжной попытки.
Документация внутри самого skeleton
Помимо кода, в репозитории есть отдельная документация:
- MS3-INTEGRATION.md — что именно ожидает MiniShop3;
- ARCHITECTURE.md — как устроен поток платежа;
- PROVIDER-PORTING.md — как адаптировать API нового провайдера;
- CHECKLIST.md — что проверить перед релизом.
Потому что «платёж создаётся» и «платёжное дополнение готово к публикации» — немного разные вещи.
Чек-лист перед релизом
Перед публикацией проверяем:
- send() возвращает необходимые данные;
- реализован PaymentWebhookHandlerInterface;
- webhook действительно проверяет подпись;
- секрет находится в msPayment.properties;
- нет ручного изменения status_id;
- webhook переводится в PaymentAttemptStatus;
- повторный webhook безопасен;
- корректно обрабатываются рубли/копейки;
- 54-ФЗ не отправляется без email;
- дополнительные ID провайдера сохраняются;
- возврат выполняется через lifecycle;
- вкладка заказа регистрируется один раз;
- проверяются права доступа;
- проходят тесты;
- проходит PHP syntax check;
- пакет собирается;
- после генерации нигде не остаётся имя skeleton.
- MODX Revolution 3.0+
- MiniShop3 >= 1.14.0-beta1
- PHP 8.2+
Получается довольно простая модель.
Было:
Новый платёжный Extra
│
├── Payment
├── API
├── Webhook
├── Attempts
├── Lifecycle
├── Refund
├── Manager UI
├── 54-ФЗ
├── Settings
├── Resolver
└── BuildСтало:
msp3PaymentSkeleton
│
├── Payment ✓
├── Attempts ✓
├── Lifecycle ✓
├── Webhook infrastructure ✓
├── Refund ✓
├── Manager UI ✓
├── 54-ФЗ ✓
├── Settings ✓
├── Resolver ✓
└── Build ✓
│
▼
PROVIDER
│
├── API endpoints
├── Signature
├── Request format
└── Webhook formatИменно последняя часть и должна отличаться между платёжными дополнениями.
Кому это пригодится
Если вы собираетесь сделать новое платёжное дополнение для MiniShop3 — попробуйте начать с skeleton.
Особенно если это:
- банк;
- платёжный агрегатор;
- СБП;
- BNPL-сервис;
- корпоративный платёжный шлюз;
- локальная платёжная система;
- внутренний API компании.
Что дальше
Это не попытка сделать ещё один «универсальный платёжный модуль».
Наоборот.
Идея в том, чтобы сделать небольшую и понятную стандартную основу для отдельных платёжных Extras.
Тогда:
MiniShop3
│
├── msp3TBank
├── msp3Sberbank
├── msp3PayKeeper
├── msp3CloudPayments
├── msp3CDEKPay
└── ...
▲
│
единый подход
│
msp3PaymentSkeletonКаждое дополнение остаётся самостоятельным и адаптируется под API своего провайдера, но общая архитектура становится гораздо более предсказуемой.
Репозиторий
→ GitHub: Ibochkarev/msp3PaymentSkeleton
Если делаете платёжный Extra для MiniShop3 — можно не начинать с нуля.
Если в процессе использования skeleton обнаружится сценарий, который приходится реализовывать одинаково в нескольких платёжных дополнениях, это как раз хороший кандидат для следующей версии шаблона.
Меньше копипаста между платёжными Extras — больше времени на нормальную интеграцию с самим API.
Техническая поддержка MODX
Сайт лежит, тормозит или остался без разработчика?
Переезд с MODX 2 на 3, PHP 7 на 8, скорость и безопасность. Поддержка со сроками и ответственностью, а не совет в чате.
Подробнее
Реклама
0
Комментарии: 0