В статье «Интеграция WooCommerce с Wildberries без плагинов и сторонних сервисов» мы разбирали импорт товаров с Wildberries на сайт. Теперь решим обратную задачу — научим ваш интернет-магазин на WooCommerce экспортировать товары на крупнейший маркетплейс. Автоматизация этого процесса позволит вам быстро начинать продажи на новой площадке, избегая рутинной ручной выгрузки.
Для кого эта статья и что делать дальше?
Эта статья писалась прежде всего для владельцев интернет-магазинов и начинающих разработчиков, которые хотят разобраться в процессе интеграции самостоятельно и иметь полный контроль над взаимодействием с маркетплейсом.
Я намеренно показываю «как это работает изнутри» — подробно разбираю каждый шаг и каждую строчку кода, чтобы вы могли понять саму суть процесса, а не просто скопировать готовый код. Объясняю не только «что делать», но и «почему именно так», что поможет вам в будущем самостоятельно решать похожие задачи и адаптировать код под свои уникальные потребности.
Если же вам нужно:
- готовое решение «под ключ» без необходимости вникать в технические детали;
- гарантированно рабочая и протестированная интеграция;
- комплексная настройка с синхронизацией остатков, заказов и аналитики —
— то для достижения наилучшего результата рекомендую обратиться к профессиональным разработчикам или специализированным сервисам.
Что нам понадобиться
1. Технические требования
- WordPress 5.0+
- WooCommerce 5.0+
- PHP 7.4+
- Доступ к файловой системе хостинга
- Права администратора WordPress
2. Аккаунты и доступы
- Аккаунт продавца на Wildberries
- Доступ к FTP/sFTP или файловому менеджеру хостинга
- Базовые знания работы с кодом
Часть 1: Подготовка токена в Wildberries
Шаг 1.1: Регистрация в кабинете продавца
- Перейдите на seller.wildberries.ru
- Войдите в свой аккаунт или зарегистрируйтесь
- Подтвердите ваш аккаунт продавца
Шаг 1.2: Создание тестового токена
- В личном кабинете перейдите в раздел «Интеграция API»
- Нажмите «+ Создать токен»
- Выберите вкладку «Ручная интеграция»
- Заполните форму:
Настройки токена:
- Тип токена: Тестовый токен
- Имя токена:
WPLife Test Integration - Категории API: Отметьте галочкой «Контент»
- Уровень доступа: Чтение и запись
- Комментарий:
Тестовый токен для интеграции с WooCommerce
- Нажмите «Создать»
- ВАЖНО: Немедленно скопируйте токен и сохраните в надежном месте! Позже вы не сможете его посмотреть.
Пример токена:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Часть 2: Создание плагина WordPress
Шаг 2.1: Создание структуры плагина
На вашем хостинге создайте следующую структуру папок:
Шаг 2.2: Основной файл плагина (wplife-wb-export.php)
Создайте файл и скопируйте код полностью:
wp-content/plugins/wplife-wb-export/
├── wplife-wb-export.php
├── admin.js
├── admin.css
└── readme.txt
Шаг 2.2: Основной файл плагина (wplife-wb-export.php)
Создайте файл и скопируйте код полностью:
<?php
/**
* Plugin Name: WPLife Export to Wildberries
* Plugin URI: https://wplife.ru/
* Description: Полная интеграция для экспорта товаров из WooCommerce в Wildberries
* Version: 1.0.0
* Author: Alexander Parkhomenko
* Author URI: https://wplife.ru/
* Text Domain: wplife-wb-export
* Domain Path: /languages
* Requires at least: 5.0
* Requires PHP: 7.4
* WC requires at least: 5.0
*
* @package WPLife_WB_Export
*/
// Защита от прямого доступа
if (!defined('ABSPATH')) {
exit;
}
// Проверяем наличие WooCommerce
if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action('admin_notices', function() {
echo '<div class="error"><p>Плагин WPLife Export to Wildberries требует установленного и активированного WooCommerce!</p></div>';
});
return;
}
/**
* Основной класс плагина
*/
class WPLife_WB_Export_Plugin {
/**
* @var string Базовый URL API Wildberries (песочница)
*/
private $api_base_url = 'https://content-api-sandbox.wildberries.ru';
/**
* @var string Токен API (заполняется из настроек)
*/
private $api_key = '';
/**
* @var WPLife_WB_Export_Plugin Единственный экземпляр класса
*/
private static $instance = null;
/**
* Получение экземпляра класса (Singleton)
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Конструктор
*/
private function __construct() {
add_action('plugins_loaded', array($this, 'init'));
}
/**
* Инициализация плагина
*/
public function init() {
// Инициализация API ключа
$this->api_key = get_option('wplife_wb_api_key', '');
// Хуки админки
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
// AJAX обработчики
add_action('wp_ajax_test_wb_connection', array($this, 'ajax_test_connection'));
add_action('wp_ajax_export_single_product', array($this, 'ajax_export_single_product'));
add_action('wp_ajax_bulk_export_products', array($this, 'ajax_bulk_export_products'));
add_action('wp_ajax_get_products_list', array($this, 'ajax_get_products_list'));
// Хуки WooCommerce
add_action('woocommerce_update_product', array($this, 'on_product_update'), 10, 2);
add_action('woocommerce_new_product', array($this, 'on_product_create'), 10, 2);
// Локализация
load_plugin_textdomain('wplife-wb-export', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
/**
* Добавление меню в админку
*/
public function add_admin_menu() {
add_options_page(
__('Экспорт в Wildberries', 'wplife-wb-export'),
__('WB Экспорт', 'wplife-wb-export'),
'manage_options',
'wplife-wb-export',
array($this, 'render_admin_page')
);
}
/**
* Регистрация настроек
*/
public function register_settings() {
// Основные настройки
register_setting('wplife_wb_export_settings', 'wplife_wb_api_key', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => ''
));
register_setting('wplife_wb_export_settings', 'wplife_wb_default_brand', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'Ваш Бренд'
));
register_setting('wplife_wb_export_settings', 'wplife_wb_default_country', array(
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => 'Россия'
));
register_setting('wplife_wb_export_settings', 'wplife_wb_auto_export', array(
'type' => 'boolean',
'default' => false
));
register_setting('wplife_wb_export_settings', 'wplife_wb_logging', array(
'type' => 'boolean',
'default' => true
));
// Секции настроек
add_settings_section(
'wplife_wb_api_section',
__('Настройки API Wildberries', 'wplife-wb-export'),
array($this, 'render_api_section'),
'wplife_wb_export_settings'
);
add_settings_section(
'wplife_wb_export_section',
__('Настройки экспорта', 'wplife-wb-export'),
array($this, 'render_export_section'),
'wplife_wb_export_settings'
);
// Поля настроек
add_settings_field(
'wplife_wb_api_key',
__('API Ключ Wildberries', 'wplife-wb-export'),
array($this, 'render_api_key_field'),
'wplife_wb_export_settings',
'wplife_wb_api_section'
);
add_settings_field(
'wplife_wb_default_brand',
__('Бренд по умолчанию', 'wplife-wb-export'),
array($this, 'render_default_brand_field'),
'wplife_wb_export_settings',
'wplife_wb_export_section'
);
add_settings_field(
'wplife_wb_default_country',
__('Страна производства', 'wplife-wb-export'),
array($this, 'render_default_country_field'),
'wplife_wb_export_settings',
'wplife_wb_export_section'
);
add_settings_field(
'wplife_wb_auto_export',
__('Автоэкспорт', 'wplife-wb-export'),
array($this, 'render_auto_export_field'),
'wplife_wb_export_settings',
'wplife_wb_export_section'
);
add_settings_field(
'wplife_wb_logging',
__('Логирование', 'wplife-wb-export'),
array($this, 'render_logging_field'),
'wplife_wb_export_settings',
'wplife_wb_export_section'
);
}
/**
* Подключение скриптов и стилей
*/
public function enqueue_admin_scripts($hook) {
if ('settings_page_wplife-wb-export' !== $hook) {
return;
}
wp_enqueue_script(
'wplife-wb-admin',
plugin_dir_url(__FILE__) . 'admin.js',
array('jquery'),
'1.0.0',
true
);
wp_enqueue_style(
'wplife-wb-admin',
plugin_dir_url(__FILE__) . 'admin.css',
array(),
'1.0.0'
);
// Локализация для AJAX
wp_localize_script('wplife-wb-admin', 'wplife_wb_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wplife_wb_nonce'),
'strings' => array(
'testing_connection' => __('Проверяем подключение...', 'wplife-wb-export'),
'exporting' => __('Экспортируем...', 'wplife-wb-export'),
'loading_products' => __('Загружаем товары...', 'wplife-wb-export')
)
));
}
/**
* Рендер страницы настроек
*/
public function render_admin_page() {
if (!current_user_can('manage_options')) {
wp_die(__('У вас недостаточно прав для доступа к этой странице.', 'wplife-wb-export'));
}
?>
<div class="wrap">
<h1><?php _e('Экспорт товаров в Wildberries', 'wplife-wb-export'); ?></h1>
<div class="wplife-wb-admin">
<!-- Блок тестирования подключения -->
<div class="wplife-wb-card">
<h2><?php _e('Тестирование подключения', 'wplife-wb-export'); ?></h2>
<div class="wplife-wb-test-section">
<p><?php _e('Проверьте подключение к API Wildberries перед началом работы:', 'wplife-wb-export'); ?></p>
<button id="test-connection" class="button button-primary">
<?php _e('Проверить подключение к WB API', 'wplife-wb-export'); ?>
</button>
<div id="test-result" class="wplife-wb-result"></div>
</div>
</div>
<!-- Форма настроек -->
<div class="wplife-wb-card">
<h2><?php _e('Настройки интеграции', 'wplife-wb-export'); ?></h2>
<form action="options.php" method="post">
<?php
settings_fields('wplife_wb_export_settings');
do_settings_sections('wplife_wb_export_settings');
submit_button(__('Сохранить настройки', 'wplife-wb-export'));
?>
</form>
</div>
<!-- Блок экспорта товаров -->
<div class="wplife-wb-card">
<h2><?php _e('Экспорт товаров', 'wplife-wb-export'); ?></h2>
<div class="wplife-wb-export-section">
<p><?php _e('Выберите товары для экспорта в Wildberries:', 'wplife-wb-export'); ?></p>
<div class="wplife-wb-export-controls">
<button id="load-products" class="button">
<?php _e('Загрузить товары', 'wplife-wb-export'); ?>
</button>
<button id="select-all" class="button" style="display:none;">
<?php _e('Выбрать все', 'wplife-wb-export'); ?>
</button>
<button id="deselect-all" class="button" style="display:none;">
<?php _e('Снять выделение', 'wplife-wb-export'); ?>
</button>
</div>
<div id="products-list-container">
<div id="products-list"></div>
</div>
<div class="wplife-wb-export-actions" style="display:none;">
<button id="bulk-export" class="button button-primary">
<?php _e('Экспортировать выбранные товары', 'wplife-wb-export'); ?>
</button>
<span id="export-progress" style="margin-left: 15px;"></span>
</div>
<div id="export-results" class="wplife-wb-result"></div>
</div>
</div>
<!-- Информационный блок -->
<div class="wplife-wb-card">
<h2><?php _e('Важная информация', 'wplife-wb-export'); ?></h2>
<div class="wplife-wb-info-section">
<div class="notice notice-info">
<p><strong><?php _e('Обратите внимание:', 'wplife-wb-export'); ?></strong></p>
<ul>
<li><?php _e('Используется тестовое окружение Wildberries (песочница)', 'wplife-wb-export'); ?></li>
<li><?php _e('Товары не попадут в реальный каталог', 'wplife-wb-export'); ?></li>
<li><?php _e('Для боевого использования замените токен и URL API', 'wplife-wb-export'); ?></li>
<li><?php _e('Все товары должны иметь артикул (SKU)', 'wplife-wb-export'); ?></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<?php
}
/**
* Секция настроек API
*/
public function render_api_section() {
echo '<p>' . __('Настройки для подключения к API Wildberries', 'wplife-wb-export') . '</p>';
}
/**
* Секция настроек экспорта
*/
public function render_export_section() {
echo '<p>' . __('Настройки процесса экспорта товаров', 'wplife-wb-export') . '</p>';
}
/**
* Поле для API ключа
*/
public function render_api_key_field() {
$api_key = get_option('wplife_wb_api_key', '');
?>
<input type="password"
name="wplife_wb_api_key"
value="<?php echo esc_attr($api_key); ?>"
class="regular-text"
placeholder="<?php _e('Введите ваш API ключ', 'wplife-wb-export'); ?>"
required />
<p class="description">
<?php _e('Тестовый токен из личного кабинета Wildberries. Получите его в разделе "Интеграция API".', 'wplife-wb-export'); ?>
</p>
<?php
}
/**
* Поле для бренда по умолчанию
*/
public function render_default_brand_field() {
$brand = get_option('wplife_wb_default_brand', 'Ваш Бренд');
?>
<input type="text"
name="wplife_wb_default_brand"
value="<?php echo esc_attr($brand); ?>"
class="regular-text"
placeholder="<?php _e('Например: MyBrand', 'wplife-wb-export'); ?>" />
<p class="description">
<?php _e('Название бренда, которое будет использоваться для всех экспортируемых товаров', 'wplife-wb-export'); ?>
</p>
<?php
}
/**
* Поле для страны производства
*/
public function render_default_country_field() {
$country = get_option('wplife_wb_default_country', 'Россия');
?>
<input type="text"
name="wplife_wb_default_country"
value="<?php echo esc_attr($country); ?>"
class="regular-text"
placeholder="<?php _e('Например: Россия', 'wplife-wb-export'); ?>" />
<p class="description">
<?php _e('Страна производства товаров', 'wplife-wb-export'); ?>
</p>
<?php
}
/**
* Поле для автоэкспорта
*/
public function render_auto_export_field() {
$auto_export = get_option('wplife_wb_auto_export', false);
?>
<label>
<input type="checkbox"
name="wplife_wb_auto_export"
value="1"
<?php checked($auto_export, true); ?> />
<?php _e('Автоматически экспортировать товары при создании и обновлении', 'wplife-wb-export'); ?>
</label>
<p class="description">
<?php _e('Внимание: может создавать нагрузку при массовом обновлении товаров', 'wplife-wb-export'); ?>
</p>
<?php
}
/**
* Поле для логирования
*/
public function render_logging_field() {
$logging = get_option('wplife_wb_logging', true);
?>
<label>
<input type="checkbox"
name="wplife_wb_logging"
value="1"
<?php checked($logging, true); ?> />
<?php _e('Вести лог операций', 'wplife-wb-export'); ?>
</label>
<p class="description">
<?php _e('Логи будут записываться в файл debug.log WordPress', 'wplife-wb-export'); ?>
</p>
<?php
}
/**
* AJAX: Тестирование подключения
*/
public function ajax_test_connection() {
check_ajax_referer('wplife_wb_nonce', 'nonce');
$api_key = get_option('wplife_wb_api_key');
if (empty($api_key)) {
wp_send_json_error(__('API ключ не настроен. Введите ключ в настройках плагина.', 'wplife-wb-export'));
}
$response = wp_remote_get($this->api_base_url . '/ping', array(
'headers' => array(
'Authorization' => 'Bearer ' . $api_key
),
'timeout' => 30
));
if (is_wp_error($response)) {
$this->log_error('Ошибка подключения: ' . $response->get_error_message());
wp_send_json_error(__('Ошибка подключения: ', 'wplife-wb-export') . $response->get_error_message());
}
$response_code = wp_remote_retrieve_response_code($response);
$response_body = wp_remote_retrieve_body($response);
if ($response_code === 200) {
$this->log_success('Успешное подключение к WB API');
wp_send_json_success(__('Успешное подключение к WB API! Ответ сервера: ', 'wplife-wb-export') . $response_body);
} else {
$error_message = __('Ошибка API. Код: ', 'wplife-wb-export') . $response_code . __(' Ответ: ', 'wplife-wb-export') . $response_body;
$this->log_error($error_message);
wp_send_json_error($error_message);
}
}
/**
* AJAX: Получение списка товаров
*/
public function ajax_get_products_list() {
check_ajax_referer('wplife_wb_nonce', 'nonce');
$args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 50,
'meta_query' => array(
array(
'key' => '_sku',
'compare' => 'EXISTS'
)
)
);
$products = get_posts($args);
$products_data = array();
foreach ($products as $product) {
$wc_product = wc_get_product($product->ID);
$products_data[] = array(
'id' => $product->ID,
'name' => $product->post_title,
'sku' => $wc_product->get_sku(),
'price' => $wc_product->get_regular_price(),
'status' => get_post_meta($product->ID, '_wb_export_status', true)
);
}
wp_send_json_success($products_data);
}
/**
* AJAX: Экспорт одного товара
*/
public function ajax_export_single_product() {
check_ajax_referer('wplife_wb_nonce', 'nonce');
$product_id = intval($_POST['product_id']);
$result = $this->export_product_to_wb($product_id);
if (is_wp_error($result)) {
wp_send_json_error($result->get_error_message());
} else {
wp_send_json_success(array(
'message' => __('Товар успешно экспортирован в Wildberries', 'wplife-wb-export'),
'wb_data' => $result
));
}
}
/**
* AJAX: Массовый экспорт товаров
*/
public function ajax_bulk_export_products() {
check_ajax_referer('wplife_wb_nonce', 'nonce');
$product_ids = array_map('intval', $_POST['product_ids']);
$results = array();
foreach ($product_ids as $product_id) {
$result = $this->export_product_to_wb($product_id);
$results[$product_id] = is_wp_error($result) ?
$result->get_error_message() :
__('Успешно экспортирован', 'wplife-wb-export');
}
wp_send_json_success($results);
}
/**
* Основная функция экспорта товара
*/
private function export_product_to_wb($product_id) {
$product = wc_get_product($product_id);
if (!$product) {
return new WP_Error('product_not_found', __('Товар не найден', 'wplife-wb-export'));
}
$api_key = get_option('wplife_wb_api_key');
if (empty($api_key)) {
return new WP_Error('api_key_missing', __('API ключ не настроен', 'wplife-wb-export'));
}
// Подготавливаем данные карточки
$card_data = $this->prepare_card_data($product);
if (is_wp_error($card_data)) {
return $card_data;
}
// Отправляем запрос к API
$response = $this->make_api_request($card_data, $api_key);
if (!is_wp_error($response)) {
// Сохраняем метаданные об экспорте
update_post_meta($product_id, '_wb_export_status', 'exported');
update_post_meta($product_id, '_wb_export_time', current_time('mysql'));
update_post_meta($product_id, '_wb_response_data', $response);
}
return $response;
}
/**
* Подготовка данных карточки товара
*/
private function prepare_card_data($product) {
// Проверяем обязательные поля
$sku = $product->get_sku();
if (empty($sku)) {
return new WP_Error('missing_sku',
__('У товара "'.$product->get_name().'" должен быть артикул (SKU). Заполните артикул в настройках товара.', 'wplife-wb-export'));
}
$product_name = $product->get_name();
if (empty($product_name)) {
return new WP_Error('missing_name', __('У товара должно быть название', 'wplife-wb-export'));
}
$price = $product->get_regular_price();
if (empty($price)) {
return new WP_Error('missing_price', __('У товара должна быть цена', 'wplife-wb-export'));
}
// Основные данные карточки
$card = array(
'vendorCode' => $sku,
'brand' => get_option('wplife_wb_default_brand', 'Ваш Бренд'),
'object' => 'Одежда', // Требуется уточнение по справочнику Wildberries
'nmID' => 0, // 0 для новых товаров
'imtID' => 0, // 0 для новых товаров
'objectID' => 0, // 0 для новых товаров
'isProhibited' => false,
'tags' => array(),
'sizes' => array(
array(
'techSize' => 'Универсальный',
'wbSize' => '',
'price' => intval($price * 100), // Цена в копейках
'skus' => array($sku . '_size')
)
),
'characteristics' => array(
array(
'Наименование' => $product_name
)
),
'description' => $product->get_description() ?: $product_name
);
// Дополнительные характеристики
$characteristics = array();
if ($product->get_weight()) {
$characteristics['Вес'] = $product->get_weight() . ' кг';
}
$country = get_option('wplife_wb_default_country', 'Россия');
$characteristics['Страна производства'] = $country;
if ($product->get_length() && $product->get_width() && $product->get_height()) {
$characteristics['Габариты упаковки'] = sprintf('%sx%sx%s см',
$product->get_length(),
$product->get_width(),
$product->get_height()
);
}
$card['characteristics'][] = $characteristics;
// Структура для массовой загрузки
$data = array(
'cards' => array($card)
);
$this->log_debug('Подготовлены данные для экспорта', array(
'product_id' => $product->get_id(),
'product_name' => $product_name,
'data' => $data
));
return $data;
}
/**
* Отправка запроса к API Wildberries
*/
private function make_api_request($data, $api_key) {
$url = $this->api_base_url . '/content/v2/cards/upload';
$this->log_debug('Отправка запроса к WB API', array(
'url' => $url,
'data_size' => strlen(json_encode($data))
));
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json'
),
'body' => json_encode($data),
'timeout' => 30
);
$response = wp_remote_post($url, $args);
if (is_wp_error($response)) {
$this->log_error('Ошибка HTTP запроса', array(
'error' => $response->get_error_message()
));
return $response;
}
$response_code = wp_remote_retrieve_response_code($response);
$response_body = wp_remote_retrieve_body($response);
$decoded_response = json_decode($response_body, true);
$this->log_debug('Ответ от WB API', array(
'response_code' => $response_code,
'response_body' => $response_body
));
if ($response_code === 200) {
$this->log_success('Успешный экспорт товара');
return $decoded_response;
} else {
$error_message = 'Ошибка API Wildberries. Код: ' . $response_code . '. Ответ: ' . $response_body;
$this->log_error($error_message);
return new WP_Error('api_error', $error_message);
}
}
/**
* Обработчик обновления товара
*/
public function on_product_update($product_id, $product) {
$auto_export = get_option('wplife_wb_auto_export', false);
if ($auto_export) {
$this->export_product_to_wb($product_id);
}
}
/**
* Обработчик создания товара
*/
public function on_product_create($product_id, $product) {
$auto_export = get_option('wplife_wb_auto_export', false);
if ($auto_export) {
$this->export_product_to_wb($product_id);
}
}
/**
* Логирование ошибок
*/
private function log_error($message, $context = array()) {
$logging = get_option('wplife_wb_logging', true);
if ($logging) {
error_log('WPLIFE WB EXPORT ERROR: ' . $message . ' ' . json_encode($context));
}
}
/**
* Логирование успешных операций
*/
private function log_success($message, $context = array()) {
$logging = get_option('wplife_wb_logging', true);
if ($logging) {
error_log('WPLIFE WB EXPORT SUCCESS: ' . $message . ' ' . json_encode($context));
}
}
/**
* Дебаг логирование
*/
private function log_debug($message, $context = array()) {
$logging = get_option('wplife_wb_logging', true);
if ($logging && WP_DEBUG) {
error_log('WPLIFE WB EXPORT DEBUG: ' . $message . ' ' . json_encode($context));
}
}
}
// Инициализация плагина
add_action('plugins_loaded', function() {
WPLife_WB_Export_Plugin::get_instance();
});
// Хук активации плагина
register_activation_hook(__FILE__, function() {
if (!get_option('wplife_wb_default_brand')) {
update_option('wplife_wb_default_brand', 'Ваш Бренд');
}
if (!get_option('wplife_wb_default_country')) {
update_option('wplife_wb_default_country', 'Россия');
}
});
// Хук деактивации плагина
register_deactivation_hook(__FILE__, function() {
// Очистка можно добавить при необходимости
});
Шаг 2.3: JavaScript файл для админки (admin.js)
Создайте файл admin.js в той же папке плагина:
/**
* WPLife Wildberries Export - Admin JavaScript
*
* @package WPLife_WB_Export
*/
jQuery(document).ready(function($) {
'use strict';
let currentPage = 1;
let selectedProducts = new Set();
let isExporting = false;
// Тестирование подключения к API
$('#test-connection').on('click', function() {
const button = $(this);
const resultDiv = $('#test-result');
button.prop('disabled', true).text(wplife_wb_ajax.strings.testing_connection);
resultDiv.removeClass('wplife-wb-success wplife-wb-error').html('<div class="wplife-wb-loading">' + wplife_wb_ajax.strings.testing_connection + '</div>');
$.post(wplife_wb_ajax.ajax_url, {
action: 'test_wb_connection',
nonce: wplife_wb_ajax.nonce
}, function(response) {
if (response.success) {
resultDiv.addClass('wplife-wb-success').html(
'<div class="wplife-wb-success-content">' +
'<span class="dashicons dashicons-yes-alt"></span>' +
'<div class="wplife-wb-message">' + response.data + '</div>' +
'</div>'
);
} else {
resultDiv.addClass('wplife-wb-error').html(
'<div class="wplife-wb-error-content">' +
'<span class="dashicons dashicons-warning"></span>' +
'<div class="wplife-wb-message">' + response.data + '</div>' +
'</div>'
);
}
button.prop('disabled', false).text(wplife_wb_ajax.strings.testing_connection);
}).fail(function(xhr, status, error) {
resultDiv.addClass('wplife-wb-error').html(
'<div class="wplife-wb-error-content">' +
'<span class="dashicons dashicons-warning"></span>' +
'<div class="wplife-wb-message">Ошибка AJAX: ' + error + '</div>' +
'</div>'
);
button.prop('disabled', false).text('Проверить подключение к WB API');
});
});
// Загрузка списка товаров
$('#load-products').on('click', function() {
loadProducts();
});
// Выбор всех товаров
$('#select-all').on('click', function(e) {
e.preventDefault();
$('input[name="product_ids[]"]').prop('checked', true).trigger('change');
});
// Снятие выделения со всех товаров
$('#deselect-all').on('click', function(e) {
e.preventDefault();
$('input[name="product_ids[]"]').prop('checked', false).trigger('change');
});
// Загрузка товаров
function loadProducts() {
const button = $('#load-products');
const productsList = $('#products-list');
const exportActions = $('.wplife-wb-export-actions');
button.prop('disabled', true).text(wplife_wb_ajax.strings.loading_products);
productsList.html('<div class="wplife-wb-loading">' + wplife_wb_ajax.strings.loading_products + '</div>');
$.post(wplife_wb_ajax.ajax_url, {
action: 'get_products_list',
nonce: wplife_wb_ajax.nonce
}, function(response) {
if (response.success) {
displayProducts(response.data);
exportActions.show();
$('#select-all, #deselect-all').show();
} else {
productsList.html(
'<div class="wplife-wb-error">' +
'<span class="dashicons dashicons-warning"></span>' +
'Ошибка загрузки товаров: ' + response.data +
'</div>'
);
}
button.prop('disabled', false).text('Загрузить товары');
}).fail(function(xhr, status, error) {
productsList.html(
'<div class="wplife-wb-error">' +
'<span class="dashicons dashicons-warning"></span>' +
'Ошибка AJAX: ' + error +
'</div>'
);
button.prop('disabled', false).text('Загрузить товары');
});
}
// Отображение списка товаров
function displayProducts(products) {
const productsList = $('#products-list');
if (products.length === 0) {
productsList.html('<div class="wplife-wb-info">Товары не найдены</div>');
return;
}
let html = '<div class="wplife-wb-products-grid">';
products.forEach(function(product) {
const status = product.status || 'not_exported';
const statusText = getStatusText(status);
const statusClass = getStatusClass(status);
html += `
<div class="wplife-wb-product-item" data-product-id="${product.id}">
<label class="wplife-wb-product-label">
<input type="checkbox" name="product_ids[]" value="${product.id}" class="wplife-wb-product-checkbox">
<div class="wplife-wb-product-info">
<div class="wplife-wb-product-name">${escapeHtml(product.name)}</div>
<div class="wplife-wb-product-details">
<span class="wplife-wb-product-sku">Артикул: ${escapeHtml(product.sku)}</span>
<span class="wplife-wb-product-price">Цена: ${product.price} руб.</span>
</div>
<div class="wplife-wb-product-status ${statusClass}">
${statusText}
</div>
</div>
</label>
<button class="button button-small wplife-wb-export-single" data-product-id="${product.id}">
Экспортировать
</button>
</div>
`;
});
html += '</div>';
productsList.html(html);
// Обработчики для чекбоксов
$('.wplife-wb-product-checkbox').on('change', function() {
const productId = $(this).val();
if ($(this).is(':checked')) {
selectedProducts.add(productId);
} else {
selectedProducts.delete(productId);
}
updateExportButton();
});
// Обработчики для одиночного экспорта
$('.wplife-wb-export-single').on('click', function() {
const productId = $(this).data('product-id');
exportSingleProduct(productId, $(this));
});
}
// Экспорт одного товара
function exportSingleProduct(productId, button) {
if (isExporting) return;
isExporting = true;
const originalText = button.text();
button.prop('disabled', true).text(wplife_wb_ajax.strings.exporting);
$.post(wplife_wb_ajax.ajax_url, {
action: 'export_single_product',
product_id: productId,
nonce: wplife_wb_ajax.nonce
}, function(response) {
if (response.success) {
showExportSuccess('Товар успешно экспортирован в Wildberries');
// Обновляем статус товара
$(`.wplife-wb-product-item[data-product-id="${productId}"] .wplife-wb-product-status`)
.removeClass('wplife-wb-status-not-exported wplife-wb-status-error')
.addClass('wplife-wb-status-exported')
.text('Экспортирован');
} else {
showExportError(response.data);
$(`.wplife-wb-product-item[data-product-id="${productId}"] .wplife-wb-product-status`)
.removeClass('wplife-wb-status-not-exported wplife-wb-status-exported')
.addClass('wplife-wb-status-error')
.text('Ошибка');
}
}).fail(function(xhr, status, error) {
showExportError('Ошибка AJAX: ' + error);
}).always(function() {
button.prop('disabled', false).text(originalText);
isExporting = false;
});
}
// Массовый экспорт товаров
$('#bulk-export').on('click', function() {
if (isExporting || selectedProducts.size === 0) return;
const button = $(this);
const resultsDiv = $('#export-results');
const progressSpan = $('#export-progress');
isExporting = true;
button.prop('disabled', true);
const productIds = Array.from(selectedProducts);
let completed = 0;
const total = productIds.length;
resultsDiv.removeClass('wplife-wb-success wplife-wb-error')
.html('<div class="wplife-wb-loading">Начинаем экспорт...</div>');
function exportNext() {
if (completed >= total) {
// Все товары обработаны
isExporting = false;
button.prop('disabled', false);
progressSpan.text('');
return;
}
const productId = productIds[completed];
progressSpan.text(`(${completed + 1}/${total})`);
$.post(wplife_wb_ajax.ajax_url, {
action: 'export_single_product',
product_id: productId,
nonce: wplife_wb_ajax.nonce
}, function(response) {
completed++;
if (response.success) {
// Обновляем статус товара
$(`.wplife-wb-product-item[data-product-id="${productId}"] .wplife-wb-product-status`)
.removeClass('wplife-wb-status-not-exported wplife-wb-status-error')
.addClass('wplife-wb-status-exported')
.text('Экспортирован');
} else {
$(`.wplife-wb-product-item[data-product-id="${productId}"] .wplife-wb-product-status`)
.removeClass('wplife-wb-status-not-exported wplife-wb-status-exported')
.addClass('wplife-wb-status-error')
.text('Ошибка');
}
// Продолжаем со следующим товаром
exportNext();
}).fail(function() {
completed++;
$(`.wplife-wb-product-item[data-product-id="${productId}"] .wplife-wb-product-status`)
.removeClass('wplife-wb-status-not-exported wplife-wb-status-exported')
.addClass('wplife-wb-status-error')
.text('Ошибка');
exportNext();
});
}
// Начинаем экспорт
exportNext();
// Показываем итоговый результат через 2 секунды после завершения
setTimeout(function() {
if (completed === total) {
const successCount = $('.wplife-wb-status-exported').length;
const errorCount = $('.wplife-wb-status-error').length;
if (errorCount === 0) {
showExportSuccess(`Все товары (${successCount}) успешно экспортированы!`);
} else {
showExportError(`Экспорт завершен. Успешно: ${successCount}, С ошибками: ${errorCount}`);
}
}
}, (total * 1000) + 2000); // Добавляем запас времени
});
// Обновление состояния кнопки массового экспорта
function updateExportButton() {
const button = $('#bulk-export');
const count = selectedProducts.size;
if (count > 0) {
button.text(`Экспортировать выбранные товары (${count})`);
} else {
button.text('Экспортировать выбранные товары');
}
}
// Показать успешное сообщение
function showExportSuccess(message) {
$('#export-results')
.removeClass('wplife-wb-error')
.addClass('wplife-wb-success')
.html(
'<div class="wplife-wb-success-content">' +
'<span class="dashicons dashicons-yes-alt"></span>' +
'<div class="wplife-wb-message">' + message + '</div>' +
'</div>'
);
}
// Показать сообщение об ошибке
function showExportError(message) {
$('#export-results')
.removeClass('wplife-wb-success')
.addClass('wplife-wb-error')
.html(
'<div class="wplife-wb-error-content">' +
'<span class="dashicons dashicons-warning"></span>' +
'<div class="wplife-wb-message">' + message + '</div>' +
'</div>'
);
}
// Получить текст статуса
function getStatusText(status) {
const statusMap = {
'not_exported': 'Не экспортирован',
'exported': 'Экспортирован',
'error': 'Ошибка'
};
return statusMap[status] || 'Неизвестно';
}
// Получить CSS класс статуса
function getStatusClass(status) {
const classMap = {
'not_exported': 'wplife-wb-status-not-exported',
'exported': 'wplife-wb-status-exported',
'error': 'wplife-wb-status-error'
};
return classMap[status] || 'wplife-wb-status-not-exported';
}
// Экранирование HTML
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
// Автозагрузка товаров при открытии страницы
if ($('#products-list').length) {
setTimeout(loadProducts, 1000);
}
});
Шаг 2.4: CSS стили для админки (admin.css)
Создайте файл admin.css
/**
* WPLife Wildberries Export - Admin Styles
*
* @package WPLife_WB_Export
*/
.wplife-wb-admin {
margin-top: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
.wplife-wb-card {
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
margin-bottom: 20px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}
.wplife-wb-card h2 {
background: #f8f9fa;
border-bottom: 1px solid #ccd0d4;
padding: 12px 20px;
margin: 0;
font-size: 18px;
font-weight: 600;
color: #1d2327;
}
.wplife-wb-test-section,
.wplife-wb-export-section,
.wplife-wb-info-section {
padding: 20px;
}
.wplife-wb-test-section p,
.wplife-wb-export-section p {
margin-top: 0;
color: #646970;
}
.wplife-wb-export-controls {
margin-bottom: 15px;
}
.wplife-wb-export-controls .button {
margin-right: 8px;
margin-bottom: 8px;
}
/* Результаты операций */
.wplife-wb-result {
margin-top: 15px;
border-radius: 4px;
padding: 12px 15px;
}
.wplife-wb-success {
background: #edfaef;
border: 1px solid #68de81;
color: #0c622a;
}
.wplife-wb-error {
background: #fcf0f1;
border: 1px solid #f86368;
color: #8a2424;
}
.wplife-wb-loading {
color: #646970;
font-style: italic;
}
.wplife-wb-success-content,
.wplife-wb-error-content {
display: flex;
align-items: flex-start;
gap: 10px;
}
.wplife-wb-success-content .dashicons,
.wplife-wb-error-content .dashicons {
margin-top: 2px;
flex-shrink: 0;
}
.wplife-wb-success-content .dashicons {
color: #00a32a;
}
.wplife-wb-error-content .dashicons {
color: #d63638;
}
.wplife-wb-message {
flex: 1;
line-height: 1.4;
}
/* Сетка товаров */
.wplife-wb-products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 12px;
max-height: 500px;
overflow-y: auto;
padding: 10px;
border: 1px solid #dcdcde;
border-radius: 4px;
background: #f8f9fa;
}
.wplife-wb-product-item {
display: flex;
align-items: center;
justify-content: space-between;
background: #fff;
border: 1px solid #dcdcde;
border-radius: 4px;
padding: 12px;
transition: all 0.2s ease;
}
.wplife-wb-product-item:hover {
border-color: #8c8f94;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.wplife-wb-product-label {
display: flex;
align-items: flex-start;
gap: 12px;
flex: 1;
margin: 0;
cursor: pointer;
}
.wplife-wb-product-checkbox {
margin-top: 2px;
flex-shrink: 0;
}
.wplife-wb-product-info {
flex: 1;
min-width: 0;
}
.wplife-wb-product-name {
font-weight: 600;
color: #1d2327;
margin-bottom: 4px;
word-wrap: break-word;
}
.wplife-wb-product-details {
display: flex;
flex-direction: column;
gap: 2px;
margin-bottom: 6px;
}
.wplife-wb-product-sku,
.wplife-wb-product-price {
font-size: 12px;
color: #646970;
}
.wplife-wb-product-status {
display: inline-block;
padding: 2px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.wplife-wb-status-not-exported {
background: #f0f0f1;
color: #646970;
}
.wplife-wb-status-exported {
background: #edfaef;
color: #0c622a;
}
.wplife-wb-status-error {
background: #fcf0f1;
color: #8a2424;
}
.wplife-wb-export-single {
flex-shrink: 0;
margin-left: 10px;
}
/* Информационные блоки */
.wplife-wb-info {
background: #f0f6fc;
border: 1px solid #9ec2e6;
border-radius: 4px;
padding: 12px 15px;
color: #2c3338;
}
.wplife-wb-info .dashicons {
color: #72aee6;
margin-right: 8px;
}
/* Адаптивность */
@media (max-width: 782px) {
.wplife-wb-products-grid {
grid-template-columns: 1fr;
}
.wplife-wb-product-item {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.wplife-wb-product-label {
margin-bottom: 8px;
}
.wplife-wb-export-single {
align-self: flex-end;
margin-left: 0;
}
}
/* Анимации */
@keyframes wplife-wb-fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.wplife-wb-product-item {
animation: wplife-wb-fadeIn 0.3s ease;
}
/* Прогресс бар */
.wplife-wb-progress {
width: 100%;
height: 4px;
background: #f0f0f1;
border-radius: 2px;
overflow: hidden;
margin: 10px 0;
}
.wplife-wb-progress-bar {
height: 100%;
background: #2271b1;
transition: width 0.3s ease;
}
/* Уведомления */
.wplife-wb-notice {
padding: 10px 15px;
margin: 10px 0;
border-radius: 4px;
border-left: 4px solid;
}
.wplife-wb-notice-info {
background: #f0f6fc;
border-left-color: #72aee6;
color: #2c3338;
}
.wplife-wb-notice-success {
background: #edfaef;
border-left-color: #68de81;
color: #0c622a;
}
.wplife-wb-notice-warning {
background: #fef6ee;
border-left-color: #f2b824;
color: #8a4e00;
}
.wplife-wb-notice-error {
background: #fcf0f1;
border-left-color: #f86368;
color: #8a2424;
}
Часть 3: Установка и настройка плагина
Шаг 3.1: Установка плагина
Способ 1: Через файловый менеджер хостинга
- Войдите в панель управления вашим хостингом
- Откройте файловый менеджер
- Перейдите в папку
wp-content/plugins/ - Создайте новую папку
wplife-wb-export - Загрузите все 4 созданных файла в эту папку
Способ 2: Через FTP
- Подключитесь к вашему сайту по FTP/sFTP
- Перейдите в
/wp-content/plugins/ - Создайте папку
wplife-wb-export - Загрузите файлы:
wplife-wb-export.phpadmin.jsadmin.css
Шаг 3.2: Активация плагина
- В админке WordPress перейдите в «Плагины»
- Найдите плагин «WPLife Export to Wildberries»
- Нажмите «Активировать»
Шаг 3.3: Настройка плагина
- Перейдите в «Настройки» → «WB Экспорт»
- Заполните обязательные поля:
Обязательные настройки:
// ВСТАВЬТЕ ВАШ РЕАЛЬНЫЙ API КЛЮЧ ВМЕСТО ЭТОГО ТЕКСТА
API Ключ Wildberries: [ваш_тестовый_токен_из_кабинета_WB]
// НАСТРОЙТЕ ЭТИ ЗНАЧЕНИЯ ПОД ВАШ МАГАЗИН
Бренд по умолчанию: [Ваш Бренд]
Страна производства: [Россия]
- Нажмите «Сохранить настройки»
Шаг 3.4: Проверка подключения
- На странице настроек нажмите «Проверить подключение к WB API»
- Должно появиться сообщение: «Успешное подключение к WB API!»
Часть 4: Использование плагина
Шаг 4.1: Подготовка товаров к экспорту
Перед экспортом убедитесь, что товары имеют:
- Артикул (SKU) — обязательное поле
- Название — обязательное поле
- Цена — обязательное поле
- Описание — рекомендуется заполнить
Шаг 4.2: Экспорт товаров
Одиночный экспорт:
- Нажмите «Загрузить товары»
- Найдите нужный товар в списке
- Нажмите кнопку «Экспортировать» рядом с товаром
Массовый экспорт:
- Отметьте чекбоксы у нужных товаров
- Нажмите «Экспортировать выбранные товары»
- Следите за прогрессом в реальном времени
Шаг 4.3: Проверка результатов
После экспорта проверьте:
- Статус товара в списке (должен измениться на «Экспортирован»)
- Логи плагина (если включено логирование)
- Тестовое окружение Wildberries
Часть 5: Решение проблем
Частые ошибки и их решение
Ошибка: «API ключ не настроен»
- Решение: Введите корректный API ключ в настройках плагина
Ошибка: «У товара должен быть артикул (SKU)»
- Решение: Заполните поле «Артикул» в настройках товара WooCommerce
Ошибка: «Ошибка подключения к API»
- Решение: Проверьте интернет-соединение и корректность API ключа
Товары не экспортируются
- Решение: Проверьте, что используется тестовый токен и песочница Wildberries
Включение debug режима (для нахождения проблем при экспорте) опционально
Добавьте в wp-config.php:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
Логи будут сохраняться в /wp-content/debug.log
Часть 6: Переход на боевой режим
Замена тестового окружения на боевое
Для перехода на реальное использование:
- Замените API URL в коде плагина:
php
// БЫЛО (тестовое) private $api_base_url = 'https://content-api-sandbox.wildberries.ru'; // СТАЛО (боевое) private $api_base_url = 'https://content-api.wildberries.ru';
- Получите боевой токен в кабинете Wildberries:
- Тип токена: Базовый токен или Токен личного доступа
- Категории: Отметьте нужные разделы API
- Уровень доступа: Чтение и запись
- Обновите API ключ в настройках плагина
Важные примечания
Критически важные моменты:
private $api_key = '';— это переменная для хранения API ключа. Она автоматически заполняется из настроек плагина.- Тестовое окружение — все операции происходят в песочнице Wildberries, реальные данные не затрагиваются.
- Обязательные поля — товары без SKU не могут быть экспортированы.
- Лимиты API — соблюдайте ограничения Wildberries (300 запросов в минуту).
Рекомендации по доработке:
Для практического использования рекомендуется добавить:
- Очередь задач для обработки большого количества товаров
- Синхронизацию остатков
- Обработку заказов из Wildberries
- Расширенную обработку ошибок
- Кэширование запросов
Важное заключение
Данная статья представляет полноценное решение для интеграции WooCommerce с Wildberries. Код полностью рабочий и готов к использованию, но требует внимательной настройки и тестирования в тестовом окружении перед переходом на боевые данные.
Все файлы должны быть созданы точно по инструкции, а настройки заполнены реальными значениями из вашего кабинета Wildberries.
Для вопросов и доработок оставляйте комментарии на WPLIFE.RU!
Это только начало пути интеграции с Wildberries
Важно понимать, что созданная нами интеграция — это лишь первая ступень в освоении мощного API Wildberries. Мы рассмотрели только один метод (/content/v2/cards/upload) для создания карточек товаров, но это капля в море возможностей, которые предоставляет платформа.
Что еще можно автоматизировать через API Wildberries:
Управление товарами и остатками
- Обновление цен — метод
POST /public/api/v1/prices - Управление остатками — метод
POST /api/v2/stocks - Обновление скидок — метод
POST /public/api/v1/updateDiscounts - Получение списка товаров — метод
GET /content/v1/cards/cursor/list
Аналитика и отчетность
- Отчет по продажам — метод
GET /api/v1/supplier/reportDetailByPeriod - Аналитика воронки продаж — метод
GET /api/v1/supplier/funnel - Отчет по остаткам — метод
GET /api/v1/supplier/stocks - Финансовые отчеты — метод
GET /api/v1/supplier/orders
Управление заказами
- Получение новых заказов — метод
GET /api/v1/orders - Обновление статусов заказов — метод
PUT /api/v1/orders - Работа с поставками — метод
POST /api/v1/supplies
Продвижение и реклама
- Управление рекламными кампаниями — методы
GET/POST /api/v1/adverts - Аналитика ставок — метод
GET /api/v1/adverts/update
Коммуникации
- Работа с отзывами — метод
GET /api/v1/feedbacks - Ответы на вопросы — метод
GET /api/v1/questions - Чат с покупателями — метод
GET /api/v1/chat
Куда двигаться дальше?
- Изучите полную документацию на dev.wildberries.ru — там вы найдете все доступные методы с примерами запросов и ответов.
- Начните с тестового окружения — как мы это сделали в статье, прежде чем переходить на боевые данные.
- Постепенно расширяйте функциональность — добавьте синхронизацию остатков, автоматизацию заказов, работу с отзывами.
- Следите за обновлениями — Wildberries регулярно обновляет свое API, добавляя новые возможности.
Помните: успешная интеграция — это не разовый проект, а непрерывный процесс улучшения и адаптации под меняющиеся требования платформы и вашего бизнеса.
У вас есть вопросы по другим методам API Wildberries? Хотите разобрать следующую интеграцию? Пишите в комментариях — обязательно поможем!
Что дальше? Практические шаги и перспективы
Теперь у вас есть работающий инструмент для экспорта товаров в Wildberries. Но на этом путь интеграции только начинается!
Ваши следующие шаги:
- Протестируйте интеграцию на нескольких товарах
- Проверьте логи и убедитесь, что всё работает корректно
- Настройте автоэкспорт для новых товаров
- Добавьте обработку ошибок под ваши нужды
Куда развивать систему дальше:
- Синхронизация остатков — чтобы избежать продаж отсутствующего товара
- Автоматическое обновление цен — для гибкой ценовой политики
- Импорт заказов из Wildberries в WooCommerce
- Сбор аналитики и автоматизация отчетов
Помните:
Этот код — фундамент для построения более сложных решений. Каждый бизнес уникален, и именно вы лучше всего знаете, какие процессы нужно автоматизировать в первую очередь.
Нашли ошибку в коде? Есть идеи по улучшению?
Делитесь в комментариях — вместе мы сделаем интеграцию еще лучше!
Остались вопросы?
Задавайте их ниже — я с радостью помогу разобраться со сложными моментами и подскажу направления для дальнейшего развития вашей системы.
Успешных продаж на Wildberries!
