Розробка інтернет-магазину на Vendure

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка інтернет-магазину на Vendure
Складна
від 2 тижнів до 3 місяців
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка інтернет-магазину на Vendure

Vendure — headless commerce фреймворк на NestJS та TypeScript з GraphQL API. На відміну від commercetools, це open-source рішення з self-hosted розгортанням: ви контролюєте інфраструктуру, дані та можете модифікувати ядро через плагіни без форків. Архітектура базується на модулях NestJS, що робить розширення передбачуваним.

Коли Vendure — правильний вибір

  • Потрібен self-hosted: дані не покидають вашу інфраструктуру
  • Вимагається глибока кастомізація бізнес-логіки (податки, доставка, промокоди)
  • TypeScript — основна мова команди
  • Бюджет не дозволяє SaaS-платформи ($500+/міс на commercetools)
  • Потрібен контроль над схемою БД (PostgreSQL/MySQL)

Архітектура проекту

vendure-project/
├── src/
│   ├── vendure-config.ts        # Головна конфігурація
│   ├── plugins/                 # Кастомні плагіни
│   │   ├── loyalty/
│   │   ├── b2b-pricing/
│   │   └── erp-sync/
│   ├── email-handlers/          # Шаблони листів
│   └── payment-handlers/        # Обробники оплати
├── storefront/                  # Next.js / Nuxt
└── docker-compose.yml

Конфігурація Vendure

// src/vendure-config.ts
import { VendureConfig } from "@vendure/core";
import { defaultEmailHandlers, EmailPlugin } from "@vendure/email-plugin";
import { AssetServerPlugin } from "@vendure/asset-server-plugin";
import { AdminUiPlugin } from "@vendure/admin-ui-plugin";

export const config: VendureConfig = {
  apiOptions: {
    port: 3000,
    adminApiPath: "admin-api",
    shopApiPath: "shop-api",
    adminApiPlayground: process.env.NODE_ENV === "development",
  },

  authOptions: {
    tokenMethod: ["bearer", "cookie"],
    superadminCredentials: {
      identifier: process.env.SUPERADMIN_USERNAME!,
      password: process.env.SUPERADMIN_PASSWORD!,
    },
    cookieOptions: {
      secret: process.env.COOKIE_SECRET!,
    },
  },

  dbConnectionOptions: {
    type: "postgres",
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT),
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    synchronize: false, // лише міграції в production
    migrations: ["dist/migrations/*.js"],
  },

  paymentOptions: {
    paymentMethodHandlers: [stripePaymentHandler, yookassaPaymentHandler],
  },

  taxOptions: {
    taxCalculationStrategy: new CustomTaxCalculationStrategy(),
  },

  shippingOptions: {
    shippingCalculators: [defaultShippingCalculator, tieredShippingCalculator],
    shippingEligibilityCheckers: [defaultShippingEligibilityChecker],
    fulfillmentHandlers: [manualFulfillmentHandler],
  },

  plugins: [
    AssetServerPlugin.init({
      route: "assets",
      assetUploadDir: path.join(__dirname, "../static/assets"),
    }),
    EmailPlugin.init({
      devMode: process.env.NODE_ENV === "development",
      handlers: defaultEmailHandlers,
      templatePath: path.join(__dirname, "../email/templates"),
      transport: {
        type: "smtp",
        host: process.env.SMTP_HOST!,
        port: 587,
        auth: {
          user: process.env.SMTP_USER!,
          pass: process.env.SMTP_PASS!,
        },
      },
    }),
    AdminUiPlugin.init({
      route: "admin",
      port: 3002,
    }),
    LoyaltyPlugin,
    B2bPricingPlugin,
    ErpSyncPlugin,
  ],
};

Модель каналів та мультитенантність

Vendure підтримує Channels — аналог мультиарендності. Один інстанс обслуговує кілька магазинів з окремим каталогом, цінами та замовленнями:

// Канал створюється через Admin API або скрипт заповнення
await channelService.create(ctx, {
  code: "ru-channel",
  token: "ru-token-abc123",
  defaultCurrencyCode: CurrencyCode.RUB,
  defaultLanguageCode: LanguageCode.ru,
  defaultTaxZone: taxZoneRU,
  defaultShippingZone: shippingZoneRU,
  pricesIncludeTax: false,
});

