Alışverişe hediye ürün

Sepetin geri kalanı bir eşiğe ulaşınca bir hediye ürünü bedava yapan Shopify indirim Function’ı. Hediye ürün sepette olmalıdır.

Function API
Discount
Hedef (target)
cart.lines.discounts.generate.run
İndirim sınıfı
PRODUCT
Plan
Herkese açık uygulamada her plan; özel uygulamada Shopify Plus
Uygulama yetkisi
write_discounts
API sürümü
2026-10
Son kontrol

Kısaca

Bu indirim Function’ı, sepetin geri kalanı bir eşiğe (varsayılan olarak mağaza para biriminde 75) ulaşınca hediye ürünün bir adedini bedava yapar. Hediyeyi sepete ekleyemez: indirim Function’ı yalnızca sepette olan satırlara indirim uygular; ekleme işini tema, Storefront API ya da bir cart transform yapmalıdır. cart.lines.discounts.generate.run hedefini kullanır, Discount Function API 2026-10.

Nasıl kullanılır

  1. Hediyeyi bir ürün varyantı olarak oluşturun ve koleksiyonlarınızın dışında tutun.
  2. Shopify CLI ile bir uygulama oluşturun, uzantıyı ekleyin (shopify app generate extension --template discount) ve dosyalarını aşağıdakilerle değiştirin.
  3. Testleri shopify app function run --input tests/threshold-met.input.json ile çalıştırın, ardından deploy edin.
  4. discountAutomaticAppCreate ile discountClasses: [PRODUCT] olan otomatik bir indirim oluşturun ve metafield’ına {"giftVariantId": "gid://shopify/ProductVariant/1234567890", "minimumAmount": 75} yazın.
  5. Eşik aşıldığında hediyeyi temadan sepete ekleyin (örneğin /cart/add.js ile), sepet eşiğin altına düşünce çıkarın.

Kod

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

[[extensions]]
name = "Free gift with purchase"
handle = "free-gift-with-purchase"
type = "function"

  [[extensions.targeting]]
  target = "cart.lines.discounts.generate.run"
  input_query = "src/cart_lines_discounts_generate_run.graphql"
  export = "cart-lines-discounts-generate-run"

  [extensions.build]
  command = ""
  path = "dist/function.wasm"
