Медуза — это платформа безголовой коммерции с открытым исходным кодом, ориентированная на разработчиков. Его можно использовать для создания полноценных интернет-магазинов. Он имеет множество необходимых функций электронной коммерции, включая автоматизированные потоки RMA, интеграцию plug-and-play, управление продуктами и заказами и многое другое.

В этом уроке вы узнаете, как создать магазин комиксов с помощью Medusa. Вы также добавите в свой магазин важные функции электронной коммерции, включая поисковую систему с помощью MeiliSearch и платежную систему с помощью PayPal.

Вы можете найти полный код этого руководства в этом репозитории GitHub.

Обзор архитектуры

Прежде чем приступить к работе с руководством, вот краткий обзор архитектуры Medusa на случай, если вы с ней не знакомы. Вы можете перейти к следующему разделу, если хотите.

Медуза состоит из 3 основных компонентов:

  1. Безголовый сервер — это ядро ​​вашего интернет-магазина. Он берет на себя всю логику, функции электронной коммерции и данные. Все остальные компоненты подключаются к серверу с помощью REST API.
  2. Medusa Admin — это пользовательский интерфейс, который операторы магазинов могут использовать для просмотра и управления данными своего магазина (например, продуктами и заказами). Medusa предоставляет интуитивно понятную готовую панель администратора, которую вы можете использовать. Кроме того, вы можете создать свой собственный и подключиться к серверу с помощью REST API.
  3. Витрина – это интернет-магазин, где покупатели просматривают товары и совершают покупки. Medusa предоставляет две стартовые витрины, одна из которых создана с помощью Next.js, а другая — с Gatsby. Вы также можете создать витрину с любой платформой по вашему выбору и подключиться к серверу с помощью REST API.

В этом руководстве вы узнаете о настройке каждого из них и о том, как их использовать.

Предпосылки

Прежде чем начать, вам потребуются следующие установленные требования:

  1. Узел v14 или выше.
  2. Создан Постгрес с пустой базой данных.
  3. MeiliSearch для поисковой системы.
  4. Аккаунт разработчика PayPal.
  5. MinIO для хранения файлов. В качестве альтернативы вы можете использовать S3 или DigitalOcean Spaces.

Установить сервер

Чтобы установить сервер Medusa, вам необходимо сначала установить интерфейс командной строки Medusa:

npm install -g @medusajs/medusa-cli

Затем выполните следующую команду, чтобы установить сервер Medusa в новый каталог comic-store:

medusa new comic-store

Установить плагины

Следующим шагом будет установка плагинов, которые вы будете использовать на своем сервере Medusa. Для этого руководства вам понадобятся плагины для интеграции PayPal, MeiliSearch и MinIO.

Запустите следующую команду в каталоге comic-store, чтобы установить 3 плагина:

npm install medusa-file-minio medusa-plugin-meilisearch medusa-payment-paypal

Замените medusa-file-minio файловой службой, которую вы используете, если это не MinIO.

Убедитесь, что в package.json версии для @medusajs/medusa, medusa-interfaces и @medusajs/medusa-cli больше или равны 1.3.0. Если нет, обновите их с помощью следующей команды:

npm install @medusajs/medusa@latest medusa-interfaces@latest @medusajs/medusa-cli@latest

Добавьте переменные среды

Medusa дает вам свободу управлять переменными среды на основе вашего сервера. В этом руководстве вы добавите все переменные среды в переменную .env.

Откройте файл .env. Добавьте следующие переменные:

#PostgreSQL Database URL
DATABASE_URL=
#MinIO configurations
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
MINIO_BUCKET=
MINIO_SERVER=
#PayPal Configurations
PAYPAL_SANDBOX=true
PAYPAL_CLIENT_ID=
PAYPAL_CLIENT_SECRET=
PAYPAL_AUTH_WEBHOOK_ID=
#MeiliSearch Configurations
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_API_KEY=

Эти переменные среды важны для конфигураций, связанных с базой данных, MinIO, PayPal и MeiliSearch.

DATABASE_URL — это URL-адрес для подключения к вашей схеме базы данных PostgreSQL. Он должен быть в формате postgres://<USERNAME>:<PASSWORD>@<HOST>/<DB_NAME>.

