What to Implement on the Headless Side
With Shopify configuration complete, implement these on the headless (Next.js) side:
- Fetch tags on login
- Calculate discount rate from tags
- Display member pricing
- Auto-apply coupons at checkout
- Invoice payment processing (optional)
Step 1: Fetch Tags on Login
When a customer logs in, use the Storefront API to get their tag list.
GraphQL Query
query getCustomer($customerAccessToken: String!) {
customer(customerAccessToken: $customerAccessToken) {
id
email
tags
}
}
Example Response
{
"data": {
"customer": {
"id": "gid://shopify/Customer/12345",
"email": "dealer@example.com",
"tags": ["standard-35", "premium-25", "b2b-account"]
}
}
}
This customer has standard-35 and premium-25 tags, so two types of discounts apply.
Step 2: Calculate Discount Rate from Tags
Implement logic to extract discount rates from tag names.
Basic Approach
// Tag name: "standard-35"
// Value to extract: 35 (discount rate)
function getDiscountRateFromTag(tag: string): number | null {
// Tag format: category-discountrate
const match = tag.match(/^(.+)-(\d+)$/);
if (!match) return null;
const discountRate = parseInt(match[2], 10);
return discountRate;
}
// Usage
getDiscountRateFromTag("standard-35"); // → 35
getDiscountRateFromTag("premium-25"); // → 25
Category and Discount Rate Mapping
Manage which tags correspond to which collections (product categories) in a config file.
// config/b2b-discounts.ts
export const B2B_DISCOUNT_CONFIG = [
{
tagPrefix: "standard",
collectionHandle: "b2b-standard-products",
// Discount rate dynamically extracted from tag name
},
{
tagPrefix: "premium",
collectionHandle: "b2b-premium-products",
},
{
tagPrefix: "elite",
collectionHandle: "b2b-elite-products",
},
{
tagPrefix: "special",
collectionHandle: "b2b-special-products",
},
];
Step 3: Display Member Pricing
Calculate and display member pricing based on tag information.
Price Calculation Logic
function calculateMemberPrice(
originalPrice: number,
customerTags: string[],
productCollections: string[]
): { memberPrice: number; discountRate: number } | null {
for (const config of B2B_DISCOUNT_CONFIG) {
// Is this product in the target collection?
if (!productCollections.includes(config.collectionHandle)) {
continue;
}
// Does customer have a discount tag for this category?
const matchingTag = customerTags.find(tag =>
tag.startsWith(`${config.tagPrefix}-`)
);
if (matchingTag) {
const discountRate = getDiscountRateFromTag(matchingTag);
if (discountRate) {
const memberPrice = originalPrice * (1 - discountRate / 100);
return { memberPrice, discountRate };
}
}
}
return null; // Not eligible for discount
}
Cart Display Example
┌─────────────────────────────────────────┐
│ Product A (Standard Category) │
│ ───────────────────────────────────── │
│ Regular Price: $100.00 │
│ Member Price: $65.00 (35% OFF) │
├─────────────────────────────────────────┤
│ Product B (Premium Category) │
│ ───────────────────────────────────── │
│ Regular Price: $200.00 │
│ Member Price: $150.00 (25% OFF) │
├─────────────────────────────────────────┤
│ Cart Total │
│ ───────────────────────────────────── │
│ Original Total: $300.00 │
│ Member Discount: -$85.00 │
│ ───────────────────────────────────── │
│ You Pay: $215.00 │
└─────────────────────────────────────────┘
Step 4: Auto-Apply Coupons
When "Proceed to Checkout" is clicked, auto-apply coupons matching tag names to the cart.
Processing Flow
Check cart items, get each item's collection
Identify applicable tags
Tag name = Coupon name, apply to cart
Discount already applied
Implementation Example (Conceptual)
async function proceedToCheckout(
cartId: string,
customerTags: string[],
cartItems: CartItem[]
) {
// Identify applicable coupons (tag names)
const applicableCoupons = getApplicableCoupons(customerTags, cartItems);
// Apply each coupon to cart
for (const couponCode of applicableCoupons) {
await applyDiscountCode(cartId, couponCode);
}
// Get checkout URL and navigate
const checkoutUrl = await getCheckoutUrl(cartId);
window.location.href = checkoutUrl;
}
function getApplicableCoupons(
customerTags: string[],
cartItems: CartItem[]
): string[] {
const coupons: string[] = [];
for (const item of cartItems) {
for (const config of B2B_DISCOUNT_CONFIG) {
// Is product in target collection?
if (!item.collections.includes(config.collectionHandle)) {
continue;
}
// Does customer have discount tag for this category?
const matchingTag = customerTags.find(tag =>
tag.startsWith(`${config.tagPrefix}-`)
);
if (matchingTag && !coupons.includes(matchingTag)) {
coupons.push(matchingTag); // Tag name = Coupon name
}
}
}
return coupons;
}
No Code Entry for Customers
This system means customers don't need to know or enter coupon codes. Just being logged in automatically applies discounts.
Step 5: Invoice Payment (Net Terms)
B2B often requires invoice payment instead of credit cards.
Implementation Using Draft Orders
Use Shopify's "Draft Order" feature to enable invoice payment.
Process Flow:
- Customer selects "Order with Invoice Payment"
- Create draft order via Admin API
- Draft order records:
- Product info (after member pricing)
- Discount rate and savings amount
- Customer info
- Email notification to sales staff
- After confirmation, issue invoice
Draft Order Creation Example
async function createInvoiceOrder(
customer: Customer,
cartItems: CartItemWithDiscount[]
) {
const lineItems = cartItems.map(item => ({
variantId: item.variantId,
quantity: item.quantity,
// Apply member pricing
appliedDiscount: {
value: item.discountRate,
valueType: "PERCENTAGE",
title: `B2B Discount (${item.discountRate}% OFF)`,
},
}));
const draftOrder = await adminApi.draftOrderCreate({
input: {
lineItems,
customerId: customer.id,
note: `Invoice payment order\nDiscount tags: ${customer.tags.join(", ")}`,
tags: ["invoice-payment", "b2b-order"],
},
});
// Notify staff
await sendNotificationEmail({
to: "sales@example.com",
subject: `New B2B Order (Invoice): ${customer.email}`,
body: `
Customer: ${customer.email}
Total: $${draftOrder.totalPrice}
Original: $${draftOrder.subtotalPrice}
Discount: $${draftOrder.totalDiscounts}
`,
});
return draftOrder;
}
Invoice Payment UI
┌─────────────────────────────────────────┐
│ Select Payment Method │
├─────────────────────────────────────────┤
│ ○ Pay with Credit Card │
│ → Proceed to Shopify checkout │
│ │
│ ● Order with Invoice Payment │
│ → We'll send an invoice later │
│ → Payment terms: Net 30 │
└─────────────────────────────────────────┘
Priority Management
How to control when multiple rules could apply to the same product.
Configuration Example
export const B2B_DISCOUNT_CONFIG = [
// Higher priority items first
{
tagPrefix: "vip",
collectionHandle: "b2b-vip-products",
priority: 1, // Highest priority
},
{
tagPrefix: "standard",
collectionHandle: "b2b-standard-products",
priority: 2,
},
// ...
];
When a higher-priority rule matches first, apply it and stop.
Summary
Key implementation points for the headless side:
| Feature | Implementation |
|---|---|
| Tag Fetch | Get logged-in customer tags via Storefront API |
| Price Calculation | Extract discount rate from tag, calculate member price |
| Price Display | Show regular and member prices side by side on cart |
| Coupon Application | Auto-apply coupon matching tag name at checkout |
| Invoice Payment | Create draft order via Admin API |
Key Benefits of This System:
- No code changes: Adding B2B customers or categories only requires Shopify config
- Customer experience: Just logging in automatically applies member pricing
- Operational efficiency: Add categories with single config file edit
This design achieves full B2B functionality without Shopify Plus, on standard plans.