=== BazaarKit – Multivendor Shipping for WooCommerce ===
Contributors: bazaarkit
Tags: dokan, multivendor, shipping, marketplace, vendor
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.3.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Let vendors set their own shipping price per listing. Buyers pay one bundled rate per shop. Multivendor shipping for Dokan Lite or plain WooCommerce.

== Description ==

One of your vendors posts silk scarves in a padded envelope for £2.50. Another ships a coffee table by pallet. WooCommerce gives you one shipping rate for the whole site, Dokan Lite does not change that, and every answer you have been given so far ends with "buy Dokan Pro".

BazaarKit gives each vendor their own postage price, set by them, on the listing itself. A buyer taking three things from the same shop pays one bundled rate, not three. There are no zones to draw by hand, no carrier account, no API key, and nothing to set up before it works.

It is a companion, not a replacement. Rates go in through WooCommerce's own shipping system, and vendors type their price into the Dokan dashboard they already use. Both plugins carry on doing everything they did before.

= What it does =

* **A postage price per listing, set by the vendor.** The field appears in the Dokan vendor dashboard when they add or edit a product, and in the Shipping tab of the WooCommerce product editor for you. Whatever they type is the postage on that item, quoted to a buyer anywhere in the world.
* **One rate per shop, not one per item.** Dokan splits a basket into a package per vendor, and each package is priced as a single line — "Bundled shipping (3 items)". You decide whether that is the highest of the item prices (the default) or the sum of them.
* **Free postage, said in words.** A price of 0 reaches the buyer as "Free shipping", not "£0.00".
* **A default price for listings nobody has got to yet**, so a shop halfway through filling in its catalogue still quotes something sensible.
* **Holiday mode.** A vendor going away presses one button, and their listings vanish from the shop, search, category pages, product rails, block grids and the Store API, and cannot be added to a basket. Nothing is unpublished and nothing is deleted. Their store page explains itself to buyers, and coming back is the same button.
* **A departed vendor leaves no live listings behind.** Delete a user in WordPress and it makes you choose: destroy everything they wrote, or hand it all to another account, usually yours. Neither is right for a marketplace — one bins the catalogue, the other leaves live listings for stock nobody has, under your name, until a customer asks where their parcel is. BazaarKit moves those listings to the trash instead, recoverable, with past orders left exactly as they were.

It works quietly. No API calls, no live rate lookups, no key to enter, no account to make, and nothing that stops working after a while. BazaarKit's own code makes no network requests. The one thing in the zip that can — the bundled Freemius SDK — is described under "External services" below, and it asks before it does.

= Running plain WooCommerce, with no vendor plugin? =

That works too. BazaarKit does not require a multivendor plugin: where there is no vendor package to read, the product's author is treated as the vendor, so per-author postage behaves the same way.

= Where it comes from =

Every part of this started on Preloved Bazaar, a working UK multivendor fashion marketplace, where shipping, escrow, offers and disputes all shipped first and got fixed where real money moves. Marketplace plumbing is unglamorous and very easy to get subtly wrong. A rate that looks right in the basket and charges differently at checkout costs a vendor real income; a shipping method that quietly stops being offered costs them all of it. That is the part this plugin tries to get right, and the reason its code is so heavily commented.

= The optional add-ons =

Six other plugins add separate marketplace features — escrow, messaging, disputes, reviews, notifications and per-continent postage. Each is its own download, bought and installed on its own. Nothing described above depends on them, and nothing above behaves differently when they are not there.

* **International Shipping** — every listing already posts worldwide at the vendor's flat rate. This add-on makes that rate precise: a different price per continent, so Ireland and New Zealand need not cost the same, and a region left blank is simply not posted to.
* **Escrow & Buyer Protection** — holds each vendor's payout on Stripe Connect until delivery, with a buyer-protection window, automatic refund reversal and chargeback claw-back.
* **Messaging & Offers** — a private buyer-to-vendor inbox with image attachments, and make-an-offer negotiation where an accepted offer checks out at the agreed price.
* **Disputes & Resolution** — buyer disputes, vendor responses, auto-escalation and an admin mediation queue.
* **Mutual Reviews & Ratings** — two-way buyer and seller reviews under one store rating.
* **Notifications & Email Preferences** — in-app notifications, an email-preference centre with one-click unsubscribe, and a searchable log of every email your site sends.

