Новый модуль для Medusa: плагины просто объявляют свои настройки, а администраторы управляют ими в Admin, без правок medusa-config и без передеплоя.

Мы в Gorgo более года делаем плагины для Medusa, которые подключают магазины к платёжным провайдерам, службам фулфилмента и ERP-системам. И в каждом всплывал один и тот же вопрос: как передавать плагину параметры и как ими управлять. API-ключи, настройки подключений и другая пользовательская конфигурация обычно задаются через и переменные окружения, поэтому любое изменение требует правки кода и передеплоя. Чтобы этого избежать, для каждого плагина приходится строить собственный UI, пробрасывать API, валидировать поля и писать воркфлоу.
Сегодня мы представляем Модуль интеграций для Medusa, который избавляет от этой рутины. Плагины просто объявляют свои параметры через провайдер модуля, а администратор магазина управляет ими в Admin через сгенерированный интерфейс, без правок и без передеплоя.
Настройка плагина Medusa обычно сводится к двум проблемам, и обе сложнее, чем должны быть.
Модуль интеграций выносит настройки плагина из кода в Admin и базу данных. Плагин описывает нужные ему параметры, администратор магазина задаёт их в разделе Настройки → Интеграции, а во время выполнения плагин читает готовые и провалидированные значения.
Как автор, вы только описываете схему настроек, а UI, хранение, шифрование и валидацию модуль берёт на себя. Если же плагину нужно что-то, чего сгенерированный UI не поддерживает, можно создать кастомный виджет.
Вы объявляете дескриптор через , где перечислены ваши опции и их типы, секции, в которые они группируются, и правила валидации. Из этого одного объявления Admin генерирует страницу настроек: подходящий UI-элемент управления для каждого поля, валидацию, условное отображение и маскированные секреты. Вам не нужно писать ни UI, ни API для CRUD-операций, ни связанные воркфлоу.
Минимальный провайдер состоит из дескриптора и класса, который его поставляет:
1import { AbstractIntegrationProvider, defineIntegration } from "@gorgo/medusa-integration"2
3const descriptor = defineIntegration({4 category: "payment",5 displayName: "acme.name",6 supportsMultipleInstances: true,7 8 options: {9 apiKey: {10 type: "string",11 required: true,12 secret: true,13 label: "acme.fields.apiKey",14 },15 mode: {16 type: "enum",17 values: ["test", "live"],18 default: "test",19 label: "acme.fields.mode",20 },21 },22
23 sections: [24 {25 id: "credentials",26 title: "acme.sections.credentials",27 options: ["apiKey", "mode"]28 },29 ],30
31 testConnection: async ({ options }) => {32 const ok = await pingAcme(options.apiKey, options.mode)33 return ok ? { status: "passed" } : { status: "failed", message: "Invalid API key" }34 },35})36
37export class AcmeIntegrationProvider extends AbstractIntegrationProvider {38 static identifier = "acme"39 get descriptor() {40 return descriptor41 }42}Значения вроде являются i18n-ключами и переводятся стандартными средствами локализации Medusa, а сами переводы хранятся в .
Далее, зарегистрируйте провайдер в :
1const ACME_INTEGRATION_ID = "acme-1"2
3module.exports = defineConfig({4 plugins: [5 {6 resolve: "@acme/medusa-payment-acme",7 options: {},8 },9 {10 resolve: "@gorgo/medusa-integration",11 options: {12 encryptionKey: process.env.INTEGRATION_ENCRYPTION_KEY,13 providers: [14 {15 resolve: "@acme/medusa-payment-acme/providers/integration-acme",16 id: ACME_INTEGRATION_ID,17 },18 ],19 },20 },21 ],22 modules: [23 {24 resolve: "@medusajs/medusa/payment",25 options: {26 providers: [27 {28 resolve: "@acme/medusa-payment-acme/providers/payment-acme",29 id: "acme",30 options: {31 id: ACME_INTEGRATION_ID // совпадает с id провайдера интеграции выше32 },33 },34 ],35 },36 },37 ],38})Здесь задаёт ключ экземпляра интеграции. Тот же нужно передать потребителю, в примере это платёжный провайдер. Именно по нему модуль находит и возвращает значения параметров нужной интеграции. Эти два задаются в независимых местах конфигурации, поэтому синхронизировать их нужно вручную, и это удобнее делать через константу.
(здесь ) задаёт ключ AES-256-GCM, которым модуль шифрует секреты в базе. Задайте его в переменных окружения и не меняйте, иначе сохранённые секреты станут нечитаемыми.
Во время работы плагин читает значения своих параметров где угодно через и получает типизированный расшифрованный объект. Неполные или отключённые настройки не резолвятся, поэтому незаполненный черновик не будет возвращён.
1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"2
3abstract class AcmeBase extends AbstractPaymentProvider {4 constructor(options) {5 super(options)6 const { apiKey, mode } = await resolveIntegrationOptions({7 identifier: "acme",8 instance_id: options.id9 })10 }11
12 // ...13}Один дескриптор даёт вашему плагину полноценный интерфейс настроек:
Если вы уже поддерживаете плагин для Medusa, переход на Модуль интеграций сводится к простому переносу существующих опций в дескриптор провайдера модуля. А читать их нужно через там, где сейчас вы читаете переменные окружения или . Все свои провайдеры мы мигрировали именно так (платежи, фулфилмент и ERP), а полный пример приложения Medusa со всеми подключёнными провайдерами можно посмотреть в репозитории.
Пошаговое руководство проводит через весь процесс миграции.
Модуль также отображает каталог доступных интеграций прямо в Admin. Администраторы магазина могут найти ваш плагин и установить его с помощью небольшой инструкции. А поскольку UI настроек генерируется, ваш плагин выглядит аккуратно и нативно, в одном стиле с Medusa Admin.
Установите модуль:
npm install @gorgo/medusa-integrationДалее объявите настройки вашего плагина и другие метаданные, подключите его в качестве провайдера Модуля интеграций и переключите на чтение параметров через . Полное руководство по установке и использованию описано в документации, а исходники на GitHub.
Модуль интеграций сейчас в бете, и мы активно его развиваем, добавляем встроенные интеграции и расширяем каталог. Если вы разрабатываете плагин для Medusa, мы поможем перейти на модуль. Если у вас магазин на Medusa, попробуйте его и расскажите, чего не хватает.
Присоединяйтесь к обсуждению в Telegram-сообществе Medusa. Issues и Pull Request'ы всегда приветствуются в репозитории на GitHub.