Вы можете обратиться к нашей документации, чтобы узнать, как получить необходимые переменные для MinIO и MeiliSearch.

Если вы используете файловую службу, отличную от MinIO, обратитесь к документации вашей файловой службы, чтобы узнать, какие переменные вам нужны.

Для PayPal вы можете обратиться к документации PayPal, чтобы получить идентификатор клиента, секрет клиента и идентификатор веб-перехватчика. Вы также включили режим песочницы для тестирования, установив PAYPAL_SANDBOX на true.

Настроить сервер

Вам необходимо передать эти переменные среды в конфигурации сервера.

Все конфигурации сервера находятся в medusa-config.js. Это включает в себя базу данных, плагины и другие конфигурации.

Откройте medusa-config.js. Добавьте в начало файла следующее:

const dotenv = require('dotenv');
let ENV_FILE_NAME = '';
switch (process.env.NODE_ENV) {
	case 'prod':
		ENV_FILE_NAME = '.env';
		break;
	case 'test':
		ENV_FILE_NAME = '.env.test';
		break;
	default:
		ENV_FILE_NAME = '.env';
		break;
}
dotenv.config({ path: process.cwd() + '/' + ENV_FILE_NAME });

Это позволяет вам загружать переменные среды из файла .env.

Затем в массив plugins добавьте в конец массива следующие 3 плагина:

const plugins = [
  //...
  {
    resolve: `medusa-payment-paypal`,
    options: {
      sandbox: process.env.PAYPAL_SANDBOX,
      client_id: process.env.PAYPAL_CLIENT_ID,
      client_secret: process.env.PAYPAL_CLIENT_SECRET,
      auth_webhook_id: process.env.PAYPAL_AUTH_WEBHOOK_ID
    }
  },
  {
    resolve: `medusa-file-minio`,
    options: {
        endpoint: process.env.MINIO_SERVER,
        bucket: process.env.MINIO_BUCKET,
        access_key_id: process.env.MINIO_ACCESS_KEY,
        secret_access_key: process.env.MINIO_SECRET_KEY,
    }
  },
  {
    resolve: `medusa-plugin-meilisearch`,
    options: {
      config: {
        host: process.env.MEILISEARCH_HOST,
        apiKey: process.env.MEILISEARCH_API_KEY
      },
      settings: {
        products: {
          searchableAttributes: ["title", "description", "variant_sku"],
          displayedAttributes: ["title", "description", "variant_sku"],
        },
      },
    },
  }
];

Это загружает 3 плагина, которые вы установили ранее, и передает необходимые параметры для каждого.

Наконец, измените конфигурации базы данных в projectConfig в экспортируемой функции, чтобы использовать вашу базу данных PostgreSQL вместо базы данных SQLite:

module.exports = {
  projectConfig: {
    //...
    database_url: DATABASE_URL,
    database_type: "postgres",
		//**comment out or remove these lines:**
    // database_database: "./medusa-db.sql",
    // database_type: "sqlite",
  },
	//...
};

Перенос и заполнение базы данных

Последним шагом перед запуском сервера является миграция и заполнение базы данных. Миграция означает добавление необходимых таблиц в схему вашей базы данных, чтобы она работала с Medusa.

Заполнение означает добавление фиктивных данных в вашу базу данных, чтобы быстро приступить к работе.

Выполните следующую команду для переноса и заполнения вашей базы данных:

npm run seed

Это подключится к вашей базе данных, используя URL-адрес, который вы передали в переменную среды DATABASE_URL. Перед выполнением этой команды убедитесь, что вы уже создали базу данных.

Запустить сервер

Убедитесь, что службы MeiliSearch и MinIO запущены. Затем запустите сервер с помощью следующей команды:

npm start

Это запустит ваш сервер на порту 9000. Вы должны поддерживать работу сервера на протяжении всего обучения, так как администратор Medusa и витрина зависят от сервера.

Настройка администратора Медузы

В этом разделе вы установите Medusa Admin, добавите в него продукты и активируете PayPal в качестве поставщика платежей.

Установить администратора

В вашем терминале и в каталоге, отличном от каталога comic-store, выполните следующую команду:

git clone <https://github.com/medusajs/admin> comic-admin

