Skip to main content

Overview

Discount Functions let you create custom discount logic beyond Shopify’s built-in discount types. Volume discounts, bundle pricing, tiered discounts, and customer-specific pricing.

How Discount Functions Work

1

Customer Action

Customer adds items to cart or enters checkout
2

Function Execution

Your function receives cart data and evaluates discount logic
3

Discount Applied

Function returns discount configuration
4

Price Updated

Shopify applies discount and updates cart/checkout

Discount Types

Volume Discounts

Discount based on quantity
  • Buy 2 get 10% off
  • Buy 5 get 20% off
  • Tiered pricing

Bundle Discounts

Discount on product combinations
  • Buy X + Y get Z free
  • Complete the set discount
  • Mix and match deals

Customer-Specific

Personalized pricing
  • VIP customer discounts
  • Loyalty tier pricing
  • Wholesale pricing

Conditional Discounts

Context-based pricing
  • First-time buyer discount
  • Time-based promotions
  • Location-based pricing

Example: Volume Discount

Generate a function that gives tiered discounts:
1

Describe Your Function

Create a discount function that gives 10% off when buying 
2-4 units of the same product, 15% off for 5-9 units, 
and 20% off for 10 or more units. Show the discount in 
the cart and checkout.
2

Review Generated Code

Synapse creates:
  • Rust or JavaScript function
  • Input query for cart data
  • Discount calculation logic
  • Output configuration
  • Extension UI (optional)
3

Deploy & Test

  • Automatic validation
  • Deployed to dev store
  • Test with different quantities
4

Activate Function

  1. Go to Discounts in admin
  2. Create new discount using your function
  3. Configure settings
  4. Set active dates

Generated Function Structure

extensions/volume-discount/
├── src/
│   ├── run.rs                 # Function logic (Rust)
│   │   OR
│   ├── run.js                 # Function logic (JS)
│   └── run.graphql           # Input query
├── shopify.extension.toml     # Config
├── Cargo.toml                 # Dependencies (Rust)
│   OR
└── package.json               # Dependencies (JS)

Function Code Examples

Common Discount Patterns

Buy one product, get another free/discounted:
// Check if customer has product X
let has_product_x = input.cart.lines.iter()
    .any(|line| line.merchandise.product.id == "gid://shopify/Product/123");

if has_product_x {
    // Apply discount to product Y
    discounts.push(Discount {
        targets: vec![Target {
            product_variant: Some(ProductVariantTarget {
                id: "gid://shopify/ProductVariant/456".to_string(),
            }),
        }],
        value: Value::Percentage(Percentage { value: Decimal::from(100) }),
        message: Some("Free with purchase!".to_string()),
    });
}
Prompt Example:
"Create a discount function where if customers buy any t-shirt 
(tagged 'tshirt'), they get 50% off any socks (tagged 'socks')"
Discount when cart total reaches threshold:
use rust_decimal::Decimal;

let cart_total: Decimal = input.cart.lines.iter()
    .map(|line| {
        let price = Decimal::from_str(&line.merchandise.price).unwrap();
        price * Decimal::from(line.quantity)
    })
    .sum();

if cart_total >= Decimal::from(100) {
    // Apply 10% off entire cart
    discounts.push(Discount {
        targets: vec![Target::OrderSubtotal],
        value: Value::Percentage(Percentage { 
            value: Decimal::from(10) 
        }),
        message: Some("10% off orders over $100".to_string()),
    });
}
Prompt Example:
"Create a discount that gives 15% off the entire order when 
the subtotal is $150 or more"
Discount for specific customer groups:
// Check customer tags
let is_vip = input.cart.buyer_identity
    .as_ref()
    .and_then(|identity| identity.customer.as_ref())
    .map(|customer| customer.has_any_tag(vec!["vip"]))
    .unwrap_or(false);

if is_vip {
    // 20% off for VIP customers
    discounts.push(Discount {
        targets: vec![Target::OrderSubtotal],
        value: Value::Percentage(Percentage { 
            value: Decimal::from(20) 
        }),
        message: Some("VIP member discount".to_string()),
    });
}
Prompt Example:
"Create a discount function that gives 25% off to customers 
tagged 'wholesale' on all products in the 'bulk' collection"
Discount for buying complete sets:
let required_products = vec![
    "gid://shopify/Product/1",
    "gid://shopify/Product/2",
    "gid://shopify/Product/3",
];