src/cart_lines_discounts_generate_run.graphql
query CartLinesDiscountsGenerateRunInput {
  cart {
    lines {
      id
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        __typename
        ... on ProductVariant {
          id
        }
      }
    }
  }
  presentmentCurrencyRate
  discount {
    discountClasses
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
src/cart_lines_discounts_generate_run.js
// @ts-check

/**
 * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunInput} CartLinesDiscountsGenerateRunInput
 * @typedef {import("../generated/api").CartLinesDiscountsGenerateRunResult} CartLinesDiscountsGenerateRunResult
 */

/**
 * Settings stored as JSON in the discount's metafield. A discount Function
 * can't add lines to the cart: the gift has to be added by the theme, the
 * Storefront API or a cart transform. This Function only makes it free.
 */
const DEFAULT_CONFIG = {
  giftVariantId: /** @type {string | null} */ (null),
  // Spend on other products, in the store's currency, that unlocks the gift.
  minimumAmount: 75,
  message: "Free gift",
};

/** @type {CartLinesDiscountsGenerateRunResult} */
const NO_DISCOUNT = { operations: [] };

/**
 * @param {CartLinesDiscountsGenerateRunInput} input
 * @returns {CartLinesDiscountsGenerateRunResult}
 */
export function cartLinesDiscountsGenerateRun(input) {
  // A Function may only return operations for the classes the discount was created with.
  if (!input.discount.discountClasses.includes("PRODUCT")) return NO_DISCOUNT;
  const config = { ...DEFAULT_CONFIG, ...input.discount.metafield?.jsonValue };
  if (!config.giftVariantId) return NO_DISCOUNT;

  /** @param {(typeof input.cart.lines)[number]} line */
  const isGift = (line) => line.merchandise.__typename === "ProductVariant" && line.merchandise.id === config.giftVariantId;
  const giftLine = input.cart.lines.find(isGift);
  if (!giftLine) return NO_DISCOUNT;

  // The gift itself doesn't count towards the threshold.
  const spend = input.cart.lines
    .filter((line) => !isGift(line))
    .reduce((sum, line) => sum + Number(line.cost.subtotalAmount.amount), 0);
  if (spend < config.minimumAmount * Number(input.presentmentCurrencyRate)) return NO_DISCOUNT;

  // Only one unit is free; extra units of the gift are charged normally.
  return {
    operations: [
      {
        productDiscountsAdd: {
          candidates: [
            {
              message: config.message,
              targets: [{ cartLine: { id: giftLine.id, quantity: 1 } }],
              value: { percentage: { value: "100" } },
            },
          ],
          selectionStrategy: "FIRST",
        },
      },
    ],
  };
}
Test girdisi ve beklenen çıktı
tests/below-threshold.input.json
{
  "cart": {
    "lines": [
      { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "60.0" } }, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1111111111" } },
      { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "15.0" } }, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1234567890" } }
    ]
  },
  "presentmentCurrencyRate": "1.0",
  "discount": {
    "discountClasses": ["PRODUCT"],
    "metafield": { "jsonValue": { "giftVariantId": "gid://shopify/ProductVariant/1234567890", "minimumAmount": 75, "message": "Free tote bag" } }
  }
}
tests/below-threshold.output.json
{ "operations": [] }
tests/gift-not-in-cart.input.json
{
  "cart": {
    "lines": [
      { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "120.0" } }, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1111111111" } }
    ]
  },
  "presentmentCurrencyRate": "1.0",
  "discount": {
    "discountClasses": ["PRODUCT"],
    "metafield": { "jsonValue": { "giftVariantId": "gid://shopify/ProductVariant/1234567890", "minimumAmount": 75, "message": "Free tote bag" } }
  }
}
tests/gift-not-in-cart.output.json
{ "operations": [] }
tests/threshold-met.input.json
{
  "cart": {
    "lines": [
      { "id": "gid://shopify/CartLine/1", "cost": { "subtotalAmount": { "amount": "80.0" } }, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1111111111" } },
      { "id": "gid://shopify/CartLine/2", "cost": { "subtotalAmount": { "amount": "15.0" } }, "merchandise": { "__typename": "ProductVariant", "id": "gid://shopify/ProductVariant/1234567890" } }
    ]
  },
  "presentmentCurrencyRate": "1.0",
  "discount": {
    "discountClasses": ["PRODUCT"],
    "metafield": { "jsonValue": { "giftVariantId": "gid://shopify/ProductVariant/1234567890", "minimumAmount": 75, "message": "Free tote bag" } }
  }
}
tests/threshold-met.output.json
{
  "operations": [
    {
      "productDiscountsAdd": {
        "candidates": [
          {
            "message": "Free tote bag",
            "targets": [{ "cartLine": { "id": "gid://shopify/CartLine/2", "quantity": 1 } }],
            "value": { "percentage": { "value": "100" } }
          }
        ],
        "selectionStrategy": "FIRST"
      }
    }
  ]
}

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

  • İndirim Function’ları sepet satırı oluşturamaz. Hediyeyi ekleyen bir tema ya da uygulama adımı yoksa hiçbir şey olmaz.
  • Yalnızca bir adet bedavadır; hediyenin fazladan adetleri normal fiyattan ücretlendirilir.
  • Hediyenin kendisi eşiğe sayılmaz; yani hediye, kampanyayı kendi tutarıyla tetikleyemez.

Sık sorulan sorular

Bir Shopify Function sepete bedava ürün ekleyebilir mi?
İndirim Function’ı ekleyemez; yalnızca mevcut satırlara indirim uygular. Bir cart transform, satın alınan ürünü ek bileşen satırlarına açabilir; tema ya da Storefront API de hediyeyi ayrı bir satır olarak ekleyebilir.
Hediye eklendikten sonra müşteri ürün çıkarırsa ne olur?
Function yeniden çalışır ve hediyedeki indirimi kaldırır; hediye ücretlendirilir. Sürpriz olmasın diye temada hediyeyi çıkarın ya da müşteriye haber verin.
Hediyeyi sipariş başına bir adetle sınırlayabilir miyim?
Evet, bu kod zaten bir adede indirim verir. Fazladan adetleri engellemek için temada da adedi sınırlayın.
  • 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.