Skip to content

Building Custom Commission Logic in Dokan Multi-Vendor

A

Al Amin Ahamed

Senior Engineer

11 min read
𝕏 in

Dokan turns WooCommerce into a multi-vendor marketplace. Out of the box, vendors get a flat percentage commission and that's it. Every serious marketplace I've worked on outgrows this within a quarter — they need tiered rates, category-specific splits, promotional bonuses, or vendor-class commissions.

Here's how Dokan's commission system actually works under the hood, and how to extend it without forking.

The Default Commission Flow

When an order is paid:

  1. dokan_after_order_create_commission fires
  2. Dokan reads vendor's dokan_admin_percentage and dokan_admin_percentage_type user meta
  3. It calculates the admin's cut and writes a row to wp_dokan_orders
  4. The vendor's earning is order_total - admin_cut

The admin percentage type can be percentage, flat, or combine (combination of percentage + flat fee).

The Filter You'll Use Most

apply_filters('dokan_get_seller_percentage', $percentage, $product_id, $vendor_id, $order_item_id);

This fires for every line item. Override it to implement custom logic:

add_filter('dokan_get_seller_percentage', function ($percentage, $product_id, $vendor_id, $order_item_id) { // Premium vendors keep more if (get_user_meta($vendor_id, 'is_premium_vendor', true) === 'yes') { return 90; // 90% to vendor } // Promotional category gets a one-month boost if (has_term('back-to-school', 'product_cat', $product_id) && time() < strtotime('2026-09-01')) { return 95; } return $percentage; }, 10, 4);

Per-Category Commission

A common ask: "Electronics get 10% commission, Books get 25%."

add_filter('dokan_get_seller_percentage', function ($percentage, $product_id) { $category_rates = array( 'electronics' => 90, 'books' => 75, 'fashion' => 80, ); $terms = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'slugs')); foreach ($terms as $slug) { if (isset($category_rates[$slug])) { return $category_rates[$slug]; } } return $percentage; }, 10, 2);

Note: WordPress runs this filter on every product on every cart calculation. Cache aggressively if you have complex logic — wp_cache_get('dokan_pct_' . $product_id) saves ~50ms on cart pages with many items.

Subscription Commissions

Dokan Pro adds subscription products. The renewal commission is a separate filter:

add_filter('dokan_subscription_get_recurring_commission', function ($commission, $vendor_id, $order_id) { // Apply a different rate to renewals return $commission * 1.05; // 5% bonus on renewals }, 10, 3);

Withdrawal Hooks

Vendor payouts go through Dokan's withdrawal system. Hook into approval to add custom validation:

add_action('dokan_withdraw_request_approved', function ($withdraw) { // Notify the vendor's manager $vendor_id = $withdraw->user_id; $manager_id = get_user_meta($vendor_id, 'account_manager', true); if ($manager_id) { wp_mail( get_userdata($manager_id)->user_email, sprintf('Withdrawal approved for vendor %d', $vendor_id), sprintf('Amount: %s', $withdraw->amount) ); } });

The Database Tables

For complex reporting, work directly with the schema:

-- Vendor earnings per order SELECT seller_id, SUM(net_amount) as earnings FROM wp_dokan_orders WHERE order_status IN ('wc-completed', 'wc-processing') AND DATE(created) >= '2026-04-01' GROUP BY seller_id; -- Pending withdrawals SELECT user_id, SUM(amount) as pending FROM wp_dokan_withdraw WHERE status = 0 GROUP BY user_id;

Testing Commission Logic

I keep a Pest suite that exercises the commission filters with fixtures:

it('applies premium vendor rate', function () { $vendor = $this->createVendor(array('is_premium_vendor' => 'yes')); $product = $this->createProduct($vendor); $rate = apply_filters('dokan_get_seller_percentage', 80, $product->ID, $vendor->ID, 0); expect($rate)->toBe(90); });

createVendor and createProduct are Pest factories I built using Dokan's vendor registration API — not the public REST one, the internal dokan_create_vendor().

What Not To Do

  • Don't override Dokan_Orders_Manager — Dokan's internals change between versions and your override breaks on upgrades. Use filters.
  • Don't write to wp_dokan_orders directly — go through dokan()->orders->update() so the right hooks fire.
  • Don't trust $order_item_id in old hooks — pre-Dokan-3.x, this was sometimes 0. Use the product+order combination as your stable key.

The filter API is enough for 95% of customisations. If you find yourself reaching for class extension, ask in the support forum first — there's usually a hook you missed.

Share 𝕏 in
A

Al Amin Ahamed

Senior software engineer & AI practitioner. Laravel, PHP, WordPress plugins, WooCommerce extensions.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.