Затем перейдите во вновь созданный каталог comic-admin и установите необходимые зависимости:

cd comic-admin
npm install

Убедитесь, что сервер Medusa все еще работает. Затем выполните следующую команду, чтобы запустить администратора:

npm start

Это запустит вашу админку Medusa на порту 7000 по умолчанию. Откройте его в своем браузере, и вы должны увидеть экран входа в систему.

Вы можете использовать адрес электронной почты по умолчанию «[email protected]» и пароль «supersecret» для входа в систему.

Добавить продукты

После входа в систему выберите на боковой панели «Продукты». Вы увидите несколько продуктов, которые были добавлены при заполнении базы данных.

Идите вперед и удалите их, нажав на 3 точки для каждого, а затем Удалить.

Затем добавьте продукты в свой магазин комиксов, нажав кнопку «Новый продукт» в правом верхнем углу.

Вам необходимо заполнить поля, связанные с информацией о продукте.

Добавьте столько продуктов, сколько хотите, прежде чем перейти к следующему шагу.

Включить PayPal

Чтобы включить PayPal в качестве поставщика платежей, нажмите «Настройки», затем выберите «Регионы».

Для каждого региона, в который вы хотите добавить PayPal в качестве поставщика платежей, нажмите «Поставщики платежей» и выберите «paypal», затем нажмите «Сохранить».

Настройка витрины

Последним шагом будет установка витрины. В этом разделе рассматривается установка витрины магазина Gatsby, внесение в нее некоторых настроек, добавление панели MeiliSearch и добавление пользовательского интерфейса для PayPal.

Установить витрину

В вашем терминале и в каталоге, отличном от каталогов comic-store и comic-admin, выполните следующую команду:

gatsby new comic-storefront <https://github.com/medusajs/gatsby-starter-medusa>

Это установит витрину Gatsby в новый каталог comic-storefront.

Затем перейдите в каталог comic-storefront и переименуйте .env.template в .env.development:

mv .env.template .env.development

Добавьте переменные среды

Вам нужно добавить переменные среды, чтобы использовать MeiliSearch и PayPal на вашей витрине. В .env.development добавьте следующие переменные:

#MeiliSearch Configurations
GATSBY_MEILISEARCH_HOST=
GATSBY_MEILISEARCH_API_KEY=
#PayPal Configurations
GATSBY_PAYPAL_CLIENT_ID=

Значения для этих конфигураций такие же, как те, которые вы использовали на своем сервере.

Запустите витрину магазина Гэтсби

Убедитесь, что сервер Medusa запущен. Затем выполните следующую команду, чтобы запустить витрину Gatsby:

npm start

Это запустит вашу витрину на localhost:8000. Откройте его в своем браузере. Вы должны увидеть изображение героя и продукты, которые вы добавили.

Настроить витрину

Баннер героя является статическим и добавляется в код. Теперь вы настроите его, чтобы показать что-то, связанное с вашим магазином комиксов.

Откройте src/pages/index.js. Вы должны найти в возвращенном JSX компонент StaticImage, за которым следует div. Измените их на следующие:

<StaticImage
  src="../images/hero.png"
  alt="A black Medusa hoodie and a white Medusa coffee mug"
  placeholder="tracedSVG"
  className="w-full lg:w-1/2 h-auto lg:my-5"
/>
<div className="lg:ml-7">
  <h1 className="text-4xl">The Best Comic Books</h1>
  <p className="mt-2 text-lg font-normal">
    Buy the best Marvel and DC Comic Books!
  </p>
</div>

Это изменяет используемый текст и изображение. Скачать новое изображение можно здесь. Поместите его в src/images с именем hero.png.

Если вы сейчас откроете витрину магазина, вы должны увидеть обновленное изображение главного героя.

Добавить панель поиска

В этом разделе вы добавите панель поиска для поиска продуктов с помощью MeiliSearch.

В терминале выполните следующую команду, чтобы установить необходимые зависимости:

npm install react-instantsearch-dom @meilisearch/instant-meilisearch

Затем создайте файл src/components/header/search.jsx со следующим содержимым:

import {
  Highlight,
  Hits,
  InstantSearch,
  SearchBox,
  connectStateResults
} from "react-instantsearch-dom"
import React from "react"
import { instantMeiliSearch } from "@meilisearch/instant-meilisearch"
const searchClient = instantMeiliSearch(
  process.env.GATSBY_MEILISEARCH_HOST,
  process.env.GATSBY_MEILISEARCH_API_KEY
)
const Search = () => {
  const Results = connectStateResults(({ searchState, searchResults, children }) =>
    searchState && searchState.query && searchResults && searchResults.nbHits !== 0 ? (
      <div className="absolute top-full w-full p-2 bg-gray-200 shadow-md">
        {children}
      </div>
    ) : (
      <div></div>
    )
  );
  return (
    <div className="relative">
      <InstantSearch indexName="products" searchClient={searchClient}>
        <SearchBox submit={null} reset={null} />
        <Results>
          <Hits hitComponent={Hit} />
        </Results>
      </InstantSearch>
    </div>
  )
}
const Hit = ({ hit }) => {
  return (
    <div key={hit.id} className="relative">
      <div className="hit-name">
        <Highlight attribute="title" hit={hit} tagName="mark" />
      </div>
    </div>
  )
}
export default Search;

При этом создается клиент поиска с использованием метода instantMeiliSearch, который экспортируется из зависимости @meilisearch/instant-meilisearch, которую вы только что установили. Вы передаете методу переменные среды, которые вы добавили ранее для конфигураций.

Затем компонент Search отображает панель поиска, используя компоненты из react-instantsearch-dom. Когда пользователь вводит запрос и есть результаты, каждый результат отображается с использованием компонента Hit.

Если вы хотите узнать больше о том, как настроить пользовательский интерфейс панели поиска и ее параметры, вы можете ознакомиться с документацией React InstantSearch by Algolia.

Далее вы добавите панель поиска на панель навигации. Для этого откройте index.jsx и импортируйте компонент Search в начало файла:

import Search from "./search"

Затем в возвращенном JSX добавьте компонент Search перед RegionPopover:

//...
<Search />
<RegionPopover regions={mockData.regions} />
//...

Сохраните все изменения и откройте витрину прямо сейчас. Вы должны увидеть строку поиска на панели навигации. Попробуйте ввести название одного из ваших продуктов, и вы должны увидеть его в результате.

Добавить пользовательский интерфейс PayPal

В этом разделе вы добавите пользовательский интерфейс, необходимый для использования PayPal в качестве способа оплаты.

В своем терминале используйте следующую команду для установки библиотеки PayPal React:

npm install @paypal/react-paypal-js

Затем создайте файл src/components/payment/paypal-payment/index.jsx со следующим содержимым:

import { PayPalButtons, PayPalScriptProvider } from "@paypal/react-paypal-js";
import React, { useMemo, useState } from "react";
import { navigate } from "gatsby"
import { useCart } from "../../../hooks/use-cart"
import { useMedusa } from "../../../hooks/use-medusa";
const paypalClientId = process.env.GATSBY_PAYPAL_CLIENT_ID || ""
 const PaypalPayment = () => {
   const { 
     cart,
     actions: { completeCart, setPaymentSession },
   } = useCart()
   const [errorMessage, setErrorMessage] = useState(undefined)
   const [processing, setProcessing] = useState(false)
   const client = useMedusa()
   const paypalSession = useMemo(() => {
     if (cart.payment_sessions) {
       return cart.payment_sessions.find(s => s.provider_id === "paypal")
     }
     return null
   }, [cart.payment_sessions])
   if (!paypalSession) {
     return null
   }
   const completeOrder = async (authorizationOrder) => {
     const cart = await setPaymentSession("paypal")
     if (!cart) {
       setProcessing(false)
       return
     }
     await client.carts.updatePaymentSession(cart.id, "paypal", {
       data: {
         data: {
           ...authorizationOrder
         }
       }
     });
     const order = await completeCart(cart.id)
     if (!order || order.object !== "order") {
       setProcessing(false)
       return
     }
     setProcessing(false)
     navigate("/order-confirmed", { state: { order } })
   }
   const handlePayment = (data, actions) => {
     actions.order.authorize().then((authorization) => {
       if (authorization.status !== 'COMPLETED') {
         setErrorMessage(`An error occurred, status: ${authorization.status}`);
         setProcessing(false);
         return;
       }
       completeOrder(authorization)
     })
   }
   return (
     <PayPalScriptProvider options={{ 
       "client-id": paypalClientId,
       "currency": cart.region.currency_code.toUpperCase(),
       "intent": "authorize"
     }}>
         {errorMessage && (
           <span className="text-rose-500 mt-4">{errorMessage}</span>
         )}
         <PayPalButtons 
           style={{ layout: "horizontal" }}
           onApprove={handlePayment}
           disabled={processing}
         />
     </PayPalScriptProvider>
   )
 }
 export default PaypalPayment;

