Discount Function API
Use the Discount Function API to create a single discount that applies to cart lines, order subtotal, and shipping rates.
Discount Function API
The Discount Function API provides a unified schema for creating function extensions. A single function processes one discount (either code-based or automatic), but can apply savings across three discount classes: product, order, and shipping.
For example, one discount can simultaneously reduce both order total and delivery costs.
Shopify Functions enable you to customize Shopify's backend logic. The Discount Function API integrates this logic into the checkout flow.
Note:
You can activate a maximum of 25 discount functions on each store. All discount functions run concurrently, and have no knowledge of each other. The potential discount that a function outputs can be combined with the candidate from another discount, in alignment with the combination and stacking rules set on the discount node.
Use cases
- Exclusions, where the discount doesn't apply to some cart lines in the order.
- Tiered discounts on products, orders, and shipping when orders include qualifying item, subtotal, and delivery requirements.
- Discount to cartlines that contain specific properties, such as an engraving on a ring.
Function target
Checkout

Compatibility with Shopify surfaces Supported (7) Partially supported (4) Unsupported (3)
-
B2B: Supported
-
Cart: Supported
-
Checkout: Supported
-
Create Order API: Not supported
-
Draft Order (Admin): Partially supported
Discount functions with network access aren't supported on draft orders.
-
Draft Order (Checkout): Partially supported
Discount functions with network access aren't supported on draft orders.
-
Order Edit (Admin): Not supported
-
Order Edit (Checkout): Partially supported
The function isn't re-run when editing an order.
-
POS: Supported
-
Pre-order and Try Before You Buy: Not supported
-
Shopify Admin: Supported
-
Storefront: Supported
-
Storefront Accelerated Checkout: Supported
-
Subscription (Recurring Orders): Partially supported
The function isn't re-run when the recurring orders are created. Any shipping discount applied to the origin order defaults to a 100% shipping discount on recurring orders, regardless of the original discount value.
Fetch target Limited access
The fetch target is limited to custom apps installed on Shopify Plus and Enterprise stores. You'll also need to request network access for Functions, as it's not currently available on development stores or in a development preview.
Getting started
Scaffolding the function using Shopify CLI automatically configures your TOML file. You can alter the default configuration to customize the way your function operates.
Terminal
shopify app generate extension --template discount
Tutorial - Build a discount function
Targets
A target is an identifier in shopify.extension.toml that specifies where you're injecting code into Shopify Function APIs, or other parts of the Shopify platform. Each target is composed of three to four namespaces. The name begins with a broad Shopify context and ends with the behavior of the extensible element.
Note:
You can't configure discount classes from a checkout UI extension. Discount classes are assigned based on their associated Discount Function targets: OrderDiscountCandidateTarget, ProductDiscountCandidateTarget, and DeliveryDiscountCandidateTarget.
Cart run target
cart.lines.discounts.generate.run
The run target calculates and applies discounts to cart lines, orders, and shipping based on the provided cart context and discount configuration, including metafields.
When your function is executed, Shopify provides the cart context as input to the run target, including details about cart lines, prices, quantities, buyer identity, and optionally fetch results from external providers. The target returns an ordered list of operations for calculating discounts.
For example, you might use this to generate product and order discounts, and validate discounts.
-
Input
OBJECT
The
Inputobject is the complete GraphQL schema that your function can query as an input to generate discounts. Your function only receives the fields that you request in the input query. To optimize performance, we highly recommend that you request only the fields that your function requires.-
cart
Cart!
non-null
The cart where the Function is running. A cart contains the merchandise that a customer intends to purchase and information about the customer, such as the customer's email address and phone number.
-
attribute
Attribute
The custom attributes associated with a cart to store additional information. Cart attributes allow you to collect specific information from customers on the Cart page, such as order notes, gift wrapping requests, or custom product details. Attributes are stored as key-value pairs.
-
key
String
Arguments
The key of the cart attribute to retrieve. For example,
"gift_wrapping".
-
key
String!
non-null
Fields
The key or name of the attribute. For example,
"customer_first_order". -
value
String
The value of the attribute. For example,
"true".
-
-
billingAddress
MailingAddress
The billing address associated with the cart.
-
address1
String
The first line of the address. Typically the street address or PO Box number.
-
address2
String
The second line of the address. Typically the number of the apartment, suite, or unit.
-
city
String
The name of the city, district, village, or town.
-
company
String
The name of the customer's company or organization.
-
countryCode
CountryCode
The two-letter code for the country of the address. For example, US.
- AC, AD, AE, AF, AG, AI, AL, AM, AN, AO, AR, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MK, ML, MM, MN, MO, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PS, PT, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TA, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW, ZZ
-
firstName
String
The first name of the customer.
-
lastName
String
The last name of the customer.
-
latitude
Float
The approximate latitude of the address.
-
longitude
Float
The approximate longitude of the address.
-
name
String
The full name of the customer, based on firstName and lastName.
-
phone
String
A unique phone number for the customer. Formatted using E.164 standard. For example, +16135551111.
-
provinceCode
String
The alphanumeric code for the region. For example, ON.
-
zip
String
The zip or postal code of the address.
-
market
Market
Deprecated
-
handle
Handle!
non-null
A human-readable unique string for the market automatically generated from its title.
-
id
ID!
non-null
A globally-unique identifier.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
namespace
String
Arguments
A category that organizes a group of metafields. Namespaces are used to prevent naming conflicts between different apps or different parts of the same app. If omitted, then the app-reserved namespace is used.
-
key
String!
required
The unique identifier for the metafield within its namespace. A metafield is composed of a namespace and a key, in the format
namespace.key.
-
jsonValue
JSON!
non-null
Fields
The data that's stored in the metafield, using JSON format.
-
type
String!
non-null
The type of data that the metafield stores in the
valuefield. -
value
String!
non-null
The data that's stored in the metafield. The data is always stored as a string, regardless of the metafield's type.
-
-
regions
[MarketRegion!]!
non-null
A geographic region which comprises a market.
-
name
String
The name of the region in the language of the current localization.
-
-
-
-
buyerIdentity
BuyerIdentity
Information about the customer that's interacting with the cart. It includes details such as the customer's email and phone number, and the total amount of money the customer has spent in the store. This information helps personalize the checkout experience and ensures that accurate pricing and delivery options are displayed to customers.
-
customer
Customer
The customer that's interacting with the cart.
-
amountSpent
MoneyV2!
non-null
The total amount that the customer has spent on orders. The amount is converted from the shop's currency to the currency of the cart using a market rate.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
- AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BTN, BWP, BYN, BZD, CAD, CDF, CHF, CLP, CNY, COP, CRC, CVE, CZK, DJF, DKK, DOP, DZD, EGP, ERN, ETB, EUR, FJD, FKP, GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRK, HTG, HUF, IDR, ILS, INR, IQD, IRR, ISK, JEP, JMD, JOD, JPY, KES, KGS, KHR, KID, KMF, KRW, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LTL, LVL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MYR, MZN, NAD, NGN, NIO, NOK, NPR, NZD, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLL, SOS, SRD, SSP, STN, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, USD, USDC, UYU, UZS, VED, VES, VND, VUV, WST, XAF, XCD, XOF, XPF, XXX, YER, ZAR, ZMW, BYR, STD, VEF
-
-
displayName
String!
non-null
The full name of the customer, based on the values for
firstNameandlastName. IffirstNameandlastNamearen't specified, then the value is the customer's email address. If the email address isn't specified, then the value is the customer's phone number. -
email
String
The customer's email address.
-
firstName
String
The customer's first name.
-
hasAnyTag
Boolean!
non-null
Whether the customer is associated with any of the specified tags. The customer must have at least one tag from the list to return
true.-
tags
[String!]!
requiredDefault:[]
Arguments
A comma-separated list of searchable keywords that are associated with the customer. For example,
"VIP, Gold"returns customers with either theVIPorGoldtag.
-
-
hasTags
[HasTagResponse!]!
non-null
Whether the customer is associated with the specified tags.
-
tags
[String!]!
requiredDefault:[]
Arguments
A comma-separated list of searchable keywords that are associated with the customer. For example,
"VIP, Gold"returns customers with both theVIPandGoldtags.
-
hasTag
Boolean!
non-null
Fields
Whether the Shopify resource has the tag.
-
tag
String!
non-null
A searchable keyword that's associated with a Shopify resource, such as a product or customer. For example, a merchant might apply the
sportsandsummertags to products that are associated with sportswear for summer.
-
-
id
ID!
non-null
A globally-unique ID for the customer.
-
lastName
String
The customer's last name.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
numberOfOrders
Int!
non-null
The total number of orders that the customer has made at the store.
-
-
email
String
The email address of the customer that's interacting with the cart.
-
isAuthenticated
Boolean!
non-null
Whether the customer is authenticated through their customer account.
-
phone
String
The phone number of the customer that's interacting with the cart.
-
purchasingCompany
PurchasingCompany
The company of a B2B customer that's interacting with the cart. Used to manage and track purchases made by businesses rather than individual customers.
-
company
Company!
non-null
The company associated to the order or draft order.
-
createdAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company was created in Shopify.
-
externalId
String
A unique externally-supplied ID for the company.
-
id
ID!
non-null
The ID of the company.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
name
String!
non-null
The name of the company.
-
updatedAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company was last modified.
-
-
contact
CompanyContact
The company contact associated to the order or draft order.
-
createdAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company contact was created in Shopify.
-
id
ID!
non-null
The ID of the company.
-
locale
String
The company contact's locale (language).
-
title
String
The company contact's job title.
-
updatedAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company contact was last modified.
-
-
location
CompanyLocation!
non-null
The company location associated to the order or draft order.
-
createdAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company location was created in Shopify.
-
externalId
String
A unique externally-supplied ID for the company.
-
id
ID!
non-null
The ID of the company.
-
locale
String
The preferred locale of the company location.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
name
String!
non-null
The name of the company location.
-
ordersCount
Int!
non-null
The number of orders placed at this company location.
-
totalSpent
MoneyV2!
non-null
The total amount spent at this company location.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
updatedAt
DateTime!
non-null
The date and time (ISO 8601 format) at which the company location was last modified.
-
-
-
-
cost
CartCost!
non-null
A breakdown of the costs that the customer will pay at checkout. It includes the total amount, the subtotal before taxes and duties, the tax amount, and duty charges.
-
subtotalAmount
MoneyV2!
non-null
The amount, before taxes and cart-level discounts, for the customer to pay.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
totalAmount
MoneyV2!
non-null
The total amount for the customer to pay at checkout.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
totalDutyAmount
MoneyV2
The duty charges for a customer to pay at checkout.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
totalTaxAmount
MoneyV2
The total tax amount for the customer to pay at checkout.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
-
deliverableLines
[DeliverableCartLine!]!
non-null
The items in a cart that are eligible for fulfillment and can be delivered to the customer.
-
attribute
Attribute
The custom attributes associated with a cart to store additional information. Cart attributes allow you to collect specific information from customers on the Cart page, such as order notes, gift wrapping requests, or custom product details. Attributes are stored as key-value pairs.
Cart line attributes are equivalent to the
line_itemobject in Liquid. -
id
ID!
non-null
The ID of the cart line.
-
merchandise
Merchandise!
non-null
The item that the customer intends to purchase.
-
CustomProduct
OBJECT
A custom product represents a product that doesn't map to Shopify's standard product categories. For example, you can use a custom product to manage gift cards, shipping requirements, localized product information, or weight measurements and conversions.
-
isGiftCard
Boolean!
non-null
Whether the merchandise is a gift card.
-
requiresShipping
Boolean!
non-null
Whether the item needs to be shipped to the customer. For example, a digital gift card doesn't need to be shipped, but a t-shirt does need to be shipped.
-
title
String!
non-null
The localized name for the product that displays to customers. The title is used to construct the product's handle, which is a unique, human-readable string of the product's title. For example, if a product is titled "Black Sunglasses", then the handle is
black-sunglasses. -
weight
Float
The product variant's weight, in the system of measurement set in the
weightUnitfield. -
weightUnit
WeightUnit!
non-null
The unit of measurement for weight.
- GRAMS, KILOGRAMS, OUNCES, POUNDS
-
-
ProductVariant
OBJECT
A specific version of a product that comes in more than one option, such as size or color. For example, if a merchant sells t-shirts with options for size and color, then a small, blue t-shirt would be one product variant and a large, blue t-shirt would be another.
-
id
ID!
non-null
A globally-unique ID for the product variant.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
product
Product!
non-null
The product associated with the product variant. For example, if a merchant sells t-shirts with options for size and color, then a small, blue t-shirt would be one product variant and a large, blue t-shirt would be another. The product associated with the product variant would be the t-shirt itself.
-
handle
Handle!
non-null
A unique, human-readable string of the product's title. A handle can contain letters, hyphens (
-), and numbers, but not spaces. The handle is used in the online store URL for the product. For example, if a product is titled "Black Sunglasses", then the handle isblack-sunglasses. -
hasAnyTag
Boolean!
non-null
Whether the product is associated with any of the specified tags. The product must have at least one tag from the list to return
true. -
hasTags
[HasTagResponse!]!
non-null
Whether the product is associated with the specified tags.
-
id
ID!
non-null
A globally-unique ID for the product.
-
inAnyCollection
Boolean!
non-null
Whether the product is in any of the specified collections. The product must be in at least one collection from the list to return
true.A collection is a group of products that can be displayed in online stores and other sales channels in categories, which makes it easy for customers to find them. For example, an athletics store might create different collections for running attire and accessories.
-
inCollections
[CollectionMembership!]!
non-null
Whether the product is in the specified collections. The product must be in all of the collections in the list to return
true.A collection is a group of products that can be displayed in online stores and other sales channels in categories, which makes it easy for customers to find them. For example, an athletics store might create different collections for running attire and accessories.
-
ids
[ID!]!
requiredDefault:[]
Arguments
A comma-separated list of globally-unique collection IDs that are associated with the product. For example,
gid://shopify/Collection/123,gid://shopify/Collection/456.
-
collectionId
ID!
non-null
Fields
A globally-unique ID for the collection.
-
isMember
Boolean!
non-null
Whether the product is in the specified collection.
-
-
isGiftCard
Boolean!
non-null
Whether the product is a gift card.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
productType
String
A custom category for a product. Product types allow merchants to define categories other than the ones available in Shopify's standard product categories.
-
title
String!
non-null
The localized name for the product that displays to customers. The title is used to construct the product's handle, which is a unique, human-readable string of the product's title. For example, if a product is titled "Black Sunglasses", then the handle is
black-sunglasses. -
vendor
String
The name of the product's vendor.
-
-
requiresShipping
Boolean!
non-null
Whether the item needs to be shipped to the customer. For example, a digital gift card doesn't need to be shipped, but a t-shirt does need to be shipped.
-
sku
String
A case-sensitive identifier for the product variant in the merchant's store. For example,
"BBC-1". A product variant must have a SKU to be connected to a fulfillment service. -
title
String
The localized name for the product variant that displays to customers.
-
weight
Float
The product variant's weight, in the system of measurement set in the
weightUnitfield. -
weightUnit
WeightUnit!
non-null
The unit of measurement for weight.
-
-
-
quantity
Int!
non-null
The quantity of the item that the customer intends to purchase.
-
-
deliveryGroups
[CartDeliveryGroup!]!
non-null
A collection of items that are grouped by shared delivery characteristics. Delivery groups streamline fulfillment by organizing items that can be shipped together, based on the customer's shipping address. For example, if a customer orders a t-shirt and a pair of shoes that can be shipped together, then the items are included in the same delivery group.
In the Order Discount and Product Discount legacy APIs, the
cart.deliveryGroupsinput is always an empty array. This means you can't access delivery groups when creating Order Discount or Product Discount Functions. If you need to apply discounts to shipping costs, then use the Discount Function API instead.-
cartLines
[CartLine!]!
non-null
Information about items in a cart that a customer intends to purchase. A cart line is an entry in the customer's cart that represents a single unit of a product variant. For example, if a customer adds two different sizes of the same t-shirt to their cart, then each size is represented as a separate cart line.
-
attribute
Attribute
The custom attributes associated with a cart to store additional information. Cart attributes allow you to collect specific information from customers on the Cart page, such as order notes, gift wrapping requests, or custom product details. Attributes are stored as key-value pairs.
Cart line attributes are equivalent to the
line_itemobject in Liquid. -
cost
CartLineCost!
non-null
The cost of an item in a cart that the customer intends to purchase. Cart lines are entries in the customer's cart that represent a single unit of a product variant. For example, if a customer adds two different sizes of the same t-shirt to their cart, then each size is represented as a separate cart line.
-
discountAllocations
[DiscountAllocation!]!
non-null
The discounts that have been applied to the cart line.
-
discountApplication
DiscountApplication!
non-null
The discount that was applied.
-
allocationMethod
DiscountApplicationAllocationMethod!
non-null
The method by which the discount's value is allocated to its entitled items.
- ACROSS, EACH
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
targetSelection
DiscountApplicationTargetSelection!
non-null
The lines on the cart targeted by the discount.
- ALL, ENTITLED, EXPLICIT
-
targetType
DiscountApplicationTarget!
non-null
The type of line (i.e. line item or shipping line) on a cart that the discount is applicable towards.
- LINE_ITEM, SHIPPING_LINE
-
totalAllocatedAmount
MoneyV2!
non-null
The total allocated amount of the discount across all items.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
value
PricingValue!
non-null
The value of the discount.
-
MoneyV2
OBJECT
A precise monetary value and its associated currency. Combines a decimal amount with a three-letter currency code to express prices, costs, and other financial values throughout the API. For example, 12.99 USD.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
PricingPercentageValue
OBJECT
The percentage value of a discount.
-
value
Decimal!
non-null
The percentage value of the discount.
-
-
-
-
discountedAmount
MoneyV2!
non-null
The amount that was discounted.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
-
id
ID!
non-null
The ID of the cart line.
-
merchandise
Merchandise!
non-null
The item that the customer intends to purchase.
-
parentRelationship
CartLineParentRelationship
The nested relationship between this line and its parent line, if any.
-
parent
CartLine!
non-null
The parent line in the relationship.
-
-
quantity
Int!
non-null
The quantity of the item that the customer intends to purchase.
-
sellingPlanAllocation
SellingPlanAllocation
The selling plan associated with the cart line, including information about how a product variant can be sold and purchased.
-
priceAdjustments
[SellingPlanAllocationPriceAdjustment!]!
non-null
A list of price adjustments, with a maximum of two. When there are two, the first price adjustment goes into effect at the time of purchase, while the second one starts after a certain number of orders. A price adjustment represents how a selling plan affects pricing when a variant is purchased with a selling plan. Prices display in the customer's currency if the shop is configured for it.
-
perDeliveryPrice
MoneyV2!
non-null
The effective price for a single delivery. For example, for a prepaid subscription plan that includes 6 deliveries at the price of $48.00, the per delivery price is $8.00.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
price
MoneyV2!
non-null
The price of the variant when it's purchased with a selling plan For example, for a prepaid subscription plan that includes 6 deliveries of $10.00 granola, where the customer gets 20% off, the price is 6 x $10.00 x 0.80 = $48.00.
-
amount
Decimal!
non-null
A monetary value in decimal format, allowing for precise representation of cents or fractional currency. For example, 12.99.
-
currencyCode
CurrencyCode!
non-null
The three-letter currency code that represents a world currency used in a store. Currency codes include standard standard ISO 4217 codes, legacy codes, and non-standard codes. For example, USD.
-
-
-
sellingPlan
SellingPlan!
non-null
A representation of how products and variants can be sold and purchased. For example, an individual selling plan could be '6 weeks of prepaid granola, delivered weekly'.
-
description
String
The description of the selling plan.
-
id
ID!
non-null
A globally-unique identifier.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
name
String!
non-null
The name of the selling plan. For example, '6 weeks of prepaid granola, delivered weekly'.
-
recurringDeliveries
Boolean!
non-null
Whether purchasing the selling plan will result in multiple deliveries.
-
-
-
-
deliveryAddress
MailingAddress
The shipping or destination address associated with the delivery group.
-
address1
String
The first line of the address. Typically the street address or PO Box number.
-
address2
String
The second line of the address. Typically the number of the apartment, suite, or unit.
-
city
String
The name of the city, district, village, or town.
-
company
String
The name of the customer's company or organization.
-
countryCode
CountryCode
The two-letter code for the country of the address. For example, US.
-
firstName
String
The first name of the customer.
-
lastName
String
The last name of the customer.
-
latitude
Float
The approximate latitude of the address.
-
longitude
Float
The approximate longitude of the address.
-
name
String
The full name of the customer, based on firstName and lastName.
-
phone
String
A unique phone number for the customer. Formatted using E.164 standard. For example, +16135551111.
-
provinceCode
String
The alphanumeric code for the region. For example, ON.
-
zip
String
The zip or postal code of the address.
-
market
Market
Deprecated
-
-
deliveryOptions
[CartDeliveryOption!]!
non-null
The delivery options available for the delivery group. Delivery options are the different ways that customers can choose to have their orders shipped. Examples include express shipping or standard shipping.
-
code
String
A unique identifier that represents the delivery option offered to customers. For example,
Canada Post Expedited. -
cost
MoneyV2!
non-null
The amount that the customer pays if they select the delivery option.
-
deliveryMethodType
DeliveryMethod!
non-null
The delivery method associated with the delivery option. A delivery method is a way that merchants can fulfill orders from their online stores. Delivery methods include shipping to an address, local pickup, and shipping to a pickup point, all of which are natively supported by Shopify checkout.
- LOCAL, NONE, PICK_UP, PICKUP_POINT, RETAIL, SHIPPING
-
description
String
A single-line description of the delivery option, with HTML tags removed.
-
handle
Handle!
non-null
A unique, human-readable identifier of the delivery option's title. A handle can contain letters, hyphens (
-), and numbers, but not spaces. For example,standard-shipping. -
title
String
The name of the delivery option that displays to customers. The title is used to construct the delivery option's handle. For example, if a delivery option is titled "Standard Shipping", then the handle is
standard-shipping.
-
-
discountAllocations
[DiscountAllocation!]!
non-null
The discounts that have been applied to the delivery group.
-
groupType
CartDeliveryGroupType!
non-null
The type of merchandise in the delivery group.
- ONE_TIME_PURCHASE, SUBSCRIPTION
-
id
ID!
non-null
A globally-unique ID for the delivery group.
-
selectedDeliveryOption
CartDeliveryOption
Information about the delivery option that the customer has selected.
-
code
String
A unique identifier that represents the delivery option offered to customers. For example,
Canada Post Expedited. -
cost
MoneyV2!
non-null
The amount that the customer pays if they select the delivery option.
-
deliveryMethodType
DeliveryMethod!
non-null
The delivery method associated with the delivery option. A delivery method is a way that merchants can fulfill orders from their online stores. Delivery methods include shipping to an address, local pickup, and shipping to a pickup point, all of which are natively supported by Shopify checkout.
-
description
String
A single-line description of the delivery option, with HTML tags removed.
-
handle
Handle!
non-null
A unique, human-readable identifier of the delivery option's title. A handle can contain letters, hyphens (
-), and numbers, but not spaces. For example,standard-shipping. -
title
String
The name of the delivery option that displays to customers. The title is used to construct the delivery option's handle. For example, if a delivery option is titled "Standard Shipping", then the handle is
standard-shipping.
-
-
-
discountApplications
[DiscountApplication!]!
non-null
The discounts that have been applied to the cart.
-
allocationMethod
DiscountApplicationAllocationMethod!
non-null
The method by which the discount's value is allocated to its entitled items.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
targetSelection
DiscountApplicationTargetSelection!
non-null
The lines on the cart targeted by the discount.
-
targetType
DiscountApplicationTarget!
non-null
The type of line (i.e. line item or shipping line) on a cart that the discount is applicable towards.
-
totalAllocatedAmount
MoneyV2!
non-null
The total allocated amount of the discount across all items.
-
value
PricingValue!
non-null
The value of the discount.
-
-
lines
[CartLine!]!
non-null
The items in a cart that the customer intends to purchase. A cart line is an entry in the customer's cart that represents a single unit of a product variant. For example, if a customer adds two different sizes of the same t-shirt to their cart, then each size is represented as a separate cart line.
-
attribute
Attribute
The custom attributes associated with a cart to store additional information. Cart attributes allow you to collect specific information from customers on the Cart page, such as order notes, gift wrapping requests, or custom product details. Attributes are stored as key-value pairs.
Cart line attributes are equivalent to the
line_itemobject in Liquid. -
cost
CartLineCost!
non-null
The cost of an item in a cart that the customer intends to purchase. Cart lines are entries in the customer's cart that represent a single unit of a product variant. For example, if a customer adds two different sizes of the same t-shirt to their cart, then each size is represented as a separate cart line.
-
discountAllocations
[DiscountAllocation!]!
non-null
The discounts that have been applied to the cart line.
-
id
ID!
non-null
The ID of the cart line.
-
merchandise
Merchandise!
non-null
The item that the customer intends to purchase.
-
parentRelationship
CartLineParentRelationship
The nested relationship between this line and its parent line, if any.
-
quantity
Int!
non-null
The quantity of the item that the customer intends to purchase.
-
sellingPlanAllocation
SellingPlanAllocation
The selling plan associated with the cart line, including information about how a product variant can be sold and purchased.
-
-
localizedFields
[LocalizedField!]!
non-null
The additional fields on the Cart page that are required for international orders in specific countries, such as customs information or tax identification numbers.
-
keys
[LocalizedFieldKey!]!
requiredDefault:[]
Arguments
The keys of the localized fields to retrieve.
- SHIPPING_CREDENTIAL_BR, SHIPPING_CREDENTIAL_CL, SHIPPING_CREDENTIAL_CN, SHIPPING_CREDENTIAL_CO, SHIPPING_CREDENTIAL_CR, SHIPPING_CREDENTIAL_EC, SHIPPING_CREDENTIAL_ES, SHIPPING_CREDENTIAL_GT, SHIPPING_CREDENTIAL_ID, SHIPPING_CREDENTIAL_KR, SHIPPING_CREDENTIAL_MX, SHIPPING_CREDENTIAL_MY, SHIPPING_CREDENTIAL_PE, SHIPPING_CREDENTIAL_PT, SHIPPING_CREDENTIAL_PY, SHIPPING_CREDENTIAL_TR, SHIPPING_CREDENTIAL_TW, SHIPPING_CREDENTIAL_TYPE_CO, TAX_CREDENTIAL_BR, TAX_CREDENTIAL_CL, TAX_CREDENTIAL_CO, TAX_CREDENTIAL_CR, TAX_CREDENTIAL_EC, TAX_CREDENTIAL_ES, TAX_CREDENTIAL_GT, TAX_CREDENTIAL_ID, TAX_CREDENTIAL_IT, TAX_CREDENTIAL_MX, TAX_CREDENTIAL_MY, TAX_CREDENTIAL_PE, TAX_CREDENTIAL_PT, TAX_CREDENTIAL_PY, TAX_CREDENTIAL_TR, TAX_CREDENTIAL_TYPE_CO, TAX_CREDENTIAL_TYPE_MX, TAX_CREDENTIAL_USE_MX, TAX_EMAIL_IT
-
key
LocalizedFieldKey!
non-null
Fields
The key of the localized field.
- SHIPPING_CREDENTIAL_BR, SHIPPING_CREDENTIAL_CL, SHIPPING_CREDENTIAL_CN, SHIPPING_CREDENTIAL_CO, SHIPPING_CREDENTIAL_CR, SHIPPING_CREDENTIAL_EC, SHIPPING_CREDENTIAL_ES, SHIPPING_CREDENTIAL_GT, SHIPPING_CREDENTIAL_ID, SHIPPING_CREDENTIAL_KR, SHIPPING_CREDENTIAL_MX, SHIPPING_CREDENTIAL_MY, SHIPPING_CREDENTIAL_PE, SHIPPING_CREDENTIAL_PT, SHIPPING_CREDENTIAL_PY, SHIPPING_CREDENTIAL_TR, SHIPPING_CREDENTIAL_TW, SHIPPING_CREDENTIAL_TYPE_CO, TAX_CREDENTIAL_BR, TAX_CREDENTIAL_CL, TAX_CREDENTIAL_CO, TAX_CREDENTIAL_CR, TAX_CREDENTIAL_EC, TAX_CREDENTIAL_ES, TAX_CREDENTIAL_GT, TAX_CREDENTIAL_ID, TAX_CREDENTIAL_IT, TAX_CREDENTIAL_MX, TAX_CREDENTIAL_MY, TAX_CREDENTIAL_PE, TAX_CREDENTIAL_PT, TAX_CREDENTIAL_PY, TAX_CREDENTIAL_TR, TAX_CREDENTIAL_TYPE_CO, TAX_CREDENTIAL_TYPE_MX, TAX_CREDENTIAL_USE_MX, TAX_EMAIL_IT
-
title
String!
non-null
The title of the localized field.
-
value
String
The value of the localized field.
-
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
poNumber
String
A purchase order number associated with the cart, often used for B2B transactions to reference the buyer's internal purchase order.
-
retailLocation
Location
The physical location where a retail order is created or completed.
-
address
LocationAddress!
non-null
The address of this location.
-
address1
String
The first line of the address for the location.
-
address2
String
The second line of the address for the location.
-
city
String
The city of the location.
-
country
String
The country of the location.
-
countryCode
String
The country code of the location.
-
formatted
[String!]!
non-null
A formatted version of the address for the location.
-
latitude
Float
The approximate latitude coordinates of the location.
-
longitude
Float
The approximate longitude coordinates of the location.
-
phone
String
The phone number of the location.
-
province
String
The province of the location.
-
provinceCode
String
The code for the province, state, or district of the address of the location.
-
zip
String
The ZIP code of the location.
-
-
handle
Handle!
non-null
The location handle.
-
id
ID!
non-null
The location id.
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
name
String!
non-null
The name of the location.
-
-
-
discount
Discount!
non-null
The discount node that owns the Shopify Function. Discounts are a way for merchants to promote sales and special offers, or as customer loyalty rewards. A single discount can be automatic or code-based, and can be applied to a cart lines, orders, and delivery.
-
discountClasses
[DiscountClass!]!
non-null
The discount classes that the discountNode supports.
- ORDER, PRODUCT, SHIPPING
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
-
enteredDiscountCodes
[EnteredDiscountCode!]!
non-null
Discount codes entered by the buyer at checkout, excluding gift cards. For cart.lines.discounts.generate.run and cart.delivery-options.discounts.generate.run targets, these discount codes are validated to ensure they are not deleted, maintain an active status, and are eligible for the current cart. For fetch targets, all entered discount codes are included, excluding gift cards.
-
code
String!
non-null
The discount code.
-
rejectable
Boolean!
non-null
Indicates whether the entered discount code can be rejected.
A discount code can't be rejected if it's associated with a discount that has already been applied to the cart.
-
-
fetchResult
HttpResponse
The result of the fetch target. Refer to network access for Shopify Functions. This input is only available in the
cart.lines.discounts.generate.runandcart.delivery-options.discounts.generate.runextension targets.-
body
String
The HTTP response body as a plain string. Use this field when the body is not in JSON format.
-
header
HttpResponseHeader
An HTTP header.
-
name
String!
required
Arguments
A case-insensitive header name.
-
name
String!
non-null
Fields
Header name.
-
value
String!
non-null
Header value.
-
-
jsonBody
JSON
The HTTP response body parsed as JSON. If the body is valid JSON, it will be parsed and returned as a JSON object. If parsing fails, then raw body is returned as a string. Use this field when you expect the response to be JSON, or when you're dealing with mixed response types, meaning both JSON and non-JSON. Using this field reduces function instruction consumption and ensures that the data is formatted in logs. To prevent increasing the function target input size unnecessarily, avoid querying both
bodyandjsonBodysimultaneously. -
status
Int!
non-null
The HTTP status code.
-
headers
[HttpResponseHeader!]!
non-nullDeprecated
-
name
String!
non-null
Header name.
-
value
String!
non-null
Header value.
-
-
-
localization
Localization!
non-null
The regional and language settings that determine how the Function handles currency, numbers, dates, and other locale-specific values during discount calculations. These settings are based on the store's configured localization practices.
-
country
Country!
non-null
The country for which the store is customized, reflecting local preferences and regulations. Localization might influence the language, currency, and product offerings available in a store to enhance the shopping experience for customers in that region.
-
isoCode
CountryCode!
non-null
The ISO code of the country.
- AC, AD, AE, AF, AG, AI, AL, AM, AN, AO, AR, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BL, BM, BN, BO, BQ, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MF, MG, MK, ML, MM, MN, MO, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PS, PT, PY, QA, RE, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TA, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VN, VU, WF, WS, XK, YE, YT, ZA, ZM, ZW, ZZ
-
-
language
Language!
non-null
The language for which the store is customized, ensuring content is tailored to local customers. This includes product descriptions and customer communications that resonate with the target audience.
-
isoCode
LanguageCode!
non-null
The ISO code.
-
-
market
Market!
non-nullDeprecated
-
-
presentmentCurrencyRate
Decimal!
non-null
The exchange rate used to convert discounts between the shop's default currency and the currency that displays to the customer during checkout. For example, if a store operates in USD but a customer is viewing discounts in EUR, then the presentment currency rate handles this conversion for accurate pricing.
-
shop
Shop!
non-null
Information about the shop where the Function is running, including the shop's timezone setting and associated metafields.
-
localTime
LocalTime!
non-null
The current time based on the store's timezone setting.
-
date
Date!
non-null
The current date relative to the parent object.
-
dateTimeAfter
Boolean!
non-null
Returns true if the current date and time is at or past the given date and time, and false otherwise.
-
dateTimeBefore
Boolean!
non-null
Returns true if the current date and time is before the given date and time, and false otherwise.
-
dateTimeBetween
Boolean!
non-null
Returns true if the current date and time is between the two given date and times, and false otherwise.
-
timeAfter
Boolean!
non-null
Returns true if the current time is at or past the given time, and false otherwise.
-
timeBefore
Boolean!
non-null
Returns true if the current time is at or past the given time, and false otherwise.
-
timeBetween
Boolean!
non-null
Returns true if the current time is between the two given times, and false otherwise.
-
-
metafield
Metafield
A custom field that stores additional information about a Shopify resource, such as products, orders, and many more. Using metafields with Shopify Functions enables you to customize the checkout experience.
-
metaobject
Metaobject
Fetch a specific Metaobject by one of its unique identifiers. Only app-owned metaobjects with the $app reserved prefix are accessible to functions.
-
handle
MetaobjectHandleInput
Arguments
The handle and type of the metaobject.
-
handle
String!
non-null
The handle of the metaobject to retrieve.
-
type
String!
non-null
The type of the metaobject. Must match an existing metaobject definition type.
-
-
id
ID
The ID of the metaobject.
-
field
MetaobjectField
Fields
The field for an object key, or null if the key has no field definition.
-
key
String!
required
Arguments
The metaobject key to access.
-
jsonValue
JSON
Fields
The assigned field value in JSON format.
-
key
String!
non-null
The object key of this field.
-
type
String!
non-null
The type of the field.
-
value
String
The assigned field value, always stored as a string regardless of the field type.
-
-
handle
String!
non-null
The unique handle of the metaobject, useful as a custom ID.
-
type
String!
non-null
The type of the metaobject.
-
-
-
triggeringDiscountCode
String
The discount code entered by a customer, which caused the Discount Function to run. This input is only available in the
cart.lines.discounts.generate.runandcart.delivery-options.discounts.generate.runextension targets.
-
Cart run function
The function processes input schema data to calculate and allocate discounts across cart lines. The function handles both fixed and percentage-based discounts while respecting discount caps and generating any messages associated with the discount, such as error messages associated with validation.
This return must follow the schema defined in the CartLinesDiscountsGenerateRunResult object.
-
CartLinesDiscountsGenerateRunResult
OBJECT
The
CartLinesDiscountsGenerateRunResultobject is the output of the function run target. The object contains the operations to generate, validate, and apply discounts to the cart.-
operations
[CartOperation!]!
non-null
The list of operations to apply discounts to the cart.
-
enteredDiscountCodesAccept
EnteredDiscountCodesAcceptOperation
An operation that selects which entered discount codes to accept. Use this to validate discount codes from external systems.
-
codes
[DiscountCode!]!
non-null
The list of discount codes to accept.
-
code
String!
non-null
The discount code.
-
-
-
enteredDiscountCodesReject
EnteredDiscountCodesRejectOperation
An operation that rejects entered discount codes with a custom message. Use this to conditionally reject discount codes based on business logic. This operation can only be used if the function is backed by an automatic discount.
-
codes
[RejectedDiscountCode!]!
non-null
The list of discount codes to reject.
-
message
String!
non-null
The custom message to display to the customer when the discount codes are rejected.
-
-
orderDiscountsAdd
OrderDiscountsAddOperation
An operation that applies order discounts to a cart that share a selection strategy.
-
candidates
[OrderDiscountCandidate!]!
non-null
The list of discounts that can be applied to an order.
-
associatedDiscountCode
AssociatedDiscountCode
An optional discount code associated with this discount candidate, for use with automatic discounts. If a code discount is the function trigger, the associated discount code will be overwritten by the triggering discount code.
-
code
String!
non-null
The discount code.
-
-
conditions
[Condition!]
The conditions that must be satisfied for an order to be eligible for a discount candidate.
-
cartLineMinimumQuantity
CartLineMinimumQuantity
The condition for checking the minimum quantity of products across a group of cart lines.
-
ids
[ID!]!
non-null
Cart line IDs with a merchandise line price that's included to calculate the minimum quantity purchased to receive the discount.
-
minimumQuantity
Int!
non-null
The minimum quantity of a cart line to be eligible for a discount candidate.
-
-
cartLineMinimumSubtotal
CartLineMinimumSubtotal
The condition for checking the minimum subtotal of products across a group of cart lines.
-
ids
[ID!]!
non-null
Cart line IDs with a merchandise line price that's included to calculate the minimum subtotal purchased to receive the discount.
-
minimumAmount
Decimal!
non-null
The minimum subtotal amount of the cart line to be eligible for a discount candidate in the shop's currency.
-
-
orderMinimumSubtotal
OrderMinimumSubtotal
The condition for checking the minimum subtotal amount of the order.
-
excludedCartLineIds
[ID!]!
non-null
Cart line IDs with a merchandise line price that's excluded to calculate the minimum subtotal amount of the order.
-
minimumAmount
Decimal!
non-null
The minimum subtotal amount of the order to be eligible for the discount in the shop's currency.
-
-
-
message
String
A notification on the Cart page informs customers about available discounts. If an automatic discount applies, the notification displays this message, such as "Save 20% on all t-shirts." If a discount code is entered, the notification displays the code instead.
-
targets
[OrderDiscountCandidateTarget!]!
non-null
The targets of the order discount candidate.
-
orderSubtotal
OrderSubtotalTarget
A method for applying a discount to the entire order subtotal. The subtotal is the total amount of the order before any taxes, shipping fees, or discounts are applied. For example, if a customer places an order for a t-shirt and a pair of shoes, then the subtotal is the sum of the prices of those items.
-
excludedCartLineIds
[ID!]!
non-null
The list of excluded cart line IDs. These cart lines are excluded from the order subtotal calculation when calculating the maximum value of the discount.
-
-
-
value
OrderDiscountCandidateValue!
non-null
The value of the order discount candidate.
-
-
selectionStrategy
OrderDiscountSelectionStrategy!
non-null
The strategy that's applied to the list of discounts.
- FIRST, MAXIMUM
-
-
productDiscountsAdd
ProductDiscountsAddOperation
An operation that applies product discounts to a cart that share a selection strategy.
-
candidates
[ProductDiscountCandidate!]!
non-null
The list of products that are eligible for the discount.
-
selectionStrategy
ProductDiscountSelectionStrategy!
non-null
The strategy that's applied to the list of products that are eligible for the cart line discount.
-
-
-
Delivery run target
cart.delivery-options.discounts.generate.run
The run target that's responsible for generating the discount on shipping costs using either Shopify data, hardcoded values, or fetch results from external providers. The target returns an ordered list of operations to be applied to the cart.
The run target evaluates carts against discount rules, calculates applicable reductions, and returns the final discount. This target generates and returns a shipping discount to potentially apply to the cart.
Note:
Checkouts and orders can include multiple delivery methods, such as shipping and pickup in the same order. When your app uses delivery or fulfillment data, iterate over all delivery groups or fulfillment orders to determine the delivery method for each one. Don't assume one method for the order. For more information, refer to split carts in checkout.
-
Input
OBJECT
The
Inputobject is the complete GraphQL schema that your function receives to generate discounts for a delivery option or delivery group. Your function only receives the fields that you request in the input query. To optimize performance, we highly recommend that you request only the fields that your function requires.
Delivery run function
The function processes input schema data to calculate and allocate shipping discounts across cart lines, handling both fixed and percentage-based discounts while respecting discount caps and displaying messages associated with the discount.
This return must follow the schema defined in the CartDeliveryOptionsDiscountsGenerateRunResult object.
-
CartDeliveryOptionsDiscountsGenerateRunResult
OBJECT
The
CartDeliveryOptionsDiscountsGenerateRunResultobject is the output of the function run target. The object contains the operations to generate, validate, and apply shipping discounts to the cart.-
operations
[DeliveryOperation!]!
non-null
An ordered list of operations to generate delivery discounts, such as validating and applying discounts to the cart.
-
Examples
-
Apply a percentage-off shipping discount.
This function implements a shipping discount function that applies a percentage discount to the cheapest shipping method based on the cart subtotal.
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle cost { amount } } } } discount { metafield(namespace: "$app:delivery-discounts", key: "configuration") { jsonValue } } }Input Query (JavaScript)
query DeliveryInput { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle cost { amount } } } } discount { metafield(namespace: "$app:delivery-discounts", key: "configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "lines": [ { "id": "gid://shopify/CartLine/0", "cost": { "subtotalAmount": { "amount": "120.00" } }, "quantity": 1 } ], "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Input Object (JavaScript)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "lines": [ { "id": "gid://shopify/CartLine/0", "cost": { "subtotalAmount": { "amount": "120.00" } }, "quantity": 1 } ], "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default)] pub struct DiscountTier { threshold: f64, percentage: f64, } #[derive(Deserialize, Default)] pub struct Configuration { tiers: Vec<DiscountTier>, } #[shopify_function] fn cart_delivery_options_discounts_generate_run(input: schema::cart_delivery_options_discounts_generate_run::Input) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { // Parse configuration from metafield let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }), }; // Get cart subtotal let subtotal = input.cart().cost().subtotal_amount().amount().0; // Find the highest applicable tier let applicable_tier = config.tiers.iter() .filter(|tier| subtotal >= tier.threshold) .max_by(|a, b| a.threshold.partial_cmp(&b.threshold).unwrap_or(std::cmp::Ordering::Equal)); // If no tier applies, return empty operations let Some(tier) = applicable_tier else { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); }; let mut operations = vec![]; // Process each delivery group for delivery_group in input.cart().delivery_groups() { // Find the cheapest delivery option if let Some(cheapest_option) = delivery_group.delivery_options().iter() .min_by(|a, b| { let a_cost = a.cost().amount().0; let b_cost = b.cost().amount().0; a_cost.partial_cmp(&b_cost).unwrap_or(std::cmp::Ordering::Equal) }) { // Add discount operation for the cheapest option operations.push(schema::DeliveryOperation::DeliveryDiscountsAdd(schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: cheapest_option.handle().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(tier.percentage), }), message: Some(format!("{}% off shipping", tier.percentage)), associated_discount_code: None, }], })); } } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
62222 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {Object} DiscountTier * @property {number} threshold * @property {number} percentage */ /** * @typedef {Object} Configuration * @property {DiscountTier[]} tiers */ /** * @typedef {import('../generated/api').DeliveryInput} DeliveryInput * @typedef {import('../generated/api').DeliveryOperation} DeliveryOperation * @typedef {import('../generated/api').DeliveryDiscountCandidate} DeliveryDiscountCandidate */ /** * cartDeliveryOptionsDiscountsGenerateRun * @param {DeliveryInput} input - The DeliveryInput * @returns {{operations: DeliveryOperation[]}} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { // Parse configuration from metafield let config; try { config = input.discount?.metafield ? JSON.parse(JSON.stringify(input.discount.metafield.jsonValue)) : { tiers: [] }; } catch (e) { return { operations: [] }; } // If no config or invalid tiers, return empty operations if (!config || !Array.isArray(config.tiers)) { return { operations: [] }; } // Get cart subtotal const subtotal = input.cart?.cost?.subtotalAmount?.amount || 0; // Find the highest applicable tier const applicableTier = config.tiers .filter(tier => subtotal >= tier.threshold) .reduce((highest, current) => !highest || current.threshold > highest.threshold ? current : highest, null); // If no tier applies, return empty operations if (!applicableTier) { console.log('no tier applies'); return { operations: [] }; } const operations = []; // Process each delivery group for (const deliveryGroup of input.cart.deliveryGroups) { if (!deliveryGroup.deliveryOptions?.length) { continue; } // Find the cheapest delivery option let cheapestOption = deliveryGroup.deliveryOptions[0]; for (let i = 1; i < deliveryGroup.deliveryOptions.length; i++) { const option = deliveryGroup.deliveryOptions[i]; if (option.cost.amount < cheapestOption.cost.amount) { cheapestOption = option; } } /** @type {DeliveryOperation} */ const operation = { deliveryDiscountsAdd: { candidates: [{ message: `${applicableTier.percentage}% off shipping`, targets: [{ deliveryOption: { handle: cheapestOption.handle, }, }], value: { percentage: { value: String(applicableTier.percentage.toFixed(1)), }, }, }], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }; operations.push(operation); } return { operations }; }Performance Cost (JavaScript)
486851 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "15% off shipping", "targets": [ { "deliveryOption": { "handle": "standard" } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "15% off shipping", "targets": [ { "deliveryOption": { "handle": "standard" } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "ALL" } } ] }cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id cost { subtotalAmount { amount } } } } }Input Query (JavaScript)
query CartInput { cart { lines { id cost { subtotalAmount { amount } } } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "lines": [ { "id": "gid://shopify/CartLine/0", "cost": { "subtotalAmount": { "amount": "120.00" } }, "quantity": 1 } ], "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Input Object (JavaScript)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "lines": [ { "id": "gid://shopify/CartLine/0", "cost": { "subtotalAmount": { "amount": "120.00" } }, "quantity": 1 } ], "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let max_cart_line = input .cart() .lines() .iter() .max_by(|a, b| { a.cost() .subtotal_amount() .amount() .partial_cmp(b.cost().subtotal_amount().amount()) .unwrap_or(std::cmp::Ordering::Equal) }) .ok_or("No cart lines found")?; Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::OrderDiscountsAdd(schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("10% OFF ORDER".to_string()), value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(10.0), }), conditions: None, associated_discount_code: None, }], }), schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: max_cart_line.id().clone(), quantity: None, })], message: Some("20% OFF PRODUCT".to_string()), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(20.0), }), associated_discount_code: None, }], }), ], }) }Performance Cost (Rust)
49374 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy, ProductDiscountSelectionStrategy, } from '../generated/api'; /** * @typedef {import("../generated/api").Input} CartInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * cartLinesDiscountsGenerateRun * @param {CartInput} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error('No cart lines found'); } const maxCartLine = input.cart.lines.reduce((maxLine, line) => { if (line.cost.subtotalAmount.amount > maxLine.cost.subtotalAmount.amount) { return line; } return maxLine; }, input.cart.lines[0]); return { operations: [ { orderDiscountsAdd: { candidates: [ { message: '10% OFF ORDER', targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], value: { percentage: { value: "10.0", }, }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }, { productDiscountsAdd: { candidates: [ { message: '20% OFF PRODUCT', targets: [ { cartLine: { id: maxCartLine.id, }, }, ], value: { percentage: { value: "20.0", }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }, ], }; }Performance Cost (JavaScript)
420610 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "20% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/0", "quantity": null } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "message": "20% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/0" } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Create a tiered discount based on line subtotals
A Shopify Discount Function that implements tiered fixed-amount discounts for cart lines based on their individual subtotals. It applies progressively larger discounts as line subtotals increase: $10 off for lines totaling $100+, $20 off for lines totaling $200+, and $30 off for lines totaling $300+. The discount is applied as a fixed amount per line rather than per item, creating effective volume discounts for higher-value purchases.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id cost { subtotalAmount { amount } } } } }Input Query (JavaScript)
query CartInput { cart { lines { id cost { subtotalAmount { amount } } } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "50.00" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "150.00" } } }, { "id": "gid://shopify/CartLine/3", "cost": { "subtotalAmount": { "amount": "250.00" } } }, { "id": "gid://shopify/CartLine/4", "cost": { "subtotalAmount": { "amount": "350.00" } } } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "50.00" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "150.00" } } }, { "id": "gid://shopify/CartLine/3", "cost": { "subtotalAmount": { "amount": "250.00" } } }, { "id": "gid://shopify/CartLine/4", "cost": { "subtotalAmount": { "amount": "350.00" } } } ] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; const TIERS: [(Decimal, i32); 3] = [ (Decimal(100.0), 10), (Decimal(200.0), 40), (Decimal(300.0), 60), ]; #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let mut operations = vec![]; for line in input.cart().lines() { let cost = line.cost().subtotal_amount().amount(); if let Some(tier) = get_tier(&cost) { let discount = tier.1; operations.push(schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: line.id().clone(), quantity: None, })], value: schema::ProductDiscountCandidateValue::FixedAmount(schema::ProductDiscountCandidateFixedAmount { amount: Decimal(discount.into()), applies_to_each_item: Some(false), }), message: Some(format!("{}$ off", discount)), associated_discount_code: None, }], selection_strategy: schema::ProductDiscountSelectionStrategy::All, })); } } Ok( schema::CartLinesDiscountsGenerateRunResult { operations, }, ) } fn get_tier(cost: &Decimal) -> Option<(Decimal, i32)> { TIERS .iter() .rev() .find(|(threshold, _)| cost.ge(threshold)) .copied() }Performance Cost (Rust)
77063 instructions
Function Code (JavaScript)
// @ts-check import {ProductDiscountSelectionStrategy} from "../generated/api"; /** * @typedef {import("../generated/api").Input} CartInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @typedef {Object} Tier * @property {number} threshold - The threshold of the tier * @property {number} discount - The discount of the tier */ const TIERS = [ {threshold: 100, discount: 10}, {threshold: 200, discount: 20}, {threshold: 300, discount: 30}, ]; /** * generateCartRun * @param {CartInput} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error("No cart lines found"); } return input.cart.lines.reduce((runResult, cartLine) => { const tier = getTier(cartLine.cost.subtotalAmount.amount); if (tier) { runResult.operations.push({ productDiscountsAdd: { candidates: [ { message: `${tier.discount}$ off`, targets: [ { cartLine: { id: cartLine.id, }, }, ], value: { fixedAmount: { amount: tier.discount, appliesToEachItem: false, }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }); } return runResult; }, /** @type {CartLinesDiscountsGenerateRunResult} */ ({operations: []})); } /** * getTier * @param {number} cost - The cost of the cart line * @returns {Tier | undefined} - The tier */ function getTier(cost) { return [...TIERS].reverse().find(tier => cost >= tier.threshold); }Performance Cost (JavaScript)
512503 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "10$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": null } } ], "value": { "fixedAmount": { "amount": "10.0", "appliesToEachItem": false } } } ], "selectionStrategy": "ALL" } }, { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "40$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/3", "quantity": null } } ], "value": { "fixedAmount": { "amount": "40.0", "appliesToEachItem": false } } } ], "selectionStrategy": "ALL" } }, { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "60$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/4", "quantity": null } } ], "value": { "fixedAmount": { "amount": "60.0", "appliesToEachItem": false } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "10$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2" } } ], "value": { "fixedAmount": { "amount": 10, "appliesToEachItem": false } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "message": "20$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/3" } } ], "value": { "fixedAmount": { "amount": 20, "appliesToEachItem": false } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "message": "30$ off", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/4" } } ], "value": { "fixedAmount": { "amount": 30, "appliesToEachItem": false } } } ], "selectionStrategy": "FIRST" } } ] } -
Create a dynamic discount from discount codes
A Shopify Discount Function that parses entered discount codes with the format "X%OFF" to extract and apply the specified percentage discount to the entire order if a discount code associated to this function exists. For example, entering "10%OFF" applies a 10% discount, while "25%OFF" applies a 25% discount.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { discount { discountClasses } triggeringDiscountCode }Input Query (JavaScript)
query CartInput { discount { discountClasses } triggeringDiscountCode }Input Object (Rust)
{ "discount": { "discountClasses": ["ORDER"] }, "triggeringDiscountCode": "20%OFF" }Input Object (JavaScript)
{ "discount": { "discountClasses": ["ORDER"] }, "triggeringDiscountCode": "20%OFF" }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { if !input .discount() .discount_classes() .contains(&schema::DiscountClass::Order) { return Ok( schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }, ); } if let Some(percentage_off) = get_percentage_off(&input.triggering_discount_code()) { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { conditions: None, targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(percentage_off as f64), }), message: Some(format!("{}% off order", percentage_off)), associated_discount_code: None, }], }, )], }); } Ok( schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }, ) } fn get_percentage_off(code: &Option<&String>) -> Option<i64> { code.as_ref().and_then(|discount_code| { discount_code .strip_suffix("%OFF") .filter(|digits| !digits.is_empty()) .and_then(|digits| digits.parse::<i64>().ok()) }) }Performance Cost (Rust)
29876 instructions
Function Code (JavaScript)
// @ts-check import {OrderDiscountSelectionStrategy, DiscountClass} from "../generated/api"; /** * @typedef {import("../generated/api").Input} CartInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * generateCartRun * @param {CartInput} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { const percentageOff = getPercentageOff(input.triggeringDiscountCode); const hasOrderDiscount = input.discount.discountClasses.includes( DiscountClass.Order, ); if (!hasOrderDiscount || !percentageOff) { return { operations: [], }; } return { operations: [ { orderDiscountsAdd: { candidates: [ { message: `${percentageOff}% off order`, targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], value: { percentage: { value: percentageOff, }, }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }, ], }; } function getPercentageOff(code) { const percentageOff = code.match(/([\d]{1,2})%OFF/); return percentageOff ? parseInt(percentageOff[1], 10) : null; }Performance Cost (JavaScript)
257152 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "20% off order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "20% off order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": 20 } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { discount { discountClasses } triggeringDiscountCode cart { deliveryGroups { id } } }Input Query (JavaScript)
query DeliveryInput { discount { discountClasses } triggeringDiscountCode cart { deliveryGroups { id } } }Input Object (Rust)
{ "discount": { "discountClasses": ["SHIPPING"] }, "triggeringDiscountCode": "50%OFF", "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] } }Input Object (JavaScript)
{ "discount": { "discountClasses": ["SHIPPING"] }, "triggeringDiscountCode": "50%OFF", "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult>{ if !input .discount() .discount_classes() .contains(&schema::DiscountClass::Shipping) { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } if let Some(percentage_off) = get_percentage_off(&input.triggering_discount_code()) { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: input .cart() .delivery_groups() .iter() .map(|group| { schema::DeliveryOperation::DeliveryDiscountsAdd(schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(percentage_off as f64), }), message: Some(format!("{}% off shipping", percentage_off)), associated_discount_code: None, }], }) }) .collect(), }); } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }) } fn get_percentage_off(code: &Option<&String>) -> Option<i64> { code.as_ref().and_then(|discount_code| { discount_code .strip_suffix("%OFF") .filter(|digits| !digits.is_empty()) .and_then(|digits| digits.parse::<i64>().ok()) }) }Performance Cost (Rust)
36844 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy, DiscountClass, } from "../generated/api"; /** * @typedef {import("../generated/api").Input} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * generateDeliveryRun * @param {RunInput} input - The DeliveryInput * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const percentageOff = getPercentageOff(input.triggeringDiscountCode); const hasShippingDiscount = input.discount.discountClasses.includes( DiscountClass.Shipping, ); if (!hasShippingDiscount || !percentageOff) { return { operations: [], }; } const operations = input.cart.deliveryGroups.map(deliveryGroup => ({ deliveryDiscountsAdd: { candidates: [ { message: `${percentageOff}% off shipping`, targets: [ { deliveryGroup: { id: deliveryGroup.id, }, }, ], value: { percentage: { value: percentageOff, }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, })); return {operations}; } function getPercentageOff(code) { const percentageOff = code.match(/([\d]{1,2})%OFF/); return percentageOff ? parseInt(percentageOff[1], 10) : null; }Performance Cost (JavaScript)
284810 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "50% off shipping", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": "50.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "50% off shipping", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": 50 } } } ], "selectionStrategy": "ALL" } } ] } -
Apply a collection-based order discount
Example implementation of a discounts function that applies a 10% discount to the order if the cart total is high enough and a product in the cart belongs to a specific collection. in this example the subtotal must be above $100 and there must be a product in the TORONTO collection. You can use this example to understand how to: * Apply a percent discount to the if the order is worth enough money * Apply a percent discount if its for a product in a certain collection * Not apply a percent discount if the order is not for the right products * Not apply a percent discount if the order does not contain products from a certain collection.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/452849795317"]) } } } } } discount { discountClasses } }Input Query (JavaScript)
query CartInput { cart { lines { merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/452849795317"]) } } } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/0", "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER", "SHIPPING"] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER", "SHIPPING"] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { if input.cart().lines().is_empty() { return Err("No cart lines found".into()); } // Check if the discount has the ORDER class let has_order_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); // If the order cannot be discounted, return early if !has_order_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Check if there are any products in the collection let products_in_collection = input.cart().lines().iter().any(|line| { let merchandise = &line.merchandise(); if let schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = merchandise { return *variant.product().in_any_collection(); } false }); // If there are products in the collection, add the order discount if products_in_collection { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("10% OFF ORDER".to_string()), value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0), }, ), conditions: None, associated_discount_code: None, }], }, )], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }) }Performance Cost (Rust)
42271 instructions
Function Code (JavaScript)
import { DiscountClass, OrderDiscountSelectionStrategy, } from "../generated/api"; /** * @typedef {import("../generated/api").CartInput} RunInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {RunInput} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error("No cart lines found"); } const discountClasses = input.discount.discountClasses || []; const canDiscountOrder = discountClasses.includes(DiscountClass.Order); // If the order cannot be discounted, return early if (!canDiscountOrder) { return { operations: [] }; } // Check if there are any products in the collection const productsInCollection = input.cart.lines.some( (line) => line.merchandise.__typename === "ProductVariant" && line.merchandise.product?.inAnyCollection ); // If there are products in the collection, add the order discount if (productsInCollection) { return { operations: [ { orderDiscountsAdd: { candidates: [ { message: "10% OFF ORDER", targets: [{ orderSubtotal: { excludedCartLineIds: [] } }], value: { percentage: { value: 10 } }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }, ], }; } return { operations: [] }; }Performance Cost (JavaScript)
227027 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": 10 } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply a discount to custom products
This example demonstrates how to create a discount function that applies a 20% discount to all custom products. Custom products are identified by a metafield on the product. This example demonstrates: * Creating a discount function * Checking the metafield on the products in the cart * Applying a discount to the cart lines that have the custom product metafield set to true * Returning the discount application result * Using
ProductDiscountSelectionStrategy.Allto ensure all eligible products receive the discountcart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id merchandise { __typename ... on ProductVariant { product { metafield(namespace: "custom", key: "isCustom") { value } } } } } } discount { discountClasses } }Input Query (JavaScript)
query CartInput { cart { lines { id merchandise { __typename ... on ProductVariant { product { metafield(namespace: "custom", key: "isCustom") { value } } } } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/0", "merchandise": { "__typename": "ProductVariant", "product": { "metafield": { "value": "true" } } } } ] }, "discount": { "discountClasses": [ "PRODUCT" ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/0", "merchandise": { "__typename": "ProductVariant", "product": { "metafield": { "value": "true" } } } } ] }, "discount": { "discountClasses": [ "PRODUCT" ] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { if input.cart().lines().is_empty() { return Err("No cart lines found".into()); } let has_product_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Product); if !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Find products with isCustom metafield set to true let custom_product_lines: Vec<_> = input .cart() .lines() .iter() .filter(|line| { // Check if the line has a product with isCustom metafield if let schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = &line.merchandise() { if let Some(metafield) = &variant.product().metafield() { return *metafield.value() == "true"; } } false }) .collect(); if !custom_product_lines.is_empty() { // Apply 20% discount to all custom products let candidates = custom_product_lines .iter() .map(|line| schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: line.id().to_string(), quantity: None, }, )], message: Some("20% OFF CUSTOM PRODUCT".to_string()), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(20.0), }), associated_discount_code: None, }) .collect::<Vec<_>>(); operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::All, candidates, }, )); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
43644 instructions
Function Code (JavaScript)
import { DiscountClass, ProductDiscountSelectionStrategy, } from '../generated/api'; /** * @typedef {import("../generated/api").CartInput} RunInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {RunInput} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error('No cart lines found'); } const hasProductDiscountClass = input.discount.discountClasses.includes( DiscountClass.Product, ); const operations = []; if (hasProductDiscountClass) { // Find products with isCustom metafield set to true const customProductLines = input.cart.lines.filter(line => { // Check if the line has a product with isCustom metafield if (line.merchandise.__typename !== 'ProductVariant') return false; const metafield = line.merchandise.product?.metafield; return metafield && metafield.value === 'true'; }); if (customProductLines.length > 0) { // Apply 20% discount to all custom products operations.push({ productDiscountsAdd: { candidates: customProductLines.map(line => ({ message: '20% OFF CUSTOM PRODUCT', targets: [ { cartLine: { id: line.id, }, }, ], value: { percentage: { value: 20, }, }, })), selectionStrategy: ProductDiscountSelectionStrategy.All, }, }); } } return { operations, }; }Performance Cost (JavaScript)
250238 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "20% OFF CUSTOM PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/0", "quantity": null } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "20% OFF CUSTOM PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/0" } } ], "value": { "percentage": { "value": 20.0 } } } ], "selectionStrategy": "ALL" } } ] } -
Apply a discount to orders with no sale items
This example implements a Shopify function that gives a configurable discount to any order whose product variants do not have a compare-at price higher than its current price. The discount percentage is stored in an app-owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity cost { amountPerQuantity { amount } compareAtAmountPerQuantity { amount } } } } discount { metafield(namespace: "$app:order-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity cost { amountPerQuantity { amount } compareAtAmountPerQuantity { amount } } } } discount { metafield(namespace: "$app:order-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2, "cost": { "amountPerQuantity": { "amount": "50.00" }, "compareAtAmountPerQuantity": null } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 10.0 } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2, "cost": { "amountPerQuantity": { "amount": "50.00" }, "compareAtAmountPerQuantity": null } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 10.0 } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { percentage: f64, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config= match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; // Check if any product has a compare-at price greater than the current price let has_higher_compare_at_price = input.cart().lines().iter().any(|line| { if let Some(compare_at_amount) = &line.cost().compare_at_amount_per_quantity() { let current_price = line.cost().amount_per_quantity().amount().0; let compare_at_price = compare_at_amount.amount().0; compare_at_price > current_price } else { false } }); if has_higher_compare_at_price { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } // Apply the discount to the entire order Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::OrderDiscountsAdd(schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { conditions: None, value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(config.percentage), }), targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some(format!("{}% off your order!", config.percentage)), associated_discount_code: None, }], }), ], }) }Performance Cost (Rust)
42161 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration or return empty discount if no metafield const configuration = input.discount?.metafield?.jsonValue || { percentage: 0 }; if (!configuration.percentage) { return { operations: [] }; } // Check if any product has a compare-at price greater than the current price const hasHigherCompareAtPrice = input.cart.lines.some(line => { const compareAtAmount = line.cost.compareAtAmountPerQuantity?.amount; const currentPrice = line.cost.amountPerQuantity.amount; return compareAtAmount > currentPrice; }); if (hasHigherCompareAtPrice) { return { operations: [] }; } // Apply the discount to the entire order return { operations: [ { orderDiscountsAdd: { candidates: [ { value: { percentage: { value: configuration.percentage.toFixed(1) } }, targets: [ { orderSubtotal: { excludedCartLineIds: [] } } ], message: `${configuration.percentage}% off your order!` } ], selectionStrategy: OrderDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% off your order!", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% off your order!", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply tiered discounts based on order subtotal
This function implements tiered discounting based on order subtotal setting a minimum order subtotal for each tier, and storing the tier in an app owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { cost { subtotalAmount { amount currencyCode } } } discount { metafield(namespace: "order-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { cost { subtotalAmount { amount currencyCode } } } discount { metafield(namespace: "$app:order-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "150.00", "currencyCode": "USD" } } }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 100.0, "percentage": 10.0 }, { "threshold": 200.0, "percentage": 15.0 } ] } } } }Input Object (JavaScript)
{ "cart": { "cost": { "subtotalAmount": { "amount": "150.00", "currencyCode": "USD" } } }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 100.0, "percentage": 10.0 }, { "threshold": 200.0, "percentage": 15.0 } ] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] struct Tier { threshold: f64, percentage: f64, } #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { tiers: Vec<Tier>, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }) } }; let subtotal = input.cart().cost().subtotal_amount().amount().0; // Get all applicable discount tiers let applicable_tiers: Vec<&Tier> = config .tiers .iter() .filter(|tier| subtotal >= tier.threshold) .collect(); if !applicable_tiers.is_empty() { let candidates = applicable_tiers .into_iter() .map(|tier| schema::OrderDiscountCandidate { value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal::from(tier.percentage), }), targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some(format!("{}% off your order", tier.percentage)), conditions: Some(vec![schema::Condition::OrderMinimumSubtotal( schema::OrderMinimumSubtotal { minimum_amount: Decimal::from(tier.threshold), excluded_cart_line_ids: vec![], }, )]), associated_discount_code: None, }) .collect(); Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::OrderDiscountsAdd(schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::Maximum, candidates, }), ], }) } else { Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }) } }Performance Cost (Rust)
57485 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult * @typedef {import('../generated/api').OrderDiscountCandidate} OrderDiscountCandidate */ /** * @typedef {Object} Tier * @property {number} threshold * @property {number} percentage */ /** * @typedef {Object} Configuration * @property {Tier[]} tiers */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Return empty operations if no cart or metafield if (!input?.discount?.metafield) { return { operations: [] }; } // Parse configuration from metafield /** @type {Configuration} */ const configuration = input.discount.metafield?.jsonValue ?? { tiers: [] }; const subtotal = parseFloat(input.cart.cost.subtotalAmount?.amount ?? "0"); // Get all applicable discount tiers const applicableTiers = configuration.tiers?.filter(tier => subtotal >= tier.threshold) ?? []; if (applicableTiers.length === 0) { return { operations: [] }; } /** @type {OrderDiscountCandidate[]} */ const candidates = applicableTiers.map(tier => ({ value: { percentage: { value: tier.percentage.toFixed(1) } }, targets: [{ orderSubtotal: { excludedCartLineIds: [] } }], message: `${tier.percentage}% off your order`, conditions: [{ orderMinimumSubtotal: { excludedCartLineIds: [], minimumAmount: tier.threshold.toFixed(1).toString() } }] })); return { operations: [ { orderDiscountsAdd: { candidates, selectionStrategy: OrderDiscountSelectionStrategy.Maximum } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": [ { "orderMinimumSubtotal": { "excludedCartLineIds": [], "minimumAmount": "100.0" } } ], "message": "10% off your order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "conditions": [ { "orderMinimumSubtotal": { "excludedCartLineIds": [], "minimumAmount": "100.0" } } ], "message": "10% off your order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] } -
Apply a fixed discount for collection-specific purchases
Example implementation of a discount function that applies a discount to the order if the order total is high enough and a product in the cart belongs to a specific collection. In this example the order subtotal must be above $100 and there must be a product in the TORONTO collection. You can use this example to understand how to: * Apply a discount to the order if the order is worth enough money * Apply an order discount if its for a product in a certain collection * Not apply a order discount if the order is not for the right products * Not apply an order discount if the order does not contain products from a certain collection.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/452849795317"]) } } } } cost { subtotalAmount { amount } } } }Input Query (JavaScript)
query Input { cart { lines { merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/452849795317"]) } } } } cost { subtotalAmount { amount } } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "100.0" } }, "lines": [ { "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } } ], "cost": { "subtotalAmount": { "amount": "100.0" } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Check if the cart contains a product from the Toronto Collection let mut is_in_collection = false; for line in input.cart().lines().iter() { let variant = match &line.merchandise() { schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) => variant, _ => continue, // Do not select for CustomProduct unless it's selected in the input query }; let in_collection = variant.product().in_any_collection(); if *in_collection == true { is_in_collection = true; break; } } // If the cart does not contain a product from the Toronto collection, // then there should be no discount applied if !is_in_collection { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } // If the value of the order is not high enough, // don't apply any discount let amount = input.cart().cost().subtotal_amount().amount().0; let min_amount = Decimal::from(100.0); if amount < *min_amount { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } // If the cart contains a product from the Toronto Collection, // and the value of the order is high enough, // then we apply a $50 discount to the order Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::OrderDiscountsAdd(schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![ schema::OrderDiscountCandidate { message: Some("$50 off the Toronto Collection!".to_string()), targets: vec![ schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], } ), ], value: schema::OrderDiscountCandidateValue::FixedAmount( schema::FixedAmount { amount: Decimal::from(50.0), } ), conditions: None, associated_discount_code: None, } ], }), ], }) }Performance Cost (Rust)
42240 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // The cart is only eligible for the discount if it contains a product from the Toronto Collection const isInCollection = input.cart.lines.some( (line) => line.merchandise.__typename === "ProductVariant" && line.merchandise.product.inAnyCollection ); // If the cart does not contain a product from a the TORONTO collection, // then there should be no discount applied if (!isInCollection) { return { operations: [] }; } // If the value of the order is not high enough, // don't apply any discount, but inform the customer if (input.cart.cost.subtotalAmount.amount < 100) { return { operations: [] }; } // If the cart contains a product from the Toronto Collection, // and the value of the order is high enough, // then we apply a $50 discount to the order return { operations: [ { orderDiscountsAdd: { candidates: [ { message: "$50 off the Toronto Collection!", value: { fixedAmount: { amount: 50.0, }, }, targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }, ], }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "$50 off the Toronto Collection!", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "fixedAmount": { "amount": "50.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "$50 off the Toronto Collection!", "value": { "fixedAmount": { "amount": 50 } }, "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ] } ], "selectionStrategy": "FIRST" } } ] } -
Apply a discount to orders containing only new collection items
This function checks if all products in the order have a specific product tag. If so, it applies a percent off discount to the order. This example demonstrates * Creating a discount function * Checking the tags on the products in the order * Applying a discount to the order
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { merchandise { __typename ... on ProductVariant { id product { hasAnyTag(tags: ["new_collection"]) } } } } } }Input Query (JavaScript)
query Input { cart { lines { merchandise { __typename ... on ProductVariant { id product { hasAnyTag(tags: ["new_collection"]) } } } } } }Input Object (Rust)
{ "cart": { "lines": [ { "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/46711460495653", "product": { "hasAnyTag": true } } } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/46711460495653", "product": { "hasAnyTag": true } } } ] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Get cart lines from input let cart_lines = &input.cart().lines(); // Check if there are any cart lines if cart_lines.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } // Check if ALL products have "new_collection" tag let all_products_new_collection = cart_lines.iter().all(|line| { if let schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = &line.merchandise() { let product = &variant.product(); return *product.has_any_tag(); } false }); // If all products have "new_collection" tag, apply 10% discount if all_products_new_collection { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::OrderDiscountsAdd(schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::Maximum, candidates: vec![ schema::OrderDiscountCandidate { conditions: None, message: Some("10% off your new collection order!".to_string()), targets: vec![ schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], } ), ], value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0), } ), associated_discount_code: None, }, ], }), ], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }) }Performance Cost (Rust)
36483 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Get cart lines from input const cartLines = input.cart?.lines || []; // Check if ALL products have "new_collection" tag const allProductsNewCollection = cartLines.length > 0 && cartLines.every(line => { const merchandise = line.merchandise; if (merchandise.__typename === 'ProductVariant') { return merchandise.product?.hasAnyTag; } return false; }); // If all products have "new_collection" tag, apply 10% discount if (allProductsNewCollection) { return { operations: [ { orderDiscountsAdd: { candidates: [ { message: "10% off your new collection order!", targets: [ { orderSubtotal: { excludedCartLineIds: [] } } ], value: { percentage: { value: "10.0" } } } ], selectionStrategy: OrderDiscountSelectionStrategy.Maximum } } ] }; } return { operations: [] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% off your new collection order!", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% off your new collection order!", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] } -
Apply a percentage discount to specific products
This example implements a Shopify function that gives a configurable amount of discount off a number of product variants. The configuration is stored in an app owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id title } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id title } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1234567890", "title": "Small / Black" } }, { "id": "gid://shopify/CartLine/2", "quantity": 1, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/9876543210", "title": "Medium / Blue" } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 15, "target_variant_ids": [ "gid://shopify/ProductVariant/1234567890", "gid://shopify/ProductVariant/9876543210" ] } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/123456", "title": "Blue T-Shirt" } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 15.0, "target_variant_ids": ["gid://shopify/ProductVariant/123456"] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { percentage: f64, target_variant_ids: Vec<String>, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; let mut candidates = vec![]; for line in input.cart().lines().iter() { let variant = match &line.merchandise() { schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) => variant, _ => continue, }; if config.target_variant_ids.contains(&variant.id()) { let message = match &variant.title() { Some(title) => format!("{}% off {}!", config.percentage, title), None => format!("{}% off this product!", config.percentage), }; candidates.push(schema::ProductDiscountCandidate { value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal::from(config.percentage), }), targets: vec![schema::ProductDiscountCandidateTarget::ProductVariant( schema::ProductVariantTarget { id: variant.id().clone(), quantity: Some(*line.quantity()), }, )], message: Some(message), associated_discount_code: None, }); } } if candidates.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates, }), ], }) }Performance Cost (Rust)
51687 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration from metafield const configuration = input?.discount?.metafield?.jsonValue ?? {}; // Return empty operations if no configuration if (!configuration || !configuration.target_variant_ids || !configuration.percentage) { return { operations: [] }; } const candidates = []; // Return empty operations if no cart or lines if (!input.cart.lines) { return { operations: [] }; } // Process each cart line for (const line of input.cart.lines) { // Skip if not a product variant if (line.merchandise.__typename !== "ProductVariant") { continue; } // Check if variant is in target list if (configuration.target_variant_ids.includes(line.merchandise.id)) { const message = line.merchandise.title ? `${configuration.percentage}% off ${line.merchandise.title}!` : `${configuration.percentage}% off this product!`; candidates.push({ value: { percentage: { value: configuration.percentage.toFixed(1).toString() } }, targets: [ { productVariant: { id: line.merchandise.id, quantity: line.quantity } } ], message }); } } if (candidates.length === 0) { return { operations: [] }; } return { operations: [ { productDiscountsAdd: { candidates, selectionStrategy: ProductDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "message": "15% off Small / Black!", "targets": [ { "productVariant": { "id": "gid://shopify/ProductVariant/1234567890", "quantity": 2 } } ], "value": { "percentage": { "value": "15.0" } } }, { "message": "15% off Medium / Blue!", "targets": [ { "productVariant": { "id": "gid://shopify/ProductVariant/9876543210", "quantity": 1 } } ], "value": { "percentage": { "value": "15.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "15% off Blue T-Shirt!", "targets": [ { "productVariant": { "id": "gid://shopify/ProductVariant/123456", "quantity": 2 } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply a discount to the first cart line
This example implements a Shopify function that gives a 20% off the first line in the cart.
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { deliveryGroups { id } } }Input Query (JavaScript)
query DeliveryInput { cart { deliveryGroups { id } } }Input Object (Rust)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/DeliveryGroup/1" } ] } }Input Object (JavaScript)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/DeliveryGroup/1" } ] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(100.0), }), message: Some("FREE DELIVERY".to_string()), associated_discount_code: None, }], }, )], }) }Performance Cost (Rust)
26776 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy} from '../generated/api'; /** * @typedef {import("../generated/api").Input} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * cartDeliveryOptionsDiscountsGenerateRun * @param {RunInput} input - The DeliveryInput * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const firstDeliveryGroup = input.cart.deliveryGroups[0]; if (!firstDeliveryGroup) { throw new Error('No delivery groups found'); } return { operations: [ { deliveryDiscountsAdd: { candidates: [ { message: 'FREE DELIVERY', targets: [ { deliveryGroup: { id: firstDeliveryGroup.id, }, }, ], value: { percentage: { value: "100.0", }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }, ], }; }Performance Cost (JavaScript)
197180 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "FREE DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/DeliveryGroup/1" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "FREE DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/DeliveryGroup/1" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity } } }Input Query (JavaScript)
query RunInput { cart { lines { id } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1 } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1 } ] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let mut operations = vec![]; let percentage = 20.0; // Check if there are any lines in the cart if let Some(first_line) = input.cart().lines().first() { operations.push(schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: first_line.id().clone(), quantity: None, })], message: Some(format!("{}% off first item", percentage)), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(percentage), }), associated_discount_code: None, }], })); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
27780 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import("../generated/api").Input} Input * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * cartLinesDiscountsGenerateRun * @param {Input} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { const percentage = 20; const operations = []; // Check if there are any lines in the cart if (input.cart.lines.length > 0) { const firstLine = input.cart.lines[0]; operations.push({ productDiscountsAdd: { candidates: [ { message: `${percentage}% off first item`, targets: [ { cartLine: { id: firstLine.id, }, }, ], value: { percentage: { value: percentage.toFixed(1), }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }); } return { operations }; }Performance Cost (JavaScript)
218931 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "20% off first item", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": null } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "20% off first item", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1" } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply a per-item discount to a product with quantity limits
This example implements a Shopify function that gives a configurable amount of discount off an item. The configuration is stored in an app owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id title product { id } } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id product { id } } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 3, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1", "product": { "id": "gid://shopify/Product/1" } } } ] }, "discount": { "metafield": { "jsonValue": { "discount_amount": 10.0, "max_quantity": 2, "product_id": "gid://shopify/Product/1" } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 3, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/123456", "product": { "id": "gid://shopify/Product/1" } } } ] }, "discount": { "metafield": { "jsonValue": { "discount_amount": 5.0, "max_quantity": 2, "product_id": "gid://shopify/Product/1" } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { discount_amount: f64, max_quantity: i32, product_id: String, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; let mut candidates = vec![]; let mut remaining_quantity = config.max_quantity; for line in input.cart().lines().iter() { let variant = match &line.merchandise() { schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) => variant, _ => continue, }; if *variant.product().id() == config.product_id && remaining_quantity > 0 { let line_quantity = line.quantity(); let discount_quantity = remaining_quantity.min(*line_quantity); remaining_quantity -= discount_quantity; let fixed_amount = schema::ProductDiscountCandidateFixedAmount { amount: config.discount_amount.into(), applies_to_each_item: Some(true), }; candidates.push(schema::ProductDiscountCandidate { value: schema::ProductDiscountCandidateValue::FixedAmount(fixed_amount), targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: line.id().to_string(), quantity: Some(discount_quantity), })], message: Some("Discount applied".to_string()), associated_discount_code: None, }); } } if candidates.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates, }), ], }) }Performance Cost (Rust)
46909 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult * @typedef {{ * discount_amount: number, * max_quantity: number, * product_id: string * }} Configuration */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration from metafield /** @type {Configuration} */ const configuration = input.discount?.metafield?.jsonValue ?? {}; if (!configuration.discount_amount || !configuration.max_quantity || !configuration.product_id) { return { operations: [] }; } const candidates = []; let remainingQuantity = configuration.max_quantity; // Iterate through cart lines for (const line of input.cart.lines) { if (line.merchandise.__typename !== "ProductVariant") { continue; } if (line.merchandise.product.id === configuration.product_id && remainingQuantity > 0) { const lineQuantity = line.quantity; const discountQuantity = Math.min(remainingQuantity, lineQuantity); remainingQuantity -= discountQuantity; candidates.push({ value: { fixedAmount: { amount: configuration.discount_amount.toFixed(1).toString(), appliesToEachItem: true } }, targets: [{ cartLine: { id: line.id, quantity: discountQuantity } }], message: "Discount applied" }); } } if (candidates.length === 0) { return { operations: [] }; } return { operations: [ { productDiscountsAdd: { candidates, selectionStrategy: ProductDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
270245 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Discount applied", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": 2 } } ], "value": { "fixedAmount": { "amount": "10.0", "appliesToEachItem": true } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "Discount applied", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": 2 } } ], "value": { "fixedAmount": { "amount": "5.0", "appliesToEachItem": true } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply a percentage discount to products with specific SKUs
This example implements a Shopify function that gives a configurable discount to any cart line whose product variant has one of the specified SKUs. The SKUs and discount percentage are stored in an app-owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id sku } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity merchandise { __typename ... on ProductVariant { id sku } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1", "sku": "DISC-SKU1" } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 15.0, "skus": [ "DISC-SKU1", "DISC-SKU2", "SALE-SKU" ] } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1", "sku": "SKU123" } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 20.0, "skus": ["SKU123", "SKU456"] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { percentage: f64, skus: Vec<String>, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; let mut candidates = vec![]; for line in input.cart().lines() { let variant = match &line.merchandise() { schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) => variant, _ => continue, }; // Check if the variant has a SKU and if it's in our target list if let Some(sku) = variant.sku() { if config.skus.contains(sku) { candidates.push(schema::ProductDiscountCandidate { value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(config.percentage), }), targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: line.id().to_string(), quantity: None, })], message: Some(format!("{}% off!", config.percentage)), associated_discount_code: None, }); } } } if candidates.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates, }), ], }) }Performance Cost (Rust)
46868 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration from metafield const configuration = input.discount?.metafield?.jsonValue ?? {}; if (!configuration.percentage || !configuration.skus || configuration.skus.length === 0) { return { operations: [] }; } const candidates = []; // Check each cart line for matching SKUs for (const line of input.cart.lines) { if (line.merchandise.__typename !== "ProductVariant") { continue; } if (line.merchandise.sku && configuration.skus.includes(line.merchandise.sku)) { candidates.push({ value: { percentage: { value: configuration.percentage.toFixed(1) } }, targets: [{ cartLine: { id: line.id } }], message: `${configuration.percentage}% off!` }); } } if (candidates.length === 0) { return { operations: [] }; } return { operations: [ { productDiscountsAdd: { candidates, selectionStrategy: ProductDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "message": "15% off!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": null } } ], "value": { "percentage": { "value": "15.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "20% off!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1" } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply an extra discount to sale items
This example implements a Shopify function that gives a configurable discount to any cart line whose product variant has a compare-at price higher than its current price. The discount percentage is stored in an app-owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity cost { amountPerQuantity { amount } compareAtAmountPerQuantity { amount } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity cost { amountPerQuantity { amount } compareAtAmountPerQuantity { amount } } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1, "cost": { "amountPerQuantity": { "amount": "80.00" }, "compareAtAmountPerQuantity": { "amount": "100.00" } } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 10.0 } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1, "cost": { "amountPerQuantity": { "amount": "40.00" }, "compareAtAmountPerQuantity": { "amount": "50.00" } } } ] }, "discount": { "metafield": { "jsonValue": { "percentage": 10.0 } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { percentage: f64, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; let mut candidates = vec![]; for line in input.cart().lines() { // Check if there's a compare-at price and if it's higher than the current price if let Some(compare_at_amount) = &line.cost().compare_at_amount_per_quantity() { let current_price = line.cost().amount_per_quantity().amount().0; let compare_at_price = compare_at_amount.amount().0; if compare_at_price > current_price { candidates.push(schema::ProductDiscountCandidate { value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(config.percentage), }), targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: line.id().to_string(), quantity: None, })], message: Some(format!("{}% off sale item!", config.percentage)), associated_discount_code: None, }); } } } if candidates.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates, }), ], }) }Performance Cost (Rust)
44951 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration from metafield const configuration = input.discount?.metafield?.jsonValue || { percentage: 0 }; if (!configuration.percentage) { return { operations: [] }; } const candidates = []; // Check each cart line for compare-at prices for (const line of input.cart.lines) { const compareAtAmount = line.cost.compareAtAmountPerQuantity?.amount; const currentPrice = line.cost.amountPerQuantity.amount; // Apply discount if compare-at price is higher than current price if (compareAtAmount && compareAtAmount > currentPrice) { candidates.push({ value: { percentage: { value: configuration.percentage.toFixed(1) } }, targets: [{ cartLine: { id: line.id } }], message: `${configuration.percentage}% off sale item!` }); } } if (candidates.length === 0) { return { operations: [] }; } return { operations: [ { productDiscountsAdd: { candidates, selectionStrategy: ProductDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "message": "10% off sale item!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": null } } ], "value": { "percentage": { "value": "10.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "10% off sale item!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1" } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply a percentage discount for VIP customers
This example implements a Shopify function that gives a configurable discount to customers with a "VIP" tag. The discount percentage is stored in an app-owned metafield and the tags are stored in a function-owned metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id quantity } buyerIdentity { customer { hasAnyTag(tags: ["VIP"]) } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { lines { id quantity } buyerIdentity { customer { hasAnyTag(tags: ["VIP"]) } } } discount { metafield(namespace: "$app:product-discount", key: "function-configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 2 } ], "buyerIdentity": { "customer": { "hasAnyTag": true } } }, "discount": { "metafield": { "jsonValue": { "percentage": 10.0 } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "quantity": 1 } ], "buyerIdentity": { "customer": { "hasAnyTag": true } } }, "discount": { "metafield": { "jsonValue": { "percentage": 15.0 } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { percentage: f64, } #[shopify_function] fn cart_lines_discounts_generate_run(input: schema::cart_lines_discounts_generate_run::Input) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Get configuration or return early if no metafield is set let config: &Configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), }; // Check if customer exists and has the VIP tag let has_vip_tag = input.cart() .buyer_identity() .as_ref() .and_then(|identity| identity.customer()) .map(|customer| *customer.has_any_tag()) .unwrap_or(false); if !has_vip_tag { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } // Create discount for all cart lines let candidates: Vec<_> = input.cart() .lines() .iter() .map(|line| { schema::ProductDiscountCandidate { value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(config.percentage), }), targets: vec![schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: line.id().clone(), quantity: None, })], message: Some(format!("{}% VIP discount!", config.percentage)), associated_discount_code: None, } }).collect(); if candidates.is_empty() { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates, }), ], }) }Performance Cost (Rust)
42014 instructions
Function Code (JavaScript)
// @ts-check import { ProductDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { // Parse configuration from metafield const configuration = input.discount?.metafield?.jsonValue || { percentage: 0 }; if (!configuration.percentage) { return { operations: [] }; } // Check if customer has VIP tag const hasVipTag = input.cart.buyerIdentity?.customer?.hasAnyTag || false; if (!hasVipTag) { return { operations: [] }; } const candidates = []; // Apply discount to all cart lines for (const line of input.cart.lines) { candidates.push({ value: { percentage: { value: configuration.percentage.toFixed(1) } }, targets: [{ cartLine: { id: line.id } }], message: `${configuration.percentage}% VIP discount!` }); } if (candidates.length === 0) { return { operations: [] }; } return { operations: [ { productDiscountsAdd: { candidates, selectionStrategy: ProductDiscountSelectionStrategy.First } } ] }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "message": "10% VIP discount!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": null } } ], "value": { "percentage": { "value": "10.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "15% VIP discount!", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1" } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "FIRST" } } ] } -
Apply tiered shipping discounts based on cart subtotal
This function implements a discount function that applies a percentage discount to the cheapest shipping method based on the cart subtotal.
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle cost { amount } } } } discount { metafield(namespace: "$app:delivery-discounts", key: "configuration") { jsonValue } } }Input Query (JavaScript)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle cost { amount } } } } discount { metafield(namespace: "$app:delivery-discounts", key: "configuration") { jsonValue } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Input Object (JavaScript)
{ "cart": { "cost": { "subtotalAmount": { "amount": "120.00" } }, "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "10.00" } }, { "handle": "express", "cost": { "amount": "20.00" } } ] } ] }, "discount": { "metafield": { "jsonValue": { "tiers": [ { "threshold": 50.0, "percentage": 10.0 }, { "threshold": 100.0, "percentage": 15.0 } ] } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] struct DiscountTier { threshold: f64, percentage: f64, } #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { tiers: Vec<DiscountTier>, } #[shopify_function] fn cart_delivery_options_discounts_generate_run(input: schema::cart_delivery_options_discounts_generate_run::Input) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let config = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![], }), }; // Get cart subtotal let subtotal = input.cart().cost().subtotal_amount().amount().0; // Find the highest applicable tier let applicable_tier = config.tiers.iter() .filter(|tier| subtotal >= tier.threshold) .max_by(|a, b| a.threshold.partial_cmp(&b.threshold).unwrap_or(std::cmp::Ordering::Equal)); // If no tier applies, return empty operations let Some(tier) = applicable_tier else { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![], }); }; let mut operations = vec![]; // Process each delivery group for delivery_group in input.cart().delivery_groups() { // Find the cheapest delivery option if let Some(cheapest_option) = delivery_group.delivery_options().iter() .min_by(|a, b| { let a_cost = a.cost().amount().0; let b_cost = b.cost().amount().0; a_cost.partial_cmp(&b_cost).unwrap_or(std::cmp::Ordering::Equal) }) { // Add discount operation for the cheapest option operations.push(schema::DeliveryOperation::DeliveryDiscountsAdd(schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: cheapest_option.handle().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(tier.percentage), }), message: Some(format!("{}% off shipping", tier.percentage)), associated_discount_code: None, }], })); } } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
61037 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').DeliveryInput} DeliveryInput * @typedef {import('../generated/api').CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * @typedef {Object} DiscountTier * @property {number} threshold * @property {number} percentage */ /** * @typedef {Object} Configuration * @property {DiscountTier[]} tiers */ /** * @param {DeliveryInput} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { // Parse configuration from metafield const config = input?.discount?.metafield?.jsonValue ?? { tiers: [] }; // If no config or invalid tiers, return empty operations if (!config || !Array.isArray(config.tiers)) { return { operations: [] }; } // Get cart subtotal const subtotal = input.cart.cost.subtotalAmount.amount; // Find the highest applicable tier const applicableTier = config.tiers .filter(tier => subtotal >= tier.threshold) .reduce((highest, current) => !highest || current.threshold > highest.threshold ? current : highest, null); // If no tier applies, return empty operations if (!applicableTier) { return { operations: [] }; } const operations = []; // Process each delivery group for (const deliveryGroup of input.cart.deliveryGroups) { if (!deliveryGroup.deliveryOptions?.length) { continue; } // Find the cheapest delivery option const cheapestOption = deliveryGroup.deliveryOptions.reduce( (cheapest, current) => current.cost.amount < cheapest.cost.amount ? current : cheapest, deliveryGroup.deliveryOptions[0] ); operations.push({ deliveryDiscountsAdd: { candidates: [{ value: { percentage: { value: applicableTier.percentage.toFixed(1) } }, targets: [{ deliveryOption: { handle: cheapestOption.handle } }], message: `${applicableTier.percentage}% off shipping` }], selectionStrategy: DeliveryDiscountSelectionStrategy.All } }); } return { operations }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "15% off shipping", "targets": [ { "deliveryOption": { "handle": "standard" } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "15% off shipping", "targets": [ { "deliveryOption": { "handle": "standard" } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "ALL" } } ] } -
Apply a shipping discount based on location
A discount function that applies a discount to a specific shipping method if the delivery address is in a specific city. This example applies a 50% shipping discount to the express shipping method. Use this example to understand: - How to apply percent discounts to shipping using the discount API - How to check the delivery address for an order - How to only apply shipping discounts depending on the customers location
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { deliveryGroups { id deliveryAddress { city } deliveryOptions { handle title cost { amount } } } } }Input Query (JavaScript)
query Input { cart { deliveryGroups { id deliveryAddress { city } deliveryOptions { handle title cost { amount } } } } }Input Object (Rust)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/0", "deliveryAddress": { "city": "Ottawa" }, "deliveryOptions": [ { "handle": "0d8a74c6e309668ff67eef9d765741ec-b1a2fe47040bfe8f971ccb0abbcf8ea1", "title": "Standard", "cost": { "amount": "0.0" } }, { "handle": "0d8a74c6e309668ff67eef9d765741ec-71c568b7bd38c0bc23d9e7457ab6aa3b", "title": "Express", "cost": { "amount": "21.9" } } ] } ] } }Input Object (JavaScript)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/0", "deliveryAddress": { "city": "Ottawa" }, "deliveryOptions": [ { "handle": "ddba6f20f9fbc5983257805852308679-b1a2fe47040bfe8f971ccb0abbcf8ea1", "title": "Standard", "cost": { "amount": "0.0" } }, { "handle": "ddba6f20f9fbc5983257805852308679-71c568b7bd38c0bc23d9e7457ab6aa3b", "title": "Express", "cost": { "amount": "21.9" } } ] } ] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run(input: schema::cart_delivery_options_discounts_generate_run::Input) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let mut operations = Vec::new(); // Process each delivery group for group in input.cart().delivery_groups() { // Check if the delivery address city is Ottawa (case-insensitive) let city = group.delivery_address() .and_then(|addr| addr.city()) .map(|city| city.to_lowercase()); // Only apply a discount if delivering to Ottawa if city.as_deref() != Some("ottawa") { continue; } let mut candidates = Vec::new(); // Check each delivery option for option in group.delivery_options() { // Only apply a discount if the shipping option is express (case-insensitive) if let Some(title) = option.title() { if title.to_lowercase().contains("express") { // Create a discount for express shipping to Ottawa let candidate = schema::DeliveryDiscountCandidate { message: Some(String::from("50% off express shipping for Ottawa")), targets: vec![ schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: option.handle().clone(), } ) ], value: schema::DeliveryDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(50.0), } ), associated_discount_code: None, }; candidates.push(candidate); break; } } } if !candidates.is_empty() { operations.push(schema::CartOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates, } )); } } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
42210 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').DeliveryInput} DeliveryInput * @typedef {import('../generated/api').CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * @param {DeliveryInput} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const { cart } = input; const operations = []; const deliveryGroups = cart.deliveryGroups || []; for (const group of deliveryGroups) { const city = group.deliveryAddress?.city?.toLowerCase(); // Only apply a discount if delivering to a specific city if (city !== "ottawa") continue; const candidates = []; for (const option of group.deliveryOptions) { // Only apply a discount if the shipping option is express if (option.title?.toLowerCase().includes("express")) { // Apply a 50% discount to shipping candidates.push({ message: "50% off express shipping for Ottawa", targets: [{ deliveryOption: { handle: option.handle } }], value: { percentage: { value: "50.0" } }, }); break; } } if (candidates.length > 0) { operations.push({ deliveryDiscountsAdd: { candidates, selectionStrategy: DeliveryDiscountSelectionStrategy.All } }); } } return { operations }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "selectionStrategy": "ALL", "candidates": [ { "message": "50% off express shipping for Ottawa", "targets": [ { "deliveryOption": { "handle": "0d8a74c6e309668ff67eef9d765741ec-71c568b7bd38c0bc23d9e7457ab6aa3b" } } ], "value": { "percentage": { "value": "50.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "50% off express shipping for Ottawa", "targets": [ { "deliveryOption": { "handle": "ddba6f20f9fbc5983257805852308679-71c568b7bd38c0bc23d9e7457ab6aa3b" } } ], "value": { "percentage": { "value": "50.0" } } } ], "selectionStrategy": "ALL" } } ] } -
Apply tiered shipping discounts for loyal customers buying from a collection
Example implementation of a Discount function. This function provides a discount on shipping for loyal customers, that are buying multiple products from a specific collection. Loyal customers are defined by a tag on the customer. This example demonstrates how to * Check if products are in a specific collection * Check if line items are in a specific collection * Apply a discount on shipping * Apply a different discount on shipping for different delivery options * Apply a discount on shipping for multiple line items * Check if the customer has any tags
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { buyerIdentity { customer { hasAnyTag(tags: ["member"]) } } lines { quantity merchandise { __typename ... on ProductVariant { product { inCollections(ids: ["gid://shopify/Collection/spring2025"]) { collectionId isMember } } } } } deliveryGroups { id deliveryOptions { handle cost { amount } } } } }Input Query (JavaScript)
query Input { cart { lines { quantity merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/1"]) } } } } buyerIdentity { customer { hasAnyTag(tags: ["member"]) } } deliveryGroups { deliveryOptions { handle cost { amount } } } } }Input Object (Rust)
{ "cart": { "buyerIdentity": { "customer": { "hasAnyTag": true } }, "lines": [ { "quantity": 4, "merchandise": { "__typename": "ProductVariant", "product": { "inCollections": [ { "collectionId": "gid://shopify/Collection/spring2025", "isMember": true } ] } } } ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/0", "deliveryOptions": [ { "handle": "cae179072e1b0a565f51700b666b62c5-95e685b97e091ac14c1f4905f7c610a9", "cost": { "amount": "0.0" } }, { "handle": "cae179072e1b0a565f51700b666b62c5-b514e01af8655a9229f7a04405396118", "cost": { "amount": "1.0" } }, { "handle": "cae179072e1b0a565f51700b666b62c5-fbcaff8dd39a68820109eb990460be0a", "cost": { "amount": "15.0" } } ] } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "quantity": 3, "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } } ], "buyerIdentity": { "customer": { "hasAnyTag": true } }, "deliveryGroups": [ { "deliveryOptions": [ { "handle": "standard", "cost": { "amount": "4.99" } }, { "handle": "express", "cost": { "amount": "10.00" } } ] } ] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run(input: schema::cart_delivery_options_discounts_generate_run::Input) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let no_discount = schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }; // Check if the customer has the "member" tag let is_member = input.cart().buyer_identity() .and_then(|buyer| buyer.customer()) .map(|customer| *customer.has_any_tag()) .unwrap_or(false); // If not a member, return no discounts if !is_member { return Ok(no_discount); } // Count items from the spring2025 collection let spring2025_items = input.cart().lines().iter().fold(0, |count, line| { // Check if it's a product variant if let schema::cart_delivery_options_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = line.merchandise() { // Check if product is from spring2025 collection let in_collection = variant.product().in_collections().iter() .any(|collection| *collection.is_member()); if in_collection { return count + line.quantity(); } } count }); // Only apply discount if customer has at least 3 items from the spring2025 collection if spring2025_items < 3 { return Ok(no_discount); } let mut operations = vec![]; // Apply discounts to each delivery option for delivery_group in input.cart().delivery_groups() { let mut candidates = vec![]; for delivery_option in delivery_group.delivery_options() { let shipping_cost = delivery_option.cost().amount().0; // For shipping that costs $5 or less, make it free // For shipping that costs more than $5, apply a $5 discount let discount_amount = if shipping_cost <= 5.0 { shipping_cost } else { 5.0 }; if discount_amount > 0.0 { let message = if shipping_cost <= 5.0 { "Free shipping for members".to_string() } else { "$5 off shipping for members".to_string() }; candidates.push(schema::DeliveryDiscountCandidate { message: Some(message), targets: vec![ schema::DeliveryDiscountCandidateTarget::DeliveryOption(schema::DeliveryOptionTarget { handle: delivery_option.handle().clone() }) ], value: schema::DeliveryDiscountCandidateValue::FixedAmount(schema::FixedAmount { amount: Decimal(discount_amount), applies_to_each_item: None, }), associated_discount_code: None, }); } } if !candidates.is_empty() { operations.push(schema::CartOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates, } )); } } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
69413 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').DeliveryInput} DeliveryInput * @typedef {import('../generated/api').CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * @param {DeliveryInput} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const { cart } = input; const operations = []; // Check if the customer has the 'member' tag const isMember = cart.buyerIdentity?.customer?.hasAnyTag || false; if (!isMember) { return { operations: [] }; } // Count how many items are from the spring collection let springCollectionCount = 0; for (const line of cart.lines) { if (line.merchandise.__typename === "ProductVariant" && line.merchandise.product.inAnyCollection) { springCollectionCount += line.quantity; } } // Apply discount only if there are 3 or more spring collection items if (springCollectionCount < 3) { return { operations: [] }; } // Apply shipping discounts for (const deliveryGroup of cart.deliveryGroups) { const candidates = []; for (const option of deliveryGroup.deliveryOptions) { const cost = parseFloat(option.cost.amount); if (cost <= 5.0) { // Make shipping free for options under $5 candidates.push({ message: "Free shipping for loyal customers!", targets: [{ deliveryOption: { handle: option.handle } }], value: { percentage: { value: "100.0" } }, }); } else { // Apply $5 off for options over $5 candidates.push({ message: "$5 off shipping for loyal customers!", targets: [{ deliveryOption: { handle: option.handle } }], value: { fixedAmount: { amount: "5.0" } }, }); } } if (candidates.length > 0) { operations.push({ deliveryDiscountsAdd: { candidates, selectionStrategy: DeliveryDiscountSelectionStrategy.All } }); } } return { operations }; }Performance Cost (JavaScript)
0 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "selectionStrategy": "ALL", "candidates": [ { "message": "Free shipping for members", "targets": [ { "deliveryOption": { "handle": "cae179072e1b0a565f51700b666b62c5-b514e01af8655a9229f7a04405396118" } } ], "value": { "fixedAmount": { "amount": "1.0" } } }, { "message": "$5 off shipping for members", "targets": [ { "deliveryOption": { "handle": "cae179072e1b0a565f51700b666b62c5-fbcaff8dd39a68820109eb990460be0a" } } ], "value": { "fixedAmount": { "amount": "5.0" } } } ] } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "Free shipping for loyal customers!", "targets": [ { "deliveryOption": { "handle": "standard" } } ], "value": { "percentage": { "value": "100.0" } } }, { "message": "$5 off shipping for loyal customers!", "targets": [ { "deliveryOption": { "handle": "express" } } ], "value": { "fixedAmount": { "amount": "5.0" } } } ], "selectionStrategy": "ALL" } } ] } -
Create discounts managed by external services
A Shopify Discount Function that implements network connectivity to retrieve data from external services for discount calculations. Through its fetch operations, it sends discount code data to an external API endpoint and receives discount operations in response. The operations received from the external API are then applied, enabling integration with existing promotion systems or third-party discount logic.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { fetchResult { jsonBody } cart { buyerIdentity { email } lines { id cost { subtotalAmount { amount } } } } discount { discountClasses } }Input Query (JavaScript)
query InputCartRun { fetchResult { jsonBody status } cart { buyerIdentity { email } lines { id cost { subtotalAmount { amount } } } } discount { discountClasses } }Input Object (Rust)
{ "fetchResult": { "jsonBody": [ { "enteredDiscountCodesAccept": { "codes": [{"code": "SUMMER10"}] } }, { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "value": { "percentage": { "value": "10.0" } }, "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/123" } } ], "message": "10% off with SUMMER10 code" } ] } }, { "orderDiscountsAdd": { "selectionStrategy": "MAXIMUM", "candidates": [ { "value": { "percentage": { "value": "15.0" } }, "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "message": "15% off entire order" } ] } } ] }, "cart": { "buyerIdentity": { "email": "customer@example.com" }, "lines": [ { "id": "gid://shopify/CartLine/123", "cost": { "subtotalAmount": { "amount": "100.00" } } }, { "id": "gid://shopify/CartLine/456", "cost": { "subtotalAmount": { "amount": "50.00" } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Input Object (JavaScript)
{ "fetchResult": { "status": 200, "jsonBody": [ { "enteredDiscountCodesAccept": { "codes": [{"code": "SUMMER10"}] } }, { "productDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "value": { "percentage": { "value": "10.0" } }, "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/123" } } ], "message": "10% off with SUMMER10 code" } ] } }, { "orderDiscountsAdd": { "selectionStrategy": "MAXIMUM", "candidates": [ { "value": { "percentage": { "value": "15.0" } }, "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "message": "15% off entire order" } ] } } ] }, "cart": { "buyerIdentity": { "email": "customer@example.com" }, "lines": [ { "id": "gid://shopify/CartLine/123", "cost": { "subtotalAmount": { "amount": "100.00" } } }, { "id": "gid://shopify/CartLine/456", "cost": { "subtotalAmount": { "amount": "50.00" } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize)] #[shopify_function(rename_all = "camelCase")] pub struct OperationItem { product_discounts_add: Option<schema::ProductDiscountsAddOperation>, order_discounts_add: Option<schema::OrderDiscountsAddOperation>, entered_discount_codes_accept: Option<schema::EnteredDiscountCodesAcceptOperation>, } pub type OperationsList = Vec<OperationItem>; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let fetch_result = input.fetch_result().ok_or("Missing fetch result")?; let discount_classes = &input.discount().discount_classes(); let has_order_discount_class = discount_classes.contains(&schema::DiscountClass::Order); let has_product_discount_class = discount_classes.contains(&schema::DiscountClass::Product); // If no relevant discount class is set, return empty operations if !has_order_discount_class && !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Use the json_body which is the available property let operation_items = match fetch_result.json_body() { Some(json_body) => json_body, None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }), }; // Convert the response into operations let mut operations = Vec::new(); // Process each operation item for item in operation_items { // Always include discount code operations if let Some(validations) = &item.entered_discount_codes_accept { operations.push(schema::CartOperation::EnteredDiscountCodesAccept( validations.clone(), )); } // Include product discounts only if that class is set if has_product_discount_class { if let Some(product_discounts_add_operation) = &item.product_discounts_add { operations.push(schema::CartOperation::ProductDiscountsAdd( product_discounts_add_operation.clone(), )); } } // Include order discounts only if that class is set if has_order_discount_class { if let Some(order_discounts_add_operation) = &item.order_discounts_add { operations.push(schema::CartOperation::OrderDiscountsAdd( order_discounts_add_operation.clone(), )); } } // Ignore delivery discounts for cart operations } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
108122 instructions
Function Code (JavaScript)
// @ts-check import {DiscountClass} from "../generated/api"; /** * @typedef {import("../generated/api").InputCartRun} InputCartRun * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * * @param {InputCartRun} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { const { fetchResult, discount: {discountClasses}, } = input; const body = fetchResult?.jsonBody; if (!body) { throw new Error("Missing response body"); } const operations = body; const hasOrderDiscountClass = discountClasses.includes(DiscountClass.Order); const hasProductDiscountClass = discountClasses.includes( DiscountClass.Product, ); // If no relevant discount classes are set, return an empty operations array if (!hasOrderDiscountClass && !hasProductDiscountClass) { return {operations: []}; } // Filter operations to include appropriate discounts based on set discount classes const filteredOperations = operations.filter(operation => { // Always include discount code operations if (operation.enteredDiscountCodesAccept) { return true; } // Include order discounts only if that class is set if (operation.orderDiscountsAdd) { return hasOrderDiscountClass; } // Include product discounts only if that class is set if (operation.productDiscountsAdd) { return hasProductDiscountClass; } return false; }); return {operations: filteredOperations}; }Performance Cost (JavaScript)
497007 instructions
Output JSON (Rust)
{ "operations": [ { "enteredDiscountCodesAccept": { "codes": [ { "code": "SUMMER10" } ] } }, { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "10% off with SUMMER10 code", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/123", "quantity": null } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "15% off entire order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] }Output JSON (JavaScript)
{ "operations": [ { "enteredDiscountCodesAccept": { "codes": [ { "code": "SUMMER10" } ] } }, { "productDiscountsAdd": { "candidates": [ { "message": "10% off with SUMMER10 code", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/123" } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "message": "15% off entire order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "MAXIMUM" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { fetchResult { jsonBody } cart { buyerIdentity { email } deliveryGroups { id } } discount { discountClasses } }Input Query (JavaScript)
query InputDeliveryRun { fetchResult { jsonBody status } cart { buyerIdentity { email } deliveryGroups { id } } discount { discountClasses } }Input Object (Rust)
{ "fetchResult": { "jsonBody": [ { "enteredDiscountCodesAccept": { "codes": [{"code": "FREESHIP"}] } }, { "deliveryDiscountsAdd": { "selectionStrategy": "ALL", "candidates": [ { "value": { "percentage": { "value": "100.0" } }, "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "message": "Free standard shipping" } ] } } ] }, "cart": { "buyerIdentity": { "email": "customer@example.com" }, "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Input Object (JavaScript)
{ "fetchResult": { "status": 200, "jsonBody": [ { "enteredDiscountCodesAccept": { "codes": [{"code": "FREESHIP"}] } }, { "deliveryDiscountsAdd": { "selectionStrategy": "FIRST", "candidates": [ { "value": { "percentage": { "value": "100.0" } }, "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "message": "Free standard shipping" } ] } } ] }, "cart": { "buyerIdentity": { "email": "customer@example.com" }, "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize)] #[shopify_function(rename_all = "camelCase")] pub struct OperationItem { delivery_discounts_add: Option<schema::DeliveryDiscountsAddOperation>, entered_discount_codes_accept: Option<schema::EnteredDiscountCodesAcceptOperation>, } pub type OperationsList = Vec<OperationItem>; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let fetch_result = input.fetch_result().ok_or("Missing fetch result")?; let discount_classes = &input.discount().discount_classes(); // Check if shipping discount class is set let has_shipping_discount_class = discount_classes.contains(&schema::DiscountClass::Shipping); // If shipping discount class is not set, return empty operations if !has_shipping_discount_class { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } // Use the json_body which is the available property let operation_items = match fetch_result.json_body() { Some(json_body) => json_body, None => { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }) } }; // Convert the response into operations let mut operations = Vec::new(); // Process each operation item for item in operation_items { // Always include discount code operations if let Some(validations) = &item.entered_discount_codes_accept { operations.push(schema::DeliveryOperation::EnteredDiscountCodesAccept( validations.clone(), )); } // Include delivery discounts (shipping discount class is already verified) if let Some(delivery_discounts_add_operation) = &item.delivery_discounts_add { operations.push(schema::DeliveryOperation::DeliveryDiscountsAdd( delivery_discounts_add_operation.clone(), )); } // Ignore cart/order discounts for delivery operations } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
65415 instructions
Function Code (JavaScript)
//@ts-check import {DiscountClass} from "../generated/api"; /** * @typedef {import("../generated/api").InputDeliveryRun} InputDeliveryRun * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * * @param {InputDeliveryRun} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const { fetchResult, discount: {discountClasses}, } = input; const body = fetchResult?.jsonBody; if (!body) { throw new Error("Missing response body"); } const operations = body; const hasShippingDiscountClass = discountClasses.includes( DiscountClass.Shipping, ); // If shipping discount class is not set, return an empty operations array if (!hasShippingDiscountClass) { return {operations: []}; } // Filter operations to only include enteredDiscountCodesAccept and delivery operations const filteredOperations = operations.filter(operation => { return ( operation.enteredDiscountCodesAccept || operation.deliveryDiscountsAdd ); }); return {operations: filteredOperations}; }Performance Cost (JavaScript)
368899 instructions
Output JSON (Rust)
{ "operations": [ { "enteredDiscountCodesAccept": { "codes": [ { "code": "FREESHIP" } ] } }, { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Free standard shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "enteredDiscountCodesAccept": { "codes": [ { "code": "FREESHIP" } ] } }, { "deliveryDiscountsAdd": { "candidates": [ { "message": "Free standard shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "FIRST" } } ] }cart.lines.discounts.generate.fetch
Input Query (Rust)
query Input { enteredDiscountCodes { code } cart { buyerIdentity { email } } }Input Query (JavaScript)
query InputCartFetch { enteredDiscountCodes { code } }Input Object (Rust)
{ "enteredDiscountCodes": [{"code": "SUMMER10"}, {"code": "WELCOME20"}], "cart": { "buyerIdentity": { "email": "customer@example.com" } } }Input Object (JavaScript)
{ "enteredDiscountCodes": [{"code": "SUMMER10"}, {"code": "WELCOME20"}], "cart": { "buyerIdentity": { "email": "customer@example.com" } } }Function Code (Rust)
use super::schema; use shopify_function; use shopify_function::prelude::*; use std::collections::BTreeMap; #[shopify_function] fn cart_lines_discounts_generate_fetch( input: schema::cart_lines_discounts_generate_fetch::Input, ) -> shopify_function::Result<schema::CartLinesDiscountsGenerateFetchResult> { let entered_discount_codes = &input.entered_discount_codes(); let json_body = JsonValue::Object(BTreeMap::from([( "enteredDiscountCodes".to_string(), JsonValue::Array( entered_discount_codes .iter() .map(|code| JsonValue::String(code.code().to_string())) .collect(), ), )])); let request = schema::HttpRequest { headers: vec![ schema::HttpRequestHeader { name: "accept".to_string(), value: "application/json".to_string(), }, schema::HttpRequestHeader { name: "Content-Type".to_string(), value: "application/json".to_string(), }, ], method: schema::HttpRequestMethod::Post, policy: schema::HttpRequestPolicy { read_timeout_ms: 2000, }, url: "<external-server-url>/api".to_string(), json_body: Some(json_body.clone()), body: None, }; Ok(schema::CartLinesDiscountsGenerateFetchResult { request: Some(request), }) }Performance Cost (Rust)
33780 instructions
Function Code (JavaScript)
// @ts-check import {HttpRequestMethod} from "../generated/api"; /** * @typedef {import("../generated/api").InputCartFetch} InputCartFetch * @typedef {import("../generated/api").CartLinesDiscountsGenerateFetchResult} CartLinesDiscountsGenerateFetchResult */ /** * * @param {InputCartFetch} input * @returns {CartLinesDiscountsGenerateFetchResult} */ export function cartLinesDiscountsGenerateFetch(input) { const {enteredDiscountCodes} = input; const jsonBody = { enteredDiscountCodes: enteredDiscountCodes.map(({code}) => code), }; const request = { headers: [ { name: "accept", value: "application/json", }, { name: "Content-Type", value: "application/json", }, ], method: HttpRequestMethod.Post, policy: { readTimeoutMs: 2000, }, url: "<external server url>/api", body: JSON.stringify(jsonBody), jsonBody, }; return {request}; }Performance Cost (JavaScript)
326051 instructions
Output JSON (Rust)
{ "request": { "body": null, "headers": [ { "name": "accept", "value": "application/json" }, { "name": "Content-Type", "value": "application/json" } ], "jsonBody": { "enteredDiscountCodes": [ "SUMMER10", "WELCOME20" ] }, "method": "POST", "policy": { "readTimeoutMs": 2000 }, "url": "<external-server-url>/api" } }Output JSON (JavaScript)
{ "request": { "body": "{\"enteredDiscountCodes\":[\"SUMMER10\",\"WELCOME20\"]}", "headers": [ { "name": "accept", "value": "application/json" }, { "name": "Content-Type", "value": "application/json" } ], "jsonBody": { "enteredDiscountCodes": ["SUMMER10", "WELCOME20"] }, "method": "POST", "policy": { "readTimeoutMs": 2000 }, "url": "<external server url>/api" } }cart.delivery-options.discounts.generate.fetch
Input Query (Rust)
query Input { enteredDiscountCodes { code } cart { buyerIdentity { email } } }Input Query (JavaScript)
query InputDeliveryFetch { enteredDiscountCodes { code } }Input Object (Rust)
{ "enteredDiscountCodes": [{"code": "FREESHIP"}], "cart": { "buyerIdentity": { "email": "customer@example.com" } } }Input Object (JavaScript)
{ "enteredDiscountCodes": [{"code": "FREESHIP"}], "cart": { "buyerIdentity": { "email": "customer@example.com" } } }Function Code (Rust)
use super::schema; use shopify_function; use shopify_function::prelude::*; use std::collections::BTreeMap; #[shopify_function] fn cart_delivery_options_discounts_generate_fetch( input: schema::cart_delivery_options_discounts_generate_fetch::Input, ) -> shopify_function::Result<schema::CartDeliveryOptionsDiscountsGenerateFetchResult> { let entered_discount_codes = &input.entered_discount_codes(); let json_body = JsonValue::Object(BTreeMap::from([( "enteredDiscountCodes".to_string(), JsonValue::Array( entered_discount_codes .iter() .map(|code| JsonValue::String(code.code().to_string())) .collect(), ), )])); let request = schema::HttpRequest { headers: vec![ schema::HttpRequestHeader { name: "accept".to_string(), value: "application/json".to_string(), }, schema::HttpRequestHeader { name: "Content-Type".to_string(), value: "application/json".to_string(), }, ], method: schema::HttpRequestMethod::Post, policy: schema::HttpRequestPolicy { read_timeout_ms: 2000, }, url: "<external-server-url>/api".to_string(), body: None, json_body: Some(json_body.clone()), }; Ok(schema::CartDeliveryOptionsDiscountsGenerateFetchResult { request: Some(request), }) }Performance Cost (Rust)
29876 instructions
Function Code (JavaScript)
//@ts-check import {HttpRequestMethod} from "../generated/api"; /** * @typedef {import("../generated/api").InputDeliveryFetch} InputDeliveryFetch * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateFetchResult} CartDeliveryOptionsDiscountsGenerateFetchResult */ /** * * @param {InputDeliveryFetch} input * @returns {CartDeliveryOptionsDiscountsGenerateFetchResult} */ export function cartDeliveryOptionsDiscountsGenerateFetch(input) { const {enteredDiscountCodes} = input; const jsonBody = { enteredDiscountCodes: enteredDiscountCodes.map(({code}) => code), }; const request = { headers: [ { name: "accept", value: "application/json", }, { name: "Content-Type", value: "application/json", }, ], method: HttpRequestMethod.Post, policy: { readTimeoutMs: 2000, }, url: "<external server url>/api", body: JSON.stringify(jsonBody), jsonBody, }; return {request}; }Performance Cost (JavaScript)
311924 instructions
Output JSON (Rust)
{ "request": { "body": null, "headers": [ { "name": "accept", "value": "application/json" }, { "name": "Content-Type", "value": "application/json" } ], "jsonBody": { "enteredDiscountCodes": [ "FREESHIP" ] }, "method": "POST", "policy": { "readTimeoutMs": 2000 }, "url": "<external-server-url>/api" } }Output JSON (JavaScript)
{ "request": { "body": "{\"enteredDiscountCodes\":[\"FREESHIP\"]}", "headers": [ { "name": "accept", "value": "application/json" }, { "name": "Content-Type", "value": "application/json" } ], "jsonBody": { "enteredDiscountCodes": ["FREESHIP"] }, "method": "POST", "policy": { "readTimeoutMs": 2000 }, "url": "<external server url>/api" } } -
Create a tiered percentage discount based on cart total using metaobjects
A Shopify Discount Function that applies a percentage discount to the entire order based on the cart subtotal. Discount tiers are defined as individual number_decimal fields in a shop-level metaobject (using the 2026-04 Shop.metaobject field), so merchants can update thresholds and percentages by editing the metaobject entry in the Shopify admin — no code changes required. The function reads up to 3 tiers, each with a threshold and percentage, and applies the highest qualifying tier.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { shop { tierConfig: metaobject(handle: { type: "$app:tiered_discount_config", handle: "default" }) { tier1Threshold: field(key: "tier_1_threshold") { value } tier1Percentage: field(key: "tier_1_percentage") { value } tier2Threshold: field(key: "tier_2_threshold") { value } tier2Percentage: field(key: "tier_2_percentage") { value } tier3Threshold: field(key: "tier_3_threshold") { value } tier3Percentage: field(key: "tier_3_percentage") { value } discountMessage: field(key: "discount_message") { value } } } cart { cost { subtotalAmount { amount currencyCode } } } }Input Query (JavaScript)
query Input { shop { tierConfig: metaobject(handle: { type: "$app:tiered_discount_config", handle: "default" }) { tier1Threshold: field(key: "tier_1_threshold") { value } tier1Percentage: field(key: "tier_1_percentage") { value } tier2Threshold: field(key: "tier_2_threshold") { value } tier2Percentage: field(key: "tier_2_percentage") { value } tier3Threshold: field(key: "tier_3_threshold") { value } tier3Percentage: field(key: "tier_3_percentage") { value } discountMessage: field(key: "discount_message") { value } } } cart { cost { subtotalAmount { amount currencyCode } } } }Input Object (Rust)
{ "shop": { "tierConfig": { "tier1Threshold": { "value": "100" }, "tier1Percentage": { "value": "5" }, "tier2Threshold": { "value": "200" }, "tier2Percentage": { "value": "10" }, "tier3Threshold": { "value": "500" }, "tier3Percentage": { "value": "20" }, "discountMessage": { "value": "{percentage}% off your order" } } }, "cart": { "cost": { "subtotalAmount": { "amount": "250.00", "currencyCode": "USD" } } } }Input Object (JavaScript)
{ "shop": { "tierConfig": { "tier1Threshold": { "value": "100" }, "tier1Percentage": { "value": "5" }, "tier2Threshold": { "value": "200" }, "tier2Percentage": { "value": "10" }, "tier3Threshold": { "value": "500" }, "tier3Percentage": { "value": "20" }, "discountMessage": { "value": "{percentage}% off your order" } } }, "cart": { "cost": { "subtotalAmount": { "amount": "250.00", "currencyCode": "USD" } } } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; struct Tier { threshold: f64, percentage: f64, } fn push_tier(tiers: &mut Vec<Tier>, threshold_value: Option<&String>, percentage_value: Option<&String>) { if let (Some(tv), Some(pv)) = (threshold_value, percentage_value) { if let (Ok(threshold), Ok(percentage)) = (tv.parse::<f64>(), pv.parse::<f64>()) { tiers.push(Tier { threshold, percentage }); } } } fn parse_tiers(config: &schema::cart_lines_discounts_generate_run::input::shop::TierConfig) -> Vec<Tier> { let mut tiers = Vec::new(); push_tier(&mut tiers, config.tier_1_threshold().and_then(|f| f.value()), config.tier_1_percentage().and_then(|f| f.value())); push_tier(&mut tiers, config.tier_2_threshold().and_then(|f| f.value()), config.tier_2_percentage().and_then(|f| f.value())); push_tier(&mut tiers, config.tier_3_threshold().and_then(|f| f.value()), config.tier_3_percentage().and_then(|f| f.value())); tiers } #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let tier_config = match input.shop().tier_config() { Some(config) => config, None => { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }) } }; let tiers = parse_tiers(tier_config); let message_template = tier_config .discount_message() .and_then(|f| f.value().cloned()) .unwrap_or_else(|| "{percentage}% off your order".to_string()); let subtotal = input.cart().cost().subtotal_amount().amount().0; // Find the highest qualifying tier let applicable_tier = tiers .iter() .filter(|t| subtotal >= t.threshold) .max_by(|a, b| a.threshold.partial_cmp(&b.threshold).unwrap()); match applicable_tier { Some(tier) => { let message = message_template.replace( "{percentage}", &format!("{}", tier.percentage as u64), ); Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::Maximum, candidates: vec![schema::OrderDiscountCandidate { value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal::from(tier.percentage), }, ), targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some(message), conditions: Some(vec![schema::Condition::OrderMinimumSubtotal( schema::OrderMinimumSubtotal { minimum_amount: Decimal::from(tier.threshold), excluded_cart_line_ids: vec![], }, )]), associated_discount_code: None, }], }, )], }) } None => Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }), } }Performance Cost (Rust)
53960 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy } from '../generated/api'; /** * @typedef {import('../generated/api').Input} Input * @typedef {import('../generated/api').CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @typedef {Object} Tier * @property {number} threshold * @property {number} percentage */ /** * Reads tier fields from the metaobject and returns an array of tiers. * The merchant configures each tier as individual number_decimal fields. * @param {any} tierConfig * @returns {Tier[]} */ function parseTiers(tierConfig) { const tiers = []; const tierPairs = [ [tierConfig.tier1Threshold, tierConfig.tier1Percentage], [tierConfig.tier2Threshold, tierConfig.tier2Percentage], [tierConfig.tier3Threshold, tierConfig.tier3Percentage], ]; for (const [thresholdField, percentageField] of tierPairs) { const threshold = parseFloat(thresholdField?.value ?? ""); const percentage = parseFloat(percentageField?.value ?? ""); if (!isNaN(threshold) && !isNaN(percentage)) { tiers.push({ threshold, percentage }); } } return tiers; } /** * @param {Input} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { const tierConfig = input.shop?.tierConfig; if (!tierConfig) { return { operations: [] }; } const tiers = parseTiers(tierConfig); const messageTemplate = tierConfig.discountMessage?.value ?? "{percentage}% off your order"; const subtotal = parseFloat(input.cart?.cost?.subtotalAmount?.amount ?? "0"); // Find the highest qualifying tier const applicableTier = tiers .filter((tier) => subtotal >= tier.threshold) .sort((a, b) => b.threshold - a.threshold)[0]; if (!applicableTier) { return { operations: [] }; } const message = messageTemplate.replace("{percentage}", String(applicableTier.percentage)); return { operations: [ { orderDiscountsAdd: { candidates: [ { value: { percentage: { value: applicableTier.percentage.toFixed(1), }, }, targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], message, conditions: [ { orderMinimumSubtotal: { excludedCartLineIds: [], minimumAmount: applicableTier.threshold.toFixed(1).toString(), }, }, ], }, ], selectionStrategy: OrderDiscountSelectionStrategy.Maximum, }, }, ], }; }Performance Cost (JavaScript)
419208 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "value": { "percentage": { "value": "10.0" } }, "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "message": "10% off your order", "conditions": [ { "orderMinimumSubtotal": { "excludedCartLineIds": [], "minimumAmount": "200.0" } } ] } ], "selectionStrategy": "MAXIMUM" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "value": { "percentage": { "value": "10.0" } }, "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "message": "10% off your order", "conditions": [ { "orderMinimumSubtotal": { "excludedCartLineIds": [], "minimumAmount": "200.0" } } ] } ], "selectionStrategy": "MAXIMUM" } } ] } -
Create a Buy X get Y discount
A Shopify Discount Function that implements a "buy one, get discount on another" promotion. When customers add a specified product (ID 123456) to their cart, they receive a 10% discount on a companion product (ID 123457) if it's also in the cart. The discount applies to only one unit of the companion product.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id merchandise { __typename ... on ProductVariant { product { id } } } } } }Input Query (JavaScript)
query CartInput { cart { lines { id merchandise { __typename ... on ProductVariant { product { id } } } } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123456" } } }, { "id": "gid://shopify/CartLine/2", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123457" } } } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123456" } } }, { "id": "gid://shopify/CartLine/2", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123457" } } } ] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let customer_buys_id = "gid://shopify/Product/123456"; let customer_gets_id = "gid://shopify/Product/123457"; let mut customer_buys_cart_line = None; let mut customer_gets_cart_line = None; input.cart().lines().iter().for_each(|line| { if let schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant( variant, ) = &line.merchandise() { let product_id = variant.product().id().as_str(); if product_id == customer_buys_id { customer_buys_cart_line = Some(line.id().clone()); } else if product_id == customer_gets_id { customer_gets_cart_line = Some(line.id().clone()); } } }); if customer_buys_cart_line.is_none() || customer_gets_cart_line.is_none() { return Ok( schema::CartLinesDiscountsGenerateRunResult { operations: vec![], }, ); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![ schema::CartOperation::ProductDiscountsAdd(schema::ProductDiscountsAddOperation{ candidates: vec![ schema::ProductDiscountCandidate { targets: vec![ schema::ProductDiscountCandidateTarget::CartLine(schema::CartLineTarget { id: customer_gets_cart_line.unwrap(), quantity: Some(1), }), ], message: Some("10% off on Y when buying X".to_string()), associated_discount_code: None, value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(10.0), }), } ], selection_strategy: schema::ProductDiscountSelectionStrategy::First, }), ]}) }Performance Cost (Rust)
45476 instructions
Function Code (JavaScript)
// @ts-check import {ProductDiscountSelectionStrategy} from "../generated/api"; /** * @typedef {import("../generated/api").Input} CartInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult * @typedef {import("../generated/api").CartLine} CartLine */ /** * generateCartRun * @param {CartInput} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { const customer_buys_id = "gid://shopify/Product/123456"; const customer_gets_id = "gid://shopify/Product/123457"; /** @type {CartLine | null} */ let customer_buys_cart_line = null; /** @type {CartLine | null} */ let customer_gets_cart_line = null; for (const line of input.cart.lines) { const productId = "product" in line.merchandise ? line.merchandise.product.id : null; if (productId === customer_buys_id) { customer_buys_cart_line = line; } else if (productId === customer_gets_id) { customer_gets_cart_line = line; } } if (!customer_buys_cart_line || !customer_gets_cart_line) { return { operations: [], }; } return { operations: [ { productDiscountsAdd: { candidates: [ { message: "10% off on Y when buying X", targets: [ { cartLine: { id: customer_gets_cart_line.id, quantity: 1, }, }, ], value: { percentage: { value: 10, }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }, ], }; }Performance Cost (JavaScript)
265742 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "10% off on Y when buying X", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": 1 } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "10% off on Y when buying X", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": 1 } } ], "value": { "percentage": { "value": 10 } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id merchandise { __typename ... on ProductVariant { product { id } } } } deliveryGroups { id } } }Input Query (JavaScript)
query DeliveryInput { cart { lines { id merchandise { __typename ... on ProductVariant { product { id } } } } deliveryGroups { id } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123456" } } }, { "id": "gid://shopify/CartLine/2", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123457" } } } ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123456" } } }, { "id": "gid://shopify/CartLine/2", "merchandise": { "__typename": "ProductVariant", "product": { "id": "gid://shopify/Product/123457" } } } ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let customer_buys_id = "gid://shopify/Product/123456"; let customer_gets_id = "gid://shopify/Product/123457"; let mut customer_buys_cart_line = None; let mut customer_gets_cart_line = None; input.cart().lines().iter().for_each(|line| { if let schema::cart_delivery_options_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = &line.merchandise() { let product_id = variant.product().id().as_str(); if product_id == customer_buys_id { customer_buys_cart_line = Some(line.id().clone()); } else if product_id == customer_gets_id { customer_gets_cart_line = Some(line.id().clone()); } } }); let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; if customer_buys_cart_line.is_none() || customer_gets_cart_line.is_none() { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(100.0), }), message: Some("Free delivery when buying X and Y".to_string()), associated_discount_code: None, }], }, )], }) }Performance Cost (Rust)
50930 instructions
Function Code (JavaScript)
// @ts-check import {DeliveryDiscountSelectionStrategy} from "../generated/api"; /** * @typedef {import("../generated/api").Input} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult * @typedef {import("../generated/api").CartLine} CartLine */ /** * generateDeliveryRun * @param {RunInput} input - The DeliveryInput * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const customer_buys_id = "gid://shopify/Product/123456"; const customer_gets_id = "gid://shopify/Product/123457"; let customer_buys_cart_line_present = false; let customer_gets_cart_line_present = false; for (const line of input.cart.lines) { const productId = "product" in line.merchandise ? line.merchandise.product.id : null; if (productId === customer_buys_id) { customer_buys_cart_line_present = true; } else if (productId === customer_gets_id) { customer_gets_cart_line_present = true; } } if (!customer_buys_cart_line_present || !customer_gets_cart_line_present) { return { operations: [], }; } const firstDeliveryGroup = input.cart.deliveryGroups[0]; if (!firstDeliveryGroup) { throw new Error("No delivery groups found"); } return { operations: [ { deliveryDiscountsAdd: { candidates: [ { message: "Free delivery when buying X and Y", targets: [ { deliveryGroup: { id: firstDeliveryGroup.id, }, }, ], value: { percentage: { value: 100, }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }, ], }; }Performance Cost (JavaScript)
274711 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Free delivery when buying X and Y", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "Free delivery when buying X and Y", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": 100 } } } ], "selectionStrategy": "ALL" } } ] } -
Create loyalty program discounts
A Shopify Discount Function that implements a loyalty program by detecting customer tags. The Function returns three discounts to tagged customers: a $10 fixed amount discount on the last item in the cart, a 10% percentage discount on the entire order subtotal, and free shipping on all available delivery options. Each type of discount is only applied when the corresponding discount class is enabled.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { buyerIdentity { customer { hasAnyTag(tags: ["loyalty-member"]) } } lines { id cost { totalAmount { amount currencyCode } } } deliveryGroups { id deliveryOptions { handle cost { amount currencyCode } } } cost { subtotalAmount { amount currencyCode } } } discount { discountClasses } }Input Query (JavaScript)
query InputCartRun { cart { buyerIdentity { customer { hasAnyTag(tags: ["loyalty-member"]) } } lines { id cost { totalAmount { amount currencyCode } } } deliveryGroups { id deliveryOptions { handle cost { amount currencyCode } } } cost { subtotalAmount { amount currencyCode } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "buyerIdentity": { "customer": { "hasAnyTag": true } }, "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "totalAmount": { "amount": "50.00", "currencyCode": "USD" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "totalAmount": { "amount": "75.00", "currencyCode": "USD" } } } ], "deliveryGroups": [], "cost": { "subtotalAmount": { "amount": "125.00", "currencyCode": "USD" } } }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Input Object (JavaScript)
{ "cart": { "buyerIdentity": { "customer": { "hasAnyTag": true } }, "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "totalAmount": { "amount": "50.00", "currencyCode": "USD" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "totalAmount": { "amount": "75.00", "currencyCode": "USD" } } } ], "deliveryGroups": [], "cost": { "subtotalAmount": { "amount": "125.00", "currencyCode": "USD" } } }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let mut operations = vec![]; let discount_classes = &input.discount().discount_classes(); let has_product_discounts = discount_classes .contains(&schema::DiscountClass::Product); let has_order_discounts = discount_classes.contains(&schema::DiscountClass::Order); if let Some(buyer_identity) = input.cart().buyer_identity() { if let Some(customer) = buyer_identity.customer() { if *customer.has_any_tag() { // Apply $10 off the last cart line (product discount) if has_product_discounts { if let Some(last_line) = input.cart().lines().last() { operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: last_line.id().clone(), quantity: Some(1), }, )], value: schema::ProductDiscountCandidateValue::FixedAmount( schema::ProductDiscountCandidateFixedAmount { amount: Decimal::from(10.0), applies_to_each_item: Some(false), }, ), message: Some( "Loyalty member discount: $10 off last item".to_string(), ), associated_discount_code: None, }], selection_strategy: schema::ProductDiscountSelectionStrategy::First, }, )); } } // Apply 10% off the order (order discount) if has_order_discounts { operations.push(schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { candidates: vec![schema::OrderDiscountCandidate { value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal::from(10.0), }), targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("Loyalty member discount: 10% off order".to_string()), conditions: None, associated_discount_code: None, }], selection_strategy: schema::OrderDiscountSelectionStrategy::First, }, )); } } } } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
60009 instructions
Function Code (JavaScript)
// @ts-check import { DiscountClass, OrderDiscountSelectionStrategy, ProductDiscountSelectionStrategy, } from "../generated/api"; /** * @typedef {import("../generated/api").Input} Input * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * generateCartRun * @param {Input} input - The Input * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { const operations = []; const discountClasses = input.discount.discountClasses; const hasProductDiscounts = discountClasses.includes(DiscountClass.Product); const hasOrderDiscounts = discountClasses.includes(DiscountClass.Order); // Check if customer has any tag const buyerIdentity = input.cart.buyerIdentity; if (buyerIdentity?.customer?.hasAnyTag) { // Apply $10 off the last cart line (product discount) if (hasProductDiscounts && input.cart.lines.length > 0) { const lastLine = input.cart.lines[input.cart.lines.length - 1]; operations.push({ productDiscountsAdd: { candidates: [ { targets: [ { cartLine: { id: lastLine.id, quantity: 1, }, }, ], value: { fixedAmount: { amount: "10.0", appliesToEachItem: false, }, }, message: "Loyalty member discount: $10 off last item", }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }); } // Apply 10% off the order (order discount) if (hasOrderDiscounts) { operations.push({ orderDiscountsAdd: { candidates: [ { value: { percentage: { value: "10.0", }, }, targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], message: "Loyalty member discount: 10% off order", }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }); } } return { operations, }; }Performance Cost (JavaScript)
398643 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Loyalty member discount: $10 off last item", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": 1 } } ], "value": { "fixedAmount": { "amount": "10.0", "appliesToEachItem": false } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "Loyalty member discount: 10% off order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "Loyalty member discount: $10 off last item", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": 1 } } ], "value": { "fixedAmount": { "amount": "10.0", "appliesToEachItem": false } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "message": "Loyalty member discount: 10% off order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { buyerIdentity { customer { hasAnyTag(tags: ["loyalty-member"]) } } lines { id cost { totalAmount { amount currencyCode } } } deliveryGroups { id deliveryOptions { handle cost { amount currencyCode } } } cost { subtotalAmount { amount currencyCode } } } discount { discountClasses } }Input Query (JavaScript)
query InputDeliveryRun { cart { buyerIdentity { customer { hasAnyTag(tags: ["loyalty-member"]) } } lines { id cost { totalAmount { amount currencyCode } } } deliveryGroups { id deliveryOptions { handle cost { amount currencyCode } } } cost { subtotalAmount { amount currencyCode } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "buyerIdentity": { "customer": { "hasAnyTag": true } }, "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "totalAmount": { "amount": "50.00", "currencyCode": "USD" } } } ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1", "deliveryOptions": [ { "handle": "standard-shipping", "cost": { "amount": "5.00", "currencyCode": "USD" } }, { "handle": "express-shipping", "cost": { "amount": "10.00", "currencyCode": "USD" } } ] } ], "cost": { "subtotalAmount": { "amount": "50.00", "currencyCode": "USD" } } }, "discount": { "discountClasses": ["SHIPPING"] } }Input Object (JavaScript)
{ "cart": { "buyerIdentity": { "customer": { "hasAnyTag": true } }, "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "totalAmount": { "amount": "50.00", "currencyCode": "USD" } } } ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1", "deliveryOptions": [ { "handle": "standard-shipping", "cost": { "amount": "5.00", "currencyCode": "USD" } }, { "handle": "express-shipping", "cost": { "amount": "10.00", "currencyCode": "USD" } } ] } ], "cost": { "subtotalAmount": { "amount": "50.00", "currencyCode": "USD" } } }, "discount": { "discountClasses": ["SHIPPING"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let mut operations = vec![]; let discount_classes = &input.discount().discount_classes(); let has_shipping_discounts = discount_classes .contains(&schema::DiscountClass::Shipping); // Only proceed if SHIPPING discounts are allowed if !has_shipping_discounts { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }); } if let Some(buyer_identity) = input.cart().buyer_identity() { if let Some(customer) = buyer_identity.customer() { if *customer.has_any_tag() { // Apply free shipping for group in input.cart().delivery_groups() { for option in group.delivery_options() { if *option.cost().amount() != Decimal::from(0.0) { operations.push(schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![ schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: option.handle().clone(), }, ), ], value: schema::DeliveryDiscountCandidateValue::FixedAmount( schema::FixedAmount { amount: *option.cost().amount(), }, ), message: Some( "Loyalty member discount: Free shipping".to_string(), ), associated_discount_code: None, }], selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, }, )); } } } } } } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
68927 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy, DiscountClass, } from "../generated/api"; /** * @typedef {import("../generated/api").Input} Input * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} DeliveryResult */ /** * @param {Input} input * @returns {DeliveryResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const operations = []; const discountClasses = input.discount.discountClasses; const hasShippingDiscounts = discountClasses.includes(DiscountClass.Shipping); // Only proceed if SHIPPING discounts are allowed if (!hasShippingDiscounts) { return {operations}; } // Check if customer has any tag const buyerIdentity = input.cart.buyerIdentity; if (buyerIdentity?.customer?.hasAnyTag) { // Apply free shipping for (const group of input.cart.deliveryGroups) { for (const option of group.deliveryOptions) { // Check if shipping cost is not already free if (option.cost.amount !== "0") { operations.push({ deliveryDiscountsAdd: { candidates: [ { targets: [ { deliveryOption: { handle: option.handle, }, }, ], value: { fixedAmount: { amount: option.cost.amount, }, }, message: "Loyalty member discount: Free shipping", }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }); } } } } return {operations}; }Performance Cost (JavaScript)
420186 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Loyalty member discount: Free shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "fixedAmount": { "amount": "5.0" } } } ], "selectionStrategy": "ALL" } }, { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Loyalty member discount: Free shipping", "targets": [ { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "fixedAmount": { "amount": "10.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "Loyalty member discount: Free shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "fixedAmount": { "amount": "5.00" } } } ], "selectionStrategy": "ALL" } }, { "deliveryDiscountsAdd": { "candidates": [ { "message": "Loyalty member discount: Free shipping", "targets": [ { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "fixedAmount": { "amount": "10.00" } } } ], "selectionStrategy": "ALL" } } ] } -
Create an order discount with exclusions
A Shopify Discount Function that applies a 10% order discount while intentionally excluding the first item in the cart from the discount calculation. This approach enables creating "second item discount" promotions or selective discounting where certain high-value or non-discountable items remain at full price while the rest of the order receives the discount.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id } deliveryGroups { id deliveryOptions { handle } } } discount { discountClasses metafield(namespace: "$app:ex-excludedline-rs", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query InputCartRun { cart { lines { id cost { subtotalAmount { amount currencyCode } } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "lines": [ {"id": "gid://shopify/CartLine/1"}, {"id": "gid://shopify/CartLine/2"}, {"id": "gid://shopify/CartLine/3"} ], "deliveryGroups": [] }, "discount": { "discountClasses": ["ORDER"] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "10.00" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "20.00" } } }, { "id": "gid://shopify/CartLine/3", "cost": { "subtotalAmount": { "amount": "30.00" } } } ], "deliveryGroups": [] }, "discount": { "discountClasses": ["ORDER"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default)] pub struct Configuration {} #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let first_cart_line_id = input.cart().lines().first().map(|line| line.id().clone()); let order_discount = schema::OrderDiscountCandidate { value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal::from(10.0), }, ), targets: vec![ schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: first_cart_line_id.into_iter().collect(), }, ), ], message: Some("10% off your order".to_string()), conditions: None, associated_discount_code: None, }; Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { candidates: vec![order_discount], selection_strategy: schema::OrderDiscountSelectionStrategy::First, }, )], }) }Performance Cost (Rust)
31941 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy, ProductDiscountSelectionStrategy, } from "../generated/api"; /** * @typedef {import("../generated/api").Input} CartInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * Generates cart discounts that exclude the first cart line from the order discount * @param {CartInput} input - The CartInput * @returns {CartLinesDiscountsGenerateRunResult} - The function result with discounts. */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error("No cart lines found"); } // Get the first cart line ID to exclude it from the discount const firstCartLine = input.cart.lines[0]; const firstCartLineId = firstCartLine.id; const maxCartLine = input.cart.lines.reduce((maxLine, line) => { if (line.cost.subtotalAmount.amount > maxLine.cost.subtotalAmount.amount) { return line; } return maxLine; }, input.cart.lines[0]); return { operations: [ { orderDiscountsAdd: { candidates: [ { message: "10% off your order", targets: [ { orderSubtotal: { excludedCartLineIds: [firstCartLineId], }, }, ], value: { percentage: { value: 10, }, }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }, { productDiscountsAdd: { candidates: [ { message: "20% OFF PRODUCT", targets: [ { cartLine: { id: maxCartLine.id, }, }, ], value: { percentage: { value: 20, }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }, ], }; }Performance Cost (JavaScript)
358630 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% off your order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": ["gid://shopify/CartLine/1"] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% off your order", "targets": [ { "orderSubtotal": { "excludedCartLineIds": ["gid://shopify/CartLine/1"] } } ], "value": { "percentage": { "value": 10 } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "message": "20% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/3" } } ], "value": { "percentage": { "value": 20 } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id } deliveryGroups { id deliveryOptions { handle } } } discount { discountClasses metafield(namespace: "$app:ex-excludedline-rs", key: "function-configuration") { jsonValue } } }Input Query (JavaScript)
query InputDeliveryRun { cart { deliveryGroups { id deliveryOptions { handle cost { amount currencyCode } } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "lines": [ {"id": "gid://shopify/CartLine/1"}, {"id": "gid://shopify/CartLine/2"} ], "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1", "deliveryOptions": [ {"handle": "standard-shipping"}, {"handle": "express-shipping"} ] } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Input Object (JavaScript)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1", "deliveryOptions": [ {"handle": "standard-shipping"}, {"handle": "express-shipping"} ] } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default)] pub struct Configuration {} #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let free_shipping = schema::DeliveryDiscountCandidate { value: schema::DeliveryDiscountCandidateValue::FixedAmount(schema::FixedAmount { amount: Decimal::from(0.0), }), targets: input .cart() .delivery_groups() .iter() .flat_map(|group| { group.delivery_options() .iter() .map(|option| { schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: option.handle().clone(), }, ) }) }) .collect(), message: Some("Free Shipping".to_string()), associated_discount_code: None, }; Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { candidates: vec![free_shipping], selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, }, )], }) }Performance Cost (Rust)
33677 instructions
Function Code (JavaScript)
// @ts-check import {DeliveryDiscountSelectionStrategy} from "../generated/api"; /** * @typedef {import("../generated/api").Input} DeliveryInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * Generates delivery discounts for free shipping on all delivery options * @param {DeliveryInput} input - The DeliveryInput * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { // Check if there are delivery groups and options if (!input.cart.deliveryGroups.length) { return {operations: []}; } // Create targets for all delivery options const targets = []; for (const group of input.cart.deliveryGroups) { for (const option of group.deliveryOptions) { targets.push({ deliveryOption: { handle: option.handle, }, }); } } // If no delivery options found, return empty operations if (targets.length === 0) { return {operations: []}; } return { operations: [ { deliveryDiscountsAdd: { candidates: [ { message: "Free Shipping", targets: targets, value: { percentage: { value: 100, }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }, ], }; }Performance Cost (JavaScript)
272865 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } }, { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "fixedAmount": { "amount": "0.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } }, { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "percentage": { "value": 100 } } } ], "selectionStrategy": "ALL" } } ] } -
Create combined multi-type discounts
A Shopify Discount Function offers, product, order and shipping discounts. When order discounts are enabled, it applies a 10% percentage discount to the entire order subtotal. When product discounts are enabled, it applies a 20% discount specifically to the most expensive item in the cart. When shipping discounts are enabled, it makes delivery options free by applying a 100% discount.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input { cart { lines { id cost { subtotalAmount { amount } } } } discount { discountClasses } }Input Query (JavaScript)
query CartInput { cart { lines { id cost { subtotalAmount { amount } } } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "50.00" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "100.00" } } }, { "id": "gid://shopify/CartLine/3", "cost": { "subtotalAmount": { "amount": "75.00" } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "50.00" } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "100.00" } } }, { "id": "gid://shopify/CartLine/3", "cost": { "subtotalAmount": { "amount": "75.00" } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let max_cart_line = input .cart() .lines() .iter() .max_by(|a, b| { a.cost() .subtotal_amount() .amount() .partial_cmp(&b.cost().subtotal_amount().amount()) .unwrap_or(std::cmp::Ordering::Equal) }) .ok_or("No cart lines found")?; let has_order_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); let has_product_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Product); if !has_order_discount_class && !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Check if the discount has the ORDER class if has_order_discount_class { operations.push(schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("10% OFF ORDER".to_string()), value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(10.0), }), conditions: None, associated_discount_code: None, }], }, )); } // Check if the discount has the PRODUCT class if has_product_discount_class { operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: max_cart_line.id().clone(), quantity: None, }, )], message: Some("20% OFF PRODUCT".to_string()), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(20.0), }), associated_discount_code: None, }], }, )); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
62847 instructions
Function Code (JavaScript)
// @ts-check import { DiscountClass, OrderDiscountSelectionStrategy, ProductDiscountSelectionStrategy, } from "../generated/api"; /** * @typedef {import("../generated/api").CartInput} RunInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult */ /** * @param {RunInput} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error("No cart lines found"); } const hasOrderDiscountClass = input.discount.discountClasses.includes( DiscountClass.Order, ); const hasProductDiscountClass = input.discount.discountClasses.includes( DiscountClass.Product, ); if (!hasOrderDiscountClass && !hasProductDiscountClass) { return {operations: []}; } const maxCartLine = input.cart.lines.reduce((maxLine, line) => { if (line.cost.subtotalAmount.amount > maxLine.cost.subtotalAmount.amount) { return line; } return maxLine; }, input.cart.lines[0]); const operations = []; if (hasOrderDiscountClass) { operations.push({ orderDiscountsAdd: { candidates: [ { message: "10% OFF ORDER", targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], value: { percentage: { value: 10, }, }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }); } if (hasProductDiscountClass) { operations.push({ productDiscountsAdd: { candidates: [ { message: "20% OFF PRODUCT", targets: [ { cartLine: { id: maxCartLine.id, }, }, ], value: { percentage: { value: 20, }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }); } return { operations, }; }Performance Cost (JavaScript)
368382 instructions
Output JSON (Rust)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "20% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": null } } ], "value": { "percentage": { "value": "20.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "orderDiscountsAdd": { "candidates": [ { "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": 10 } } } ], "selectionStrategy": "FIRST" } }, { "productDiscountsAdd": { "candidates": [ { "message": "20% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/3" } } ], "value": { "percentage": { "value": 20 } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { deliveryGroups { id } } discount { discountClasses } }Input Query (JavaScript)
query DeliveryInput { cart { deliveryGroups { id } } discount { discountClasses } }Input Object (Rust)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Input Object (JavaScript)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"] } }Function Code (Rust)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let has_shipping_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Shipping); if !has_shipping_discount_class { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(100.0), }), message: Some("FREE DELIVERY".to_string()), associated_discount_code: None, }], }, )], }) }Performance Cost (Rust)
31245 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy, DiscountClass, } from "../generated/api"; /** * @typedef {import("../generated/api").DeliveryInput} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * @param {RunInput} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const firstDeliveryGroup = input.cart.deliveryGroups[0]; if (!firstDeliveryGroup) { throw new Error("No delivery groups found"); } const hasShippingDiscountClass = input.discount.discountClasses.includes( DiscountClass.Shipping, ); if (!hasShippingDiscountClass) { return {operations: []}; } return { operations: [ { deliveryDiscountsAdd: { candidates: [ { message: "FREE DELIVERY", targets: [ { deliveryGroup: { id: firstDeliveryGroup.id, }, }, ], value: { percentage: { value: 100, }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }, ], }; }Performance Cost (JavaScript)
235651 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "FREE DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "FREE DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": 100 } } } ], "selectionStrategy": "ALL" } } ] } -
Create a configurable discount with metafields
A Shopify Discount Function that reads its configuration from a discount metafield, enabling merchant-configurable promotions. The Function parses the metafield JSON to retrieve product discount percentage, order discount percentage, eligible collection IDs and shipping discount percentage. It can selectively apply percentage discounts to products within specified collections and/or offer a separate percentage discount on the entire order or apply a shipping discount, with all values configurable through the metafield.
cart.lines.discounts.generate.run
Input Query (Rust)
query Input($collectionIds: [ID!]) { cart { lines { id cost { subtotalAmount { amount } } merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: $collectionIds) } } } } } discount { discountClasses metafield( namespace: "$app:example-discounts--ui-extension" key: "function-configuration" ) { jsonValue } } }Input Query (JavaScript)
query CartInput($collectionIds: [ID!]) { cart { lines { id cost { subtotalAmount { amount } } merchandise { __typename ... on ProductVariant { product { inAnyCollection(ids: $collectionIds) } } } } } discount { discountClasses metafield( namespace: "$app:example-discounts--ui-extension" key: "function-configuration" ) { value } } }Input Object (Rust)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "100.00" } }, "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "50.00" } }, "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": false } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"], "metafield": { "jsonValue": { "cartLinePercentage": 15.0, "orderPercentage": 10.0, "collectionIds": ["gid://shopify/Collection/123"] } } } }Input Object (JavaScript)
{ "cart": { "lines": [ { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "100.00" } }, "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": true } } }, { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "50.00" } }, "merchandise": { "__typename": "ProductVariant", "product": { "inAnyCollection": false } } } ] }, "discount": { "discountClasses": ["PRODUCT", "ORDER"], "metafield": { "value": "{\"cartLinePercentage\": 15.0, \"orderPercentage\": 10.0, \"collectionIds\": [\"gid://shopify/Collection/123\"]}" } } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize)] #[shopify_function(rename_all = "camelCase")] pub struct Configuration { cart_line_percentage: f64, order_percentage: f64, collection_ids: Vec<String>, } #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { let configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }), }; let discount_classes = input.discount().discount_classes(); let has_order_discount_class = discount_classes.contains(&schema::DiscountClass::Order); let has_product_discount_class = discount_classes.contains(&schema::DiscountClass::Product); if !has_order_discount_class && !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Add product discounts first if available and allowed if has_product_discount_class && configuration.cart_line_percentage > 0.0 { let mut cart_line_targets = vec![]; for line in input.cart().lines() { if let schema::cart_lines_discounts_generate_run::input::cart::lines::Merchandise::ProductVariant(variant) = &line.merchandise() { if *variant.product().in_any_collection() || configuration.collection_ids.is_empty() { cart_line_targets.push(schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: line.id().clone(), quantity: None, }, )); } } } if !cart_line_targets.is_empty() { operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: cart_line_targets, message: Some(format!( "{}% OFF PRODUCT", configuration.cart_line_percentage )), value: schema::ProductDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(configuration.cart_line_percentage), }, ), associated_discount_code: None, }], }, )); } } // Then add order discounts if available and allowed if has_order_discount_class && configuration.order_percentage > 0.0 { operations.push(schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some(format!("{}% OFF ORDER", configuration.order_percentage)), value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(configuration.order_percentage), }), conditions: None, associated_discount_code: None, }], }, )); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
76876 instructions
Function Code (JavaScript)
// @ts-check import { OrderDiscountSelectionStrategy, ProductDiscountSelectionStrategy, DiscountClass, } from "../generated/api"; /** * @typedef {import("../generated/api").CartInput} RunInput * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult * @typedef {import("../generated/api").ProductDiscountCandidateTarget} ProductDiscountCandidateTarget */ /** * @param {RunInput} input * @returns {CartLinesDiscountsGenerateRunResult} */ export function cartLinesDiscountsGenerateRun(input) { if (!input.cart.lines.length) { throw new Error("No cart lines found"); } const {cartLinePercentage, orderPercentage, collectionIds} = parseMetafield( input.discount.metafield, ); const hasOrderDiscountClass = input.discount.discountClasses.includes( DiscountClass.Order, ); const hasProductDiscountClass = input.discount.discountClasses.includes( DiscountClass.Product, ); if (!hasOrderDiscountClass && !hasProductDiscountClass) { return {operations: []}; } const operations = []; // Add product discounts first if available and allowed if (hasProductDiscountClass && cartLinePercentage > 0) { const cartLineTargets = input.cart.lines.reduce((targets, line) => { if ( "product" in line.merchandise && (line.merchandise.product.inAnyCollection || collectionIds.length === 0) ) { targets.push({ cartLine: { id: line.id, }, }); } return targets; }, /** @type {ProductDiscountCandidateTarget[]} */ ([])); if (cartLineTargets.length > 0) { operations.push({ productDiscountsAdd: { candidates: [ { message: `${cartLinePercentage}% OFF PRODUCT`, targets: cartLineTargets, value: { percentage: { value: cartLinePercentage, }, }, }, ], selectionStrategy: ProductDiscountSelectionStrategy.First, }, }); } } // Then add order discounts if available and allowed if (hasOrderDiscountClass && orderPercentage > 0) { operations.push({ orderDiscountsAdd: { candidates: [ { message: `${orderPercentage}% OFF ORDER`, targets: [ { orderSubtotal: { excludedCartLineIds: [], }, }, ], value: { percentage: { value: orderPercentage, }, }, }, ], selectionStrategy: OrderDiscountSelectionStrategy.First, }, }); } return {operations}; } function parseMetafield(metafield) { try { const value = JSON.parse(metafield.value); return { cartLinePercentage: value.cartLinePercentage || 0, orderPercentage: value.orderPercentage || 0, collectionIds: value.collectionIds || [], }; } catch (error) { console.error("Error parsing metafield", error); return { cartLinePercentage: 0, orderPercentage: 0, collectionIds: [], }; } }Performance Cost (JavaScript)
458807 instructions
Output JSON (Rust)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "15% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1", "quantity": null } } ], "value": { "percentage": { "value": "15.0" } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "conditions": null, "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": "10.0" } } } ], "selectionStrategy": "FIRST" } } ] }Output JSON (JavaScript)
{ "operations": [ { "productDiscountsAdd": { "candidates": [ { "message": "15% OFF PRODUCT", "targets": [ { "cartLine": { "id": "gid://shopify/CartLine/1" } } ], "value": { "percentage": { "value": 15 } } } ], "selectionStrategy": "FIRST" } }, { "orderDiscountsAdd": { "candidates": [ { "message": "10% OFF ORDER", "targets": [ { "orderSubtotal": { "excludedCartLineIds": [] } } ], "value": { "percentage": { "value": 10 } } } ], "selectionStrategy": "FIRST" } } ] }cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { deliveryGroups { id } } discount { discountClasses metafield( namespace: "$app:example-discounts--ui-extension" key: "function-configuration" ) { jsonValue } } }Input Query (JavaScript)
query DeliveryInput { cart { deliveryGroups { id } } discount { discountClasses metafield( namespace: "$app:example-discounts--ui-extension" key: "function-configuration" ) { value } } }Input Object (Rust)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"], "metafield": { "jsonValue": { "deliveryPercentage": 100.0 } } } }Input Object (JavaScript)
{ "cart": { "deliveryGroups": [ { "id": "gid://shopify/CartDeliveryGroup/1" } ] }, "discount": { "discountClasses": ["SHIPPING"], "metafield": { "value": "{\"deliveryPercentage\": 100.0}" } } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize)] #[shopify_function(rename_all = "camelCase")] pub struct Configuration { delivery_percentage: f64, } #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult> { let configuration = match input.discount().metafield() { Some(metafield) => metafield.json_value(), None => { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }) } }; let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; let discount_classes = input.discount().discount_classes(); let has_shipping_discount_class = discount_classes.contains(&schema::DiscountClass::Shipping); if !has_shipping_discount_class { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Only add delivery discount if both the class is allowed and percentage is set if configuration.delivery_percentage > 0.0 { operations.push(schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(configuration.delivery_percentage), }), message: Some(format!( "{}% OFF DELIVERY", configuration.delivery_percentage )), associated_discount_code: None, }], }, )); } Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations }) }Performance Cost (Rust)
38792 instructions
Function Code (JavaScript)
// @ts-check import { DeliveryDiscountSelectionStrategy, DiscountClass, } from "../generated/api"; /** * @typedef {import("../generated/api").DeliveryInput} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * @param {RunInput} input * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { const firstDeliveryGroup = input.cart.deliveryGroups[0]; if (!firstDeliveryGroup) { throw new Error("No delivery groups found"); } const {deliveryPercentage} = parseMetafield(input.discount.metafield); const hasShippingDiscountClass = input.discount.discountClasses.includes( DiscountClass.Shipping, ); if (!hasShippingDiscountClass) { return {operations: []}; } const operations = []; if (hasShippingDiscountClass && deliveryPercentage > 0) { operations.push({ deliveryDiscountsAdd: { candidates: [ { message: `${deliveryPercentage}% OFF DELIVERY`, targets: [ { deliveryGroup: { id: firstDeliveryGroup.id, }, }, ], value: { percentage: { value: deliveryPercentage, }, }, }, ], selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }); } return {operations}; } function parseMetafield(metafield) { try { const value = JSON.parse(metafield.value); return {deliveryPercentage: value.deliveryPercentage || 0}; } catch (error) { console.error("Error parsing metafield", error); return {deliveryPercentage: 0}; } }Performance Cost (JavaScript)
299419 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "100% OFF DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "100% OFF DELIVERY", "targets": [ { "deliveryGroup": { "id": "gid://shopify/CartDeliveryGroup/1" } } ], "value": { "percentage": { "value": 100 } } } ], "selectionStrategy": "ALL" } } ] } -
Create a free shipping discount with currency thresholds
A Shopify Discount Function that provides free shipping when the cart subtotal exceeds configurable thresholds. Using shop metafields to store configuration, it supports multiple currency-specific thresholds (such as $100 USD or €80 EUR), automatically detecting the cart's currency and applying the appropriate threshold. When the threshold is met, the function offers free shipping on all available delivery options.
cart.delivery-options.discounts.generate.run
Input Query (Rust)
query Input { cart { cost { subtotalAmount { amount currencyCode } } deliveryGroups { id deliveryOptions { handle } } } shop { metafield(namespace: "shipping", key: "free_shipping_thresholds") { jsonValue } } }Input Query (JavaScript)
query Input { cart { cost { subtotalAmount { amount currencyCode } } deliveryGroups { id deliveryOptions { handle } } } shop { metafield(namespace: "shipping", key: "free_shipping_thresholds") { jsonValue } } }Input Object (Rust)
{ "cart": { "cost": { "subtotalAmount": { "amount": "150.00", "currencyCode": "USD" } }, "deliveryGroups": [ { "id": "gid://Shopify/CartDeliveryGroup/1", "deliveryOptions": [ { "handle": "standard-shipping" }, { "handle": "express-shipping" } ] } ] }, "shop": { "metafield": { "jsonValue": { "thresholds": [ {"currency": "USD", "minimum_subtotal": 100.0}, {"currency": "CAD", "minimum_subtotal": 120.0}, {"currency": "EUR", "minimum_subtotal": 80.0} ] } } } }Input Object (JavaScript)
{ "cart": { "cost": { "subtotalAmount": { "amount": "150.00", "currencyCode": "USD" } }, "deliveryGroups": [ { "id": "gid://Shopify/CartDeliveryGroup/1", "deliveryOptions": [ { "handle": "standard-shipping" }, { "handle": "express-shipping" } ] } ] }, "shop": { "metafield": { "jsonValue": { "thresholds": [ {"currency": "USD", "minimum_subtotal": 100.0}, {"currency": "CAD", "minimum_subtotal": 120.0}, {"currency": "EUR", "minimum_subtotal": 80.0} ] } } } }Function Code (Rust)
use crate::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] struct FreeShippingThreshold { currency: String, minimum_subtotal: f64, } #[derive(Deserialize, Default, PartialEq)] pub struct Configuration { thresholds: Vec<FreeShippingThreshold>, } #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result<schema::CartDeliveryOptionsDiscountsGenerateRunResult>{ let config: &Configuration = match input.shop().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }), }; let subtotal = input.cart().cost().subtotal_amount().amount().as_f64(); let currency = &input.cart().cost().subtotal_amount().currency_code(); let applicable_threshold = config.thresholds.iter().find(|threshold| { &threshold.currency == *currency && subtotal >= threshold.minimum_subtotal }); if applicable_threshold.is_some() { let candidates: Vec<schema::DeliveryDiscountCandidate> = input .cart() .delivery_groups() .iter() .flat_map(|group| { group .delivery_options() .iter() .map(|option| schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryOption( schema::DeliveryOptionTarget { handle: option.handle().to_string(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal::from(100.0), }), message: Some("Free Shipping".to_string()), associated_discount_code: None, }) }) .collect(); Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { candidates, selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, }, )], }) } else { Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }) } }Performance Cost (Rust)
68856 instructions
Function Code (JavaScript)
// @ts-check import {DeliveryDiscountSelectionStrategy} from "../generated/api"; /** * @typedef {import("../generated/api").Input} RunInput * @typedef {import("../generated/api").CartDeliveryOptionsDiscountsGenerateRunResult} CartDeliveryOptionsDiscountsGenerateRunResult */ /** * Configuration for free shipping thresholds * @typedef {Object} FreeShippingThreshold * @property {string} currency - The currency code * @property {number} minimum_subtotal - The minimum subtotal required for free shipping */ /** * generateDeliveryRun * @param {RunInput} input - The DeliveryInput * @returns {CartDeliveryOptionsDiscountsGenerateRunResult} - The function result with discounts. */ export function cartDeliveryOptionsDiscountsGenerateRun(input) { // Parse configuration from metafield (namespace: shipping, key: free_shipping_thresholds) const config = input.shop.metafield?.jsonValue ?? {thresholds: []}; const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount); const currency = input.cart.cost.subtotalAmount.currencyCode; // Find applicable threshold const applicableThreshold = config.thresholds.find( threshold => threshold.currency === currency && subtotal >= threshold.minimum_subtotal, ); if (!applicableThreshold) { return { operations: [], }; } // Collect all delivery options into candidates const candidates = input.cart.deliveryGroups.flatMap(group => group.deliveryOptions.map(option => ({ targets: [ { deliveryOption: { handle: option.handle, }, }, ], value: { percentage: { value: 100, }, }, message: "Free Shipping", })), ); // Return single operation with all candidates return { operations: [ { deliveryDiscountsAdd: { candidates, selectionStrategy: DeliveryDiscountSelectionStrategy.All, }, }, ], }; }Performance Cost (JavaScript)
354206 instructions
Output JSON (Rust)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "associatedDiscountCode": null, "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "percentage": { "value": "100.0" } } }, { "associatedDiscountCode": null, "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "percentage": { "value": "100.0" } } } ], "selectionStrategy": "ALL" } } ] }Output JSON (JavaScript)
{ "operations": [ { "deliveryDiscountsAdd": { "candidates": [ { "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "standard-shipping" } } ], "value": { "percentage": { "value": 100 } } }, { "message": "Free Shipping", "targets": [ { "deliveryOption": { "handle": "express-shipping" } } ], "value": { "percentage": { "value": 100 } } } ], "selectionStrategy": "ALL" } } ] }
Validations
The following is a summary of validations that are run on functions results. Violating any of the following checks will yield an error when the function runs:
- There can only be one of each operation (
enteredDiscountCodesAccept,deliveryDiscountsAdd,orderDiscountsAdd, orproductDiscountsAdd) in a single function result. enteredDiscountCodesAccept.codesmust only contain discount codes that are a subset of theenteredDiscountCodesinput to the fetch targets.enteredDiscountCodesReject.codesmust only contain discount codes that are a subset of theenteredDiscountCodesinput to the run targets (or fetch targets if network access enabled) and arerejectable: true.- The
associatedDiscountCode.codefield in theproductDiscountsAdd,orderDiscountsAddanddeliveryDiscountsAddoperations must only contain discount codes that are a subset of theenteredDiscountCodesinput to the run target. If the function has network access, then it must be a subset ofenteredDiscountCodesinput to the fetch target.
Migrate from deprecated Discount Function APIs
The following sections outline the changes to the Product Discount, Order Discount, and Shipping Discount Function APIs, and how you can migrate from Shopify Scripts to Discount Functions.
Product Discount Function API and Order Discount Function API
The Product Discount Function API and Order Discount Function API are now merged into the Discount Function API in the cart.lines.discounts.generate.run target. The target returns a CartLinesDiscountsGenerateRunResult object, which returns an operations field that includes fields that aren't present in the deprecated FunctionRunResult object. The following table outlines key changes:
Input query
| Product Discount Function API and Order Discount Function API | Discount Function API | Description | Available in run target | Available in fetch target |
| - | - | - | - | - |
| N/A | enteredDiscountCodes | The discounts entered at checkout by the buyer | Yes | Yes |
| discountNode | discount | The discount associated to the Function that can be queried by the input query | Yes | Yes |
| N/A | triggeringDiscountCode | The discount code that triggered the Function | Yes | Yes |
CartLinesDiscountsGenerateRunResult output
| Product Discount Function API and Order Discount Function API | Discount Function API | Description | Available in run target | Available in fetch target |
| - | - | - | - | - |
| discounts | operations | Lists of discount operations to be applied | Yes | No |
| discountApplicationStrategy | ProductDiscountSelectionStrategy or OrderDiscountSelectionStrategy | Specifies which outputs to apply FIRST, MAXIMUM, or ALL (product discounts only) | Yes | No |
| N/A | candidates | The list of product or order discount candidates to be applied. | Yes | No |
| N/A | associatedDiscountCode | A list of valid discount codes that correspond to external discounts. This can only be used by functions with network access. | Yes | No |
| N/A | orderDiscountsAdd | A group of order discounts that share a selection strategy. | Yes | No |
| N/A | productDiscountsAdd | A group of product discounts that share a selection strategy. | Yes | No |
| N/A | enteredDiscountCodesAccept | A list of valid discount codes that correspond to external discounts. | Yes | No |
| N/A | enteredDiscountCodesReject | A list of rejectable discount codes that should not be applied to the cart. | Yes | No |
Shipping Discount Function API
The Shipping Discount Function API is now merged into the Discount Function API in the cart.delivery-options.discounts.generate.run target. The target returns a CartDeliveryOptionsDiscountsGenerateRunResult object, which includes fields that aren't present in the deprecated FunctionRunResult object. The following table outlines key changes:
Input query
| Shipping Discount Function API | Discount Function API | Description | Available in run target | Available in fetch target |
| - | - | - | - | - |
| N/A | enteredDiscountCodes | The discounts entered at checkout by the buyer | Yes | Yes |
| discountNode | discount | The discount associated to the Function that can be queried by the input query | Yes | Yes |
| N/A | triggeringDiscountCode | The discount code that triggered the Function | Yes | Yes |
CartDeliveryOptionsDiscountsGenerateRunResult output
| Shipping Discount Function API | Discount Function API | Description | Available in run target | Available in fetch target |
| - | - | - | - | - |
| discounts | operations | Lists of discount operations to be applied | Yes | No |
| N/A | deliveryDiscountsAdd | A group of delivery discounts that share a selection strategy. | Yes | No |
| N/A | enteredDiscountCodesAccept | A list of valid discount codes that correspond to external discounts. | Yes | No |
| N/A | candidates | The list of delivery discount candidates to be applied. | Yes | No |
| N/A | selectionStrategy | The strategy that's applied to the list of discounts. | Yes | No |
| productVariant | cartLineIds | Target identifiers for the discount application | Yes | No |
| N/A | enteredDiscountCodesReject | A list of rejectable discount codes that should not be applied to the cart. | Yes | No |
Migrate from line item scripts to discount functions
If you want to migrate an existing line item script to Shopify Functions, then you can use the following mappings:
| Shopify Scripts method | Description | Shopify Functions target | Additional context |
| - | - | - | - |
| change_line_price | Applies a discount to a cart line such as a loyalty discount on a product by specifying a new reduced price, and a message either set by the developer or the merchant in the Function's configuration. | Discount | Apply order discounts or product discounts by specifying the OrderDiscountCandidateValue or ProductDiscountCandidateValue (percentage or fixedAmount off), and an optional message. |
| split | Splits a product into multiple lines so that you can apply discounts to partial quantities. For example, if a customer has five t-shirts in their cart, you can apply a discount to only two items while keeping the other three at full price | Discount | Use the optional quantity field in the ProductDiscountCandidateTarget object to limit the number of units the discount may be applied to. |
| change_properties | Adds or changes a line item property | Not available | Refer to applyAttributeChange in the checkout UI extensions API to apply attribute changes. |
| reject | Disallows a discount code from being applied at checkout with a custom error message | Discount | Use the enteredDiscountCodesReject operation to reject discount codes with custom messages. Query enteredDiscountCodes to access codes that can be rejected. See this tutorial for an example of how to use this functionality. |
Migrate from shipping scripts to discount functions
To migrate an existing shipping script to Shopify Functions, then you can use the following mapping:
| Shopify Scripts method | Description | Shopify Functions target |
| - | - | - |
| apply_discount | Applies a discount to a shipping rate | Shipping discounts |
Additional resources
Explore comprehensive guides and references to help you build, deploy, and optimize your Shopify Functions.
Working with Functions
These guides cover essential concepts for building Shopify Functions effectively. Learn how functions process data, execute during checkout, and handle versioning, localization, and external APIs.
Explore how functions process input data and generate operations.
Learn when Function APIs execute during the checkout process.
Understand schema versioning, release and upgrade requirements.
Check which Shopify plans support functions in custom apps.
Localize functions for international markets.
Configure functions to fetch data from external APIs.
Performance and troubleshooting
Optimize function performance and ensure reliable operation from development through production deployment.
Resource limits and performance
Understand function performance requirements and resource limitations for optimal execution.
Test and debug Shopify Functions
Explore comprehensive testing workflows from local development to production deployment.
Considerations for programming languages
Choose languages that compile to WebAssembly for optimal function performance.
Monitoring and handling errors in production
Master testing and debugging workflows for reliable function development.