Ürünleri etiketli müşterilere sınırlama

Kısıtlı etiketli ürünlerle yalnızca trade ya da wholesale gibi etiketi olan müşterilerin ödeme yapmasına izin veren Shopify doğrulama Function’ı.

Function API
Cart and Checkout Validation
Hedef (target)
cart.validations.generate.run
Plan
Herkese açık uygulamada her plan; özel uygulamada Shopify Plus
Uygulama yetkisi
write_validations
API sürümü
2026-10
Son kontrol

Kısaca

Bu doğrulama Function’ı, kısıtlı etiket taşıyan ürünlerle yalnızca giriş yapmış ve belirli bir etikete (örneğin trade) sahip müşterilerin ödeme yapmasına izin verir. Diğer müşteriler ürünlerin adını içeren bir mesaj görür. İki etiket listesi de input değişkenleriyle tek bir JSON metafield’dan gelir. Cart and Checkout Validation API’nin cart.validations.generate.run hedefini kullanır, sürüm 2026-10.

Nasıl kullanılır

  1. Shopify CLI 4.9.0 ya da üstüyle bir uygulama oluşturun ve uzantıyı ekleyin: shopify app generate extension --template cart_checkout_validation, dil olarak JavaScript.
  2. Oluşturulan shopify.extension.toml dosyasını ve src/ içindeki iki dosyayı aşağıdakilerle değiştirin; input_variables, sorguyu yapılandırma metafield’ına bağlar.
  3. Testleri çalıştırın: shopify app function run --input tests/guest-with-restricted-product.input.json.
  4. shopify app deploy ile deploy edin ve doğrulamayı validationCreate ile etkinleştirin.
  5. Doğrulamanın $app:function-configuration metafield’ını ayarlayın; örneğin {"customerTags": ["trade"], "productTags": ["trade-only"]}.

Kod

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

[[extensions]]
name = "Restrict products to tagged customers"
handle = "restrict-products-tagged-customers"
type = "function"

  [[extensions.targeting]]
  target = "cart.validations.generate.run"
  input_query = "src/cart_validations_generate_run.graphql"
  input_variables = { namespace = "$app", key = "function-configuration" }
  export = "cart-validations-generate-run"

  [extensions.build]
  command = ""
  path = "dist/function.wasm"