Чтобы кратко объяснить этот фрагмент кода:

  • Вы создаете кнопку PayPal, которая позволяет клиентам платить через PayPal, используя компоненты из @paypal/react-paypal-js, которые вы только что установили. Вы передаете компоненту PayPalScriptProvider идентификатор клиента PayPal из переменных среды.
  • При нажатии на кнопку выполняется метод handlePayment, который инициирует авторизацию в PayPal по методу actions.order.authorize(). Откроется платежный портал PayPal в новом окне.
  • После успешного завершения платежа клиентом выполняется функция обратного вызова выполнения, переданная then. Если в авторизации есть какие-либо ошибки, будет показано сообщение об ошибке. В противном случае будет вызван метод completeOrder.
  • В методе completeOrder PayPal сначала устанавливается в качестве сеанса оплаты текущей корзины. Затем он обновляется на сервере данными, полученными от PayPal после авторизации платежа клиентом.
  • Наконец, заказ размещается, и покупатель перенаправляется на страницу order-confirmed, где он может просмотреть сводную информацию о своем заказе.

Далее в src/components/payment/index.jsx добавляем импорт для компонента PaypalPayment в начало файла:

import PaypalPayment from "./paypal-payment"

Затем в возвращенном JSX вы найдете оператор switch, который отображает компоненты на основе идентификатора поставщика платежных услуг. Добавьте новый случай в оператор switch перед случаем default. Это отображает PaypalPayment, когда идентификатор поставщика платежных услуг, доступный для клиента, равен paypal:

switch (ps.provider_id) {
  case "stripe":
    //...
  case "manual":
    //...
  case "paypal":
    return <PaypalPayment />
  default:
    return null
}

Сохраните все изменения, прежде чем переходить к тестированию всего потока.

Тестовый процесс оформления заказа

В этом разделе вы попробуете разместить заказ на витрине магазина, а затем просмотреть детали у администратора и зафиксировать платеж.

Убедитесь, что все 3 компонента (сервер Medusa, администрация Medusa и витрина) запущены. Затем на витрине выберите товар и отправьте его в корзину.

Затем нажмите на значок корзины и нажмите кнопку «Оформить заказ» во всплывающем окне.

Вы попадете на страницу оформления заказа, где вам нужно будет ввести свои данные и выбрать способ доставки.

Как только вы дойдете до последнего шага оформления заказа, вы должны увидеть доступные способы оплаты в текущем регионе, включая PayPal.

Если вы не видите PayPal, убедитесь, что в правом верхнем углу панели навигации выбран правильный регион.

Попробуйте оплатить с помощью PayPal, нажав первую кнопку PayPal. Откроется новая страница, где вас попросят войти в PayPal и авторизовать платеж.

Как только вы авторизуете платеж с помощью учетной записи песочницы, вы вернетесь на витрину магазина. Если авторизация прошла успешно, вы вскоре будете перенаправлены на страницу «Сводка заказа».

В админке Medusa нажмите «Заказы» на боковой панели. Вы должны увидеть новый заказ.

Нажмите на заказ. Вы увидите детали заказа, включая заказанные товары и платежные реквизиты.

Чтобы зафиксировать платеж, нажмите на кнопку «Зафиксировать платеж».

Что дальше?

Вы только что создали магазин комиксов с помощью Medusa, у которого есть поисковая система, использующая MeiliSearch и PayPal в качестве платежной системы.

Вы можете делать гораздо больше со своим интернет-магазином:

Если у вас есть какие-либо проблемы или вопросы, связанные с Medusa, не стесняйтесь обращаться к команде Medusa через Discord.