~/icsd.ir — bash
SYSTEM_ONLINE

🏪 فصل ۱۲: مارکت‌پلیس چندفروشنده

در این فصل یاد می‌گیرید چطور ووکامرس را به یک مارکت‌پلیس چندفروشنده (مثل دیجی‌کالا، باسلام) تبدیل کنید — هم با افزونه‌های آماده و هم با پیاده‌سازی اختصاصی.

🎯 مفهوم Multi-Vendor

در یک مارکت‌پلیس:

  • چندین فروشنده روی یک سایت محصول می‌فروشند
  • سایت اصلی از هر فروش کمیسیون می‌گیرد
  • هر فروشنده داشبورد، آمار، پروفایل اختصاصی دارد
  • خریدار از چند فروشنده در یک سفارش می‌خرد
  • سفارش به فروشندگان مربوطه split می‌شود

📊 مقایسه افزونه‌های موجود

افزونه ویژگی‌های شاخص قیمت
Dokan Lite محبوب‌ترین، رابط زیبا، RTL رایگان + Pro $149
WC Vendors سبک، انعطاف‌پذیر رایگان + Pro $159
WCFM Marketplace کامل‌ترین، RTL، چت داخلی رایگان + Ultimate $79
YITH Multi Vendor یکپارچه با اکوسیستم YITH $149/سال
✅ توصیه برای ایران: Dokan یا WCFM – هر دو رایگان شروع می‌شوند، RTL کامل دارند، و امکان توسعه گسترده دارند.

🆔 ساخت نقش Vendor از صفر

اگر می‌خواهید بدون افزونه و کاملاً سفارشی پیاده‌سازی کنید:

includes/setup-vendor-role.php
// ساخت نقش Vendor در فعال‌سازی
register_activation_hook(__FILE__, function() {
    add_role('vendor', 'فروشنده', [
        'read'                   => true,
        'edit_posts'             => false,
        'delete_posts'           => false,
        'edit_products'          => true,
        'publish_products'       => false, // نیاز به تایید
        'delete_products'        => true,
        'edit_published_products'=> true,
        'edit_others_products'   => false, // فقط محصولات خود
        'manage_woocommerce_orders' => false, // فقط سفارش‌های خود
        'view_woocommerce_reports'  => true,
        'upload_files'           => true,
    ]);
});

// محدود کردن دسترسی به محصولات دیگران
add_action('pre_get_posts', function($query) {
    if (!is_admin() || !$query->is_main_query()) return;
    
    $screen = function_exists('get_current_screen') ? get_current_screen() : null;
    if (!$screen || $screen->post_type !== 'product') return;
    
    if (current_user_can('vendor') && !current_user_can('manage_options')) {
        $query->set('author', get_current_user_id());
    }
});

// مخفی کردن منوهای غیرضروری از پنل
add_action('admin_menu', function() {
    if (!current_user_can('vendor')) return;
    
    remove_menu_page('index.php');
    remove_menu_page('edit-comments.php');
    remove_menu_page('themes.php');
    remove_menu_page('plugins.php');
    remove_menu_page('users.php');
    remove_menu_page('tools.php');
    remove_menu_page('options-general.php');
}, 999);

// تغییر ادمین‌بار برای فروشنده
add_action('admin_bar_menu', function($wp_admin_bar) {
    if (!current_user_can('vendor')) return;
    
    $wp_admin_bar->remove_node('comments');
    $wp_admin_bar->remove_node('new-content');
    
    $wp_admin_bar->add_node([
        'id'    => 'vendor-dashboard',
        'title' => '📊 داشبورد فروشنده',
        'href'  => home_url('/vendor-dashboard/'),
    ]);
}, 999);

🛍️ محصولات هر فروشنده

نمایش فروشنده در صفحه محصول

