Changelog
All notable changes to Orbit Wholesale B2B are documented here.
v1.0.9 — 2026-07-27
New: Draft Orders — let B2B customers order on account
A whole-cart "order on account" flow. Eligible B2B buyers submit their cart for approval without paying; you confirm and send a Shopify draft order with a payment link (or Net-terms invoice).
Submit Order for Approval — for eligible customers, the app replaces the cart's Checkout button with a submit-for-approval button (works on Dawn and most free themes; no Shopify Plus needed). The whole cart is captured, no payment is taken, and the cart is cleared on submit
Draft Orders inbox — new admin page (Orders → Draft Orders) with status tracking (Pending / Invoiced / Paid / Cancelled); open a request to review items, customer, and payment term, then Confirm & send payment link or Cancel
Payment terms — uses Shopify's built-in terms (Due on receipt, Net 15/30/45/60/90). Cascade: per-order override → Customer Group default → shop default → receipt. Set a group's term in Customer Groups → B2B payment term
Auto-create on submit — optional setting to skip manual review: the draft order + payment link are created and emailed automatically the moment the buyer submits
Emails — admin notified on a new request; customer emailed the payment link on confirm. Editable templates (English + Vietnamese defaults)
Multi-language storefront — button label and confirmation strings are translatable
Plan — runs on all Shopify plans
Fixed
Quote currency — quotes now display in the store's live currency instead of always showing USD, and amounts render with the plain currency symbol (e.g.
$240.00 NZD, notNZ$240.00)B2B group tags — the storefront eligibility tag list now re-syncs on every Customer Group save (previously only on a tag rename), so newly added groups take effect immediately
Quotes — the seller now gets an email when a customer replies or counter-offers on a quote (previously only an in-app badge)
v1.0.8 — 2026-07-13
Improved: One-form registration layout, translatable Company section, real store name in emails
One continuous form — the Company information section no longer sits inside a bordered box; when a customer picks "A company", the extra fields now flow seamlessly with the rest of the form
Company section fully translatable — the I'm registering as options, the "Company information" heading, all its field labels, and the country/state picker placeholders are now editable under Form editor → Content Settings → Company section labels, per language (defaults ship in 6 languages). Previously these texts always displayed in English
Emails show your store name — customer-facing emails (application approved/rejected, email confirmation, company invites) now sign off with your actual store name instead of your
.myshopify.comsubdomain. No setup needed
v1.0.7 — 2026-07-08
New: Custom registration fields & file upload
Add your own fields to the B2B registration form, on top of the built-in set.
New field types — Number, Radio, Checkbox (single consent), Checkbox group, and Date, alongside Text, Textarea, Dropdown, and File upload
+ Add element — build custom fields in the form editor, configure per-type settings (list options, number min/max/step, date range, file rules), reorder, and delete
File upload → Shopify Files — applicants attach documents (business license, tax certificate, …); files upload straight to Shopify admin → Content → Files, with server-enforced type and size limits, and appear as links in the submission detail
Auto-generated IDs — each custom field gets a stable ID from its label (e.g.
reseller_license_ab12), frozen after the first save so submissions keep their referenceBuilt-in fields untouched — the standard fields still work on every plan and can't be deleted or retyped
Plan — Custom fields require Advanced+; built-in fields remain available on all plans
v1.0.6 — 2026-06-29
New: Catalog Visibility
Hide your prices from shoppers who aren't your B2B customers. One global setting (Pricing → Catalog Visibility).
Two modes —
Hide price only(still buyable) orHide price and block Add-to-cartAudience — show prices to any logged-in customer, or only to specific B2B customer groups; everyone else is gated
Product scope — All products, Specific products, Specific collections, Product tags, or Vendors, plus an Exclude list that stays visible to everyone
Masked price — gated shoppers see the currency symbol next to a blurred
XXX; the real price is never sent to the page, so it can't be revealed by editing CSS. Price ranges ($10 – $20) are fully masked"Unlock wholesale price" button — appears under the price on product pages and collection cards; opens Shopify's (passwordless) sign-in and returns the shopper to the same page. The label is editable + translatable in Settings → Storefront Text → Catalog Visibility (defaults ship in 6 languages)
Takes priority over pricing — a gated shopper never sees a Wholesale Discount badge, B2B price, or Volume Pricing table, even when a rule targets "All Customers"; normal pricing returns the moment they qualify
Hard checkout backstop — in
Hide price and block Add-to-cartmode, a Cart & Checkout Validation Function blocks checkout for an ineligible buyer who force-adds a gated productSurfaces — product pages, collection/listing cards, search results, the Volume Pricing table, and the cart
Plan — Advanced+
v1.0.5 — 2026-05-31
New: B2B Customer Portal
The single-page Reorder Portal grows into a full B2B Customer Portal at /apps/orbit-boost-wholesale/portal — a sidebar shell wrapping four sections so wholesale buyers can self-serve orders, quotes, profile, and invoices without leaving the storefront. Server-rendered through the App Proxy; gated by login + plan + B2B eligibility (Customer Group membership). On mobile the sidebar collapses to horizontal tabs.
Sidebar shell — every section (My Orders · My Quotes · Profile · Invoices) renders inside one consistent shell with the active item highlighted
My Orders (
/portal,/portal/orders) — recent orders with one-click reorder; the/portal/reorder?order=<id>preview shows live B2B + volume pricing (what the buyer pays today, not the historical price)My Quotes (
/portal/quotes) — the buyer's quote list embedded in the shell. This is now the canonical "all quotes" entry. Standalone/quotesand/quotes/:idstay for email deep links and not-logged-in accessProfile (
/portal/profile) — view name / email / company / pricing tier; edit phone (flag country-code picker → E.164) and default address (Country select + dependent Province select). Returns the specific Shopify Admin error and preserves the buyer's input on failure; saves the rest of the address even when a province isn't a valid Shopify subdivision (best-effort, with a warning)Invoices (
/portal/invoices) — accepted quotes with Download PDF (Net / Prepay terms) or Pay / View (Due-on-receipt receipt)
Improved: Quotes
QuoteSettingsnow applies on the storefront — theformFieldstoggles (target price, needed-by date, file attachment, PO number, tax ID) and thecopyoverrides (heading, description, button, success / reject messages) actually drive the storefront modal. The contact-info section is hidden when the customer is logged inSpecific, localized errors (English + Vietnamese) with the actual threshold values — auto-reject minimum quantity / floor %, eligibility (must log in / must be approved B2B), and public-mode rate limit now say exactly what failed instead of a generic "Could not save"
Persisted request details — PO number, needed-by date, tax / VAT ID, and submitter IP are stored on the quote and shown in the admin Quote detail. Guest submissions allow a null customer ID
Admin deep link fixed — the "Open quote in admin" link in admin emails (
/quotes/:id) now opens that quote pre-selected in the inbox
Fixed
App Proxy JSON responses no longer wrapped in theme HTML — the quotes, reorder-price, and portal JSON endpoints force
Content-Type: application/json, sofetch().json()works (Shopify was rendering JSON inside the theme Liquid layout)Reorder preview pricing — the preview now mirrors the checkout discount Function: it evaluates the wholesale and volume pools, resolves the customer-group tier, and combines them per the shop's discount strategy (stack vs best). Changing a line quantity re-prices via the API so volume tiers show before checkout
Quote settings toggles were unlabeled — the admin switches now render their label + helper text correctly
"Create group" / quote-settings modals open reliably from the page title bar
v1.0.4 — 2026-05-23
New: Reorder Portal
Customer-facing portal at /apps/orbit-boost-wholesale/portal/orders where eligible B2B customers see their recent orders and one-click reorder selected items back into the cart. Pairs with a scheduled reminder email that nudges customers to come back N days after fulfillment.
Recent orders list at
/apps/orbit-boost-wholesale/portal/orders— last 20 orders with B2B badge,B2B onlyfilter toggle, Reorder button per rowReorder preview at
/apps/orbit-boost-wholesale/portal/reorder?order=<id>— preview each item, adjust qty, deselect out-of-stock items, Add selected to cartWholesale-aware pricing in the preview — the price each item shows is what the customer would pay today (their current customer group + active rules + product eligibility), not the historical order price. Cart total matches the preview total
Scheduled reminder cron — up to 3 reminders at configurable delays (e.g. 30 / 60 / 90 days after fulfillment). Daily at 06:00 UTC, multi-server safe via Mongo lock. 3-day query window absorbs missed ticks
Reorder reminder email template under Settings → Email Templates → Reorder Reminder, with shortcodes
{{first_name}},{{shop_name}},{{days_ago}},{{reorder_link}},{{unsubscribe_link}}. Per-locale translations (en, vi, fr, de, es, ja)HMAC-signed unsubscribe link in every email — customer clicks → recorded in
ReorderUnsubscribe→ all future cron runs skip themCustomerGroup-derived eligibility — a customer is B2B-eligible iff they belong to any Customer Group. No tag list to configure. Eligibility used everywhere: portal access, order tagging, reminder cron targeting
orders/createwebhook auto-tags B2B orders withorb2b-b2b. Idempotent. Reminder cron queries by this tagSchedule + sender config under Apps → Orders → Reorder Settings → Schedule (enable, day delays, marketing consent). Email content lives in Email Templates so it's edited alongside Registration + Quote emails
New: B2B Prices app embed
Replaces the drag-into-section Wholesale Price + Card Prices blocks with a single app embed (target: head) that overrides retail prices everywhere on the storefront — PDP, sticky add-to-cart, and product cards on collection / search / homepage surfaces.
One toggle, applies everywhere — Theme editor → App embeds → Orbit B2B Wholesale → ON. No per-section drag, no theme code edits, no separate blocks for PDP vs cards
Two display modes — Strikethrough (default:
~$25.00~ $21.25 [Save 15%]) or Replace ($21.25). Pick under Settings → General → B2B Price DisplaySave badge computed from total savings — works correctly when Wholesale + Volume stack.
(retail - final) / retailformula so a 30% wholesale + 5% volume stack showsSave 34%, not the nominal value of any single ruleOptional currency code suffix (
$21.25 USD) toggleable under the same settings sectionTheme adapter chain — built-in adapters for Dawn, Horizon, Craft, Refresh + a generic fallback covering most other themes. Each adapter declares the right CSS selectors for PDP price, sticky bar, card selector, card link, card price container, card price text
Merchant selector overrides — the embed's advanced section in Theme editor (or the
theme_overrideshop metafield for operators) lets you point at custom selectors if your theme is exotic. Block settings take precedence; metafield is the operator escape hatchVariant-change reactivity without flash — uses the cart Add to cart button's
disabledattribute as the theme render lifecycle signal. Pre-hide CSS gate re-engages on enabled → disabled, releases on disabled → enabled. Eliminates flash of retail price during Section Rendering API swapsQuantity-change reactivity — Volume Pricing tiers update live as the customer changes the qty input or presses +/- stepper buttons. The PDP shows exactly what the customer will pay at checkout for that qty
Pre-hide layer —
orb2b-pendingclass on<html>hides theme price selectors viavisibility: hiddenuntil the resolver decides what to do. 1.5s safety release prevents permanent freezeSettings → General → B2B Price Display is the merchant entry point: pick mode + toggle Save badge + toggle currency code
Removed
wholesale-price.liquidsection block (drag-into-PDP) — replaced by the B2B Prices app embedwholesale-card-prices.liquidbody embed — folded into the new app embed's payload + listing JSextensions/wholesale-block/src/main.jsx,pricing.js,PriceDisplay.jsx,vite.config.js— the wholesale-price build pipeline (volume-pricing-table build kept)Per-feature From name + Reply-to fields in Reorder Settings → Schedule — reorder reminders now use the shared SMTP From email (same as registration + quote emails)
eligibilityTagstext input in Reorder Settings — replaced by automatic eligibility derivation from CustomerGroup membership
Improved: Email Templates
Reorder reminder is the third feature with editable Email Templates (alongside Registration and Quote). The Reorder Reminder template lives under Settings → Email Templates → Email Templates · Reorder, edited the same way as the others.
The cron job uses EmailTemplateService.getForSending and the shared EmailService.send (shop's SMTP config) instead of a feature-specific renderer. One sender path, one template editor, one set of shortcodes.
Improved: Wholesale rule "winning" logic clarified in Help Center
Existing "Which rule wins when multiple match" docs updated to reflect the actual specificity → best-price → priority tiebreak chain in pricing.js. Old docs claimed priority is the primary factor (it's actually the final tiebreaker). Updated example calls out the gotcha that caused several support tickets: a logged_in rule with 0% discount silently outranks an all rule with 15% discount because logged_in scores higher on specificity.
Improved: Settings → General → B2B Price Display section
New subsection on the existing General settings page. Display mode (strikethrough / replace) + Save badge toggle + Currency code toggle. Synced to shop.metafields.orbit_wholesale.price_display via DiscountSyncService so the storefront embed reads it without a per-request DB hit.
Improved: PLAN_ENFORCEMENT=off (local dev) also writes platinum metafield
Setting PLAN_ENFORCEMENT=off previously unlocked admin UI + proxy gates but storefront Liquid blocks (which read shop.metafields.orb2b.plan) still saw the shop as Free. The boot-time hook now iterates active merchants and writes platinum to each shop's plan metafield when env=off, so storefront blocks render unlocked on local dev without per-shop unlock-store curls.
Fixed
Pre-hide CSS gate properly re-engages on variant change so retail price doesn't flash during theme's async section render
Wholesale rule resolver no longer returns the LAST rule applied as "winning" in stack mode — Save badge text now reflects total savings (
(retail - final) / retail) not the last rule's discountValue. Eliminates "Save 0%" displaying $7 offCart overlay markup wipes the theme's existing price text before injecting the B2B overlay — eliminates the triple-render of retail + struck-retail + B2B in some themes
Collection card overlay same fix — was duplicating the theme's
$600.00 USDtext alongside the overlayCustomer.acceptsMarketingdeprecated GraphQL field swapped toemailMarketingConsent.marketingState(removed in API 2024-01+; would have silently broken reorder reminders on 2025-07 API)
New: Quote requests (RFQ)
A full B2B price-negotiation workflow inspired by Alibaba's "Contact Supplier" flow. Buyers click Request a Quote on any product page, your team replies with custom prices, buyer accepts → Shopify draft order created. Works on every Shopify plan, no Plus required.
Storefront block — Drag the Request a Quote app block onto product pages from theme editor. The modal opens inline (no redirect) with the product's variants pre-loaded. Logged-in customer info auto-fills name + email via Liquid.
Buyer flow — Set quantities per variant, suggest a target price (optional), add a message, attach a PDF / JPG / PNG up to 2 MB.
Admin inbox — Email-style inbox at Apps → Orbit B2B → Quotes with filter tabs (Pending / Replied / Negotiating / Accepted / Rejected / Expired), search, unread badges, and background polling every 30s.
Customer info card — Each quote shows the buyer's company link, pricing tier link, Shopify tags, and full quote history (total / accepted / rejected / expired + accept rate %).
Items + adjustments editor — Edit unit prices, add discount (fixed / percentage), shipping, validity (3 / 7 / 14 / 30 days or custom), payment terms.
Conversation thread — Back-and-forth messaging between buyer and admin. Two reply buttons: Send message only (text reply, no price change) vs Send price update (saves a new revision + emails the buyer).
Customer storefront page — Buyers see their quotes at
/apps/orbit-boost-wholesale/quotesand individual detail pages with Accept / Decline / Send reply buttons (appear once admin has replied).Eligibility rules — Approved B2B only (default — requires
b2b/wholesale/tier-*tag), any logged-in customer, or public. Backend re-validates at submit time so frontend tags can't be spoofed.Auto-reject — Floor percentage below catalog (reject low-ball offers) and minimum total quantity (filter retail-size requests). Rate limit per IP for public mode.
New: Payment terms with PDF invoice (any Shopify plan)
Quotes support Due on receipt, Net 15 / Net 30 / Net 60, Prepay 50%, and Custom payment terms — without requiring Shopify Plus's B2B feature set.
Due on receipt — buyer accepts → redirected to Shopify checkout invoice → pays immediately
Net X / Prepay / Custom — app generates a PDF invoice from the quote, emails it to the buyer with payment instructions. Buyer pays offline (bank transfer / wire / check). Admin marks the order paid manually in Shopify Orders when the money lands.
Shopify draft order tags — Every accepted quote creates a draft order tagged with
Quote #<number>and aPayment terms: Net 30custom attribute so admin can trace it from Shopify Orders.orders/paid webhook — Once admin marks paid, the order links back to the quote via webhook for full traceability.
New: Auto-expiry + reminders
A Mongo-locked hourly cron job:
Expires any Replied / Negotiating quote past its
validity.validUntil→ status flips to Expired and buyer receives a polite expired emailSends an Expiring soon reminder email 24 hours before validity ends (one-shot, idempotent)
Skips quotes with no validity set (e.g. still Pending)
New: 6 quote email templates
Editable per language under Email Templates → Quote:
Quote Submitted (to admin) when a buyer submits a new request
Quote Reply (to buyer) when admin sends a price update
Quote Accepted (to admin) when buyer accepts the offer
Quote Invoice (Net terms) (to buyer, with PDF attached) for Net X / Prepay / Custom payment flow
Quote Expiring Soon (to buyer) 24h before validity
Quote Expired (to buyer) when validity lapses
All templates ship with default translations for English, Vietnamese, French, German, Spanish, Japanese.
New: Multi-language storefront text
Every visible string on the Quote modal + customer detail page is editable per language under Settings → Storefront Text in 6 groups: Modal form, Detail page, Status badges, Errors & notices, Payment terms, plus the existing Volume Pricing / Register Form / Global groups.
Pushed to
shop.metafields.orbit_wholesale.app_settings_i18non saveStorefront block reads it inline via Liquid — no extra round trip on cold load
Default translations for 6 languages, unlimited additional languages via the language tabs
Improved: customers/delete webhook (GDPR)
When a Shopify customer is deleted, the app now also deletes their Quotes + QuoteMessages alongside the existing Company / CustomerGroup cleanup. Idempotent + safe to retry.
Improved: Documentation
New Quotes section in the in-app Help Center (Documentation menu) — overview, modal flow, admin inbox, payment terms, auto-expiry, email templates
Workflow diagrams updated with 5 flows: Buyer requests → Admin replies → Accept (Due on receipt) → Accept (Net X PDF) → Auto-expiry
New troubleshooting guides for common Quote issues
Fixed
Modal "Request a Quote" no longer redirects to App Proxy URL — opens inline on product page
Liquid block now uses Shopify CDN via
asset_urlfilter forquote-storefront.{js,css}(same pattern as variant-drawer) — faster + resilient to backend outagesQuote thread message authors now show Wholesale Team / Customer / System correctly based on
author.typeinstead of falling back to "Customer" for everyone"Send price update" admin button no longer triggers loading spinner on both reply buttons — each tracks its own action
v1.0.3 — 2026-05-15
New: Quick Order
Bulk-order form for B2B buyers, embeddable on any page or as a dedicated /pages/quick-order.
Multi-form — Create different forms for different customer groups (Gold, Silver, Wholesale, etc.). Storefront automatically picks the most specific active form for the logged-in customer
4 product modes — All products, Specific products, Specific collections, or Products with tags. Use the Shopify resource picker to select
Auto-populate — In
Specific productsmode, the form pre-fills rows with the chosen products on page load. Multi-variant products show as parent group with chevron toggle to expand/collapse variantsMOQ rules — Min, max, and step quantity per row. Industry presets (Beverage, Apparel, Cosmetics, Electronics, Food) one-click apply suggested values
Quick-pick chips — Configurable shortcuts under each row (e.g. 12 / 24 / 48 / 96). Click to fill the focused row or bulk-fill all available rows
Pagination & infinite scroll —
None/Pagination(Prev/Next + page number) /Infinite scroll(Load more button). Items per page configurable. Background prefetch loads remaining chunks while the user is busy with the first batch — pagination shows the correct total page count from first paintSkeleton loader — Shimmer-animated placeholder while the form initializes; respects
prefers-reduced-motionFull design tokens — Primary color, button text color, text/subdued/border/background/row-hover, font size, row padding, border radius, custom CSS
Per-locale translations — Heading, description, button labels translatable per published locale. Generic strings (In stock / Out of stock / Quick pick / errors) come from bundled translations (en, vi, fr, de, es, ja)
Tier pricing in Unit column — Wholesale Discount applied per variant for the logged-in customer (matches the price shown on product pages). Volume Pricing tiers apply at checkout via the Shopify Function
New: Metafield-first storefront pattern
All storefront blocks (Wholesale Price, Variant Drawer, Quick Order) now read directly from Shopify metafields in Liquid — zero app proxy round trip on cold load. Eligibility, locale, and config resolve client-side using the customer tags Liquid hands down. App proxy is used only for customer-aware work (per-variant tier pricing).
New: Tax hub (preview)
New top-level menu Tax with four coming-soon sub-pages: Tax Rules, Tax-exempt customers, Invoices, Settings. UI placeholder for the upcoming VAT / invoice feature.
Improved: Hub URL consistency
All hub landing pages now use the -hub suffix: /pricing-hub, /customers-hub, /orders-hub, /tax-hub, /settings-hub. Feature pages keep their short URLs (/wholesale-pricing, /companies, /orders, /settings). Existing breadcrumb links updated.
Improved: Quick Order admin polish
Settings consolidated into the admin form (drop
show_header/show_image/max_visible_rowsfrom theme block schema — they live on the form now and apply across all block instances)Locale-aware Copy section with per-locale fallback to primary
Translation index lists Quick Order forms with missing-keys count + restore default
List page mirrors Wholesale Discount pattern: status tabs, search, sort popover, bulk delete, pagination, inline status switch
Collection list uses local state + immediate picker result (no waiting for GraphQL refetch)
v1.0.2 — 2026-05-13
New: Companies (B2B organizations)
A dedicated record for each B2B business buyer. Each Company can have multiple contacts (the people who place orders for it) and an assigned Customer Group (pricing tier). When you set a tier on a Company, every contact in that company automatically gets the tier's tag and the wholesale pricing that goes with it.
5 tabs in the Company editor — General, Customer group, Address, Registration (shareable invite link), Contacts (B2B Customer Picker UI for add/remove)
One-click invite links — Each Company gets a unique registration URL. Share it with the company's team so they can self-register against that company in one click
Invite by email — Send the invite link to one or many addresses with a single click; recipients get the templated invite email
Tier inheritance — Assign a Customer Group to a Company → all contacts inherit the tier's tag automatically. Remove a contact → tag removed (unless they're a contact of another company with the same tier)
Active / Inactive status — Inactive companies hold contacts dormant: their tier is intentionally NOT applied until you flip the status back to active
New: B2B Customers — unified view
The Registration page now has two views you switch between with a button at the top:
Applications — the inbox of sign-up requests waiting for review (the old default view)
All customers — aggregated list of every active B2B customer regardless of how they were added (form approval, Customer Group, or Company contact)
Filters on the All customers view:
Buyer-type buttons: All / Individual buyers (solo wholesale — not in any company) / Company contacts
Tier dropdown next to search: pick All tiers (default), a specific Customer Group, or "No tier yet" to surface customers you still need to assign a discount tier to
Empty state with quick links to Customer Groups and Form Settings when no customers exist yet
New: Three registration modes on the storefront form
The storefront registration form now supports three modes via a toggle:
Individual buyer — solo wholesale customer, fills personal info + tax ID + address inline in the main form
A company — registers as a new company. A "Company information" section appears with company name + tax ID + HQ address. On approval, the app auto-creates a Company record + adds the submitter as its first contact
Join company — when the form is opened via a company's invite link (
?company=<slug>), the toggle is hidden and the customer registers as a contact of that company (only personal info asked; the linked company already has the rest)
Mode toggle labels (Individual buyer / A company / Company information heading) are translatable via Settings → Translations and ship with defaults for English, Vietnamese, French, German, Spanish, and Japanese.
New: Company registration approved email
A dedicated template (company_registration_approved) sent when a "new-company" mode submission is approved. Welcomes the contact and includes the company's invite link so they can share it with their team. Ships with translations for the 6 supported locales out of the box.
New: Help Center inside the app
The Dashboard's "Documents" button now opens an in-app Help Center (/help-center) with a sidebar of topics mirroring this docs site. No more leaving the admin to read the manual.
Improved: Layout for editor pages
All New / Edit pages for Wholesale Pricing, Volume Pricing, and Companies now use a 3-column grid (300px left tab nav | content | 300px right info sidebar) with consistent widths and Polaris inlineSize="large". Companies info card now renders properly as a card section.
Improved: Rich text editor loading state
The email body editor shows a spinner placeholder while templates are loading from the server, instead of mounting an empty Quill instance that flashes blank.
Operational: Scheduled jobs
Two safety-net jobs now run automatically in the backend:
Reconcile Company ↔ CustomerGroup membership — daily at 03:00 UTC. Backfills
CustomerGroup.customerIdsif any drift slips past the webhook pathCleanup orphan customer IDs — weekly Sunday 04:00 UTC. Removes Shopify customer GIDs from Company contacts and Customer Group members when the customer no longer exists on Shopify (covers missed
customers/deletewebhooks)
Both jobs use a Mongo lock so multi-instance deploys don't double-run. Set DISABLE_SCHEDULED_JOBS=1 in development to skip.
v1.0.1 — 2026-05-09
New: Variant Drawer
A brand-new feature that lets B2B customers order multiple variants at once through a slide-out drawer.
Multi-variant ordering — Customers select quantities for every variant (size, color, style) in a single drawer and add them all to cart at once
Grouped variant display — Products with 2 options (e.g. Color + Size) show the first option as clickable swatches that filter the variant list below
4 display modes — Open on variant swatch click, secondary button next to Add to Cart, replace Add to Cart, or floating sticky button
Order constraints — Set minimum order quantity per variant, minimum total across all variants, and maximum per variant. Real-time validation in the drawer
Action buttons — Configurable Add to Cart (primary) and Start Order (secondary) buttons. Each can be enabled/disabled per rule
25+ design tokens — Full control over every visual element: drawer background, header, swatches, variant rows, quantity stepper, footer, action buttons, MOQ notice, trigger button, and custom CSS
Zero-latency styling — Design settings stored in Shopify metafields and read in Liquid at page render. Drawer opens with correct styling instantly, no style jump
Per-locale labels — Drawer title, button labels, and action text can be translated for each published store language. Fallback to primary locale for untranslated fields
Live admin preview — Full-page editor with 3-panel layout (section nav, live preview, settings). Preview uses real product data from your store
Real product picker — Click the product link in the editor sidebar to pick any product from your catalog. Variants and options update dynamically in the preview
Customer/product/market targeting — Same powerful eligibility system as Wholesale Pricing: target by customer tags, groups, login status, specific products, collections, product tags, or Shopify Markets
Exclude rules — Exclude specific customers, products, or collections even if they match the eligibility criteria
Priority system — When multiple rules match the same product, the highest priority rule wins
Improved: Save bar across all pages
All admin pages now use Shopify's native
<ui-save-bar>component with proper loading stateSave button shows a spinner while saving; Discard button is disabled during save
Applied to: Wholesale Pricing (New/Edit), Volume Pricing (New/Edit), Customer Groups (Detail), Settings
Error feedback via
shopify.toast.show()with error details
Improved: Settings — About section
Updated app description and version number
Added feature list reflecting all current capabilities
v1.0.0 — 2026-04-01
Initial release
Wholesale Pricing — Create rules to set special prices for B2B customers by percentage off, fixed amount off, or fixed price. Target by customer tag, login status, specific products, collections, product tags, or Shopify Markets. Per-variant pricing support. Rule priority and scheduling with start/end dates
Volume Pricing — Quantity-break and amount-break tier discounts. Three discount methods: same variant, mix variants, mix products. Storefront pricing table with multiple templates (Default, Alibaba Cards). Table design customization
Customer Groups — Organize B2B buyers into tiers (Bronze, Silver, Gold, Diamond) with color-coded tags. Bulk assignment via registration approval
B2B Registration Form — Drag-and-drop form builder with 9 field types. Custom design settings. Approval/rejection workflow with resubmission support. Auto-approve option
Email Notifications — 4 email templates (new submission, approved, rejected, auto-approved). Rich text editor with shortcodes. Custom SMTP support. Built-in relay fallback
Storefront Translations — Translate pricing labels, volume table headers, registration form fields, and email templates into any published store language
Card Prices — Automatic wholesale price display on collection pages and product cards
Discount Strategy — Choose between "best price for customer" (default) or "stack" mode for combining wholesale + volume discounts
Shopify Functions — Checkout discounts applied via Shopify's native discount function — no coupon codes needed
Last updated
Was this helpful?