Skip to main content

Create a local pickup charges function

To generate custom local pickup options that are available to customers during checkout, use a local pickup delivery option generator.

Local pickup is an option that allows customers to pick up their orders from your physical location. You can activate it in the Shipping and delivery settings in the Shopify admin. Customers can then choose the local pickup option at checkout. To learn more, visit the Shopify Help Center.

In this tutorial series, you'll use Shopify Functions to create a function that adds a price to local pickup options when the cart contains bulky items. You can use this tutorial as a guide to creating other functions that generate local pickup charges.


In this tutorial, you’ll learn how to do the following tasks:

  • Generate starter code for Shopify Functions.
  • Use GraphQL to define the input of your function.
  • Deploy a local pickup delivery option generator function to the Shopify platform.
  • Review logs for your function.

Plus

Only stores on the Shopify Plus plan can use local pickup delivery option generators.

Anchor to Rust-specific requirementsRust-specific requirements

The following requirements are specific to Rust-based development with Shopify Functions.

  • You've installed Rust.

    On Windows, Rust requires the Microsoft C++ Build Tools. Make sure to select the Desktop development with C++ workload when installing the tools.

  • You've installed the wasm32-wasip1 target:

    Terminal

    rustup target add wasm32-wasip1

Anchor to Step 1: Create the local pickup delivery option generator functionStep 1: Create the local pickup delivery option generator function

To create your local pickup delivery option generator function, you can use Shopify CLI to generate a starter function, specify the inputs for your function using an input query, and implement your function logic.

  1. Navigate to your app directory:

    Terminal

    cd <directory>
  2. Run the following command to create a new local pickup delivery option generator extension:

    Terminal

    shopify app generate extension --template local_pickup_delivery_option_generator --name local-pickup-generator
    1. Choose the language that you want to use. For this tutorial, you should select either Rust or JavaScript.

      Shopify defaults to Rust as the most performant and recommended language choice to stay within the platform limits. For more information, refer to language considerations.

      Terminal

      ? What would you like to work in?
      > (1) Rust
      (2) JavaScript
      (3) TypeScript
      (4) Wasm
    Tip

    Shopify Functions support any language that compiles to WebAssembly (Wasm), such as Rust, AssemblyScript, or TinyGo. You specify the Wasm template option when you're using a language other than Rust and can conform to the Wasm API. Learn more about the Wasm API.

  3. Navigate to extensions/local-pickup-generator:

    Terminal

    cd extensions/local-pickup-generator
  4. Replace the contents of the src/run.graphql file with the following code:

    run.graphql defines the input for the function.

    The query differs slightly in Rust and JavaScript due to code generation requirements.

    src/run.graphql

    src/run.graphql

    query Input {
    cart {
    lines {
    id
    merchandise {
    __typename
    ...on ProductVariant
    {
    id
    product {
    productType
    hasAnyTag(tags: ["bulky"])
    }
    }
    }
    }
    }
    locations {
    id
    handle
    name
    }
    }
    query RunInput {
    cart {
    lines {
    id
    merchandise {
    __typename
    ...on ProductVariant
    {
    id
    product {
    productType
    hasAnyTag(tags: ["bulky"])
    }
    }
    }
    }
    }
    locations {
    id
    handle
    name
    }
    }
  5. If you're using JavaScript, then run the following command to regenerate types based on your input query:

    Terminal

    shopify app function typegen
  6. Replace the src/run.rs or src/run.js file with the following code.

    File

    src/run.rs

    use super::schema;
    use shopify_function::prelude::*;
    use shopify_function::Result;

    #[shopify_function]
    fn run(input: schema::run::Input) -> Result<schema::FunctionRunResult> {
    let has_bulky_variant = input.cart().lines().into_iter().any(|line| {
    match line.merchandise() {
    schema::run::input::cart::lines::Merchandise::ProductVariant(variant) => *variant.product().has_any_tag(),
    _ => false, // Default case for any other merchandise types
    }
    });

    let item_cost;
    let pickup_instruction_text;
    if has_bulky_variant {
    item_cost = Decimal(2.99);
    pickup_instruction_text = String::from("Ready for pickup next business day.");
    } else {
    item_cost = Decimal(0.00);
    pickup_instruction_text = String::from("Ready for pickup now!");
    }

    let operations: Vec<schema::Operation> = input.locations()
    .iter()
    .map(|location_data| {
    schema::Operation {
    add: schema::LocalPickupDeliveryOption {
    title: Some(location_data.name().clone()),
    cost: Some(item_cost),
    pickup_location: schema::PickupLocation {
    location_handle: location_data.handle().clone(),
    pickup_instruction: Some(pickup_instruction_text.clone()),
    },
    metafields: None,
    }
    }
    })
    .collect();

    Ok(schema::FunctionRunResult { operations })
    }
    // @ts-check

    /**
    * @typedef {import("../generated/api").RunInput} RunInput
    * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
    */

    /**
    * @param {RunInput} input
    * @returns {FunctionRunResult}
    */
    export function run(input) {
    const hasBulkyVariant = input.cart.lines.some(line => {
    if (line.merchandise.__typename === 'ProductVariant') {
    return line.merchandise.product.hasAnyTag;
    }
    return false;
    });

    const cost = hasBulkyVariant ? 2.99 : 0.00;
    const pickupInstruction = hasBulkyVariant
    ? "Ready for pickup next business day."
    : "Ready for pickup now!";

    return {
    operations: input.locations.map(location => ({
    add: {
    title: location.name,
    cost: cost,
    pickupLocation: {
    locationHandle: location.handle,
    pickupInstruction: pickupInstruction
    }
    }
    }))
    };
    }
  7. If you're using Rust, then build the function's Wasm module:

    Terminal

    cargo build --target=wasm32-wasip1 --release

    If you encounter any errors, then ensure that you've installed Rust and the wasm32-wasip1 target.


Anchor to Step 2: Preview the function on a development storeStep 2: Preview the function on a development store

To test your function, you need to make it available to your development store.

  1. If you're developing a function in a language other than JavaScript or TypeScript, ensure you have configured build.watch in your function extension configuration.
  1. Navigate back to your app root:

    Terminal

    cd ../..
  1. Use the Shopify CLI dev command to start app preview:

    Terminal

    shopify app dev

    You can keep the preview running as you work on your function. When you make changes to a watched file, Shopify CLI rebuilds your function and updates the function extension's drafts, so you can immediately test your changes.

  2. Follow the CLI prompts to preview your app, and install it on your development store.


Anchor to Step 3: Test the local pickup delivery option generatorStep 3: Test the local pickup delivery option generator

You can test your local pickup delivery option generator to ensure it's working as expected, and review logs for your function.

  1. From the Shopify admin, go to Settings > Shipping and delivery.

    Make sure you have at least one location in your store with local pickup enabled.

  2. Activate a pickup service connected to your function.

  3. Open your development store, add a product with the "bulky" tag to your cart, proceed through checkout, and select Pick up.

    You should see a charge of $2.99 added to the local pickup option.

  4. Try again with a product without the "bulky" tag.

    You should see no additional charge for local pickup.



Was this page helpful?