add_action('woocommerce_single_product_summary', function() {
    global $product;
    $author_id = $product->get_meta('_vendor_id') ?: get_post_field('post_author', $product->get_id());
    $vendor = get_userdata($author_id);
    
    if (!$vendor || !in_array('vendor', $vendor->roles)) return;
    
    $vendor_name = get_user_meta($vendor->ID, '_vendor_shop_name', true) ?: $vendor->display_name;
    $vendor_url = get_user_meta($vendor->ID, '_vendor_shop_url', true);
    ?>
    

🏪 فروشنده:

ID, '_vendor_avg_rating', true); if ($rating) { echo '⭐ امتیاز: ' . esc_html($rating) . ' از ۵'; } ?>

صفحه فروشگاه فروشنده

// rewrite rule برای /vendor/{slug}/
add_action('init', function() {
    add_rewrite_rule('^vendor/([^/]+)/?$', 'index.php?vendor_slug=$matches[1]', 'top');
    add_rewrite_tag('%vendor_slug%', '([^&]+)');
});

// قالب
add_action('template_redirect', function() {
    $slug = get_query_var('vendor_slug');
    if (!$slug) return;
    
    $vendor = get_user_by('slug', $slug);
    if (!$vendor || !in_array('vendor', (array) $vendor->roles)) {
        global $wp_query;
        $wp_query->set_404();
        return;
    }
    
    set_query_var('current_vendor', $vendor);
    locate_template(['vendor-shop.php'], true);
    exit;
});

// قالب vendor-shop.php
?>
<?php get_header(); ?>
<div class="vendor-shop">
    <h1>🏪 <?= esc_html($vendor_name) ?></h1>
    
    <?php
    $products = wc_get_products([
        'author' => $vendor->ID,
        'limit'  => 12,
        'status' => 'publish',
    ]);
    
    foreach ($products as $product) {
        wc_get_template_part('content', 'product');
    }
    ?>
</div>
<?php get_footer(); ?>

✂️ تقسیم سفارش بین فروشندگان

وقتی خریدار از چند فروشنده محصول داشته باشد، باید سفارش به sub-orderها تقسیم شود:

add_action('woocommerce_checkout_order_processed', function($order_id) {
    $order = wc_get_order($order_id);
    
    // گروه‌بندی آیتم‌ها بر اساس فروشنده
    $vendors_items = [];
    foreach ($order->get_items() as $item) {
        $product_id = $item->get_product_id();
        $vendor_id = get_post_meta($product_id, '_vendor_id', true)
                     ?: get_post_field('post_author', $product_id);
        
        $vendors_items[$vendor_id][] = $item;
    }
    
    // اگر فقط یک فروشنده دارد، چیزی split نکن
    if (count($vendors_items) <= 1) return;
    
    // برای هر فروشنده، sub-order بساز
    foreach ($vendors_items as $vendor_id => $items) {
        $sub_order = wc_create_order([
            'customer_id'   => $order->get_customer_id(),
            'parent'        => $order_id,
        ]);
        
        $sub_total = 0;
        foreach ($items as $item) {
            $sub_order->add_product(
                $item->get_product(),
                $item->get_quantity(),
                ['total' => $item->get_total()]
            );
            $sub_total += $item->get_total();
        }
        
        // کپی آدرس
        $sub_order->set_address($order->get_address('billing'), 'billing');
        $sub_order->set_address($order->get_address('shipping'), 'shipping');
        
        $sub_order->update_meta_data('_vendor_id', $vendor_id);
        $sub_order->update_meta_data('_parent_order_id', $order_id);
        $sub_order->update_meta_data('_is_sub_order', 'yes');
        
        // محاسبه کمیسیون
        $commission_rate = get_user_meta($vendor_id, '_commission_rate', true) ?: 10; // ۱۰٪
        $commission = ($sub_total * $commission_rate) / 100;
        $vendor_earning = $sub_total - $commission;
        
        $sub_order->update_meta_data('_commission_amount', $commission);
        $sub_order->update_meta_data('_vendor_earning', $vendor_earning);
        $sub_order->update_meta_data('_commission_rate', $commission_rate);
        
        $sub_order->set_status('processing');
        $sub_order->calculate_totals();
        $sub_order->save();
        
        // ثبت در جدول کمیسیون
        record_commission($vendor_id, $sub_order->get_id(), $commission, $vendor_earning);
    }
});

💰 محاسبه کمیسیون

جدول کمیسیون‌ها

CREATE TABLE wp_icsd_vendor_commissions (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    vendor_id BIGINT UNSIGNED NOT NULL,
    order_id BIGINT UNSIGNED NOT NULL,
    sub_total DECIMAL(15,2) NOT NULL,
    commission_rate DECIMAL(5,2) NOT NULL,
    commission_amount DECIMAL(15,2) NOT NULL,
    vendor_earning DECIMAL(15,2) NOT NULL,
    status ENUM('pending', 'paid', 'cancelled') DEFAULT 'pending',
    created_at DATETIME NOT NULL,
    paid_at DATETIME,
    KEY vendor_id (vendor_id),
    KEY status (status)
);

کمیسیون پویا (بر اساس دسته)

function calculate_commission_rate($vendor_id, $product_id) {
    // ۱. کمیسیون اختصاصی محصول
    $product_rate = get_post_meta($product_id, '_custom_commission', true);
    if ($product_rate !== '') return floatval($product_rate);
    
    // ۲. کمیسیون اختصاصی دسته
    $cats = wp_get_post_terms($product_id, 'product_cat', ['fields' => 'ids']);
    foreach ($cats as $cat_id) {
        $cat_rate = get_term_meta($cat_id, '_category_commission', true);
        if ($cat_rate !== '') return floatval($cat_rate);
    }
    
    // ۳. کمیسیون اختصاصی فروشنده
    $vendor_rate = get_user_meta($vendor_id, '_commission_rate', true);
    if ($vendor_rate !== '') return floatval($vendor_rate);
    
    // ۴. پیش‌فرض سایت
    return floatval(get_option('icsd_default_commission', 10));
}

کمیسیون پلکانی

// هرچه فروش بالاتر، کمیسیون کمتر
function tier_commission($vendor_id, $monthly_sales) {
    $tiers = [
        ['min' => 0,         'rate' => 15], // 15٪
        ['min' => 5_000_000, 'rate' => 12],
        ['min' => 20_000_000, 'rate' => 10],
        ['min' => 50_000_000, 'rate' => 8],
        ['min' => 100_000_000, 'rate' => 5],
    ];
    
    $rate = 15;
    foreach ($tiers as $tier) {
        if ($monthly_sales >= $tier['min']) {
            $rate = $tier['rate'];
        }
    }
    return $rate;
}

📊 داشبورد فروشنده

صفحه داشبورد در فرانت‌اند

// shortcode
add_shortcode('vendor_dashboard', function() {
    if (!is_user_logged_in()) {
        return wp_login_form(['echo' => false]);
    }
    
    $user = wp_get_current_user();
    if (!in_array('vendor', $user->roles)) {
        return '

شما فروشنده نیستید.

'; } ob_start(); ?> get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}icsd_vendor_commissions WHERE vendor_id = %d AND DATE(created_at) = CURDATE()", $vendor_id )); $month_earning = $wpdb->get_var($wpdb->prepare( "SELECT SUM(vendor_earning) FROM {$wpdb->prefix}icsd_vendor_commissions WHERE vendor_id = %d AND status = 'paid' AND MONTH(created_at) = MONTH(NOW())", $vendor_id )); $pending_payout = $wpdb->get_var($wpdb->prepare( "SELECT SUM(vendor_earning) FROM {$wpdb->prefix}icsd_vendor_commissions WHERE vendor_id = %d AND status = 'pending'", $vendor_id )); $product_count = count_user_posts($vendor_id, 'product'); ?>

سفارش‌های امروز

درآمد ماه

قابل برداشت

تعداد محصولات

💸 سیستم تسویه

درخواست برداشت توسط فروشنده

// فرم درخواست برداشت
function vendor_payout_request_form() {
    $vendor_id = get_current_user_id();
    $available = get_vendor_available_balance($vendor_id);
    
    if (isset($_POST['payout_amount']) && wp_verify_nonce($_POST['_payout_nonce'] ?? '', 'request_payout')) {
        $amount = floatval($_POST['payout_amount']);
        $iban = sanitize_text_field($_POST['iban'] ?? '');
        
        if ($amount > $available) {
            echo '

مبلغ بیشتر از موجودی است

'; } elseif ($amount < 100000) { echo '

حداقل برداشت ۱۰۰ هزار تومان است

'; } else { global $wpdb; $wpdb->insert($wpdb->prefix . 'icsd_payout_requests', [ 'vendor_id' => $vendor_id, 'amount' => $amount, 'iban' => $iban, 'status' => 'pending', 'created_at'=> current_time('mysql'), ]); echo '

درخواست شما ثبت شد

'; } } ?>

موجودی قابل برداشت:

get_var($wpdb->prepare( "SELECT COALESCE(SUM(vendor_earning), 0) FROM {$wpdb->prefix}icsd_vendor_commissions WHERE vendor_id = %d AND status = 'pending'", $vendor_id )); $pending_payouts = $wpdb->get_var($wpdb->prepare( "SELECT COALESCE(SUM(amount), 0) FROM {$wpdb->prefix}icsd_payout_requests WHERE vendor_id = %d AND status IN ('pending', 'approved')", $vendor_id )); return max(0, $earned - $pending_payouts); }

تایید و پرداخت توسط مدیر

// در پنل ادمین، صفحه payouts
add_action('admin_menu', function() {
    add_menu_page('درخواست‌های برداشت', 'برداشت‌ها', 'manage_options',
                  'icsd-payouts', 'render_payouts_page', 'dashicons-money-alt', 56);
});

function render_payouts_page() {
    global $wpdb;
    
    if (isset($_POST['approve_id']) && check_admin_referer('approve_payout')) {
        $id = absint($_POST['approve_id']);
        $wpdb->update($wpdb->prefix . 'icsd_payout_requests',
            ['status' => 'paid', 'paid_at' => current_time('mysql')],
            ['id' => $id]);
        
        // علامت کمیسیون‌های مربوطه به‌عنوان paid
        // ... (پیاده‌سازی منطق)
    }
    
    $requests = $wpdb->get_results(
        "SELECT r.*, u.display_name FROM {$wpdb->prefix}icsd_payout_requests r
         JOIN {$wpdb->users} u ON r.vendor_id = u.ID
         WHERE r.status = 'pending'"
    );
    ?>
    
فروشندهمبلغشباتاریخعملیات
display_name) ?> amount) ?> iban) ?> created_at) ?>

📝 خلاصه فصل

  • مفهوم Multi-Vendor و افزونه‌های آماده (Dokan، WCFM)
  • ساخت نقش Vendor با capabilities محدود
  • محدود کردن دسترسی به محصولات خود
  • تقسیم سفارش به sub-order برای هر فروشنده
  • کمیسیون پویا، پلکانی و دسته‌ای
  • داشبورد فرانت‌اند فروشنده
  • سیستم درخواست برداشت با شبا
💡 پیشنهاد: برای پروژه‌های واقعی، شروع با Dokan یا WCFM Free بهتر است؛ سپس فقط بخش‌های لازم را با کد سفارشی توسعه دهید.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.