src/cart_validations_generate_run.graphql
query CartValidationsGenerateRunInput($customerTags: [String!], $productTags: [String!]) {
  buyerJourney {
    step
  }
  cart {
    buyerIdentity {
      customer {
        hasAnyTag(tags: $customerTags)
      }
    }
    lines {
      merchandise {
        __typename
        ... on ProductVariant {
          product {
            title
            hasAnyTag(tags: $productTags)
          }
        }
      }
    }
  }
  validation {
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
src/cart_validations_generate_run.js
// @ts-check

/**
 * @typedef {import("../generated/api").CartValidationsGenerateRunInput} CartValidationsGenerateRunInput
 * @typedef {import("../generated/api").CartValidationsGenerateRunResult} CartValidationsGenerateRunResult
 */

/**
 * Settings stored as JSON in the validation's metafield. The same metafield
 * feeds the query's $customerTags and $productTags variables (see
 * input_variables in shopify.extension.toml), so Shopify checks the tags.
 */
const DEFAULT_CONFIG = {
  // {products} is replaced with the restricted product titles.
  message: "{products} can only be ordered by approved trade customers. Please sign in or remove it from your cart.",
};

/** Errors about the cart as a whole show at the top of checkout and on the cart. */
const CART_TARGET = "$.cart";

/** @type {CartValidationsGenerateRunResult} */
const NO_ERRORS = { operations: [] };

/**
 * @param {CartValidationsGenerateRunInput} input
 * @returns {CartValidationsGenerateRunResult}
 */
export function cartValidationsGenerateRun(input) {
  // Let anyone build a cart (a trade customer may sign in at checkout); the rule applies from checkout on.
  if (input.buyerJourney?.step === "CART_INTERACTION") return NO_ERRORS;
  // Guests have no customer record, so they never carry the tag.
  if (input.cart.buyerIdentity?.customer?.hasAnyTag) return NO_ERRORS;

  const restricted = input.cart.lines
    .map((line) => line.merchandise)
    .filter((merchandise) => merchandise.__typename === "ProductVariant" && merchandise.product.hasAnyTag)
    .map((merchandise) => merchandise.product.title);
  if (restricted.length === 0) return NO_ERRORS;

  const config = { ...DEFAULT_CONFIG, ...input.validation?.metafield?.jsonValue };
  const products = [...new Set(restricted)].join(", ");
  return {
    operations: [{ validationAdd: { errors: [{ message: config.message.replace("{products}", products), target: CART_TARGET }] } }],
  };
}
Test girdisi ve beklenen çıktı
tests/guest-adding-to-cart.input.json
{
  "buyerJourney": {
    "step": "CART_INTERACTION"
  },
  "cart": {
    "buyerIdentity": {
      "customer": null
    },
    "lines": [
      {
        "merchandise": {
          "__typename": "ProductVariant",
          "product": {
            "title": "Pro Barista Grinder",
            "hasAnyTag": true
          }
        }
      },
      {
        "merchandise": {
          "__typename": "ProductVariant",
          "product": {
            "title": "Coffee Beans 1 kg",
            "hasAnyTag": false
          }
        }
      }
    ]
  },
  "validation": {
    "metafield": {
      "jsonValue": {
        "customerTags": [
          "trade"
        ],
        "productTags": [
          "trade-only"
        ]
      }
    }
  }
}
tests/guest-adding-to-cart.output.json
{
  "operations": []
}
tests/guest-with-restricted-product.input.json
{
  "cart": {
    "buyerIdentity": { "customer": null },
    "lines": [
      { "merchandise": { "__typename": "ProductVariant", "product": { "title": "Pro Barista Grinder", "hasAnyTag": true } } },
      { "merchandise": { "__typename": "ProductVariant", "product": { "title": "Coffee Beans 1 kg", "hasAnyTag": false } } }
    ]
  },
  "validation": {
    "metafield": { "jsonValue": { "customerTags": ["trade"], "productTags": ["trade-only"] } }
  }
}
tests/guest-with-restricted-product.output.json
{
  "operations": [
    {
      "validationAdd": {
        "errors": [
          {
            "message": "Pro Barista Grinder can only be ordered by approved trade customers. Please sign in or remove it from your cart.",
            "target": "$.cart"
          }
        ]
      }
    }
  ]
}
tests/no-restricted-products.input.json
{
  "cart": {
    "buyerIdentity": { "customer": { "hasAnyTag": false } },
    "lines": [
      { "merchandise": { "__typename": "ProductVariant", "product": { "title": "Coffee Beans 1 kg", "hasAnyTag": false } } }
    ]
  },
  "validation": {
    "metafield": { "jsonValue": { "customerTags": ["trade"], "productTags": ["trade-only"] } }
  }
}
tests/no-restricted-products.output.json
{ "operations": [] }
tests/tagged-customer.input.json
{
  "cart": {
    "buyerIdentity": { "customer": { "hasAnyTag": true } },
    "lines": [
      { "merchandise": { "__typename": "ProductVariant", "product": { "title": "Pro Barista Grinder", "hasAnyTag": true } } },
      { "merchandise": { "__typename": "ProductVariant", "product": { "title": "Coffee Beans 1 kg", "hasAnyTag": false } } }
    ]
  },
  "validation": {
    "metafield": { "jsonValue": { "customerTags": ["trade"], "productTags": ["trade-only"] } }
  }
}
tests/tagged-customer.output.json
{ "operations": [] }

Lintel tarafından yazıldı. MIT lisansı: kendi mağazanızda da müşteri işlerinizde de ücretsiz kullanabilirsiniz.

Sınırlar ve özel durumlar

  • Misafir müşterilerin müşteri kaydı olmadığından, giriş yapana kadar kısıtlı ürünleri satın alamazlar.
  • Function yalnızca checkout’u engeller. Ürünler görünür kalır ve sepete eklenebilir; tamamen gizlenmeleri gerekiyorsa temada gizleyin.
  • Metafield yoksa iki değişken de null olur ve hiçbir ürün kısıtlı sayılmaz.

Sık sorulan sorular

Shopify’da bazı ürünleri yalnızca onaylı müşterilere nasıl satarım?
Müşterileri ve ürünleri etiketleyin; ardından bu örnekteki gibi, etiketsiz bir müşterinin sepetinde etiketli ürün varken checkout’u engelleyen bir doğrulama Function’ı kullanın.
Müşteriler kısıtlı ürünleri görmeye devam eder mi?
Evet. Function ürün sayfalarında değil, sepet ve checkout’ta çalışır. Etiketi olmayan müşterilere fiyatı ya da satın al düğmesini gizlemek için temayı kullanın.
Etiketler neden kodda değil de input değişkenlerinde?
Etiketleri Shopify, hasAnyTag ile kontrol eder. Değişken sayesinde her mağaza ve her doğrulama, yeniden deploy etmeden etiketlerini kendi metafield’ında belirler.
  • Function API sürümü 2026-10 için yazıldı ve gösterilen tarihte Shopify’ın başvuru belgeleriyle karşılaştırıldı. Shopify her çeyrekte yeni bir sürüm yayımlar.
  • Function içeren özel (custom) uygulamalar yalnızca Shopify Plus mağazalarında çalışır. Aynı Function herkese açık bir uygulamada, sayfada aksi yazmıyorsa her planda çalışır.
  • Her örnek test verileriyle gelir ve yayımlamadan önce bu testleri çalıştırırız. Kodu değiştirdikten sonra testleri tekrar çalıştırın: shopify app function run.
  • MIT lisansı. Kodu kendi mağazanızda da müşteri işlerinizde de kullanabilirsiniz.

Mağazanız için kurulmasını, test edilmesini ve bakımını isterseniz: Shopify Functions geliştirme.

Geliştirmek istediğiniz bir şey mi var? Gelin, birlikte konuşalım.

Aynı anda sınırlı sayıda proje alıyoruz.

Gencer KarakayaShopify mühendisi

Proje özetinizi bir mühendis okur; size e-postayla yanıt veririz.