Introduction to Vendwick
Vendwick is a commerce plugin and license server for EmDash. You sell software, digital files, services and physical goods from your own EmDash site, take payments through Stripe, PayPal, Razorpay or Polar, and deliver license keys, releases and updates to buyers of WordPress plugins and themes and EmDash plugins.
It’s for developers and small studios who sell their own plugins, themes or files and want the store, the license API and the update channel on the site they already run. It’s a port of the Core Commerce subsystem of the Core Forms 5.0.2 WordPress plugin, with an npm registry for EmDash products added.
- Payments: hosted checkout pages on Stripe, PayPal, Razorpay and Polar, signed webhooks with event dedupe, test and live modes, a mock gateway for local testing, and per-currency gateway routing.
- Selling: a cart with quantities, pay-what-you-want and free plans, bundles, order bumps, coupons, tax and EU VAT reverse charge, upgrades with proration, and automatic renewals through each provider.
- Physical goods: shipping zones and rates, shipping addresses, stock with holds while the buyer pays, and shipments with tracking.
- Licensing: license keys with a configurable format, site activations with seat limits, a free-staging rule, releases for WordPress ZIPs and EmDash tarballs, and an EDD Software Licensing compatible API.
- Updates: signed, expiring package URLs for WordPress, and an npm-compatible registry so buyers of EmDash plugins run
pnpm addandpnpm updatewith their license key. - Back office: refunds and disputes from the admin, branded emails and reminders, order invoices and credit notes as pages and PDFs, client invoices, reports with CSV export, page access for buyers, outgoing webhooks, a FluentCart and EDD importer, customer export and erase, and 75 MCP tools.
- Admin: "Store" and "Store settings" folders you can pin under Content, dashboard widgets, a setup wizard, and a Store panel in the content editor.
Vendwick is a commercial plugin, sold at gauravtiwari.org/product/vendwick. Documentation is at gauravtiwari.org/docs/vendwick and the changelog at gauravtiwari.org/changelogs/vendwick. Read "What’s verified and what isn’t" below before you take real money with it.
Modules
Payments and Licensing are on by default. Everything else is off until you turn it on under Modules. A module can’t be turned on before what it needs.
| Module | Needs | What it adds |
|---|---|---|
| Payments | Stripe, PayPal, Razorpay and Polar, refunds and disputes. | |
| Licensing and Storefront | Products, plans, the cart, licenses, the license API, checkout and portal. Can’t be turned off while in use. | |
| Customer Emails and Reminders | Licensing | Editable, branded emails, expiry reminders, abandoned-checkout recovery. |
| Coupons | Licensing | Percentage and fixed discounts with limits, dates and ?coupon= links. |
| Tax and VAT | Licensing | Rates by country and region, inclusive or exclusive prices, EU reverse charge with VIES checks. |
| Automatic Renewals | Licensing, Payments | Provider subscriptions that renew licenses. Can’t be turned off while in use. |
| License Upgrades | Licensing | Move to a bigger plan, keep the key, pay a prorated price. |
| Invoices | Licensing | Sequential invoice and credit note numbers, invoice pages and PDFs. |
| Reports | Licensing | Revenue, MRR, renewal rate, refunds, top products, CSV export. |
| Page Access | Licensing | Pages and paths only buyers can open. |
| Client Invoices | Licensing, Payments | Invoices you draft, issue and email, paid online, with reminders and repeating schedules. |
| Physical Goods and Shipping (0.2.0) | Licensing, Payments | Physical products, shipping zones and rates, addresses, stock, shipments and packing slips. See Physical Products: Shipping, Stock and Shipments. |
| Outgoing Webhooks (0.2.0) | Licensing | Signed events to your own endpoints, with retries and a delivery log. See Outgoing Webhooks. |
| Store Importer (0.2.0) | Licensing | Imports a FluentCart or Easy Digital Downloads store. See Import a FluentCart or EDD Store. |
Pay what you want, free orders, bundles and order bumps need no module: see Pay What You Want, Free Orders, Bundles and Order Bumps.
What’s verified and what isn’t
Verified:
- An automated test suite of more than 2,100 tests covers payment state, webhook signatures, the cart and its gateway line items, pay what you want and free-order limits, bundles, shipping rates and stock holds, refunds and disputes (including concurrent and out-of-order events), fulfillment with crash injection, seat limits, the license API, release validation, coupons, tax, upgrades, renewals, reports, invoices and PDFs, the importer, outgoing webhooks, customer export and erase, and data written by 0.1.0.
- A demo site passes end-to-end purchases over real HTTP with the built-in test gateway: a single product, a two-item cart, a bundle, a cart with a physical item that is then shipped, and a partial and a full refund from the admin.
- In a browser (Chromium): every admin page in light and dark mode, the storefront at phone, tablet and desktop widths, and a cart purchase with shipping.
- The WordPress updater and the store exporter pass their own test scripts against a stubbed WordPress.
- Vendwick’s own license check runs against the live license server on a deployed Cloudflare Worker.
Not verified yet:
- Live gateway sandboxes. Stripe, PayPal, Razorpay and Polar are covered by recorded request and response shapes and the mock gateway only, including the 0.2.0 refund calls, failed-refund events and dispute events. Polar’s shapes are the least certain. Refund a test-mode order from the admin with each provider before you rely on it.
- Cloudflare Browser Rendering for PDFs. The request follows Cloudflare’s documentation; the built-in engine is what the tests cover.
- A real FluentCart or EDD store for the importer. The FluentCart column names for addresses, subscriptions and coupons come from its documentation, and the EDD exporter is built from EDD 3’s documented tables. Both are tested with sample rows only.
- Remote R2 on a deployed Cloudflare Worker. Vendwick runs on a deployed Worker with remote D1, where the store pages and the license API answer; uploads and downloads against remote R2 haven’t been run there.
- Browsers other than Chromium, including Safari’s
<dialog>handling for the cart drawer, and right-to-left text. - A real provider return from Stripe or PayPal to the redesigned checkout, and in the admin: a real release upload, customer erase, archive undo and bulk receipt resend.
- Real email delivery through an email provider plugin. Tests capture what would be sent.
- Turnstile and VIES against the live services.
Known limits
- One subscription per cart. A cart can hold one subscription line. Only Stripe takes one-time lines beside it; on PayPal, Razorpay and Polar a subscription is bought on its own and the checkout offers to split the cart.
- A Stripe subscription checkout doesn’t send the shipping address to Stripe. In a cart with a subscription and a physical item, the address is kept on the order but Stripe doesn’t receive it.
- No live carrier rates or labels. Shipping uses your zones and rate tables. One stock location, one shipping address per order, no per-product shipping prices, and physical plans can’t be subscriptions.
- Polar can’t sell physical goods and handles disputes itself (as merchant of record), and it can’t take a checkout with tax or a coupon. Razorpay recurring plans can’t lower the first charge, run a trial or restart a canceled subscription.
- PayPal has no event for a refund that fails later. Check pending PayPal refunds in its dashboard. Vendwick doesn’t submit dispute evidence; answer disputes in the provider’s dashboard.
- A refund that completed a full refund can’t be undone if the provider fails it later: the coupon use and the provider subscription were already released. You are alerted and correct it by hand.
- The built-in PDF engine is Latin-only (Windows-1252): other scripts need the optional Cloudflare Browser Rendering engine. It draws the logo only when it is a JPEG or PNG from the media library, not a logo URL.
- Webhooks can’t reach localhost. EmDash blocks plugin requests to private and loopback addresses; use a public
httpstunnel to receive webhooks on your machine. - Import has no undo, and imported subscriptions don’t renew automatically (provider subscriptions can’t move between stores). Release files, images, refund records, tax rates and settings aren’t imported, and FluentCart’s own licensing SDK isn’t emulated.
- Pay what you want can’t renew automatically, and a license can’t be upgraded to a pay-what-you-want plan. A license from a bundle can’t be upgraded on its own.
- The Core Forms bridge has no address field, so it can’t sell physical products (it fails with
ADDRESS_REQUIRED). - Customer erase doesn’t clear shipping addresses stored on orders and checkouts, and doesn’t touch the 30-day webhook delivery log. See Data, Privacy, Export and Erase.
- The Appearance page has no controls for the cart badge, order bump cards and drawer side; each preset sets them, and CSS can override them.
- One file per release. A WordPress release is one ZIP, an EmDash release one npm tarball, a digital release one file. EmDash versions are immutable, as on npm; a WordPress version can only be replaced by republishing it with
replaceExisting. - Page access has EmDash’s limits. EmDash has no content gating API, so gating happens in your templates (
AccessGate,hasAccess()) and in the companion middleware for paths. Sitemaps, site search, prerendered pages and public media files aren’t gated. - Injected pages don’t use your layout. Build your own pages from the components if you need your header and footer.
- The mailer can’t set From, BCC or attachments. That’s up to your email provider plugin; invoice PDFs are sent as links.
- The sidebar highlight. EmDash highlights "Store overview" on Vendwick pages that aren’t in the sidebar (Releases, Deliveries, the setup wizard) and on General. A plugin can’t change that; Vendwick’s own header marks the right page.
- Client invoices are UTC-only and have no partial payments.
- Four gateways. The other gateways of the WordPress plugin, legacy token formats and
/wp-jsonpaths aren’t ported. - MCP tools are static. Turning a module off makes its tools answer
MODULE_DISABLED; it doesn’t hide them. Changing any tool definition means the admin consents again.