--- gid: e424d91a-f6e0-41e3-a3e3-aa01206dfez1 title: Build a Discounts Allocator Function description: Learn how to build a Discounts Allocator Function to implement custom logic for discounts across line items. --- import RequirementPartial from 'app/views/partials/apps/discounts/requirements_partial.mdx'; import CreateFunction from 'app/views/partials/apps/discounts/create-function.mdx'; import RegenerateTypes from 'app/views/partials/apps/discounts/regenerate-types.mdx'; import AccessScopeForAllocator from 'app/views/partials/apps/discounts/deploy-and-reinstall-app.mdx'; <Repo extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix" /> <Repo extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix" /> <Picker name="extension"> <PickerOption name="javascript" /> <PickerOption name="rust" /> </Picker> <Overview> <Notice type="beta" title="Developer Preview"> <p>This Function API is available only in the <a href="/docs/api/developer-previews#discounts-allocator-developer-preview">Discounts Allocator developer preview</a>.</p> <p>The Discounts Allocator Function will be available only to [Shopify Plus](https://www.shopify.com/plus) merchants.</p> </Notice> Building a Discounts Allocator Function allows for greater customization in defining discount strategies, like implementing custom logic to distribute discounts across line items in an order. ## What you'll learn In this tutorial, you'll create a Discounts Allocator Function that uses metafields to help define its custom allocation logic. The first metafield is applied at the order level. It sets a cap on the discount, ensuring that a 50% discount won't exceed a specified dollar amount. The second metafield is applied at the shop level. It sets a maximum discounted amount for a cart. </Overview> <Requirements> <RequirementPartial /> </Requirements> <StepSection> <Step> ## Update your app's access scopes Your app requires the `write_discounts_allocator_functions` [access scope](/docs/api/usage/access-scopes) to create and update discounts. <Substep> ### Update the `shopify.app.toml` file <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/shopify.app.toml" tag="discount-allocator.access_scopes" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/shopify.app.toml" tag="discount-allocator.access_scopes" /> Add the `write_discounts_allocator_functions` scope to your app's `shopify.app.toml` file. </Substep> </Step> <Step> <AccessScopeForAllocator /> </Step> <Step> <CreateFunction functionName="Discounts Allocator"> ```bash shopify app generate extension --template discounts_allocator --name discounts-allocator ``` </CreateFunction> </Step> <Step> ## Update the configuration <Substep> ### Replace the content of the extension's `run.graphql` file <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-js/src/run.graphql" tag="discounts-allocator.run-graphql-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-rust/src/run.graphql" tag="discounts-allocator.run-graphql-configuration" /> This GraphQL query is structured to retrieve all necessary data for applying discounts to shopping cart items, including discount details, cart line items, and shop metafields. It ensures the function has access to discount rules, item specifics, and cap limits to accurately calculate and apply discounts according to predefined policies. </Substep> <If extension="javascript"> <Substep> Navigate to your extension's directory: <Codeblock terminal> ```bash cd extensions/discounts-allocator ``` </Codeblock> </Substep> <Substep> <RegenerateTypes /> </Substep> </If> </Step> <Step> ## Update the function logic <Substep> ### Replace the content of <If extension="javascript">`run.js`</If> <If extension="rust">`run.rs`</If> file. <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-js/src/run.js" tag="discounts-allocator.run-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-rust/src/run.rs" tag="discounts-allocator.run-configuration" /> This function applies discounts to shopping cart items according to set rules and limits. It calculates discount amounts, enforces caps to prevent excessive reductions, and compiles the results and any errors into a structured output, ensuring fair and policy-compliant discount application. </Substep> </Step> <Step> ## Define discount metafields for orders and the shop Use [the GraphiQL app](https://shopify-graphiql-app.shopifycloud.com/login) to create metafield definitions for the discount and shop. <Substep> ### Create the discount metafield definition <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/discountMetafieldDefinitionCreate.graphql" tag="discounts-allocator.discount-metafield-defination-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/discountMetafieldDefinitionCreate.graphql" tag="discounts-allocator.discount-metafield-defination-configuration" /> To set the maximum discount amount for each discount, use the [`metafieldDefinitionCreate`](/docs/api/admin-graphql/latest/mutations/metafieldDefinitionCreate) mutation with `ownerType: "DISCOUNT"`. </Substep> <Substep> ### Create the shop metafield definition <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/shopMetafieldDefinitionCreate.graphql" tag="discounts-allocator.shop-metafield-defination-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/shopMetafieldDefinitionCreate.graphql" tag="discounts-allocator.shop-metafield-defination-configuration" /> To set the maximum discount amount for the cart, use the [`metafieldDefinitionCreate`](/docs/api/admin-graphql/latest/mutations/metafieldDefinitionCreate) mutation with `ownerType: "SHOP"`. </Substep> </Step> <Step> ## Create the frontend UI for registering your Discounts Allocator Function After a merchant installs your app, they'll need to register the Discounts Allocator Function to actively allocate discounts. <Notice type="caution"> You're replacing the Shopify discount engine with the Discounts Allocator Function. Your Function will take precedence over most [discount features that are built by Shopify](https://help.shopify.com/en/manual/discounts/combining-discounts/discount-combinations). </Notice> <Substep> ### Create a new route for the Discounts Allocator Function <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.add-ui" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.add-ui" /> In `app/routes`, create a new file named `app.discounts-allocator.$functionId.jsx`. --- The Shopify Remix app template uses file-based routing, so the filename determines the page's URL. The `$` prefix indicates that `functionId` is a [dynamic segment](https://remix.run/docs/en/main/guides/routing#dynamic-segments). The path for this page is `/app/discounts-allocator/{functionId}`. </Substep> <Substep> ### Add a Remix `action` function to handle form submission <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.action" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.action" /> Add a Remix `action` to `app.discounts-allocator.$functionId.jsx` to handle the form submission. This function calls the `discountsAllocatorFunctionRegister` mutation to register the Discounts Allocator Function. </Substep> <Substep> ### Create the UI for registering the Discounts Allocator Function <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.ui-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/app/routes/app.discounts-allocator.%24functionId.jsx" tag="discounts-allocator.ui-configuration" /> Use the `primaryAction` in the `Page` component to expose a button that calls the `action` function. This button allows merchants to register the Discounts Allocator Function. </Substep> <Substep> ### Update the UI path <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-js/shopify.extension.toml" tag="discounts-allocator.toml-configuration" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/extensions/discounts-allocator-js/shopify.extension.toml" tag="discounts-allocator.toml-configuration" /> In `extensions/discounts-allocator/shopify.extension.toml`, populate the `create` and `details` setting in the `[extensions.ui.paths]` section. This change is automatically applied as long as you're running `dev`. --- The settings in the `shopify.extension.toml` file define the URLs that Shopify uses to enable merchants to create and edit discounts based on your Function. Shopify automatically fills in any [dynamic tokens](/docs/apps/build/functions/input-output/metafields-for-input-queries#dynamic-id-values) in these URLs. </Substep> </Step> <Step> ## Register your Discounts Allocator Function 1. If your app isn't already running, then start it using Shopify CLI: <Codeblock terminal> ```terminal shopify app dev ``` </Codeblock> 1. Follow the CLI prompts to preview your app, and install or view it on your development store. 1. In your development store, navigate to `/app/discounts-allocator/:functionId` and register your Discounts Allocator Function.  <Notice type="tip"> The `functionId` is a dynamic segment in the route. It's used to identify the Discounts Allocator Function that the merchant is registering. You can find your `functionId` in your app's `.env` file or in your [Partner Dashboard](https://partners.shopify.com/current/apps) if you navigate to <If extension="javascript">`Apps > "your app" > Extensions > discounts-allocator-js`</If><If extension="rust">`Apps > "your app" > Extensions > discounts-allocator-rust`</If>. </Notice> <Troubleshooting> If you have trouble registering because the `discountsAllocatorFunctionRegister` is not found, you will need to use a more recent GraphQL API or the unstable one. This can be changed in: - `/app/shopify.server.js` by setting `apiVersion` to `ApiVersion.Unstable`; - And in your `extensions/discount-allocator/shopify.extension.toml` file with `api_version = "unstable"`. </Troubleshooting> </Step> <Step> ## Create a discount and build a cart To test the `single-discount-cap` capability of your allocator, prepare an automatic discount and build a cart to apply the discount. 1. In your Shopify admin, go to **Discounts**. 1. Click the **Create discount** button near the top right of your page and select **Amount off products**. Fill in the values for the discount: * For **Method**, use **Automatic**. * For **Discount percentage**, use **50**. 1. Open your development store and build a cart with some eligible products. The discount amount applied should be **50 percent off**. </Step> <Step> ## Test your Discounts Allocator Function with a Single Discount Cap <Substep> ### Set the maximum eligible amount for individual discounts <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/singleCapDiscountMutation.graphql" tag="discount-allocator.single-cap-discount-metafield-mutation" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/singleCapDiscountMutation.graphql" tag="discount-allocator.single-cap-discount-metafield-mutation" /> Use the [`metafieldsSet`](/docs/api/customer/latest/mutations/metafieldsSet) mutation to set the `single-discount-cap` metafield for the discount you created. </Substep> <Substep> ### Replace the placeholders <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/singleCapDiscountMutation.graphql" tag="discount-allocator.replace-placeholders" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/singleCapDiscountMutation.graphql" tag="discount-allocator.replace-placeholders" /> Replace `DISCOUNT_ID` with the `ID` of the discount you created and replace `SINGLE_DISCOUNT_CAP` with the desired cap, for example `500`. </Substep> --- Notice that the discount amount applied to your cart is capped at the value you set in the metafield when you refresh the cart. </Step> <Step> ## Create a maximum discount amount for a cart <Substep> ### Set the maximum discount amount for a cart <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/perCartDiscountMetafieldMutation.graphql" tag="discount-allocator.per-cart-discount-metafield-mutation" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/perCartDiscountMetafieldMutation.graphql" tag="discount-allocator.per-cart-discount-metafield-mutation" /> Use the [`metafieldsSet`](/docs/api/customer/latest/mutations/metafieldsSet) mutation in your shop to utilize the `per-cart-cap` capability of your allocator. </Substep> <Substep> ### Replace the placeholders <CodeRef extension="javascript" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/perCartDiscountMetafieldMutation.graphql" tag="discount-allocator.replace-placeholders" /> <CodeRef extension="rust" href="https://github.com/Shopify/tutorial-discount-functions-with-remix/blob/main/graphql_examples/perCartDiscountMetafieldMutation.graphql" tag="discount-allocator.replace-placeholders" /> Replace `SHOP_ID` with the `ID` of your shop and replace `PER_CART_CAP` with the desired per cart cap, for example `50`. </Substep> </Step> <Step> ## Test your Discounts Allocator Function with a Per Cart Cap 1. Click the **Create discount** button and select **Amount off order**. Fill in the values for the discount: * For **Method**, use **Automatic**. * For **Fixed Amount**, use **500** or any amount higher than your `PER_CART_CAP`. 1. Go to the cart you created and refresh the page, the total savings will be capped at the `PER_CART_CAP` value you set in the metafield. </Step> </StepSection>