--- title: Add configuration to the payments function description: Use metafields to make your payment customization function configurable. source_url: html: 'https://shopify.dev/docs/apps/build/checkout/payments/add-configuration' md: 'https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md' --- ExpandOn this page * [What you'll learn](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#what-youll-learn) * [Requirements](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#requirements) * [Step 1: Configure the function](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-1-configure-the-function) * [Step 2: Populate the payment customization configuration metafield](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-2-populate-the-payment-customization-configuration-metafield) * [Step 3: Test the payment customization](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-3-test-the-payment-customization) * [Next steps](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#next-steps) # Add configuration to the payments function To configure your payment customization function, use metafields. Metafields provide flexibility for Shopify Functions and enable you to create merchant-facing UIs for managing function settings in the Shopify admin. *** ## What you'll learn In this tutorial, you'll learn how to do the following tasks: * Define what configuration settings will be surfaced to merchants. * Read and use the merchant-defined values in your function. Caution In this tutorial, you'll use a metafield namespace that's accessible to any app so that the metafield namespace can be populated using the GraphiQL app. To make your function ready for production, you should update the metafield namespace to use a [reserved prefix](https://shopify.dev/docs/apps/build/custom-data/ownership#reserved-prefixes) so that other apps can't use your metafield. You'll make this update in [the next tutorial](https://shopify.dev/docs/apps/checkout/payments/ui). *** ## Requirements * You've completed the [Getting started with building payment customizations](https://shopify.dev/docs/apps/build/checkout/payments/create-payments-function) tutorial. *** ## Step 1: Configure the function To make your function reusable, you can replace hardcoded values in your function with metafield values. You can update your [input query](https://shopify.dev/docs/apps/build/functions/input-queries/metafields-for-input-queries) to request a metafield value on the created payment customization, which is the [function owner](https://shopify.dev/docs/apps/build/functions/input-queries/metafields-for-input-queries#how-it-works) for this function API. You can then use that value in your function logic. 1. Navigate to your function in `extensions/payment-customization`. ```terminal cd extensions/payment-customization ``` 2. Replace the code in the `src/run.graphql` file with the following code. This update to the input query adds a metafield from the `paymentCustomization` object, which is the function owner. The query differs slightly in Rust and JavaScript due to code generation requirements. ## run.graphql ## src/run.graphql ```graphql query Input { cart { cost { totalAmount { amount } } } paymentMethods { id name } paymentCustomization { metafield(namespace: "payment-customization", key: "function-configuration") { jsonValue } } } ``` ```graphql query RunInput { cart { cost { totalAmount { amount } } } paymentMethods { id name } paymentCustomization { metafield(namespace: "payment-customization", key: "function-configuration") { jsonValue } } } ``` ##### Rust input query ``` query Input { cart { cost { totalAmount { amount } } } paymentMethods { id name } paymentCustomization { metafield(namespace: "payment-customization", key: "function-configuration") { jsonValue } } } ``` ##### JavaScript input query ``` query RunInput { cart { cost { totalAmount { amount } } } paymentMethods { id name } paymentCustomization { metafield(namespace: "payment-customization", key: "function-configuration") { jsonValue } } } ``` 3. If you're using JavaScript, then run the following command to regenerate types based on your input query: ## Terminal ```terminal shopify app function typegen ``` 4. If you're using Rust replace the `src/main.rs` file with the following code that will convert the metafield into a data structure in the Rust program. ## Rust src/main.rs ```rust use shopify_function::prelude::*; use std::process; pub mod run; #[typegen("schema.graphql")] pub mod schema { #[query("src/run.graphql", custom_scalar_overrides = { "Input.paymentCustomization.metafield.jsonValue" => super::run::Configuration, } )] pub mod run {} } fn main() { log!("Please invoke a named export."); process::abort(); } ``` 5. Replace the `src/run.rs` or `src/run.js` file with the following code. This update includes parsing the JSON metafield value, and using values from that JSON in the function logic instead of hardcoded values. This change is automatically reflected as long as you're running `dev`. ## File ## src/run.rs ```rust use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] #[shopify_function(rename_all = "camelCase")] pub struct Configuration { payment_method_name: String, cart_total: f64, } #[shopify_function] fn run(input: schema::run::Input) -> Result { let no_changes = schema::FunctionRunResult { operations: vec![] }; let config: &Configuration = match input.payment_customization().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(no_changes), }; let cart_total: f64 = input.cart().cost().total_amount().amount().as_f64(); if cart_total < config.cart_total { log!("Cart total is not high enough, no need to hide the payment method."); return Ok(no_changes); } let operations = input .payment_methods() .iter() .find(|&method| method.name().contains(&config.payment_method_name)) .map(|method| { vec![schema::Operation::Hide(schema::HideOperation { payment_method_id: method.id().clone(), placements: None, })] }) .unwrap_or_default(); Ok(schema::FunctionRunResult { operations }) } ``` ```javascript // @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ /** * @type {FunctionRunResult} */ const NO_CHANGES = { operations: [], }; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { // Define a type for your configuration, and parse it from the metafield /** * @type {{ * paymentMethodName: string * cartTotal: number * }} */ const configuration = input?.paymentCustomization?.metafield?.jsonValue ?? {}; if (!configuration.paymentMethodName || !configuration.cartTotal) { return NO_CHANGES; } const cartTotal = parseFloat(input.cart.cost.totalAmount.amount ?? "0.0"); // Use the configured cart total instead of a hardcoded value if (cartTotal < configuration.cartTotal) { console.error("Cart total is not high enough, no need to hide the payment method."); return NO_CHANGES; } // Use the configured payment method name instead of a hardcoded value const hidePaymentMethod = input.paymentMethods .find(method => method.name.includes(configuration.paymentMethodName)); if (!hidePaymentMethod) { return NO_CHANGES; } return { operations: [{ hide: { paymentMethodId: hidePaymentMethod.id } }] }; }; ``` ##### Rust ``` use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[derive(Deserialize, Default, PartialEq)] #[shopify_function(rename_all = "camelCase")] pub struct Configuration { payment_method_name: String, cart_total: f64, } #[shopify_function] fn run(input: schema::run::Input) -> Result { let no_changes = schema::FunctionRunResult { operations: vec![] }; let config: &Configuration = match input.payment_customization().metafield() { Some(metafield) => metafield.json_value(), None => return Ok(no_changes), }; let cart_total: f64 = input.cart().cost().total_amount().amount().as_f64(); if cart_total < config.cart_total { log!("Cart total is not high enough, no need to hide the payment method."); return Ok(no_changes); } let operations = input .payment_methods() .iter() .find(|&method| method.name().contains(&config.payment_method_name)) .map(|method| { vec![schema::Operation::Hide(schema::HideOperation { payment_method_id: method.id().clone(), placements: None, })] }) .unwrap_or_default(); Ok(schema::FunctionRunResult { operations }) } ``` ##### JavaScript ``` // @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ /** * @type {FunctionRunResult} */ const NO_CHANGES = { operations: [], }; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { // Define a type for your configuration, and parse it from the metafield /** * @type {{ * paymentMethodName: string * cartTotal: number * }} */ const configuration = input?.paymentCustomization?.metafield?.jsonValue ?? {}; if (!configuration.paymentMethodName || !configuration.cartTotal) { return NO_CHANGES; } const cartTotal = parseFloat(input.cart.cost.totalAmount.amount ?? "0.0"); // Use the configured cart total instead of a hardcoded value if (cartTotal < configuration.cartTotal) { console.error("Cart total is not high enough, no need to hide the payment method."); return NO_CHANGES; } // Use the configured payment method name instead of a hardcoded value const hidePaymentMethod = input.paymentMethods .find(method => method.name.includes(configuration.paymentMethodName)); if (!hidePaymentMethod) { return NO_CHANGES; } return { operations: [{ hide: { paymentMethodId: hidePaymentMethod.id } }] }; }; ``` *** ## Step 2: Populate the payment customization configuration metafield To populate the configuration metafield, you'll first use the [`paymentCustomizations`](https://shopify.dev/docs/api/admin-graphql/current/queries/paymentCustomizations) query to confirm the payment customization ID, and then use the [`metafieldsSet`](https://shopify.dev/docs/api/admin-graphql/current/mutations/metafieldsSet) mutation to populate the same metafield that you specified in the input query. 1. Open the [Shopify GraphiQL app](https://shopify-graphiql-app.shopifycloud.com/) on your dev store. 2. In the GraphiQL app, in the **API Version** field, select the **2023-07** version. 3. Execute the following query, and make note of the `id` value of the payment customization that you created in the [previous tutorial](https://shopify.dev/docs/apps/build/checkout/payments/create-payments-function). For more information about global IDs, refer to [Global IDs in Shopify APIs](https://shopify.dev/docs/api/usage/gids). ## payment-customization-query.graphql ```graphql query { paymentCustomizations(first: 100) { edges { node { id title } } } } ``` 4. Execute the following mutation and replace `YOUR_CUSTOMIZATION_ID_HERE` with the full global ID of your payment customization. The value of the metafield specifies that the function should hide **Cash on Delivery** when the cart total is above 150. ## metafield-set-mutation.graphql ```graphql mutation { metafieldsSet(metafields: [ { ownerId: "YOUR_CUSTOMIZATION_ID_HERE" namespace: "payment-customization" key: "function-configuration" value: "{ \"paymentMethodName\": \"Cash on Delivery\", \"cartTotal\": 150 }" type: "json" } ]) { metafields { id } userErrors { message } } } ``` You should receive a GraphQL response that includes the ID of the created metafield. If the response includes any messages under `userErrors`, then review the errors, check that your mutation and `ownerId` are correct, and try the request again. *** ## Step 3: Test the payment customization 1. Open your development store and build a cart with a total (including shipping and tax) under 150. The **Cash on Delivery** payment method should display in checkout. 2. Add additional items to your cart to raise the total over 150. Your payment function should now hide the **Cash on Delivery** payment option. 1) Open your terminal where `shopify app dev` is running, and review your function executions. When [testing functions on development stores](https://shopify.dev/docs/apps/build/functions/test-debug-functions#test-your-function-on-a-development-store), the output of `dev` includes executions of your functions, any debug logging you have added to them, and a link to a local file with the full function execution details. 2) In a new terminal window, use the Shopify CLI [`app function replay`](https://shopify.dev/docs/api/shopify-cli/app/app-function-replay) command to [replay a function execution locally](https://shopify.dev/docs/apps/build/functions/test-debug-functions#execute-the-function-locally-using-shopify-cli), and debug your function without the need to re-trigger the function execution on Shopify. ## Terminal ```terminal shopify app function replay ``` 1. Select the function execution from the top of the list. Press `q` to quit when you are finished debugging. ![Screenshot that shows function input and output in the Partner Dashboard](https://shopify.dev/assets/assets/images/apps/checkout/payment-customization-configuration-in-partners-CvYJR1t8.png) *** ## Next steps * Build a [payment customization user interface](https://shopify.dev/docs/apps/build/checkout/payments/build-ui) with App Bridge. * Learn how to use [variables](https://shopify.dev/docs/apps/build/functions/input-queries/use-variables-input-queries) in your input query. *** * [What you'll learn](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#what-youll-learn) * [Requirements](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#requirements) * [Step 1: Configure the function](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-1-configure-the-function) * [Step 2: Populate the payment customization configuration metafield](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-2-populate-the-payment-customization-configuration-metafield) * [Step 3: Test the payment customization](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#step-3-test-the-payment-customization) * [Next steps](https://shopify.dev/docs/apps/build/checkout/payments/add-configuration.md#next-steps)