Siparişler ve Teklifler

Teklif talepleri

Bir sepeti B2B teklif talebi (RFQ) olarak gönderme, teklif durum akışı ve müşteriye dönük teklif yapısı.

Markdown olarak görüntüle

Teklif (RFQ) akışı, alıcının doğrudan ödeme yapmak yerine sepeti fiyatlandırılmış bir teklif için satıcıya göndermesini sağlar. Yalnızca mağazada sales modülü etkin ve ayarlarda teklif talepleri açık olduğunda kullanılabilir; bunlardan biri eksik olan bir mağazada endpoint 404 döner. Bazı sepetler bu akıştan geçmek zorundadır: bir teklif tetikleyicisi devreye girdiğinde checkout 409 QUOTE_REQUIRED ile reddeder ve sepetin requires_quote alanı true olur.

Teklif durum akışı

DurumAnlamı
submittedBu endpoint tarafından oluşturuldu. Sepet dönüştürülür; satıcı talebi görür.
offeredSatıcı satırları fiyatlandırdı ve teklifi gönderdi. Toplamlar dondurulur ve 48 karakterlik bir kabul token'ı alıcıya e-postayla gönderilir (sunucu tarafında yalnızca SHA-256 hash'i saklanır).
acceptedAlıcı token üzerinden kabul etti. Teklif edilen fiyatlarla bir sipariş oluşturuldu; converted_order_id ayarlanır. Terminal.
declinedAlıcı token üzerinden reddetti. Satıcı yine de yeniden teklif verebilir veya iptal edebilir.
expiredvalid_until tarihi geçmiş, teklif verilmiş bir teklif, bir sonraki token veya satıcı dokunuşunda tembel (lazy) olarak expired durumuna geçer.
cancelledSatıcı tarafından iptal edildi (submitted, offered, declined, expired durumlarından mümkündür). Terminal.

Kabul ve ret, token endpoint'leri üzerinden yapılır (Teklif token'ları sayfasına bakın).

Teklif tetikleyicileri

Mağazada sales modülü etkin ve teklif talepleri açıkken, aşağıdaki koşullar bir sepeti RFQ akışına zorlar. Aksi halde hiçbir tetikleyici devreye girmez, requires_quote false kalır ve checkout hiçbir zaman QUOTE_REQUIRED ile reddetmez. Bu koşullar her sepet response'unda requires_quote ve quote_reasons olarak canlı şekilde görünür:

NedenNe zaman tetiklenir
cart_thresholdSepet toplamı yapılandırılmış eşik tutarına ulaştığında veya adet toplamı eşik adedine ulaştığında.
configurator_choiceSeçilen bir configurator seçenek tercihi teklif gerektirecek şekilde işaretlendiğinde.
customer_groupMüşterinin kişi grubu her zaman teklif olarak ayarlandığında (grup üzerinde bir sales ayarı).
product_purchasabilitySepetteki bir ürünün purchasability değeri quote olduğunda. Böyle bir ürün yine de sepete eklenebilir; sepet bu nedeni bildirir ve checkout yerine bu endpoint'ten geçer.

Hiçbir tetikleyiciyi devreye sokmayan sepetler için de teklif talebi gönderilebilir; alıcı her zaman bir teklif isteyebilir.

Müşteriye dönük teklif yapısı

Teklif talebi endpoint'i ve iki token okuma endpoint'i aynı zarfı döner: {"data": {...quote...}}. Bu, müşteriye dönük serileştirmedir: merchant_note, cart_id, customer_id ve contact_id asla bulunmaz; kabul token'ı (veya hash'i) asla açığa çıkarılmaz. Teklif nesnesi ayrıca origin (burada oluşturulan bir teklif için "storefront"), intro, terms, signed_at, acceptance_mode, pdf_url ve signed_pdf_url alanlarını taşır; bunlar Teklif token'ları sayfasında belgelenmiştir. Sepet ve sipariş response'larının aksine, tüm teklif tutarları money nesnesi değil, minör birim cinsinden düz tamsayılardır. Tarihler ISO-8601 dizeleridir.