They are £5 / $5 a month each, cancel any time — except Messaging & Offers, which is £7 / $7 because it is two features in one purchase. There is also a one-off lifetime bundle covering all six and anything released after them.

If you would rather try than read: [bazaarkit.online](https://bazaarkit.online/) has a marketplace you can log into and drive — set a shipping price, put two shops in one basket, see what a vendor sees — and the documentation is at [bazaarkit.online/docs/](https://bazaarkit.online/docs/).

== Installation ==

1. Install and activate **WooCommerce**, and **[Dokan Lite](https://wordpress.org/plugins/dokan-lite/)** if you are running a multivendor marketplace.
2. Install BazaarKit from **Plugins → Add New**, or upload it to `/wp-content/plugins/`, and activate it.
3. That is it. Vendors now see a shipping-price field when they add or edit a listing, and WooCommerce is left able to quote shipping even on a store with no zones of its own.
4. Optional: set a default price for unpriced listings, and choose how several items from one shop are charged, under **BazaarKit → Settings**.

== Frequently Asked Questions ==

= Do I need Dokan Pro? =

No. Vendor shipping is one of the things Dokan Lite leaves to Dokan Pro — its own "Pro Features" screen lists Shipping Management among them. This plugin adds per-vendor flat-rate postage to Dokan Lite, free, and keeps working whether or not you ever buy Pro.

= Is this a replacement for Dokan? =

No, and it is not trying to be. It is a companion that sits on top of Dokan Lite and WooCommerce. Both keep doing everything they already do, and rates are added through WooCommerce's own shipping system rather than around it.

= Does it work without Dokan? =

Yes. On a plain WooCommerce shop the product's author is treated as the vendor, so per-author shipping works the same way.

= I already have shipping zones set up. Will this break them? =

No. BazaarKit only takes over a package once a vendor has actually put a price on one of the listings in it. Until then your existing rates are quoted exactly as before, untouched. That rule matters more than it sounds, and it is why it exists: without it, activating the plugin on an established shop replaced that store's own rates with £0.00. It was caught on a real basket during testing, and the guard has been there ever since.

= What if a vendor never sets a price? =

Then that shop keeps whatever shipping your store already offers. If some of a shop's listings are priced and others are not, the unpriced ones fall back to the default you set under **BazaarKit → Settings**, so the bundle still adds up.

= A buyer bought four things from one vendor. What do they pay? =

One shipping charge, not four. By default it is the highest of the four item prices, which is how most marketplaces treat a bundled parcel. Quantity counts in both modes — order two of the same jacket and that line is two lots of its postage — so it is the dearest line, not the dearest item, that sets the rate. Switch it to the sum under **BazaarKit → Settings** if you would rather.

= Can a vendor offer free postage? =

Yes. A price of 0 is a real answer, not a blank, and the buyer sees "Free shipping".

= Do I have to build WooCommerce shipping zones? =

No. WooCommerce only asks plugins for shipping rates when it believes the basket needs shipping at all, and with no shipping method configured anywhere it decides nothing does — so vendors would silently earn nothing towards postage. BazaarKit makes sure that condition is met on a store that has nothing of its own, and leaves a store that already has zones completely alone.

= Will it work with my theme? =

Yes. It uses documented WooCommerce and Dokan hooks and hardcodes no theme markup, so it behaves the same on Storefront, on a block theme, on a page-builder theme or on something you wrote yourself. Holiday mode covers the block product grids and the Store API as well as the classic shop loop, which is what a block theme actually renders.

= Will it slow my site down? =

It should not be measurable. BazaarKit's own code makes no HTTP requests, there are no external rate lookups and no per-page licence check. Shipping is worked out from product meta WooCommerce has already loaded. Holiday mode reads one option that is deliberately kept out of WordPress's autoloaded set, and when no vendor is away it stops before doing any work at all.

= Is VAT charged on the postage? =

If your store charges tax on shipping, yes — BazaarKit's rates are taxed the way WooCommerce's own flat rate would be taxed on the same basket, including the default "inherit" shipping tax class, and a VAT-exempt customer is not charged it.

= Does it work with variable products? =

Yes. The price is read from the parent product and covers its variations; where the parent works out at zero, a price set on the variation is used instead.

= What happens to existing orders? =

Nothing. Shipping is decided while a basket is being priced, so orders already placed are never revisited. That holds when a vendor leaves as well: their listings go to the trash, and the orders they already fulfilled keep their own record of what was bought and what it cost.

= Someone follows an old link to an away vendor's product. What do they see? =

The product page, marked unavailable, with no working buy button — not a 404. Hiding a shop for a fortnight should not break every bookmark, email link and search result pointing at it. Adding the item to a basket is refused on the server as well, so a stale page cannot be used to buy anyway.

= Where do vendors find the holiday switch? =

At the top of their Dokan dashboard, rather than buried in a settings tab, because the moment anyone needs it is the moment they are leaving tomorrow and orders are still arriving.

= Does it work on multisite? =

Yes, and the two places that usually go wrong on a network are handled: deleting a user through Network Admin trashes their listings the same way a single-site delete does, and clearing cached pages after a holiday switch is scoped to the site it happened on rather than flushing the whole network.

= Does it support WooCommerce's High-Performance Order Storage? =

Yes. It declares compatibility with the custom order tables, and touches no order storage of its own.

= Do I need a licence key or an account? =

No. There is nothing to activate and no key to enter. Everything described above runs on any WordPress site the moment you switch the plugin on.

= What happens if I deactivate it? =

Your vendors' shipping prices stay in the database untouched, and reactivating picks up where you left off. Uninstalling removes BazaarKit's own settings and takes back the shipping method it added for you, unless you have since given that method a price of your own — at which point it is yours, and it is left alone.

= Are the add-ons required? =

No. Everything under "What it does" works on its own, indefinitely, with none of them installed. They are separate plugins that add separate features, and most marketplaces will never need one.

== Screenshots ==

1. A vendor sets their own postage on a listing, in the Dokan dashboard they already use. Whatever they type is the postage on that item.
2. Checkout with two shops in one basket: a rate each, and "Bundled shipping (2 items)" where a shop is sending more than one thing.
3. Settings — the default price for listings nobody has priced, and whether several items from one shop are charged at the highest rate or the sum.
4. Holiday mode: the vendor's own shop paused. Their listings are hidden and nobody can buy, with nothing unpublished and nothing to undo.
5. The same field in wp-admin, on the WooCommerce product editor's Shipping tab, for when the shop owner sets postage themselves.

== External services ==

**BazaarKit's own code makes no network requests.** Every feature described above works with
the site offline: nothing is fetched, nothing is checked, no licence is validated, and no data
about your site leaves it unless you opt in, or you click through to buy an add-on.

It does bundle the **Freemius** SDK (Freemius, Inc.), which is how the separately-sold add-ons
identify themselves if you ever install one. In this free plugin the SDK does one thing:

* On activation it shows an **opt-in screen, and you can click "Skip".** If you skip it, the SDK
  contacts nobody. If you opt in, it sends your site URL, admin email and WordPress/PHP/plugin
  versions to Freemius as usage diagnostics, and you can disconnect any time from **BazaarKit →
  Account**. Nothing in the plugin behaves differently either way.

If you click an add-on's Buy button, your site URL, admin email and WordPress/PHP versions are
passed to the Freemius checkout so the licence can be bound to this install. That cannot happen
on this plugin alone — there is nothing here to buy — but it is worth knowing before you install
one of the paid add-ons.

If you opt in, or buy an add-on, that data goes to Freemius under their Terms of Service
(https://freemius.com/terms/) and Privacy Policy (https://freemius.com/privacy/).

Each optional **paid add-on** is a separate plugin you install yourself. Those connect to Freemius
to validate their own licence and deliver their own updates, and each discloses what it sends in
its own readme.

== Changelog ==

= 0.3.4 =
* **Fixed: a shop could start shipping free without anyone choosing to.** Saving a product from Dokan's newer dashboard did not carry a shipping price — it saves through Dokan's REST route, which sends its own fields — and BazaarKit read that silence as a deliberate "free". It then took the shop's postage over and quoted £0.00, replacing whatever the store charged. It now writes nothing when no price is sent, so a listing nobody has priced is left alone, exactly as the FAQ describes.
* Corrected the 0.3.3 notes below, which credited only WP Super Cache for the holiday-mode cache fix and omitted the rest of that release.

= 0.3.3 =
* Fixed: the **Settings** link on the plugins list, and the Settings button on the Per-Vendor Shipping card, led to a page that refused to load. The menu slug had changed and three links were left pointing at the old one; every settings link is now built from a single constant so it cannot drift again.
* Fixed: **holiday mode did not reliably clear the page cache.** On WP Super Cache sites it fired an action that plugin does not publish, so a paused shop's listings kept being served out of stale HTML. On W3 Total Cache it fired the internal broadcast rather than the published function, which skipped the filter a site owner can veto a flush with and, on a clustered install, never left the node that ran it. Both now go through each plugin's own documented entry point, scoped to the current site so a multisite network is not flushed wholesale.
* The **BazaarKit menu now opens Settings**, not the add-ons catalogue. The first screen after activating a free plugin should be the one you can operate.
* The vendor-dashboard add-ons tab no longer carries prices or buy links. It renders on the site front end, where a vendor cannot install a plugin anyway; cards name the module and say whether it is installed.
* That tab now asks Dokan to draw its own dashboard chrome, through the documented `dokan_get_template_part` filter, instead of firing six of Dokan's own hooks by hand to reproduce it. The page looks the same; the hooks are Dokan's to fire, not ours.
* Fixed: a crafted URL could change what the add-on checkout screen was buying. A blanked `$_GET` had been enough until an SDK update started reading `$_REQUEST`, which PHP fills from GET, POST and cookies; both are now blanked and restored, and the regression test reproduces the real request shape rather than half of it.
* Fixed: the `bazaarkit_bundle_rate_calc` filter cast its return value with a bare `(int)`, so a third-party filter returning something unusable quietly became £0.00 postage instead of failing where anyone could see it. A non-numeric answer is now treated as no answer, and the calculated rate stands.
* The holiday-mode form checks the caller's capability as well as the nonce.
* Uninstall no longer aborts if a second copy of the plugin is present; it was possible for the cleanup to stop halfway and leave settings behind.
* Settings sections for add-ons you do not have are now plain informational notes — no price, no prominent button. The offer is made once, on the Add-ons screen.
* Corrected the description of the site-wide default, which promised a protection it does not provide, and the vendor shipping-price help text, which described the bundle as "highest rate" even when the store is set to charge the sum.
* Removed comparisons to Dokan Pro that we cannot substantiate.
* Packaging: the bundled Freemius SDK moved to `vendor/`, which is where Freemius requires it for WordPress.org submissions.

= 0.3.2 =
* **International orders now get a shipping rate.** The vendor's flat price is quoted for any destination, at home or abroad. Previously a foreign address was left with no rate at all, which blocked the sale — that was wrong, and the WordPress.org review was right to say so.
* The add-ons screen no longer contacts our server. It quoted a live bundle price, which meant a request left your site simply because you opened a screen. It names no bundle price now and links to the pricing page instead.
* Updated the bundled Freemius SDK.

= 0.3.1 =
* The add-ons screen now shows the price the checkout will actually bill. The launch discount retires itself after 100 licences, and a price compiled into a release would have gone on promising it afterwards; the screen now reads the current state and falls back to the regular price if it cannot.
* Fixed: the add-ons screen could fail to load, because a link on it pointed at a constant that had been removed.
* Added a live price lookup on the add-ons screen. Removed again in 0.3.2 — see below.

= 0.3.0 =
* **Per-continent international shipping** (Intl Shipping add-on). A listing can charge a different price to Europe and to Oceania, instead of one flat worldwide figure — or leave a region blank and simply not post there. Existing listings keep their single price with nothing to migrate.
* **Holiday mode.** Sellers can pause their shop with one switch: their listings are hidden everywhere and can't be bought, without unpublishing anything. Turning it off restores everything exactly as it was.
* **Departed sellers no longer leave live listings behind.** WordPress reassigns a deleted user's posts to the site owner, leaving orders nobody can fulfil. Their listings are now moved to the trash instead — recoverable for 30 days, and order history is untouched.
* Fixed: an unset `author__not_in` could pick up an empty value and produce a query excluding author 0.

= 0.2.0 =
* Licence enforcement: paid add-ons must now be entitled, not merely installed (new `BazaarKit\Licence`, `ModuleState::is_usable()`).
* Admins can no longer switch on an unlicensed add-on, and are told when one is installed but inactive.

= 0.1.0 =
* Initial release. Free core: the module framework + Per-Vendor Shipping module.
