ไล่ดูและค้นหาสินค้า
/api/v1/productscatalog:readไล่ดูและค้นหาสินค้าในแคตตาล็อก ทีละ platform · สามเรื่องที่ต้องรู้ก่อนเขียนโค้ด: (1) platform เป็นพารามิเตอร์เดียวที่บังคับ และขอทุก platform พร้อมกันในคำขอเดียวไม่ได้ ถ้าจะดึงทั้งแคตตาล็อกต้องวนทีละ platform · (2) สินค้าทุกชิ้นมี type ซึ่งเป็นตัวบอกว่าคุณจะสั่งซื้อมันยังไง — cdkey (คีย์เกม) กับ account1 (บัญชีใหม่ที่ยังไม่เคยใช้) สั่งด้วย product_id อย่างเดียว ส่วน account2 (บัญชีมือสอง) ต้องไปเลือกบัญชีรายชิ้นมาก่อนแล้วส่ง item_id มาด้วย · (3) ราคาที่ได้จากหน้านี้เป็นเงินบาทและหักส่วนลดของคีย์คุณมาแล้ว แต่ยังไม่ใช่ราคาสุดท้าย เพราะหน้านี้ข้ามการคิดราคาขั้นต่ำรายสินค้า ราคาที่ได้จึงอาจต่ำกว่าราคาจริงเล็กน้อย — ก่อนสั่งซื้อต้องไปอ่านราคาจากเอนด์พอยต์รายชิ้นเสมอ
available ที่ไม่มีค่า แปลว่ายังไม่ได้ตรวจ ไม่ใช่ของหมด
ฟิลด์นี้มีได้แค่ false (ยืนยันแล้วว่าหมด) กับ null (ยังไม่ได้ตรวจกับต้นทาง) ไม่มีค่า true — ให้ถือว่าเป็นค่าที่ไม่มีอยู่ ไม่ใช่ค่าที่รอให้มันโผล่มา · เอนด์พอยต์แคตตาล็อกตอบจากข้อมูลที่ร้านมีอยู่แล้ว ไม่ได้ไปถามต้นทางทุกครั้งที่เรียก สินค้าส่วนใหญ่จึงกลับมาเป็น null ซึ่งเป็นความจริง คือรอบนี้ไม่มีใครไปตรวจสต็อกของสินค้าชิ้นนั้น · ห้ามเทียบ available === true และห้ามเขียนเงื่อนไขทำนองว่าไม่ใช่ true คือของหมด — อ่านแบบนั้นคือซ่อนแคตตาล็อกเกือบทั้งก้อน ซึ่งเป็นสินค้าที่ขายได้จริง ออกจากหน้าร้านของคุณเอง · ให้ซ่อนเฉพาะชิ้นที่เป็น false · สต็อกจริงถูกตรวจกับต้นทางอีกครั้ง ตอนสั่งซื้อ ซึ่งเป็นจุดเดียวที่คำตอบเรื่องของมีหรือไม่มีเชื่อถือได้
ราคาที่ได้กลับมาหักส่วนลดแล้ว ห้ามหักซ้ำอีกรอบ
price คือจำนวนบาทที่ คีย์ใบนี้ต้องจ่าย ไม่ใช่ราคาป้ายของร้าน และ discount_percent บอกว่าหักออกจากราคาป้ายไปแล้วกี่เปอร์เซ็นต์ · เอาส่วนลดของคุณไปหักซ้ำอีกรอบจะได้ราคาที่ระบบไม่มีวันยอมรับ แล้วทุกคำสั่งซื้อจะถูกปฏิเสธเรื่องราคาโดยไม่มีอะไรบอกว่าทำไม · ส่วนลดของคุณตั้งไว้เป็นตัวเลขเดียว แต่ สินค้าแต่ละประเภทมีเพดานของตัวเอง ส่วนลดที่ได้จริงคือค่าที่น้อยกว่าระหว่างขั้นส่วนลดของคุณกับเพดานของประเภทนั้น และคุณอ่านค่าที่คิดเสร็จแล้วรายประเภทได้จาก GET /api/v1/me ที่ฟิลด์ discount.effective_percent — อย่าคำนวณราคาฝั่งคุณเองจากเลขส่วนลดดิบ · ราคายังขยับตลอดเวลาทั้งตามราคาขั้นต่ำที่เรารับได้และตามการตั้งราคาของร้านเอง จะเก็บไว้นานแค่ไหนก็ได้ แต่ต้องอ่านราคาใหม่ทุกครั้งก่อนสั่งซื้อ
ราคาจากลิสต์นี้ยังไม่ใช่ราคาสุดท้าย ต้องอ่านราคาจากเอนด์พอยต์รายชิ้นก่อนสั่ง
นอกจากเพดานส่วนลดรายประเภทแล้ว สินค้าแต่ละชิ้นยังมี ราคาขั้นต่ำ ของตัวเองอีกชั้น คือถ้าหักส่วนลดของคุณแล้วราคาจะต่ำกว่าที่เรารับได้ ระบบจะดันราคากลับขึ้นมาที่ขั้นต่ำนั้น (แต่ไม่เกินราคาป้าย) แล้วรายงาน discount_percent เป็นส่วนลดที่คุณได้จริง ซึ่งน้อยกว่าเพดาน · ⚠️ GET /api/v1/products ไม่ได้คิดราคาขั้นต่ำนี้ให้ ราคาที่ได้จากลิสต์จึงเป็นค่าประมาณ และ ต่ำกว่า ราคาจริงที่ระบบยอมรับได้ · ก่อนสั่งซื้อให้อ่านราคาจริงจาก GET /api/v1/products/{product_id} สำหรับ cdkey และ account1 หรือจาก GET /api/v1/products/{product_id}/items สำหรับ account2 แล้วส่งค่านั้นเป็น expected_price · ราคาที่ไม่ตรงกับที่ระบบคำนวณได้ตอนสั่งจะถูก ปฏิเสธ ไม่ใช่ถูกปรับให้เงียบ ๆ
การค้นหาไม่มีหน้าสอง — ส่ง search คู่กับ cursor จะถูกปฏิเสธ
การค้นแบบ ขึ้นต้นด้วย ใช้ตัวชี้ตำแหน่งควบคู่กับการเรียงลำดับด้วยฟิลด์อื่นไม่ได้ การค้นหาจึงไล่หน้าไม่ได้เลย · ส่ง search กับ cursor มาในคำขอเดียวกันจะถูกปฏิเสธด้วย 400 cursor_with_search ไม่ใช่ถูกตอบ ซึ่งตั้งใจ เพราะพฤติกรรมเดิมที่มองข้าม cursor ไปเงียบ ๆ ทำให้ลูป sync วนอยู่กับชุดเดิมไม่รู้จบ โดยไม่มีสถานะ error และไม่มีฟิลด์ไหนบอกว่าเกิดอะไรขึ้น · การค้นหาจึงคืนหน้าเดียว คือทุกอย่างที่แมตช์ ไปจนถึงเพดานที่ search_capped รายงาน · ถ้าชนเพดานนั้น ให้ค้นใหม่ด้วยคำที่ยาวและเจาะจงขึ้น อย่าพยายามเลื่อนหน้าข้ามมันไป · จะ sync ทั้งแคตตาล็อกให้เดิน cursor โดยไม่มี search ทีละ platform
การปฏิเสธที่ด่านยืนยันตัวตนเกิดกับเอนด์พอยต์นี้ด้วย
ห้ารหัสของด่านยืนยันตัวตน — invalid_key (401) · key_revoked (401) · insufficient_scope (403) · quota_exceeded (429) · too_many_inflight (429) — เกิดได้กับทุกเอนด์พอยต์ รวมทั้งเอนด์พอยต์นี้ เพราะถูกโยนตั้งแต่ก่อนคำขอจะเดินไปถึงตรรกะของหน้านี้เลยสักบรรทัด (scope ที่ต้องมีคือค่าที่ประกาศไว้หัวหน้านี้) · แท็บคำตอบด้านบนจึงไล่เฉพาะสิ่งที่เอนด์พอยต์นี้เองตอบ ไม่ได้แปลว่าห้ารหัสนั้นเกิดที่นี่ไม่ได้ · รายละเอียดครบทุกตัวอยู่ที่ หน้าเอนด์พอยต์ตัวตนที่ /developers/access/me ที่เดียว ทั้ง HTTP status ของแต่ละตัว · ตัวไหนกินโควตารายวันบ้าง · header อะไรกลับมาบ้าง · และทำไม 429 สองตัวต้องรับมือคนละแบบ — จงใจไม่คัดลอกมาไว้ทุกหน้า เพราะสำเนาที่สองคือที่ที่ลืมแก้ตามในวันที่กติกาเปลี่ยน
ข้อมูลที่ต้องส่ง
Query string
| ชื่อ | ชนิด | วิธีใช้ |
|---|---|---|
| platformบังคับ | string | platform ของสินค้า เทียบแบบตรงตัวรวมตัวพิมพ์ใหญ่เล็ก เช่น Steamรายละเอียดและข้อควรระวัง
|
| typeไม่บังคับ | string | จะเอาสินค้าประเภทไหนในสามประเภท ค่าเริ่มต้นคือ all และค่าที่ไม่รู้จักถูกอ่านเป็น all ด้วยรายละเอียดและข้อควรระวัง
|
| genreไม่บังคับ | string | กรองด้วยค่าหนึ่งค่าจากฟิลด์ genres ของสินค้ารายละเอียดและข้อควรระวัง
|
| searchไม่บังคับ | string | เทียบแบบ ขึ้นต้นด้วย กับชื่อที่ normalize แล้ว ซึ่งแปลว่าคุณต้อง normalize คำค้นเองมาก่อน เพราะสิ่งเดียวที่เราทำกับมันคือตัดช่องว่างหัวท้ายแล้วแปลงเป็นตัวพิมพ์เล็ก คำค้นที่ยังมีช่องว่างหรือเครื่องหมายวรรคตอนติดอยู่ ( counter-strike, counter strike) จึงแมตช์ ไม่ได้เลยสักชิ้น และคืนลิสต์ว่างโดยไม่มี error ให้เห็นรายละเอียดและข้อควรระวัง
|
| sortไม่บังคับ | string | ลำดับการเรียง ค่าเริ่มต้นคือ rating และค่าที่ไม่รู้จักถูกอ่านเป็น rating ด้วยรายละเอียดและข้อควรระวัง
|
| limitไม่บังคับ | number | จำนวนต่อหน้า ค่าเริ่มต้น 30 สูงสุด 60รายละเอียดและข้อควรระวัง
|
| cursorไม่บังคับ | string | ตัวชี้ตำแหน่งของหน้าถัดไป ให้คัดลอกค่า next_cursor ของหน้าก่อนมาใส่แบบตรงตัวรายละเอียดและข้อควรระวัง
|
Header
| ชื่อ | ชนิด | วิธีใช้ |
|---|---|---|
| Authorizationบังคับ | string | คีย์ของคุณในรูป Bearer <คีย์> ต้องส่งมาทุกคำขอ ไม่มีวิธียืนยันตัวตนทางอื่น |
ข้อมูลที่ได้รับ
| ชื่อ | ชนิด | วิธีใช้ |
|---|---|---|
| products | อาร์เรย์ | สินค้าในหน้านี้ เรียงตาม sort ที่ขอมา · จำนวนน้อยกว่า limit ได้เสมอ และเป็นลิสต์ว่างได้โดยไม่ใช่ error |
| products.id | สตริง หรือ null | รหัสสินค้า · เป็นค่าเดียวกับ product_id ที่ใช้ตอนสั่งซื้อ และใช้ตอนขอรายการบัญชีมือสองรายชิ้น — เวลาไล่หน้าให้ใช้ค่านี้ตัดสินค้าที่ซ้ำกันออก |
| products.game | สตริง หรือ null | ชื่อสินค้าที่แสดงให้ลูกค้าเห็น |
| products.platform | สตริง หรือ null | platform ของสินค้า — ค่าเดียวกับที่ส่งเป็นพารามิเตอร์ platform |
| products.type | สตริง หรือ null | ประเภทสินค้า: cdkey · account1 · account2 — เป็นตัวตัดสินว่าจะสั่งซื้ออย่างไร ดูพารามิเตอร์ type ข้างบน |
| products.slug | สตริง หรือ null | ชื่อที่ normalize แล้ว ใช้ประกอบลิงก์หน้าร้าน และเป็นค่าเดียวกับที่พารามิเตอร์ search เทียบด้วย |
| products.image | สตริง หรือ null | URL ปกแนวตั้ง · เป็น null เมื่อไม่มีรูปที่ใช้ได้ ซึ่งเกิดกับสินค้าจริงบ่อยพอที่คุณต้องมีรูปสำรองของตัวเอง |
| products.rating | ตัวเลข หรือ null | คะแนนของเกมจากแหล่งข้อมูลของเรา — ไม่ใช่คะแนนรีวิวของร้าน และไม่ได้บ่งบอกว่าสินค้ามีของ |
| products.genres | อาร์เรย์ของสตริง | แนวเกม · ค่าไหนในนี้ก็ใช้เป็นพารามิเตอร์ genre ได้ · อาร์เรย์ว่างแปลว่าไม่มีการบันทึกแนวไว้ ไม่ใช่แปลว่าไม่มีแนวไหนตรง |
| products.available | false หรือ null | สถานะของในสต็อก มีแค่สองค่าเท่านั้น รายละเอียดและข้อควรระวัง
|
| products.price | ตัวเลข | ยอดที่คีย์นี้ต้องจ่าย หน่วยบาท หักส่วนลดของคุณมาแล้ว รายละเอียดและข้อควรระวัง
|
| products.discount_percent | ตัวเลข | ส่วนลดที่คุณได้จริงกับสินค้าชิ้นนี้ หน่วยเปอร์เซ็นต์ · หักออกจาก price ไปเรียบร้อยแล้ว ฟิลด์นี้มีไว้บอกให้รู้เฉย ๆ ไม่ใช่ส่วนลดที่รอให้คุณเอาไปหักเองอีกรอบ |
| has_more | boolean | true = ยังมีหน้าถัดไป ให้ไล่ต่อรายละเอียดและข้อควรระวัง
|
| next_cursor | สตริง หรือ null | ส่งค่านี้กลับมาเป็น cursor ของคำขอถัดไปรายละเอียดและข้อควรระวัง
|
| search_capped | boolean | true = ผลที่ตรงกับคำค้นมีมากเกินกว่าที่เราอ่านให้ได้ในคำขอเดียว จึงมีบางส่วนถูกตัดออกไป (ให้ค้นใหม่ด้วยคำที่เจาะจงขึ้น) · เป็น false เสมอเมื่อไม่ได้ส่ง search มา |
สถานะที่อาจได้รับ
| สถานะ | รหัส | ความหมาย |
|---|---|---|
| 200 | — | หนึ่งหน้า และยังมีหน้าถัดไป has_more เป็น true และ next_cursor มีตัวชี้ตำแหน่งของคำขอถัดไปมาให้รายละเอียดและข้อควรระวัง
|
| 200 | — | หน้าสุดท้าย — หยุดได้ has_more เป็น false พร้อมกับ next_cursor เป็น null คือหยุดได้แล้วรายละเอียดและข้อควรระวัง
|
| 200 | — | ไม่มีอะไรตรงตัวกรอง — ไม่ใช่ error ลิสต์ว่างคือคำตอบปกติของตัวกรองที่แคบเกินไป ( genre ที่ไม่มีสินค้า หรือ type ที่ platform นั้นไม่ได้ขาย)รายละเอียดและข้อควรระวัง
|
| 200 | — | ผลค้นหาเยอะเกิน — ถูกตัดออกบางส่วน search_capped เป็น true แปลว่ามีสินค้าที่ตรงกับคำค้นของคุณอยู่ แต่ถูกตัดไม่ได้ส่งมา เพราะจำนวนที่ตรงเกินกว่าที่เราจะอ่านให้ได้ในคำขอเดียวรายละเอียดและข้อควรระวัง
|
| 400 | bad_request | ไม่ได้ส่ง platform มาplatform เป็นพารามิเตอร์เดียวที่เอนด์พอยต์นี้บังคับ · เติมค่าให้ถูกแล้วยิงใหม่ได้เลย ไม่มีอะไรค้างอยู่กลางทาง |
| 400 | invalid_cursor | cursor ที่ส่งมาใช้กับคำขอนี้ไม่ได้ให้เริ่มไล่หน้าใหม่ตั้งแต่หน้าแรกโดยไม่ส่ง cursorรายละเอียดและข้อควรระวัง
|
| 400 | cursor_with_search | ส่ง cursor มาพร้อม search (ใช้ด้วยกันไม่ได้)ให้ส่งอย่างใดอย่างหนึ่ง ห้ามส่งพร้อมกัน รายละเอียดและข้อควรระวัง
|