Розробка кастомних API Extensions для commercetools

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

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

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомних API Extensions для commercetools
Складна
~5 робочих днів
Часті питання

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

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

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

  • 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

Розробка кастомних API Extensions для commercetools

API Extensions — механізм синхронних хуків у commercetools. При певних операціях з ресурсами платформа робить HTTP-запит до вашого сервісу перед або після збереження, очікуючи відповідь протягом 2 секунд. Це єдиний спосіб додати бізнес-логіку прямо в lifecycle ресурсів без форкування платформи.

Типи тригерів

Тригер Ресурс Коли спрацьовує
Create Cart, Order, Customer, Payment На POST
Update Cart, Order, Customer, Payment На POST /ID
Delete Customer, Shopping List На DELETE /ID

Найбільш затребувані: Cart-Update (пересчет цін, валідація промокодів), Order-Create (отправка в ERP), Payment-Update (синхронізація статусів оплати).

Реєстрація Extension

const extension = await apiRoot.extensions().post({
  body: {
    key: "cart-validation-extension",
    destination: {
      type: "HTTP",
      url: "https://extensions.your-service.com/cart-validate",
      authentication: {
        type: "AuthorizationHeader",
        headerValue: `Bearer ${process.env.EXTENSION_SECRET}`,
      },
    },
    triggers: [
      {
        resourceTypeId: "cart",
        actions: ["Create", "Update"],
      },
    ],
    timeoutInMs: 2000,
  },
}).execute();

Extension з типом AWSLambda або GoogleCloudFunction викликається прямо без HTTP — платформа підписує запит через IAM.

Структура Extension сервісу

// extensions/src/cart-handler.ts
import express from "express";
import { ExtensionInput, CartUpdateAction } from "@commercetools/platform-sdk";

const app = express();
app.use(express.json());

app.post("/cart-validate", async (req, res) => {
  const input: ExtensionInput = req.body;
  const { action, resource } = input;
  const cart = resource.obj; // повний об'єкт кошика

  const actions: CartUpdateAction[] = [];
  const errors: ExtensionError[] = [];

  // Приклад: мінімальна сума замовлення
  if (action === "Update" && cart.totalPrice.centAmount < 50000) {
    errors.push({
      code: "InvalidInput",
      message: "Мінімальна сума замовлення — 500 ₽",
      extensionExtraInfo: { field: "totalPrice" },
    });
  }

  // Приклад: автоматично застосувати VIP знижку
  if (cart.customerGroup?.id === "vip-group-id") {
    const alreadyHasVipDiscount = cart.discountCodes?.some(
      (dc) => dc.discountCode.id === "vip-discount-id"
    );
    if (!alreadyHasVipDiscount) {
      actions.push({
        action: "addDiscountCode",
        code: "VIP10",
      });
    }
  }

  if (errors.length > 0) {
    return res.status(400).json({ errors });
  }

  res.json({ actions });
});

Відповідь з actions — commercetools застосує їх атомарно до ресурсу. Відповідь з errors — операція відхиляється, клієнт отримує 400.

Extension для пересчета цін

Сценарій: ціни беруться з зовнішнього ERP, не з commercetools Price.

app.post("/cart-reprice", async (req, res) => {
  const { resource } = req.body as ExtensionInput;
  const cart = resource.obj;
  const actions: CartUpdateAction[] = [];

  // Отримати актуальні ціни з ERP
  const skus = cart.lineItems.map((li) => li.variant.sku).filter(Boolean);
  const erpPrices = await fetchErpPrices(skus, cart.customerId);

  for (const lineItem of cart.lineItems) {
    const erpPrice = erpPrices[lineItem.variant.sku!];
    if (!erpPrice) continue;

    const currentCentAmount = lineItem.price.value.centAmount;
    const erpCentAmount = Math.round(erpPrice * 100);

    if (currentCentAmount !== erpCentAmount) {
      actions.push({
        action: "setLineItemPrice",
        lineItemId: lineItem.id,
        externalPrice: {
          value: {
            centAmount: erpCentAmount,
            currencyCode: cart.totalPrice.currencyCode,
          },
        },
      });
    }
  }

  res.json({ actions });
});

Extension для Order-Create: відправка в ERP

app.post("/order-created", async (req, res) => {
  const { resource } = req.body as ExtensionInput;
  const order = resource.obj;

  try {
    const erpOrderId = await sendToErp({
      commercetoolsOrderId: order.id,
      orderNumber: order.orderNumber,
      customer: order.customerEmail,
      lines: order.lineItems.map((li) => ({
        sku: li.variant.sku,
        quantity: li.quantity,
        price: li.price.value.centAmount,
      })),
      shippingAddress: order.shippingAddress,
    });

    // Зберегти ERP ID як custom field замовлення
    res.json({
      actions: [
        {
          action: "setCustomField",
          name: "erpOrderId",
          value: erpOrderId,
        },
      ],
    });
  } catch (err) {
    // Не відхиляємо створення замовлення при помилці ERP —
    // краще записати в чергу й обробити асинхронно
    await enqueueForRetry({ orderId: order.id, error: err });
    res.json({ actions: [] });
  }
});

Розгортання Extension-сервісу

Extension повинен відповідати за 2000 мс. Рекомендовані варіанти розгортання:

  • AWS Lambda + API Gateway — холодний старт ≤ 200мс з provisioned concurrency
  • Google Cloud Run — min-instances=1 виключає холодний старт
  • Kubernetes — якщо вже є кластер
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist/ ./dist/
EXPOSE 3000
CMD ["node", "dist/server.js"]

Моніторинг та відладка

commercetools логує всі Extension-виклики в Merchant Center → Developer → Extension Logs (зберігає 7 днів). Кожен лог містить:

  • Тіло запиту (payload)
  • HTTP-статус відповіді Extension
  • Тіло відповіді
  • Час виконання

При timeout або 5xx від Extension операція відхиляється — це жорстке поведення, яке не можна змінити. Тому Extension-сервіс повинен бути стабільнішим за основний API.

Часові рамки розробки

Extension Складність Часова рамка
Валідація кошика Низька 1–2 дні
Пересчет цін з ERP Висока 3–5 днів
Order → ERP інтеграція Середня 2–4 дні
Синхронізація Payment статусів Середня 2–3 дні
Кастомні знижки + промокоди Висока 4–6 днів