AdTürZorunluAçıklama
idstring (UUID)her zamanTeklif id'si.
numberstringher zamanKullanıcıya gösterilen teklif numarası; "Q-" artı mağaza başına bir tamsayı, örneğin "Q-17".
statusstringher zamanYukarıdaki durum tablosuna bakın.
emailstringher zamanTalep sahibinin e-postası.
namestringher zamanTalep sahibinin adı.
phonestring or nullher zamanTalep sahibinin telefonu.
billing_addressobject or nullher zamanGönderildiği haliyle.
shipping_addressobject or nullher zamanGönderildiği haliyle.
currencystringher zamanEUR.
customer_notestring or nullher zamanAlıcının talepteki notu.
message_to_customerstring or nullher zamanSatıcının mesajı; teklif verilirken ayarlanır.
valid_untilstring (ISO-8601) or nullher zamanTeklifin geçerlilik sonu; teklif verilirken ayarlanır.
subtotal_amountinteger or nullher zamanTeklif anında dondurulur; submitted durumundayken null. Minör birim.
tax_total_amountinteger or nullher zamanTeklif anında dondurulur; submitted durumundayken null.
shipping_total_amountinteger or nullher zamanTeklif anında dondurulur; submitted durumundayken null.
total_amountinteger or nullher zamanTeklif anında dondurulur; submitted durumundayken null.
tax_breakdownarray or nullher zamanOran bazında döküm {"rate": float, "base": int, "tax": int}; submitted durumundayken null.
converted_order_idstring (UUID) or nullher zamanKabul üzerine oluşturulan sipariş, aksi halde null.
submitted_atstring (ISO-8601) or nullher zamanTalebin gönderildiği zaman.
offered_atstring (ISO-8601) or nullher zamanSatıcının teklifi gönderdiği zaman.
accepted_atstring (ISO-8601) or nullher zamanAlıcının kabul ettiği zaman.
declined_atstring (ISO-8601) or nullher zamanAlıcının reddettiği zaman.
cancelled_atstring (ISO-8601) or nullher zamanSatıcının iptal ettiği zaman.
created_atstring (ISO-8601) or nullher zamanSatırın oluşturulma zamanı.
updated_atstring (ISO-8601) or nullher zamanSon güncelleme zamanı.
linesarrayher zamanTeklif satırları, aşağıya bakın.

lines[]

AdTürZorunluAçıklama
idstring (UUID)her zamanTeklif satırı id'si.
product_idstring (UUID) or nullher zamanÜrün; satıcının eklediği özel satırlarda null.
variant_idstring (UUID) or nullher zamanVarsa varyant.
is_custombooleanher zamanSatıcının eklediği özel satırlarda true.
titlestringher zamanSatır başlığı (ürün adı snapshot'ı veya özel satırlarda serbest metin).
skustring or nullher zamanSKU snapshot'ı.
quantitynumberher zamanSepet satırından kopyalanan miktar: piece olarak satılan bir ürün için tam sayı, diğer tüm birimler için en fazla üç ondalık basamak (örneğin 0.75). Satıcının hizmet ve malzeme satırları da ondalıklı olabilir.
indicative_unit_priceinteger or nullher zamanGönderim anında talep sahibinin fiyatlandırma bağlamından snapshot'lanan birim fiyat (minör birim).
quoted_unit_priceinteger or nullher zamanSatıcının teklif ettiği birim fiyat; fiyatlandırılana kadar null.
tax_class_idstring (UUID) or nullher zamanVarsa satıcının atadığı vergi sınıfı.
attributesobject or nullher zamanYapılandırılabilir satırlarda yapılandırma snapshot'ı, aksi halde null.
sort_orderintegerher zamanGörüntüleme sırası.
kindstringher zamanSepetten oluşturulan satırlar için product; satıcı satırları service, material veya other olabilir.
unitstring or nullher zamanBirim etiketi. Sepetten oluşturulan bir satır, piece olarak satılan ürün için pcs, aksi halde ürünün birimini (kg, m2, hour, ...) taşır.
descriptionstring or nullher zamanSatır düzeyinde isteğe bağlı not.
tax_ratestring or nullher zamanSatıcı teklifi gönderdiğinde kaydedilen ondalık vergi oranı (örneğin "21.00"); o zamana kadar null.
unit_priceinteger or nullher zamanquoted_unit_price alanının takma adı.

POST /v1/api/commerce/carts/{id}/quote-request

Sepeti bir teklif talebi olarak gönderir. commerce:write scope'unu ve active, boş olmayan bir sepet gerektirir; sepet sahipliği denetiminden geçer. Başarı durumunda sepet converted olur, her sepet satırı talep sahibinin fiyatlandırma bağlamından gelen gösterge niteliğinde bir birim fiyatla snapshot'lanarak submitted durumunda bir teklif oluşturulur, talep sahibine alındı onayı e-postası kuyruğa alınır ve müşteriye dönük teklif 201 durum koduyla döner.

İletişim bilgisi çözümlemesi: email ve name toplamda zorunludur, ancak her biri ya request body'den ya da geçerli bir X-Customer-Token gönderildiğinde müşterinin profilinden gelebilir. Geçerli bir token teklifi müşteriye de bağlar.

Parametreler

Path

AdTürZorunluAçıklama
idstring (UUID)evetSepet id'si. active ve boş olmayan bir sepet olmalıdır.

Body

Tüm body alanları isteğe bağlı ve nullable'dır; müşteri profili sağlayamadığında email ve name zorunlu hale gelir.

AdTürZorunluAçıklama
emailstringkoşulluGeçerli e-posta. Token'lı müşterinin profil e-postasına geri düşer.
namestringkoşulluTalep sahibinin adı. Müşteri profilindeki ada geri düşer.
phonestringhayırİletişim telefonu.
billing_addressobjecthayırSerbest biçimli adres nesnesi.
shipping_addressobjecthayırSerbest biçimli adres nesnesi.
customer_notestringhayırSatıcıya mesaj.

Headers

AdTürZorunluAçıklama
X-Customer-TokenstringhayırVarsa çözümlenmesi gerekir, aksi halde 401. Teklifi müşteriye bağlar, iletişim bilgisi geri düşüşlerini sağlar ve gösterge fiyatlar için fiyatlandırma bağlamını seçer.
Request
curl -X POST https://api.borkol.com/v1/api/commerce/carts/9d3f2a10-6b7c-4e6d-9f21-8a54c1d2e3f4/quote-request \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "buyer@corp.example",
    "name": "Bob Buyer",
    "phone": "+31 6 12345678",
    "customer_note": "Need delivery before October.",
    "billing_address": {
      "name": "Corp BV",
      "street": "Main St 1",
      "postal_code": "1000 AA",
      "city": "Amsterdam",
      "country": "NL"
    }
  }'
Request (JS)
const res = await fetch(
  `https://api.borkol.com/v1/api/commerce/carts/${cartId}/quote-request`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      email: "buyer@corp.example",
      name: "Bob Buyer",
      customer_note: "Need delivery before October.",
    }),
  }
);
const { data: quote } = await res.json();
Response
{
  "data": {
    "id": "6e7f8a9b-0c1d-4e2f-a3b4-c5d6e7f8a9b0",
    "number": "Q-17",
    "status": "submitted",
    "email": "buyer@corp.example",
    "name": "Bob Buyer",
    "phone": "+31 6 12345678",
    "billing_address": {
      "name": "Corp BV",
      "street": "Main St 1",
      "postal_code": "1000 AA",
      "city": "Amsterdam",
      "country": "NL"
    },
    "shipping_address": null,
    "currency": "EUR",
    "customer_note": "Need delivery before October.",
    "message_to_customer": null,
    "valid_until": null,
    "subtotal_amount": null,
    "tax_total_amount": null,
    "shipping_total_amount": null,
    "total_amount": null,
    "tax_breakdown": null,
    "converted_order_id": null,
    "submitted_at": "2026-08-14T10:15:30+00:00",
    "offered_at": null,
    "accepted_at": null,
    "declined_at": null,
    "cancelled_at": null,
    "created_at": "2026-08-14T10:15:30+00:00",
    "updated_at": "2026-08-14T10:15:30+00:00",
    "lines": [
      {
        "id": "9b0c1d2e-3f4a-4b5c-d6e7-f8a9b0c1d2e3",
        "product_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
        "variant_id": null,
        "is_custom": false,
        "title": "Garden Chair",
        "sku": "CHAIR-GRN",
        "quantity": 25,
        "indicative_unit_price": 2250,
        "quoted_unit_price": null,
        "tax_class_id": null,
        "attributes": null,
        "sort_order": 0,
        "kind": "product",
        "unit": "pcs",
        "description": null,
        "tax_rate": null,
        "unit_price": null
      }
    ],
    "origin": "storefront",
    "intro": null,
    "terms": null,
    "signed_at": null,
    "acceptance_mode": "click",
    "pdf_url": null,
    "signed_pdf_url": null
  }
}

Gönderimden sonra teklifi id ile sorgulayacak bir storefront endpoint'i yoktur: alıcı teklifi e-postayla alır ve oradan itibaren token endpoint'leri devreye girer.

Hatalar

DurumGövdeNe zaman
404{"error": "Module not enabled: sales"}Mağazada sales modülü etkin değil. error anahtarına ve 404 durumuna dikkat edin (diğer modül kapıları 403 döner).
404{"message": "..."}Mağaza için teklif talepleri kapalı, ya da sepet bilinmiyor, süresi dolmuş veya active değil.
401{"message": "Invalid customer token."}X-Customer-Token mevcut ancak çözümlenemiyor.
422{"message": "...", "errors": {"email": ["Email is required."], "name": ["Name is required."]}}Ne body ne de müşteri profili email veya name sağlıyor.
422{"message": "...", "errors": {"cart": ["Cart is empty."]}}Sepette satır yok.
403sahiplik, scope, commerce modülü veya askıya alma gövdesiSepetler sayfasına bakın.
429{"message": "Too Many Attempts."}Rate limit aşıldı.