Issue with Discount Function Tutorial

I’ve been trying to learn how to create a discount function using this tutorial: https://shopify.dev/docs/apps/build/discounts/experience/build-discounts-function

I’ve successfully installed the app in my development store, but when I try to test it I keep getting InvalidOutputError.

I feel like I’ve followed the tutorial correctly and copied the Javascript provided into run.js and I’m not sure what the issue is. Any ideas?

Here is the code for run.js from the tutorial:

// -check
import { DiscountApplicationStrategy } from "../generated/api";

// Use JSDoc annotations for type safety
/**
* @typedef {import("../generated/api").RunInput} RunInput
* @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
* @typedef {import("../generated/api").Target} Target
* @typedef {import("../generated/api").ProductVariant} ProductVariant
*/

/**
*  {FunctionRunResult}
*/
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.First,
  discounts: [],
};

// The configured entrypoint for the 'purchase.product-discount.run' extension target
/**
* @param {RunInput} input
* @returns {FunctionRunResult}
*/
export function run(input) {
  const targets = input.cart.lines
  // Only include cart lines with a quantity of two or more
  .filter(line => line.quantity >= 2)
  .map(line => {
    return /**  {Target} */ ({
      // Use the cart line ID to create a discount target
      cartLine: {
        id: line.id
      }
    });
  });

  if (!targets.length) {
    // You can use STDERR for debug logs in your function
    console.error("No cart lines qualify for volume discount.");
    return EMPTY_DISCOUNT;
  }

  // The /shopify_function package applies JSON.stringify() to your function result
  // and writes it to STDOUT
  return {
    discounts: [
      {
        // Apply the discount to the collected targets
        targets: targets,
        // Define a percentage-based discount
        value: {
          percentage: {
            value: "10.0"
          }
        }
      }
    ],
    discountApplicationStrategy: DiscountApplicationStrategy.First
  };
};

Here is run.graphql:

query RunInput {
  cart {
    lines {
      id
      quantity
    }
  }
}

Here’s information from a run log:

Input (STDIN)

{
  "cart": {
    "lines": [
      {
        "id": "gid://shopify/CartLine/0",
        "quantity": 2
      }
    ]
  }
}

Output (STDOUT)

{
  "discounts": [
    {
      "targets": [
        {
          "cartLine": {
            "id": "gid://shopify/CartLine/0"
          }
        }
      ],
      "value": {
        "percentage": {
          "value": "10.0"
        }
      }
    }
  ],
  "discountApplicationStrategy": "FIRST"
}

Error message

[
  {
    "path": [
      "discounts",
      0,
      "targets",
      0
    ],
    "explanation": "Expected one of valid values: productVariant. Got: cartLine"
  }
]

Hi Vbjornsson,

This could be due to the version you’re calling in your toml file. You should update your toml file to make sure you’re using 2024-07 and it should work then. Let me know if you’re still seeing issues.

Thanks for the reply Liam, but it looks like I already have that api version set:

Try changing

cartLine: {
        id: line.id
      }

to

productVariant: {
          id: variant.id
        }

Hi again Vbjornsson,

We also updated the functions example on our side so it should work with 2024-07 now.

Thanks Liam. What functions example did you change on your end?

After seeing @SomeUsernameHe comment, I looked up the tutorial again and saw there was a link to a git repository and the run.js code on there was written a little differently than the tutorial I had found on the shopify.dev site: https://github.com/Shopify/function-examples/blob/main/sample-apps/discounts/extensions/product-discount-js/src/run.js

Vs the tutorial I was going by: https://shopify.dev/docs/apps/build/discounts/experience/build-discounts-function

The code on Git had implemented user configuration but I just took what I needed to modify my run.graphql and run.js files and this what I ended up with (below). After running “shopify app function typegen” command and running the app again it worked!

Here is my modified run.js:

// @ts-check
import { DiscountApplicationStrategy } from "../generated/api";

// Use JSDoc annotations for type safety
/**
* @typedef {import("../generated/api").RunInput} RunInput
* @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
* @typedef {import("../generated/api").Target} Target
* @typedef {import("../generated/api").ProductVariant} ProductVariant
*/

/**
* @type {FunctionRunResult}
*/
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.First,
  discounts: [],
};

// The configured entrypoint for the 'purchase.product-discount.run' extension target
/**
* @param {RunInput} input
* @returns {FunctionRunResult}
*/
export function run(input) {
  const targets = input.cart.lines
    // Only include cart lines with a quantity of two or more
    .filter(line => line.quantity >= 2 &&
      line.merchandise.__typename == "ProductVariant")
    .map(line => {
      const variant = /** @type {ProductVariant} */ (line.merchandise);
      return /** @type {Target} */ ({
        // Use the cart line ID to create a discount target
        productVariant: {
          id: variant.id
        }
      });
    });

  if (!targets.length) {
    // You can use STDERR for debug logs in your function
    console.error("No cart lines qualify for volume discount.");
    return EMPTY_DISCOUNT;
  }

  // The @shopify/shopify_function package applies JSON.stringify() to your function result
  // and writes it to STDOUT
  return {
    discounts: [
      {
        // Apply the discount to the collected targets
        targets: targets,
        // Define a percentage-based discount
        value: {
          percentage: {
            value: 10.0
          }
        },
        message: "10% OFF"
      }
    ],
    discountApplicationStrategy: DiscountApplicationStrategy.First
  };
};

And my modified run.graphql:

query RunInput {
  cart {
    lines {
      quantity
      merchandise {
        __typename
        ... on ProductVariant {
          id
        }
      }
    }
  }
}