สมาชิก
สมาชิก คือคน 1 คนที่สมัครเข้าระบบสมาชิกของร้าน — ระดับสมาชิก · แต้มและเครดิตที่ใช้ได้ · คูปองในมือ และสรุปพฤติกรรมเล็กน้อย (มากี่ครั้ง · มาล่าสุดเมื่อไหร่) · นี่คือทรัพยากรที่ระบบ CRM · ระบบสะสมแต้ม หรือคลังข้อมูลวิเคราะห์ลูกค้า ใช้อ่าน
มี 3 เส้น:
GET /v1/members— สมาชิกของฐานสมาชิก 1 ฐาน ทีละหน้าGET /v1/members/{memberToken}— สมาชิก 1 คนจากโทเคนGET /v1/members/lookup— โทเคนสมาชิกที่อยู่เบื้องหลัง LINE user id ที่คุณถืออยู่แล้ว
ทั้ง 3 เส้นเป็นการอ่านอย่างเดียว
สมาชิกคืออะไร
หัวข้อที่มีชื่อว่า “สมาชิกคืออะไร”ระบบสมาชิกของ ScanFood ไม่ได้ผูกกับร้าน แต่ผูกกับ ฐานสมาชิก ที่ระบุด้วย
providerId · หลายสาขาใช้ฐานเดียวกันเป็นเรื่องปกติ ⇒ ลูกค้าที่สมัครที่สาขาหนึ่ง
จะถูกจำได้ที่ทุกสาขาที่ใช้ฐานนั้นร่วมกัน
นี่คือเหตุผลที่เส้นนี้กำหนดขอบเขตด้วย providerId ไม่ใช่ shopId
| ระดับกุญแจ | ฐานสมาชิกที่อ่านได้ |
|---|---|
shop |
providerId เดียวของร้านนั้น |
franchise |
ทุก providerId ที่ไม่ซ้ำใต้แฟรนไชส์ |
providerId เป็นค่า บังคับ ของเส้นลิสต์ และต้องเป็นค่าที่กุญแจของคุณครอบถึง
ไม่งั้นได้ 403 provider_not_in_scope · ค่าที่ใช้ได้ดูจาก
GET /v1/shops
ข้อมูลที่ระบบเก็บเกี่ยวกับสมาชิกมีมากกว่าที่ส่งออกมาก — ข้อมูลจริงถือบัญชีแชต · เบอร์โทร · อีเมล · วันเกิด · เพศ · รูปภาพ · โน้ตข้อความอิสระ · ประวัติการมาใช้บริการทุกครั้งพร้อมยอด รายครั้ง และการเคลื่อนไหวของแต้มและเครดิตทุกก้อน · API คืนเฉพาะชุดที่คัดไว้ตายตัว (ดูหัวข้อ “ความเป็นส่วนตัว”)
โทเคนสมาชิก
หัวข้อที่มีชื่อว่า “โทเคนสมาชิก”สมาชิกถูกระบุด้วย โทเคนแบบทึบ ที่ขึ้นต้นด้วย m1_ เสมอ:
m1_ZmQ3YjJkNGE1ZTZmNzg5MGExYjJjM2Q0ZTVmNjc4OTBhYmNkZWYxMjMโทเคนนี้เป็นรหัสเดียวที่ API เปิดให้สำหรับตัวบุคคล · มันมาแทนรหัสข้อมูลภายใน ซึ่งประกอบจากบัญชีแชตของลูกค้า และจะเปิดเผยตัวตนจริงถ้าถูกส่งออกไป
สิ่งที่คุณพึ่งพาได้:
| คุณสมบัติ | รายละเอียด |
|---|---|
| เสถียร | คนเดิมได้โทเคนเดิมเสมอ ⇒ ใช้เป็นคีย์หลัก · คีย์ join หรือ cursor ได้ |
| ทั้งระบบ | โทเคนไม่ขึ้นกับว่ามองจากกุญแจใบไหน ร้านไหน หรือแฟรนไชส์ไหน — 2 กุญแจที่อ่านคนเดียวกันเห็นโทเคนเดียวกัน |
| ย้อนกลับไม่ได้ | ถอดกลับไปเป็นตัวตนของลูกค้าไม่ได้ |
| มีรุ่น | m1_ คือเครื่องหมายรุ่น · ถ้าวันไหนมีการหมุนกุญแจเข้ารหัส โทเคนใหม่จะขึ้นต้น m2_ |
memberId บนบิลคือโทเคนตัวนี้เป๊ะ ⇒ การ join บิลกับสมาชิกไม่ต้องแปลงอะไรเลย:
// บิลจาก /v1/transactionsif (bill.memberId) { const member = await getMember(bill.memberId); // /v1/members/{memberToken}}ค้นสมาชิกจาก LINE user id
หัวข้อที่มีชื่อว่า “ค้นสมาชิกจาก LINE user id”ถ้าคุณมี LINE user id ของลูกค้าอยู่แล้ว — เพราะเขากำลังคุยกับบอทของคุณ หรือเปิด LIFF ของคุณอยู่ — คุณแลกมันเป็นโทเคนสมาชิกของคนนั้นได้เลย โดยไม่ต้องถามอะไรลูกค้าเพิ่ม:
GET /v1/members/lookup— โทเคนสมาชิก 1 ตัว จาก LINE user id ที่คุณถืออยู่
GET /ext/v1/members/lookup?providerId=pv3Yh9DkQ2sLmT6RwX1B&lineUserId=U4af4980629…คำตอบมีแค่โทเคน ไม่มีอย่างอื่น:
{ "ok": true, "data": { "memberId": "m1_ZmQ3YjJkNGE1ZTZmNzg5MGExYjJjM2Q0ZTVmNjc4OTBhYmNkZWYxMjM" }, "serverTime": "2026-09-18T04:10:00.000Z"}เป็นโทเคนตัวเดียวกับที่ใช้ทุกที่ ⇒ เอาไปเรียก
GET /v1/members/{memberToken} ต่อเพื่อดูระดับ แต้ม
และคูปอง · และบิลทุกใบใน GET /v1/transactions ที่มี
memberId ค่านั้นคือการซื้อของคนคนเดียวกัน · นี่คือเคส
“ลูกค้าเพิ่งทักเข้ามา — เป็นสมาชิกไหม แล้วเป็นใครกับเรา”
สังเกตทิศทางของข้อมูล: คุณส่ง LINE user id ที่คุณมีอยู่แล้วเข้ามา แล้วได้โทเคนกลับไป ·
API ยังไม่ปล่อย LINE user id ออกไปเหมือนเดิม — ช่อง hasLine ยังเป็นสิ่งเดียวที่
response ของสมาชิกบอกเกี่ยวกับช่องทางนี้
เปิดเป็นรายกุญแจ
หัวข้อที่มีชื่อว่า “เปิดเป็นรายกุญแจ”การค้นนี้ ปิดไว้เป็นค่าตั้งต้น และเปิดให้ทีละกุญแจ แบบเดียวกับ tel และ email
(ดูหัวข้อ “ความเป็นส่วนตัว”) · กุญแจที่ยังไม่ได้เปิดจะได้ 403 lookup_not_enabled
ทุกครั้งที่เรียก · การเปิดเป็นการตัดสินใจเชิงพาณิชย์และเชิงกฎหมายเรื่องฐานการประมวลผล
และความยินยอมตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล ไม่ใช่แค่การกดสวิตช์ ⇒
ติดต่อผู้ดูแลของ ScanFood และเตรียมชี้แจงว่าจะเอาการค้นนี้ไปใช้ทำอะไร
พารามิเตอร์
หัวข้อที่มีชื่อว่า “พารามิเตอร์”| พารามิเตอร์ | บังคับ | หมายเหตุ |
|---|---|---|
providerId |
ใช่ | ฐานสมาชิกที่จะค้น · ต้องอยู่ในขอบเขตกุญแจ ไม่งั้น 403 provider_not_in_scope |
lineUserId |
ใช่ | LINE user id ของลูกค้า · รูป U ตามด้วยเลขฐานสิบหก 32 ตัว |
ข้อผิดพลาด
หัวข้อที่มีชื่อว่า “ข้อผิดพลาด”| สถานะ | error |
เกิดเมื่อ |
|---|---|---|
400 |
bad_query |
providerId ว่าง หรือ lineUserId ไม่ได้อยู่ในรูป U + เลขฐานสิบหก 32 ตัว |
403 |
provider_not_in_scope |
ฐานสมาชิกอยู่นอกขอบเขตที่กุญแจครอบ |
403 |
lookup_not_enabled |
กุญแจของคุณยังไม่ได้เปิดสิทธิ์ค้น |
404 |
not_found |
ไม่พบสมาชิกที่ตรงกัน |
curl -s -G https://api.scanfood.co/ext/v1/members/lookup \ -H "Authorization: Bearer sf_live_4b8f2c1e9d07_3f9a2c1b7e4d8506a1b2c3d4e5f60718" \ --data-urlencode "providerId=pv3Yh9DkQ2sLmT6RwX1B" \ --data-urlencode "lineUserId=U4af4980629304a1b8c2d3e4f5a6b7c8d"const BASE = 'https://api.scanfood.co/ext/v1';const KEY = process.env.SCANFOOD_API_KEY;
/** LINE user id -> โทเคนสมาชิก · ไม่เจอ = null */async function lookupMember(providerId, lineUserId) { const url = new URL(`${BASE}/members/lookup`); url.searchParams.set('providerId', providerId); url.searchParams.set('lineUserId', lineUserId);
const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }); if (res.status === 404) return null; // ไม่ได้เป็นสมาชิกของแบรนด์นี้ const body = await res.json(); if (!res.ok) throw new Error(`${res.status} ${body.error}: ${body.message}`); return body.data.memberId;}
// ตอนมีข้อความเข้ามา — เก็บผลไว้ใช้ซ้ำ อย่าเรียกทุกข้อความconst token = await lookupMember('pv3Yh9DkQ2sLmT6RwX1B', event.source.userId);if (token) { const member = await getMember(token); // /v1/members/{memberToken} console.log(member.rankName, member.points);}import osimport requests
BASE = "https://api.scanfood.co/ext/v1"HEADERS = {"Authorization": f"Bearer {os.environ['SCANFOOD_API_KEY']}"}
def lookup_member(provider_id, line_user_id): """LINE user id -> โทเคนสมาชิก · ไม่เจอ = None""" res = requests.get( f"{BASE}/members/lookup", headers=HEADERS, params={"providerId": provider_id, "lineUserId": line_user_id}, timeout=60, ) if res.status_code == 404: return None # ไม่ได้เป็นสมาชิกของแบรนด์นี้ body = res.json() if res.status_code != 200: raise RuntimeError(f"{res.status_code} {body['error']}: {body['message']}") return body["data"]["memberId"]
token = lookup_member("pv3Yh9DkQ2sLmT6RwX1B", event.source.user_id)if token: member = get_member(token) # /v1/members/{memberToken} print(member["rankName"], member["points"])สมาชิกที่สมัครซ้ำหลายรอบจนมีมากกว่า 1 ระเบียนในฐานเดียวกัน — การค้นจะคืน ระเบียนที่สมัครล่าสุด
รูป response ของเส้นลิสต์:
{ "ok": true, "data": [ { "…": "หนึ่งก้อนต่อหนึ่งคน" } ], "nextCursor": "m1_…", "serverTime": "2026-09-10T01:45:00.000Z"}เส้นสมาชิกรายคนคืนก้อนเดียวกันใต้ data และไม่มี nextCursor
| ฟิลด์ | ชนิด | เป็น null ได้ไหม | ความหมาย | ตัวอย่าง |
|---|---|---|---|---|
memberId |
string | ได้ | โทเคนสมาชิกแบบทึบ · ค่าเดียวกับ memberId บนบิล |
"m1_ZmQ3YjJkNGE1…" |
rank |
string | ได้ | รหัสระดับที่บันทึกไว้บนตัวสมาชิก · ไม่มีบันทึก = null |
"GOLD" |
rankLevel |
number | ได้ | ชั้นของสมาชิกในลิสต์ระดับของฐาน เป็นจำนวนเต็มนับจาก 0 · มีความหมายเฉพาะเมื่อฐานเปิดเลื่อนระดับอัตโนมัติ · ไม่มีชั้นที่ใช้ได้บันทึกไว้ = null |
2 |
rankName |
string | ได้ | ชื่อระดับที่สมาชิกอยู่ตอนนี้ เทียบกับลิสต์ระดับปัจจุบันของฐาน · ไม่มีระดับ = null · ใช้ช่องนี้แสดงผล ไม่ใช่ rank |
"โกลด์" |
points |
number | ไม่ | แต้มที่ใช้ได้ หลังคิดวันหมดอายุแล้ว — ไม่ใช่แต้มสะสมทั้งชีวิต | 14097 |
credit |
number | ไม่ | เครดิตร้านที่ใช้ได้ หลังคิดอายุแล้ว · ฐานที่ไม่ได้เปิดระบบเครดิต = 0 |
200 |
creditEnabled |
boolean | ไม่ | ฐานสมาชิกนี้เปิดระบบเครดิตร้านไหม · อ่านช่องนี้ก่อนแสดง credit ให้ใครดู |
true |
coupons |
array | ไม่ (เป็น [] ได้) |
คูปองในมือสมาชิก (ดูหัวข้อ “คูปอง”) | — |
labels |
string[] | ไม่ (เป็น [] ได้) |
ป้ายกำกับที่ร้านใส่ให้สมาชิก | ["vip", "แพ้ถั่ว"] |
persona |
string[] | ไม่ (เป็น [] ได้) |
ป้ายบุคลิกที่ร้านใส่ให้ | ["สายหวาน"] |
createdDate |
string | ได้ | วันที่สมัครสมาชิก · ISO-8601 พร้อม offset +07:00 (เวลาไทย) — ฐานสมาชิกไม่มีเขตเวลาของตัวเอง เวลาของสมาชิกจึงเป็นเวลาไทยเสมอ |
"2026-03-04T16:15:00.000+07:00" |
visitCount |
integer | ไม่ | จำนวนครั้งที่มาใช้บริการ · ตัวรายการไม่ถูกส่งออก | 57 |
lastVisit |
string | ได้ | วันที่มาล่าสุด รูป YYYY-MM-DD · ไม่เคยมา = null |
"2026-09-07" |
hasTel |
boolean | ไม่ | มีเบอร์โทรในระบบไหม — ไม่ใช่ตัวเบอร์ | true |
hasEmail |
boolean | ไม่ | มีอีเมลในระบบไหม | false |
hasLine |
boolean | ไม่ | ผูกบัญชีแชตไว้ไหม | true |
อีก 2 ฟิลด์จะโผล่ เฉพาะกุญแจที่ได้รับสิทธิ์ข้อมูลส่วนบุคคลไว้ชัดเจน (ดูหัวข้อ “ความเป็นส่วนตัว”):
| ฟิลด์ | ชนิด | เป็น null ได้ไหม |
|---|---|---|
tel |
string | ได้ |
email |
string | ได้ |
ช่องที่ จงใจไม่มี: ชื่อและชื่อเล่นของสมาชิก · เบอร์โทรและอีเมล (ถ้าไม่ได้รับสิทธิ์) ·
รหัสบัญชีแชต · วันเกิด · เพศ · รูปภาพ · ที่อยู่ · โน้ตข้อความอิสระ · บัตรที่บันทึกไว้ ·
ประวัติการมาใช้บริการแบบดิบ และรายการเคลื่อนไหวของแต้ม/เครดิตแบบดิบ ·
และ ไม่มีกลุ่มลูกค้าสำเร็จรูป (ลูกค้าประจำ · เสี่ยงหาย · ลูกค้าใหม่ ฯลฯ) เพราะกลุ่มพวกนี้
คำนวณเป็นรอบที่อื่น ไม่ได้ติดอยู่กับตัวสมาชิก ⇒ คำนวณเองจาก visitCount · lastVisit
และประวัติการซื้อ
coupons[] คือคูปองที่อยู่ในมือสมาชิก · แต่ละใบ:
| ฟิลด์ | ชนิด | เป็น null ได้ไหม | ความหมาย |
|---|---|---|---|
code |
string | ได้ | รหัสสั้นที่สมาชิกโชว์ที่เคาน์เตอร์ · ไม่ซ้ำต่อใบ |
status |
"available" | "used" |
ได้ | "available" = ยังใช้ได้ · "used" = ใช้ไปแล้ว |
templateId |
string | ได้ | แคตตาล็อกคูปองที่ใบนี้ออกมาจาก · หลายใบใช้ templateId เดียวกันได้ |
name |
string | ได้ | ชื่อคูปองตามที่สมาชิกเห็น |
expireAt |
string | ได้ | วันหมดอายุ · ISO-8601 พร้อม offset +07:00 (เวลาไทย) เหมือนเวลาอื่นของสมาชิก · ไม่มีวันหมดอายุ = null |
2 เรื่องที่ควรวางแผนไว้:
- คูปองที่
status: "used"ยังอยู่ในรายการ ⇒ กรองด้วยstatusก่อนแสดงคำว่า “คูปองที่ใช้ได้” statusบอกเรื่อง การใช้ไปแล้วหรือยัง ไม่ได้บอกเรื่องหมดอายุ ⇒ ให้ถือexpireAtเป็นอีกเงื่อนไขหนึ่ง และเทียบกับserverTimeไม่ใช่นาฬิกาบนเครื่องของคุณเอง
คูปองถูกอ่านภายในฐานสมาชิกที่คุณขอเสมอ ⇒ คูปองของแบรนด์อื่นไม่มีทางหลุดเข้ามา
การดึงสมาชิกทั้งหมด
หัวข้อที่มีชื่อว่า “การดึงสมาชิกทั้งหมด”ฝั่งสมาชิก ไม่มีโหมดดึงเฉพาะส่วนที่เปลี่ยน — updatedSince ถูกปัดด้วย
400 not_supported
⇒ ให้ไล่อ่านทั้งฐานด้วย cursor
| พารามิเตอร์ | บังคับ | ค่าตั้งต้น | หมายเหตุ |
|---|---|---|---|
providerId |
ใช่ | — | ต้องอยู่ในขอบเขตกุญแจ ไม่งั้น 403 provider_not_in_scope |
limit |
ไม่ | 100 |
เกิน 200 ถูกลดเหลือ 200 · ค่าที่ไม่ถูกต้องตกกลับไปที่ 100 |
cursor |
ไม่ | — | ค่า nextCursor ของหน้าก่อน — เป็นโทเคน m1_… |
updatedSince |
— | — | ไม่รองรับ — 400 not_supported |
สมาชิกถูกเรียงตาม วันสมัคร จากเก่าไปใหม่ ⇒ การไล่อ่านทั้งฐานจึงนิ่ง: แถวที่ส่งไปแล้ว
จะไม่กระโดดแซงหน้าคุณระหว่างไล่ · หน้าสุดท้าย nextCursor = null
# หน้าแรกcurl -s -G https://api.scanfood.co/ext/v1/members \ -H "Authorization: Bearer sf_live_4b8f2c1e9d07_3f9a2c1b7e4d8506a1b2c3d4e5f60718" \ --data-urlencode "providerId=pv3Yh9DkQ2sLmT6RwX1B" \ --data-urlencode "limit=200"
# หน้าถัดไป — ส่ง nextCursor ที่ได้จาก response ข้างบนcurl -s -G https://api.scanfood.co/ext/v1/members \ -H "Authorization: Bearer sf_live_4b8f2c1e9d07_3f9a2c1b7e4d8506a1b2c3d4e5f60718" \ --data-urlencode "providerId=pv3Yh9DkQ2sLmT6RwX1B" \ --data-urlencode "limit=200" \ --data-urlencode "cursor=m1_ZmQ3YjJkNGE1ZTZmNzg5MGExYjJjM2Q0ZTVmNjc4OTBhYmNkZWYxMjM"const BASE = 'https://api.scanfood.co/ext/v1';const KEY = process.env.SCANFOOD_API_KEY;
async function getPage(path, params) { const url = new URL(`${BASE}${path}`); for (const [k, v] of Object.entries(params)) { if (v != null) url.searchParams.set(k, v); } const res = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }); const body = await res.json(); if (!res.ok) throw new Error(`${res.status} ${body.error}: ${body.message}`); return body;}
/** ไล่อ่านฐานสมาชิก 1 ฐานจนครบ */async function syncMembers(providerId, onMember) { let cursor = null; let seen = 0;
do { const page = await getPage('/members', { providerId, limit: 200, cursor }); for (const member of page.data) { onMember(member); // upsert ด้วย member.memberId seen += 1; } cursor = page.nextCursor; // เก็บ `cursor` ไว้ตรงนี้ ถ้าอยากให้รอบที่ขาดกลางคันวิ่งต่อได้ } while (cursor);
return seen;}
const total = await syncMembers('pv3Yh9DkQ2sLmT6RwX1B', upsertMember);console.log('ซิงก์แล้ว', total, 'คน');
// สมาชิก 1 คน จากบิลตรง ๆasync function getMember(token) { const body = await getPage(`/members/${encodeURIComponent(token)}`, {}); return body.data;}import osimport requestsfrom urllib.parse import quote
BASE = "https://api.scanfood.co/ext/v1"HEADERS = {"Authorization": f"Bearer {os.environ['SCANFOOD_API_KEY']}"}
def get_page(path, params=None): query = {k: v for k, v in (params or {}).items() if v is not None} res = requests.get(f"{BASE}{path}", headers=HEADERS, params=query, timeout=60) body = res.json() if res.status_code != 200: raise RuntimeError(f"{res.status_code} {body['error']}: {body['message']}") return body
def sync_members(provider_id, on_member): """ไล่อ่านฐานสมาชิก 1 ฐานจนครบ""" cursor, seen = None, 0 while True: page = get_page("/members", { "providerId": provider_id, "limit": 200, "cursor": cursor, }) for member in page["data"]: on_member(member) # upsert ด้วย member["memberId"] seen += 1 cursor = page["nextCursor"] # เก็บ `cursor` ไว้ตรงนี้ ถ้าอยากให้รอบที่ขาดกลางคันวิ่งต่อได้ if not cursor: return seen
total = sync_members("pv3Yh9DkQ2sLmT6RwX1B", upsert_member)print("ซิงก์แล้ว", total, "คน")
def get_member(token): """สมาชิก 1 คน จากบิลตรง ๆ""" return get_page(f"/members/{quote(token, safe='')}")["data"]ถ้า cursor ชี้ไปที่สมาชิกที่ถูกลบไปแล้ว คุณจะได้ 400 bad_cursor ⇒ ให้เริ่มหน้านั้นใหม่
โดยไม่ส่ง cursor แทนที่จะถือว่าเป็น error ร้ายแรง
ตัวอย่าง response
หัวข้อที่มีชื่อว่า “ตัวอย่าง response”{ "ok": true, "data": [ { "memberId": "m1_ZmQ3YjJkNGE1ZTZmNzg5MGExYjJjM2Q0ZTVmNjc4OTBhYmNkZWYxMjM", "rank": "GOLD", "rankLevel": 2, "rankName": "โกลด์", "points": 14097, "credit": 200, "creditEnabled": true, "coupons": [ { "code": "ABC123", "status": "available", "templateId": "cp7Vb2NqZ5tMx8LwR3Kd", "name": "ส่วนลด 50 บาท", "expireAt": "2026-12-31T23:59:59.000+07:00" }, { "code": "XYZ789", "status": "used", "templateId": "cpQ4mL8vT2zNb6RwK9Ys", "name": "ฟรีชาเย็น", "expireAt": null } ], "labels": ["vip", "แพ้ถั่ว"], "persona": ["สายหวาน"], "createdDate": "2026-03-04T16:15:00.000+07:00", "visitCount": 57, "lastVisit": "2026-09-07", "hasTel": true, "hasEmail": false, "hasLine": true } ], "nextCursor": null, "serverTime": "2026-09-10T01:45:00.000Z"}ความเป็นส่วนตัว
หัวข้อที่มีชื่อว่า “ความเป็นส่วนตัว”ข้อมูลสมาชิกคือข้อมูลส่วนบุคคล · API นี้ถูกออกแบบให้กุญแจ ได้ข้อมูลเกินกว่าที่ให้ไว้ ไม่ได้แม้โดยอุบัติเหตุ
ค่าตั้งต้นคือไม่ส่งข้อมูลติดต่อออกเลย — คุณได้ hasTel · hasEmail · hasLine
ซึ่งพอให้รู้ว่ามีช่องทางติดต่อไหมและแบ่งกลุ่มได้ แต่ไม่ได้ตัวค่า
เบอร์โทรและอีเมลเปิดได้ รายกุญแจ และเปิดได้แค่ 2 ช่องนี้เท่านั้น ไม่มีสวิตช์ไหน ที่เปิดของอย่างอื่นได้ · มีอีกธงหนึ่งแยกต่างหากที่เปิดการ ค้นสมาชิกจาก LINE user id ⇒ ดูให้ชัดว่าธงนั้นทำอะไร และไม่ทำอะไร: มันให้กุญแจ ส่งเข้ามา ซึ่ง LINE user id ที่ผู้เรียกมีอยู่แล้ว แล้วรับโทเคนสมาชิกกลับไป · มันไม่ได้เพิ่มรหัสบัญชีแชตเข้าไปใน response ใด ๆ ⇒ ไม่มีกุญแจระดับไหนอ่านรหัสบัญชีแชตออกจาก ScanFood ได้ · การเปิดเป็นการตัดสินใจเชิงพาณิชย์และเชิงกฎหมายเรื่องฐานการ ประมวลผลและความยินยอมตาม พ.ร.บ. คุ้มครองข้อมูลส่วนบุคคล ไม่ใช่แค่การกดสวิตช์ ⇒ ติดต่อผู้ดูแลของ ScanFood และเตรียมชี้แจงว่าจะใช้ข้อมูลทำอะไร
ที่เหลือทั้งหมดเกี่ยวกับตัวบุคคล อยู่ในระบบ ScanFood ถาวร:
| กลุ่ม | ไม่มีให้ไม่ว่ากุญแจระดับไหน |
|---|---|
| ตัวตน | ชื่อ · ชื่อเล่น · รหัสบัญชีแชต · วันเกิด · เพศ · รูปภาพ · ที่อยู่ |
| ข้อความอิสระ | โน้ตที่พนักงานเขียนถึงสมาชิก |
| ประวัติดิบ | ประวัติการมาใช้บริการเต็ม (พร้อมรหัสบิลและยอดรายครั้ง) · การเคลื่อนไหวแต้ม · การเคลื่อนไหวเครดิต · ประวัติโปรโมชัน |
| เครื่องมือชำระเงิน | บัตรที่บันทึกไว้ |
| ภายใน | ข้อมูลการแก้ไข · ตัวนับยอดใช้จ่าย · ข้อมูลการเลื่อนระดับ |
อ่านต่อ
หัวข้อที่มีชื่อว่า “อ่านต่อ”- รายการขาย —
memberIdบนบิลชี้ตรงมาที่สมาชิก - ร้านค้า —
providerIdมาจากที่นี่ - การแบ่งหน้าและการซิงก์ — กติกา cursor แบบละเอียด
- ข้อผิดพลาด — รหัสทุกตัวที่เส้นเหล่านี้คืนได้
- แนวปฏิบัติด้านความปลอดภัย