# Ü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’ı.

Sayfa: https://lintelagency.com/tr/kaynaklar/shopify-functions/urunleri-etiketli-musterilere-sinirlama

## 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.

## Bilgiler

- **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:** 2 Kasım 2026

## 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

```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

```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

```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

```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

```json
{
  "operations": []
}
```

### tests/guest-with-restricted-product.input.json

```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

```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

```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

```json
{ "operations": [] }
```

### tests/tagged-customer.input.json

```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

```json
{ "operations": [] }
```

## 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.

## 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.

## Shopify belgeleri

- [Cart and Checkout Validation Function API](https://shopify.dev/docs/api/functions/latest/cart-and-checkout-validation)
- [Variables in input queries](https://shopify.dev/docs/apps/build/functions/input-queries/use-variables-input-queries)

## İlgili içerikler

- [Teslimat, ödeme ya da doğrulama Function’ını etkinleştirme](https://lintelagency.com/tr/kaynaklar/graphql/function-ozellestirmesini-etkinlestirme)
- [Kurallarınıza uymayan Shopify siparişlerini Cart and Checkout Validation ile durdurun](https://lintelagency.com/tr/blog/shopify-odeme-dogrulama)
- [Shopify’da B2B: plana ve özelliğe göre seçenekler](https://lintelagency.com/tr/blog/shopify-b2b-secenekleri)
- [Function’ları metafield (meta alan) ile yapılandırmak: kuralı yeni sürüm yayınlamadan değiştirin](https://lintelagency.com/tr/blog/shopify-functions-metafield-ile-yapilandirma)

Lisans: MIT. Kendi mağazanızda da müşteri işlerinizde de ücretsiz kullanabilirsiniz.