Кожен запит до Shop API повинен містити заголовок vendure-token: <channel-token>.

Checkout flow через Shop API

# 1. Додати товар до замовлення
mutation AddToOrder($productVariantId: ID!, $quantity: Int!) {
  addItemToOrder(productVariantId: $productVariantId, quantity: $quantity) {
    ... on Order {
      id
      code
      totalWithTax
      lines {
        id
        quantity
        productVariant { name sku }
        unitPriceWithTax
      }
    }
    ... on ErrorResult {
      errorCode
      message
    }
  }
}

# 2. Встановити адресу доставки
mutation SetShippingAddress($input: CreateAddressInput!) {
  setOrderShippingAddress(input: $input) {
    ... on Order { id shippingAddress { fullName streetLine1 city } }
    ... on NoActiveOrderError { errorCode message }
  }
}

# 3. Отримати методи доставки та вибрати
query GetShippingMethods {
  eligibleShippingMethods {
    id
    name
    price
    priceWithTax
    description
  }
}

mutation SetShippingMethod($id: [ID!]!) {
  setOrderShippingMethod(shippingMethodId: $id) {
    ... on Order { id shipping shippingWithTax }
  }
}

Платіжна інтеграція (YooKassa)

// src/payment-handlers/yookassa.handler.ts
import { CreatePaymentResult, PaymentMethodHandler, LanguageCode } from "@vendure/core";

export const yookassaPaymentHandler = new PaymentMethodHandler({
  code: "yookassa",
  description: [{ languageCode: LanguageCode.ru, value: "YooKassa" }],
  args: {
    shopId: { type: "string" },
    secretKey: { type: "string", ui: { component: "password-form-input" } },
  },

  async createPayment(ctx, order, amount, args, metadata): Promise<CreatePaymentResult> {
    const yookassa = new YooKassa({
      shopId: args.shopId,
      secretKey: args.secretKey,
    });

    const payment = await yookassa.createPayment({
      amount: {
        value: (amount / 100).toFixed(2),
        currency: order.currencyCode,
      },
      capture: true,
      confirmation: {
        type: "redirect",
        return_url: `${process.env.SHOP_URL}/checkout/confirmation`,
      },
      description: `Замовлення #${order.code}`,
      metadata: { vendure_order_id: order.id },
    });

    return {
      amount,
      state: "Authorized",
      transactionId: payment.id,
      metadata: { confirmationUrl: payment.confirmation.confirmation_url },
    };
  },

  async settlePayment(ctx, order, payment, args) {
    // YooKassa з capture=true — оплата знімається автоматично
    return { success: true };
  },

  async refundPayment(ctx, order, payment, args, lines, adjustment) {
    const yookassa = new YooKassa({ shopId: args.shopId, secretKey: args.secretKey });
    const refund = await yookassa.createRefund(payment.transactionId, {
      amount: { value: (adjustment / 100).toFixed(2), currency: order.currencyCode },
    });
    return { state: "Settled", transactionId: refund.id };
  },
});

Продуктивність та масштабування

Vendure підтримує Worker/Server розділення: важкі завдання (email, експорт, indexing) обробляються в окремому Worker-процесі через Bull:

// server.ts
import { bootstrap } from "@vendure/core";
bootstrap(config);

// worker.ts — запускається окремо
import { bootstrapWorker } from "@vendure/core";
bootstrapWorker(config);

Для Production: 2+ інстанси сервера (load balanced), 1+ воркер, Redis для черг.

Етапи розробки та часові рамки

Етап Часова рамка
Встановлення, конфігурація, БД, міграції 2–3 дні
Імпорт каталогу (Products, Variants, Assets) 3–7 днів
Кастомні плагіни (податки, доставка, промокоди) 5–10 днів
Storefront (Next.js + GraphQL) 10–20 днів
Платіжні інтеграції (2–3 провайдери) 4–6 днів
Admin UI кастомізація 2–4 дні
Всього 26–50 днів