skills/pricing-promotions/discount-engine/SKILL.md
Create a flexible discount system supporting percentage off, fixed amounts, buy-one-get-one, tiered thresholds, and complex conditional rules
npx skillsauth add finsilabs/awesome-ecommerce-skills discount-engineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A discount engine evaluates promotions against a cart and applies the right discounts in the right order. Every major e-commerce platform has one built in — you should configure the platform's native system before considering custom code. This skill covers how to set up complex discount logic (percentage off, fixed amount, BOGO, tiered thresholds, conditional rules) on each platform, and when to use apps or plugins to extend native capabilities.
| Platform | Built-in Discount Capabilities | When to Add Apps/Plugins | |----------|-------------------------------|--------------------------| | Shopify | Automatic discounts, code-based discounts, BOGO, tiered Buy X Get Y — all in Discounts admin | Shopify Plus Scripts for advanced stacking and custom logic; Bold Discounts for visual rule building | | WooCommerce | Coupons (core) cover basic cases | For BOGO, tiered quantity pricing, or automatic rule-based discounts: YITH WooCommerce Dynamic Pricing & Discounts (~$70/yr) or Dynamic Pricing extension by WooCommerce (~$99/yr) | | BigCommerce | Promotions engine supports cart-level and item-level discounts, free products, BOGO | BigCommerce's built-in Promotions are powerful — use custom scripts for edge cases | | Custom / Headless | Must build — see below | N/A |
Automatic discounts (no code required):
BOGO (Buy X Get Y):
Tiered quantity discounts (Shopify): Native Shopify does not support tiered quantity breaks (e.g., 1–4 units = full price, 5–9 = 10% off, 10+ = 20% off) without an app. Options:
Stacking rules: In Shopify, each discount can be configured to combine with others:
WooCommerce core coupons cover single-code, percentage, and fixed-amount discounts. For automatic, rule-based, and BOGO discounts, use the Dynamic Pricing & Discounts plugin.
Installing Dynamic Pricing:
Creating a tiered quantity rule:
BOGO in WooCommerce:
Preventing unintended stacking: In WooCommerce core coupons, tick Individual use only to prevent a coupon from combining with other coupons. For automatic rules in Dynamic Pricing, each rule has a "Stops further rules" option that prevents lower-priority rules from applying.
BigCommerce has a native Promotions system that covers most discount scenarios.
Tiered quantity pricing on BigCommerce: Use the Price Lists feature (Plus plan and above):
For custom storefronts, the discount engine evaluates applicable discounts and allocates them to cart lines. The key principles are: evaluate server-side, apply in priority order, and enforce stacking rules.
interface Discount {
id: string;
type: 'percentage' | 'fixed_amount' | 'bogo' | 'free_shipping';
value: number; // percentage (0-100) or cents
target: 'order' | 'line_item' | 'shipping';
isStackable: boolean;
minCartCents?: number;
minQuantity?: number;
entitledProductIds?: string[];
excludedProductIds?: string[];
startsAt: Date;
endsAt?: Date;
}
interface CartLine {
id: string;
productId: string;
quantity: number;
unitPriceCents: number;
}
function evaluateDiscounts(
cart: { lines: CartLine[]; subtotalCents: number },
discounts: Discount[]
): { discountId: string; amountCents: number; affectedLineIds: string[] }[] {
const now = new Date();
const eligible = discounts.filter(d =>
d.startsAt <= now && (!d.endsAt || d.endsAt > now)
);
// Sort: non-stackable first, then higher-value
const sorted = [...eligible].sort((a, b) => (a.isStackable ? 1 : -1) - (b.isStackable ? 1 : -1));
const applications: { discountId: string; amountCents: number; affectedLineIds: string[] }[] = [];
let nonStackableApplied = false;
for (const discount of sorted) {
if (!discount.isStackable && nonStackableApplied) continue;
// Check minimum cart value
if (discount.minCartCents && cart.subtotalCents < discount.minCartCents) continue;
const eligibleLines = cart.lines.filter(line =>
(!discount.entitledProductIds || discount.entitledProductIds.includes(line.productId)) &&
(!discount.excludedProductIds || !discount.excludedProductIds.includes(line.productId))
);
if (eligibleLines.length === 0) continue;
let amountCents = 0;
if (discount.type === 'percentage') {
amountCents = Math.round(
eligibleLines.reduce((s, l) => s + l.unitPriceCents * l.quantity, 0) * discount.value / 100
);
} else if (discount.type === 'fixed_amount') {
amountCents = Math.min(discount.value, cart.subtotalCents);
} else if (discount.type === 'bogo') {
const buyQty = discount.minQuantity ?? 1;
for (const line of eligibleLines) {
const freeItems = Math.floor(line.quantity / (buyQty + discount.value)) * discount.value;
amountCents += freeItems * line.unitPriceCents;
}
}
if (amountCents > 0) {
applications.push({ discountId: discount.id, amountCents, affectedLineIds: eligibleLines.map(l => l.id) });
if (!discount.isStackable) nonStackableApplied = true;
}
}
return applications;
}
Always re-run this evaluation at order creation, not just at cart-add time, to catch race conditions and expired discounts.
| Problem | Solution | |---------|----------| | Discount applied after order is placed (stale cart) | Re-validate all discounts at order creation; remove expired or invalid ones before charging | | BOGO applied to a single-unit cart | Enforce a minimum cart quantity equal to the buy quantity before the BOGO triggers | | Discount causes an order total to go negative | Cap the total discount at the cart subtotal; no order total should go below zero | | Automatic and code-based discounts stack unexpectedly | Review the "Combinations" settings on each discount in Shopify; use "Individual use only" in WooCommerce coupons | | Tiered discount shows wrong tier when quantity is updated | Recalculate the applicable tier on every cart quantity change, not just at checkout |
tools
Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price
development
Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode
development
Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch
development
Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion