Search for a command to run...
В этой главе вы узнаете о Модуле интеграций и о том, как его использовать.
Модуль интеграций можно применять в самых разных сценариях. Он не зависит от типа провайдера, и ему всё равно, для чего нужны параметры.
Если администратор магазина должен уметь управлять настройкой, её можно сделать через Модуль интеграций.
В интеграции участвуют две роли разработчика, а между ними находится администратор магазина.
Провайдер и потребитель обычно живут в одном пакете. Платёжный плагин объявляет свои учётные данные, затем считывает их обратно в своей платёжной логике. Потребителем может быть и посторонний код, например API-роут, подписчик или запланированная задача, которым нужны параметры настроенной интеграции.
Дескриптор представляет собой единое объявление метаданных интеграции, её параметров, секций настроек, валидации и необязательной проверки соединения. Создаётся он через .
Вот дескриптор платёжного провайдера с двумя параметрами и одной секцией:
providers/integration-my/services/my-integration.ts1import { defineIntegration, AbstractIntegrationProvider } from "@gorgo/medusa-integration"23const descriptor = defineIntegration({4 category: "payment",5 displayName: "my.name",6 options: {7 apiKey: {8 type: "string",9 control: "secret",10 secret: true,11 required: true,12 label: "my.apiKey"13 },14 sandbox: {15 type: "boolean",16 control: "switch",17 default: false,18 label: "my.sandbox"19 },20 },21 sections: [22 {23 id: "credentials",24 title: "my.credentials",25 options: ["apiKey", "sandbox"]26 },27 ],28})2930export class MyIntegration extends AbstractIntegrationProvider {31 static identifier = "my"3233 get descriptor() {34 return descriptor35 }36}
Дескриптор служит единым источником истины для интеграции: в этом примере он объявляет и , сгруппированные в одну секцию . По нему модуль генерирует CRUD API, валидирует запись, отрисовывает UI настроек и определяет, что потребители получают во время выполнения. Вам не нужно писать ни модели данных, ни API-роуты, ни UI-формы.
Инстанс представляет собой отдельную регистрацию провайдера интеграции с собственной конфигурацией. Один и тот же провайдер можно зарегистрировать несколько раз, и каждая регистрация даёт самостоятельный инстанс со своими настройками в Admin.
Каждый инстанс интеграции адресуется по вида:
В подставляется провайдера, общий для всех его инстансов. берётся из конкретной регистрации и присутствует только у именованных инстансов.
У каждой интеграции есть стабильный идентификатор, например , заданный как в провайдере:
providers/integration-my/services/my-integration.ts1import { defineIntegration, AbstractIntegrationProvider } from "@gorgo/medusa-integration"23// ...45export class MyIntegration extends AbstractIntegrationProvider {6 static identifier = "my"78 // ...9}
Все инстансы провайдера используют общий . Он всегда . Различает инстансы только в .
Это вариант по умолчанию. У него нет ID инстанса (), поэтому его выглядит, например, как .
Зарегистрируйте его без :
medusa-config.ts1module.exports = defineConfig({2 // ...3 plugins: [4 {5 resolve: "@gorgo/medusa-integration",6 options: {7 // ...8 providers: [9 {10 resolve: "@gorgo/medusa-payment-my/providers/integration-my",11 options: {},12 },13 ],14 },15 },16 ],17})
Без в записи регистрации провайдер получает единственный, безымянный инстанс с ключом .
Зарегистрируйте один и тот же провайдер несколько раз, каждый со своим . Каждый инстанс настраивается независимо, например для поддержки нескольких аккаунтов интегрируемого сервиса.
Например, две независимые регистрации одного и того же провайдера:
medusa-config.ts1module.exports = defineConfig({2 // ...3 plugins: [4 {5 resolve: "@gorgo/medusa-integration",6 options: {7 // ...8 providers: [9 {10 resolve: "@gorgo/medusa-payment-my/providers/integration-my",11 id: "eu",12 options: {},13 },14 {15 resolve: "@gorgo/medusa-payment-my/providers/integration-my",16 id: "us",17 options: {},18 },19 ],20 },21 },22 ],23})
Это даст и соответственно. задаётся на верхнем уровне записи. Если случайно поместить его внутрь , провайдер зарегистрируется под инстансом по умолчанию (безымянным) без предупреждения.
Потребитель получает параметры по идентификатору и инстансу, поэтому один и тот же код может обращаться к нужному.
Параметры интеграции движутся по предсказуемому пути. Автор объявляет их в дескрипторе, администратор настраивает, модуль валидирует, они становятся активными, как только интеграция включена и заполнена, а потребитель получает их во время выполнения. По пути проверка соединения может сверить их со сторонним сервисом.
Каждый параметр в представляет собой объект вида . Поле определяет доступные значения и остальные поля, общие для всех типов:
У некоторых типов есть свои поля сверх общих:
Модуль генерирует CRUD API из дескриптора и валидирует каждую запись. Валидация покрывает правила на уровне отдельного параметра (типы, диапазоны, паттерны) вместе с правилами между секциями, охватывающими всю конфигурацию. Ничего писать под каждую интеграцию не нужно.
Правила между секциями объявляются через дескриптора. В отличие от отдельного параметра, он получает всю собранную конфигурацию и может пометить проблемой любое поле:
providers/integration-my/services/my-integration.ts1const descriptor = defineIntegration({2 // ...3 validate: (full, { addIssue }) => {4 if (full.mode === "webhook" && !full.webhookSecret) {5 addIssue({ path: ["webhookSecret"], message: "Required when mode is \"webhook\"" })6 }7 },8})
В отличие от или конкретного параметра, это правило запускается только при полной валидации (проверке готовности интеграции к включению), а не при сохранении отдельной секции.
Параметры становятся активными, только когда интеграция и включена, и заполнена, то есть проходит полную валидацию. Незавершённый черновик или выключенная интеграция никогда не отдают параметры, поэтому недоделанная конфигурация не может попасть в работающее приложение.
Параметры с пометкой шифруются при хранении алгоритмом AES-256-GCM и никогда не попадают в браузер. CRUD API маскирует их и сообщает лишь, задано значение или нет. Сохранение секрета пустым оставляет прежнее хранимое значение, а не стирает его.
Для шифрования модулю нужен ключ. Задайте его как при регистрации модуля в :
medusa-config.ts1module.exports = defineConfig({2 // ...3 plugins: [4 {5 resolve: "@gorgo/medusa-integration",6 options: {7 encryptionKey: process.env.INTEGRATION_ENCRYPTION_KEY,8 providers: [9 // ...10 ],11 },12 },13 ],14})
Модуль вычисляет этот ключ один раз, при первом обращении, и затем использует его для секретных полей всех зарегистрированных интеграций. Задайте то же значение как переменную окружения:
.envINTEGRATION_ENCRYPTION_KEY=supersecret
Ключом может быть любая непустая строка, но для продакшена подойдёт высокоэнтропийное значение, например . Без него модуль выбрасывает ошибку при первой попытке сохранить или получить параметры интеграции, у которой есть хотя бы один параметр с .
Дескриптор может объявить необязательную проверку соединения, которая сверяет учётные данные со сторонним сервисом. Администраторы запускают её по требованию, а запланированная задача ежедневно перепроверяет настроенные интеграции.
Добавьте в дескриптор. Он получает уже итоговые (расшифрованные) параметры и возвращает статус:
providers/integration-my/services/my-integration.ts1const descriptor = defineIntegration({2 // ...3 testConnection: async ({ options }) => {4 const res = await fetch("https://api.my.com/ping", {5 headers: { Authorization: `Bearer ${options.apiKey}` },6 })78 if (!res.ok) {9 return { status: "failed", message: `My responded with ${res.status}` }10 }1112 return { status: "passed" }13 },14})
принимает одно из значений: , или ; необязательное отображается в Admin рядом с результатом проверки.
Потребители читают типизированные, проверенные и расшифрованные параметры с применёнными значениями по умолчанию из дескриптора. Незаполненная или выключенная интеграция возвращает пустой результат, а не частичные данные. Итоговые параметры кэшируются ненадолго и обновляются при каждом изменении конфигурации.
Модуль генерирует UI интеграции из её дескриптора, поэтому строить страницы не нужно.
Параметры группируются в секции настроек и рендерятся через LayoutComposer Medusa в виде карточек с изменяемым порядком, в одну или две колонки.
Когда сгенерированного UI недостаточно, вы можете построить для своих параметров любой интерфейс на собственных admin-виджетах. Это обычные admin-виджеты Medusa на том же механизме и зон внедрения, что вы уже используете в других местах, так что учить ничего нового не придётся. Модуль предоставляет зоны внедрения на странице каждой интеграции (например, ), и виджет, нацеленный на такую зону, получает готовый и напрямую читает и пишет параметры интеграции.
Метки, подсказки и заголовки в дескрипторе задаются как i18n-ключи. Поставляйте переводы вместе со своей интеграцией, и UI настроек локализуется автоматически.
Всё это построено на стандартном i18n Medusa, так что учить ничего нового не нужно. Регистрируйте переводы привычным способом, и ключи подставляются по активному языку Admin с откатом на английский, если ключ отсутствует. Собственные виджеты локализуются так же, используя тот же каталог сообщений, что и сгенерированная форма.
По умолчанию:
По умолчанию: