\n{% endif %}","targetProduct":"Shopify"},{"@type":"SoftwareSourceCode","name":"Draft Order Approval Queue","programmingLanguage":"text/plain","codeSampleType":"snippet","text":"Trigger: When a draft order is created\n\nCondition: draft order has tag \"wholesale_pending_approval\"\n\nActions:\n 1. Send Slack/email notification to #wholesale-team or approval-email@company.com\n Message: \"New PO from [company name]: [order total]. Review in admin.\"\n 2. Tag draft order with \"wholesale_pending_approval\"\n 3. (Optional) Create a task or alert in your ticketing system\n\n— \n\nSecond Flow (on approval):\n\nTrigger: When a draft order is completed\n\nCondition: draft order tag contains \"wholesale_approved\"\n\nActions:\n 1. Send email to customer with order confirmation and estimated ship date\n 2. Tag the resulting order with \"wholesale_order\"\n 3. (Optional) Log to a webhook to sync fulfillment to your warehouse system","targetProduct":"Shopify"}]} \n{% endif %}"}]},{"@type":"HowToStep","position":4,"name":"Draft Order Approval Queue","text":"Every PO submission creates a draft order tagged for review; your team approves, adjusts line items or pricing, then converts it to a real order—buyer sees the status update in their portal. (Paste target: Shopify Flow editor: When → Then.)","itemListElement":[{"@type":"HowToDirection","text":"Trigger: When a draft order is created\n\nCondition: draft order has tag \"wholesale_pending_approval\"\n\nActions:\n 1. Send Slack/email notification to #wholesale-team or approval-email@company.com\n Message: \"New PO from [company name]: [order total]. Review in admin.\"\n 2. Tag draft order with \"wholesale_pending_approval\"\n 3. (Optional) Create a task or alert in your ticketing system\n\n— \n\nSecond Flow (on approval):\n\nTrigger: When a draft order is completed\n\nCondition: draft order tag contains \"wholesale_approved\"\n\nActions:\n 1. Send email to customer with order confirmation and estimated ship date\n 2. Tag the resulting order with \"wholesale_order\"\n 3. (Optional) Log to a webhook to sync fulfillment to your warehouse system"}]}]}
Tom Sailors
Brief · Anonymized case study

Wholesale Portal & Contract Pricing

I'd build this in layers: first, a B2B Companies structure in Shopify where each wholesale buyer is tagged with their tier and company ID. Then a Shopify Function that reads that company ID from the customer metafield and applies the right contract price at cart time—no public catalog changes. The portal itself is a Remix app that gates access by company ID and swaps "Add to Cart" for "Add to PO," landing everything in draft orders. Finally, Shopify Flow watches for new draft orders, notifies the approval team, and lets them review and convert without the buyer having to wait in a public checkout.

A mid-market wholesale distributor needs to serve multiple buyer tiers, each with negotiated per-product pricing, without exposing those prices to other customers or modifying their public catalog. Their buyers need a gated portal to browse at contract rates and submit purchase orders, with an approval workflow so the team can review, adjust, and confirm orders before conversion.
Four pieces
B2B / Wholesale

Company Tier & Pricing Setup

Create wholesale companies in Shopify, assign them to tiers (tier 1, tier 2, etc), and attach contract-specific prices per product without changing your public catalog.

Admin dashboard + metafields
B2B / Wholesale

Contract Price Override

Store per-company product pricing on each company as metafields, so one buyer sees $50 while another sees $45 for the same SKU.

Metafields + Shopify Functions
extensions/discount-or-cart-transform/src/run.graphql function
# Function input query — Cart Transform or Discount Function
query Input {
  cart {
    lines {
      id
      quantity
      merchandise {
        __typename
        ... on ProductVariant {
          id
          sku
          product {
            id
            handle
          }
        }
      }
      cost {
        amountPerQuantity { amount }
        totalAmount { amount }
      }
    }
    buyerIdentity {
      customer {
        id
        metafield(namespace: "wholesale", key: "company_id") { value }
      }
    }
  }
}

# In your function logic:
# 1. Read cart.buyerIdentity.customer.metafield("wholesale", "company_id")
# 2. Fetch that company's contract prices from Shopify Admin
# 3. For each line, if a contract price exists, apply it via Cart Transform or Discount
Requires Shopify Plus for Cart Transform Function; Discount Function available on all plans.
B2B / Wholesale

Wholesale Buyer Portal

Private storefront where only authenticated wholesale customers land, browse products at their contract prices, and submit orders as POs instead of completing checkout.

Custom Shopify app (Remix) + theme gating
theme/snippets/wholesale-gate.liquid liquid
{% comment %}
Place this at the top of your product/collection/cart pages.
It redirects non-wholesale customers to public storefront.
{% endcomment %}

{% if customer and customer.metafields.wholesale.company_id %}
  {% comment %} Wholesale customer — show contract price {% endcomment %}
  {% assign contract_price = customer.metafields.wholesale.product_price %}
  {% if contract_price %}
    <div style="background: #f0f0f0; padding: 1rem; margin-bottom: 1rem;">
      <strong>Contract Price:</strong> {{ contract_price | money }}
    </div>
  {% endif %}
  
  {% comment %} Show PO submission form button instead of "Add to Cart" {% endcomment %}
  <form method="post" action="/apps/wholesale-portal/submit-po">
    <input type="hidden" name="product_id" value="{{ product.id }}" />
    <input type="hidden" name="variant_id" value="{{ variant.id }}" />
    <label for="qty">Quantity:</label>
    <input type="number" id="qty" name="quantity" value="1" min="1" />
    <button type="submit">Add to Purchase Order</button>
  </form>

{% else %}
  {% comment %} Not a wholesale customer — redirect {% endcomment %}
  <script>
    window.location.href = "{{ shop.secure_url }}";
  </script>
{% endif %}
Customize the redirect URL and styling to match your brand.
Operations

Draft Order Approval Queue

Every PO submission creates a draft order tagged for review; your team approves, adjusts line items or pricing, then converts it to a real order—buyer sees the status update in their portal.

Shopify Flow + admin dashboard
Shopify Flow editor: When → Then flow
Trigger: When a draft order is created

Condition: draft order has tag "wholesale_pending_approval"

Actions:
  1. Send Slack/email notification to #wholesale-team or approval-email@company.com
     Message: "New PO from [company name]: [order total]. Review in admin."
  2. Tag draft order with "wholesale_pending_approval"
  3. (Optional) Create a task or alert in your ticketing system

— 

Second Flow (on approval):

Trigger: When a draft order is completed

Condition: draft order tag contains "wholesale_approved"

Actions:
  1. Send email to customer with order confirmation and estimated ship date
  2. Tag the resulting order with "wholesale_order"
  3. (Optional) Log to a webhook to sync fulfillment to your warehouse system
Draft order creation can be done via the portal app or Admin API; Flow listens for the tag change.

Got a similar problem?

Sketch your build in 30 seconds — voice, type, or attach a screenshot.

Sketch the build →