let has_all = required_products.iter().all(|product_id| {
    input.cart.lines.iter()
        .any(|line| line.merchandise.product.id == *product_id)
});

if has_all {
    // 30% off when buying complete bundle
    discounts.push(Discount {
        targets: required_products.iter().map(|id| {
            Target::ProductVariant(/* ... */)
        }).collect(),
        value: Value::Percentage(Percentage { 
            value: Decimal::from(30) 
        }),
        message: Some("Bundle discount!".to_string()),
    });
}
Prompt Example:
"Create a bundle discount where if customers buy a camera, 
lens, and memory card together, they get 25% off the total 
for those three items"

Function Configuration

Functions can have configurable settings:
# shopify.extension.toml
[[extensions.settings]]
name = "min_quantity"
type = "number_integer"
description = "Minimum quantity for discount"

[[extensions.settings]]
name = "discount_percentage"
type = "number_decimal"
description = "Discount percentage to apply"

[[extensions.settings]]
name = "eligible_products"
type = "product_reference"
description = "Products eligible for discount"
Access settings in your function:
let min_quantity = input.discount_node.metafield
    .as_ref()
    .and_then(|m| m.value.parse::<i64>().ok())
    .unwrap_or(2);

let discount_percentage = input.discount_node.metafield
    .as_ref()
    .and_then(|m| m.value.parse::<f64>().ok())
    .unwrap_or(10.0);

Testing Discount Functions

1

Create Discount in Admin

  1. Go to Discounts
  2. Create → Discount
  3. Select your function
  4. Configure settings
2

Test in Storefront

  • Add eligible products to cart
  • Verify discount appears
  • Check discount amount is correct
  • Test edge cases
3

Review Logs

Check function execution logs:
shopify app function logs
View input/output for each execution
4

Performance Testing

  • Test with many cart items
  • Measure execution time
  • Verify under 5ms threshold

Performance Best Practices

Discount functions must execute in under 5ms:

Minimize Loops

Avoid nested loops over cart items
// Good: Single pass
let total = lines.iter().map(|l| l.price).sum();

// Bad: Nested loops
for line in &lines {
    for other in &lines {
        // ...
    }
}

Use Rust

Rust functions are 10-100x faster than JavaScript
  • Compiled to WebAssembly
  • No garbage collection
  • Type-safe
  • Recommended for production

Cache Calculations

Don’t recalculate the same value
// Calculate once
let cart_total = calculate_total(&input.cart);

// Reuse
if cart_total > threshold {
    // ...
}

Limit API Surface

Only query data you need in run.graphql
# Good: Specific fields
query RunInput {
  cart {
    lines {
      quantity
      merchandise { id }
    }
  }
}

Debugging Functions

  • Local Testing
  • Live Logs
  • Error Handling
Test functions locally before deploying:
cd extensions/discount-function

# Test with sample input
shopify app function run

# Provide test input
cat test-input.json | shopify app function run

Limitations

Discount Functions have certain restrictions:
LimitationDetails
Execution TimeMust complete in under 5ms
No Network CallsCannot fetch external data
DeterministicSame input must give same output
StatelessNo access to previous executions
Cart Data OnlyLimited to provided input query

Example Prompts

Create a discount function with multiple spend tiers:
- $50-$99: 5% off
- $100-$199: 10% off
- $200+: 15% off
Apply to entire order. Show the tier achieved in the discount message.
Create a discount where customers get 20% off when they buy 
at least 3 items from the 'summer-collection'. Items can be 
any combination. Show "Summer Mix & Match: 20% off" message.
Give first-time customers (tagged 'first-order') 15% off 
their entire order. Check if customer is tagged, apply 
discount to order subtotal. Message: "Welcome! First order discount"
Create tiered discounts based on customer tags:
- 'loyalty-bronze': 5% off
- 'loyalty-silver': 10% off
- 'loyalty-gold': 15% off
Apply to all products. Show tier name in message.

Next Steps