Настройка Shopify Functions для кастомной логики скидок/доставки

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка Shopify Functions для кастомной логики скидок/доставки
Сложная
~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

Настройка Shopify Functions для кастомной логики скидок/доставки

Shopify Functions — механизм запуска пользовательского кода непосредственно в инфраструктуре Shopify. В отличие от приложений с webhook, Functions выполняются синхронно в процессе чекаута: Shopify вызывает функцию, получает ответ и применяет результат.

Где работают Functions

API функций Назначение
cart_transform Изменение строк корзины перед чекаутом (бандлы, модификации)
discounts Кастомные скидки (фиксированные, процент, BXGY)
payment_customization Скрытие/переименование способов оплаты
shipping_discount Скидка на доставку
delivery_customization Скрытие/переименование/сортировка методов доставки
fulfillment_constraints Ограничения на разбивку по складам
order_routing Маршрутизация заказов по складам

Functions пишутся на Rust (официальный язык, компилируется в WebAssembly) или JavaScript/TypeScript (через Javy runtime). Rust предпочтительнее для сложной логики — быстрее и экономнее по лимитам.

Установка и инициализация

# Functions создаются внутри Shopify App
shopify app generate extension --template discount --name custom-volume-discount
cd extensions/custom-volume-discount

Структура extension:

extensions/custom-volume-discount/
├── src/
│   └── run.ts        # (или run.rs для Rust)
├── input.graphql     # Описание входных данных
├── shopify.extension.toml
└── package.json

Пример: скидка за объём заказа (JavaScript)

input.graphql — описывает, какие данные Shopify передаст функции:

# input.graphql
query RunInput {
  cart {
    lines {
      id
      quantity
      merchandise {
        ... on ProductVariant {
          id
          product {
            id
            tags
            metafield(namespace: "custom", key: "volume_discount_tier") {
              value
            }
          }
        }
      }
    }
  }
  discountNode {
    metafield(namespace: "custom", key: "tiers") {
      value
    }
  }
}

src/run.ts — логика функции:

import type { RunInput, FunctionRunResult } from "../generated/api";

interface DiscountTier {
  quantity: number;
  percentage: number;
}

export function run(input: RunInput): FunctionRunResult {
  // Читаем конфиг скидок из метаполя discount node
  const tiersJson = input.discountNode?.metafield?.value;
  const tiers: DiscountTier[] = tiersJson ? JSON.parse(tiersJson) : [];

  if (!tiers.length) return { discounts: [], discountApplicationStrategy: "FIRST" };

  const discounts = [];

  for (const line of input.cart.lines) {
    const variant = line.merchandise;
    if (variant.__typename !== "ProductVariant") continue;

    const product = variant.product;
    // Применяем скидку только к товарам с тегом 'volume-eligible'
    if (!product.tags.includes("volume-eligible")) continue;

    // Находим подходящий tier по количеству
    const applicableTier = tiers
      .filter(t => line.quantity >= t.quantity)
      .sort((a, b) => b.quantity - a.quantity)[0];

    if (!applicableTier) continue;

    discounts.push({
      targets: [{ cartLine: { id: line.id } }],
      value: { percentage: { value: String(applicableTier.percentage) } },
      message: `Скидка ${applicableTier.percentage}% за количество (от ${applicableTier.quantity} шт.)`,
    });
  }

  return {
    discounts,
    discountApplicationStrategy: "FIRST",
  };
}

Пример: кастомная логика доставки (Rust)

Скрыть методы доставки в зависимости от содержимого корзины:

// src/run.rs
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result<output::FunctionRunResult> {
    let cart_weight_grams: i64 = input.cart.lines.iter()
        .map(|line| {
            let variant = &line.merchandise;
            let weight = variant.weight.unwrap_or(0.0);
            (weight * 1000.0 * line.quantity as f64) as i64
        })
        .sum();

    let operations = input.cart.delivery_groups.iter()
        .flat_map(|group| group.delivery_options.iter().map(|opt| {
            // Скрыть «Экспресс-доставка» если посылка >20кг
            let hide = opt.title.contains("Экспресс") && cart_weight_grams > 20_000;
            output::DeliveryCustomizationOperation {
                hide: if hide {
                    Some(output::HideOperation { delivery_option_handle: opt.handle.clone() })
                } else {
                    None
                },
                move_: None,
                rename: None,
            }
        }))
        .collect();

    Ok(output::FunctionRunResult { operations })
}

Конфигурация функции в TOML

# shopify.extension.toml
api_version = "2025-01"

[[extensions]]
name = "custom-volume-discount"
handle = "custom-volume-discount"
type = "function"

[extensions.targeting]
  module = "./src/run.ts"

[[extensions.targeting]]
  target = "purchase.product-discount.run"

[extensions.build]
  command = "npm run build"
  path = "dist/function.wasm"

Активация через Shopify Admin

Functions не работают автоматически после деплоя — нужно создать Discount (или Customization) в Admin и выбрать функцию как источник логики:

// Программное создание скидки через Admin GraphQL API
const CREATE_DISCOUNT = `
  mutation discountAutomaticAppCreate($automaticAppDiscount: DiscountAutomaticAppInput!) {
    discountAutomaticAppCreate(automaticAppDiscount: $automaticAppDiscount) {
      automaticAppDiscount {
        discountId
        title
        startsAt
      }
      userErrors { field message }
    }
  }
`;

await adminClient.query({
  data: {
    query: CREATE_DISCOUNT,
    variables: {
      automaticAppDiscount: {
        title: "Объёмная скидка",
        functionId: "gid://shopify/ShopifyFunction/abc123",
        startsAt: new Date().toISOString(),
        metafields: [{
          namespace: "custom",
          key: "tiers",
          type: "json",
          value: JSON.stringify([
            { quantity: 5, percentage: 5 },
            { quantity: 10, percentage: 10 },
            { quantity: 20, percentage: 15 }
          ])
        }]
      }
    }
  }
});

Ограничения Functions

  • Лимит времени выполнения: 5 мс для Rust/WASM, 10 мс для JS
  • Лимит памяти: 10 МБ
  • Нет сетевых запросов: функция не может делать HTTP-вызовы — все данные должны прийти через input.graphql
  • Нет доступа к БД: сложную конфигурацию передаём через Metafields в запрос входных данных
  • Checkout UI Extensions: визуальный UI для чекаута — отдельный механизм (не Functions)

Из-за лимитов на время выполнения всю бизнес-логику нужно максимально упрощать. Тяжёлые вычисления перекладываются на подготовительный этап — результат кешируется в Metafields и передаётся в функцию как готовые данные.

Тестирование

# Запуск тестов функции локально
npm run test  # или cargo test

# Деплой extension к приложению
shopify app deploy

# Просмотр логов выполнения (через Shopify Partners)
shopify app logs

Сроки

Простая функция скидки (процент по тегу или коллекции): 2–4 дня. Сложная многоуровневая функция с конфигурацией через метаполя и Admin UI для управления правилами: 1–2 недели. Функция доставки с геологикой и интеграцией внешних тарифов (через метаполя): 1–2 недели.