Rename and reorder payment methods at checkout

A Shopify payment customization Function that renames payment methods and changes their order at checkout from a JSON metafield. Tested JavaScript.

Function API
Payment Customization
Target
cart.payment-methods.transform.run
Plan
Any plan in a public app; Shopify Plus in a custom app
App scope
write_payment_customizations
API version
2026-10
Last checked

In short

This payment customization Function renames payment methods and puts them in the order you choose at checkout, for example a clearer name for bank transfer and cards first. Both come from a JSON metafield, so nothing is hardcoded. It uses the cart.payment-methods.transform.run target of the Payment Customization API, version 2026-10, with the paymentMethodRename and paymentMethodMove operations.

How to use it

  1. Create an app with the Shopify CLI, then add the extension: shopify app generate extension --template payment_customization, with JavaScript.
  2. Replace the generated shopify.extension.toml and the two files in src/ with the files below.
  3. Run the fixtures with shopify app function run --input tests/rename-and-reorder.input.json.
  4. Deploy with shopify app deploy and create the customization with paymentCustomizationCreate.
  5. Set the $app:function-configuration metafield, for example {"renames": {"Bank Deposit": "Bank transfer"}, "order": ["Credit card", "Bank Deposit"]}.

Code

shopify.extension.toml
api_version = "2026-10"

[[extensions]]
name = "Rename and reorder payment methods"
handle = "rename-reorder-payment-methods"
type = "function"

  [[extensions.targeting]]
  target = "cart.payment-methods.transform.run"
  input_query = "src/cart_payment_methods_transform_run.graphql"
  export = "cart-payment-methods-transform-run"

  [extensions.build]
  command = ""
  path = "dist/function.wasm"
src/cart_payment_methods_transform_run.graphql
query CartPaymentMethodsTransformRunInput {
  paymentMethods {
    id
    name
  }
  paymentCustomization {
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
src/cart_payment_methods_transform_run.js
// @ts-check

/**
 * @typedef {import("../generated/api").CartPaymentMethodsTransformRunInput} CartPaymentMethodsTransformRunInput
 * @typedef {import("../generated/api").CartPaymentMethodsTransformRunResult} CartPaymentMethodsTransformRunResult
 */

/**
 * Settings stored as JSON in the customization's metafield. Both are empty by
 * default, so checkout stays unchanged until the merchant configures them.
 */
const DEFAULT_CONFIG = {
  // Current name → name shown at checkout, matched case-insensitively.
  renames: /** @type {Record<string, string>} */ ({}),
  // Names in the order they should appear; unlisted methods keep their place after them.
  order: /** @type {string[]} */ ([]),
};

/**
 * @param {CartPaymentMethodsTransformRunInput} input
 * @returns {CartPaymentMethodsTransformRunResult}
 */
export function cartPaymentMethodsTransformRun(input) {
  const config = { ...DEFAULT_CONFIG, ...input.paymentCustomization?.metafield?.jsonValue };
  const renames = new Map(Object.entries(config.renames).map(([from, to]) => [from.toLowerCase(), to]));
  /** @param {string} name */
  const findMethod = (name) =>
    input.paymentMethods.find((method) => method.name.toLowerCase() === name.toLowerCase());

  // Rename operations refer to the method's ID, so they're independent of the order below.
  const renameOperations = input.paymentMethods
    .filter((method) => renames.has(method.name.toLowerCase()))
    .map((method) => ({
      paymentMethodRename: { paymentMethodId: method.id, name: renames.get(method.name.toLowerCase()) ?? method.name },
    }));

  // Index counts only methods present in this checkout, so a configured method
  // that isn't offered (another market, an inactive gateway) leaves no gap.
  const moveOperations = config.order
    .map(findMethod)
    .filter((method) => method != null)
    .map((method, index) => ({ paymentMethodMove: { paymentMethodId: method.id, index } }));

  return { operations: [...renameOperations, ...moveOperations] };
}
Test input and expected output
tests/missing-method-skipped.input.json
{
  "paymentMethods": [
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Credit card" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "PayPal" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/3", "name": "Bank Deposit" }
  ],
  "paymentCustomization": {
    "metafield": { "jsonValue": { "order": ["Cash on Delivery (COD)", "PayPal"] } }
  }
}
tests/missing-method-skipped.output.json
{
  "operations": [
    { "paymentMethodMove": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/2", "index": 0 } }
  ]
}
tests/not-configured.input.json
{
  "paymentMethods": [
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Credit card" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "PayPal" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/3", "name": "Bank Deposit" }
  ],
  "paymentCustomization": { "metafield": null }
}
tests/not-configured.output.json
{ "operations": [] }
tests/rename-and-reorder.input.json
{
  "paymentMethods": [
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Credit card" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "PayPal" },
    { "id": "gid://shopify/PaymentCustomizationPaymentMethod/3", "name": "Bank Deposit" }
  ],
  "paymentCustomization": {
    "metafield": {
      "jsonValue": {
        "renames": { "bank deposit": "Bank transfer (2% off, ships after payment)" },
        "order": ["Bank Deposit", "Credit card"]
      }
    }
  }
}
tests/rename-and-reorder.output.json
{
  "operations": [
    {
      "paymentMethodRename": {
        "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/3",
        "name": "Bank transfer (2% off, ships after payment)"
      }
    },
    { "paymentMethodMove": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/3", "index": 0 } },
    { "paymentMethodMove": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/1", "index": 1 } }
  ]
}

Written by Lintel. MIT license: free to use in your own store and in client work.

Limits and edge cases

  • Methods whose name is a logo can’t be renamed: Shop Pay, Apple Pay, Google Pay, other wallets and the gift card field.
  • Wallets can be hidden but not reordered; move operations for them have no effect.
  • Names are matched as the customer sees them at checkout, ignoring case. A method listed in the configuration but not offered in this checkout is skipped.
  • Inside Shop Pay, payment customizations only affect the gift card field.

Questions

How do I change the order of payment methods in Shopify checkout?
The payment settings don’t let you sort methods. A payment customization Function returns paymentMethodMove operations with the position for each method, which is what this example does.
Can I rename Cash on Delivery or bank transfer?
Yes. Manual methods such as Cash on Delivery and bank deposit can be renamed with paymentMethodRename. Wallets and logo-only methods like Shop Pay can’t.
Can the new name change by language?
This example uses one name. Add localization { language { isoCode } } to the input query and keep one renames map per language in the metafield if you sell in several languages.
  • Written for Function API version 2026-10 and checked against Shopify’s reference on the date shown. Shopify releases a new version every quarter.
  • Custom apps that contain Functions only run on Shopify Plus stores; a public app with the same Function runs on any plan unless the page says otherwise.
  • Every example ships with test fixtures, and we run them before publishing. Run them again after you change the code: shopify app function run.
  • MIT license. Use the code in your own store or in client work.

Need this built, tested and maintained for your store? See Shopify Functions development.

Have something to build? Let’s talk it through.

Taking a limited number of projects at a time. Every brief is read by an engineer, not a salesperson.

Gencer KarakayaShopify Engineer

An engineer reads your brief, and we reply by email.