การแมปข้อมูลการขายของ Square กับข้อมูลการขายของ Receipt Roller
คำอธิบายทางเทคนิคเกี่ยวกับกลไกที่ดึงข้อมูลการขายจาก Square API แล้วแมปเข้าสู่โมเดลข้อมูลการขายของ Receipt Roller บทความนี้จัดทำขึ้นสำหรับนักพัฒนาและผู้ดูแลระบบที่ต้องการทำความเข้าใจข้อกำหนดภายใน ครอบคลุมตารางเทียบฟิลด์ การแปลงรหัสสถานะ การปรับมาตรฐานวิธีการชำระเงิน การระบุข้อมูลรายละเอียด/ลูกค้า/พนักงาน การจัดการโซนเวลาและความคงเส้นคงวา
บทความนี้สรุปพฤติกรรมของ SquarePaymentMapper ในฝั่ง Receipt Roller สำหรับขั้นตอนทั่วไปอย่าง "การเชื่อมต่อกับ Square" หรือ "เกิดอะไรขึ้นเมื่อซิงค์" ดูได้ที่ การตั้งค่าเชื่อมต่อ POS ของ Square สำหรับข้อกำหนดโมเดลมาตรฐานแยกตามฟิลด์ ดูได้ที่ ข้อกำหนด PosTransactionDto (ฟิลด์อ้างอิง)
โครงสร้างพื้นฐาน
Square และ Receipt Roller มีความละเอียดของข้อมูลการขายที่แตกต่างกัน
- ฝั่ง Square แบ่งออกเป็น Payment(การชำระเงิน)และ Order(คำสั่งซื้อ)Payment มีข้อมูลการชำระเงิน ส่วน Order มีข้อมูลรายละเอียด
- ฝั่ง Receipt Roller รวบรวมการชำระเงิน + รายละเอียด + ลูกค้า + พนักงานไว้ใน PosTransactionDto รายการเดียว
- เมื่อดึงข้อมูล จะเริ่มจาก Payment แล้วดึง Order / TeamMember / Customer ที่เกี่ยวข้องเพิ่มเติม
ตารางเทียบฟิลด์หลัก
| ฝั่ง Receipt Roller | ฝั่ง Square | หมายเหตุ |
|---|---|---|
TransactionId | Payment.id | ใช้ ID การชำระเงินของ Square ตามเดิม เป็นคีย์สำหรับการ upsert แบบคงเส้นคงวา |
ExternalTransactionId | Payment.id | เช่นเดียวกับข้างต้น(เก็บซ้ำไว้สำหรับการอ้างอิงภายนอก) |
TransactionDateTime | Payment.created_at | แปลงจากสตริง ISO 8601 เป็น DateTime แบบ UTC |
Currency | Payment.amount_money.currency | รหัสสกุลเงิน ค่าเริ่มต้นคือ JPY |
TotalAmount | Payment.amount_money.amount | JPY ใช้ค่าตามเดิม สกุลเงินอื่นแปลงจากหน่วยที่เล็กที่สุด (cents) เป็นหน่วยหลัก |
TaxAmount | Payment.tax_money.amount | เช่นเดียวกับข้างต้น |
SubtotalAmount | ค่าที่คำนวณ | amount - tax |
DiscountAmount | Payment.discount_money.amount | เช่นเดียวกับข้างต้น |
TipAmount | Payment.tip_money.amount | ทิป มีเฉพาะกรณีที่มี |
PaymentMethod | ตรวจสอบว่ามี Payment.card_details หรือไม่ | หากมีข้อมูลบัตรจะเป็น CreditCard หากไม่มีจะเป็น Other(ผู้ให้บริการหลัก รายละเอียดดูใน Payments ด้านล่าง) |
Payments[] | Payment 1 รายการ + card_details.card | รายละเอียดผู้ให้บริการชำระเงิน Method / Amount(total_money)/ Brand(card_brand)/ Last4(last_4) |
Status | Payment.status | ดู "ตารางเทียบรหัสสถานะ" ด้านล่าง |
ReceiptNumber | Payment.receipt_number | ออกโดย Square |
ReceiptUrl | Payment.receipt_url | URL ใบเสร็จสำหรับลูกค้า บันทึกในฟิลด์ที่มีโครงสร้าง(เดิมเก็บไว้ใน Notes แต่ยกเลิกแล้ว) |
ExternalIds | order_id / location_id / customer_id | ID สำหรับอ้างอิงข้ามผู้ให้บริการ(ProviderOrderId / LocationId / ProviderCustomerId) |
StaffId | Payment.team_member_id | เป็นค่าว่างหากยังไม่ได้กำหนด |
StaffName | TeamMember.display_name | ระบุแยกต่างหากผ่าน TeamMembers API |
Source | ค่าคงที่ "API" หรือ "Webhook" | ตามเส้นทางที่ดึงข้อมูล |
RawJson | Payload ดิบของ Payment + Order | เก็บฟิลด์ที่ไม่ได้แปลงเป็นโมเดล(เช่น card fingerprint)ไว้เพื่อให้สามารถกู้คืนได้ ไม่ส่งคืนผ่าน API |
LineItems | Order.line_items[] | ระบุแยกต่างหากผ่าน Orders API ดู "การแมปข้อมูลรายละเอียด" ด้านล่าง |
ตารางเทียบรหัสสถานะ
Square Payment.status | Receipt Roller Status | ความหมาย |
|---|---|---|
COMPLETED | Completed | บันทึกบัญชีเสร็จสมบูรณ์ |
CANCELED | Voided | ยกเลิก |
FAILED | Voided | ล้มเหลว(ไม่รวมอยู่ในยอดขาย) |
| อื่นๆ | Pending | กำลังดำเนินการ/ยังไม่ยืนยัน |
การแปลงสกุลเงินของจำนวนเงิน
อ็อบเจ็กต์ Money ของ Square มีวิธีจัดการหน่วยที่เล็กที่สุดแตกต่างกันไปตามสกุลเงิน
- JPY: 1 เยนเป็นหน่วยที่เล็กที่สุด ใช้ค่า
amountตามเดิม - USD・EUR เป็นต้น: 1 เซ็นต์เป็นหน่วยที่เล็กที่สุด แปลงเป็นหน่วยหลักด้วย
amount / 100
ฝั่ง Receipt Roller จะเก็บทุกค่าเป็นหน่วยหลัก (decimal) และเก็บรหัสสกุลเงินไว้ใน Currency
การแมปข้อมูลรายละเอียด
เนื่องจาก Payment ของ Square ไม่มีข้อมูลรายละเอียดรวมอยู่ด้วย ระบบจะดึง Order ที่เกี่ยวข้องแยกต่างหากเพื่อขยายเป็นรายละเอียด หากไม่สามารถดึง Order ได้(สิทธิ์ไม่เพียงพอ, API error, OAuth token หมดอายุ ฯลฯ)รายการขายจะถูกบันทึกโดยไม่มีรายละเอียด และสามารถเติมเต็มได้ในภายหลังผ่านการซิงค์ซ้ำ
Receipt Roller LineItem | Square Order.line_items[] | หมายเหตุ |
|---|---|---|
ProductName | name + variation_name | หากมี variation จะเป็นรูปแบบ "ชื่อสินค้า (ชื่อ variation)" |
Quantity | quantity | แปลงสตริงเป็นจำนวนเต็ม หากล้มเหลวจะเป็น 1 |
UnitPrice | base_price_money.amount | มีการแปลงสกุลเงิน |
Subtotal | total_money.amount | หากดึงไม่ได้จะใช้ UnitPrice × Quantity |
TaxRate | ค่าที่คำนวณ | คำนวณจาก (total_tax_money / (total_money - total_tax_money)) × 100 แล้วปัดเศษทศนิยมตำแหน่งที่ 1 |
TaxAmount | total_tax_money.amount | จำนวนภาษีระดับรายละเอียด เก็บเป็นจำนวนเงินไม่ใช่แค่อัตราภาษี |
DiscountAmount | total_discount_money.amount | จำนวนส่วนลดระดับรายละเอียด |
CatalogObjectId | catalog_object_id | ID สำหรับเชื่อมโยงกับแคตตาล็อกของ Square |
Modifiers | modifiers[] | ตัวเลือก/รายการปรับแต่ง(name + total_price_money) |
Category | — | ปัจจุบันเป็นค่าว่างเสมอ การระบุผ่าน Square Catalog API อยู่ระหว่างการพิจารณาในอนาคต |
การดึงคีย์จับคู่ลูกค้า
เพื่อส่งต่อไปยังไปป์ไลน์การกระจายอัตโนมัติของ Receipt Roller(การเชื่อมต่อ CRM)ระบบจะดึงตัวระบุลูกค้าฝั่ง Square ออกมา
- คีย์หลัก:
Payment.customer_id(ID ลูกค้าใน Square Customer Directory)เก็บไว้เป็นExternalIds.ProviderCustomerIdในรายการขายด้วย - สำรอง:
Payment.card_details.card.fingerprint(แฮชของหมายเลขบัตร ไม่ใช่ PAN แต่เป็นตัวระบุที่เสถียรซึ่ง Square สร้างขึ้น เก็บไว้ในRawJsonไม่ส่งคืนผ่าน API)
เนื่องจาก Square Payment ไม่มีหมายเลขโทรศัพท์หรืออีเมลรวมอยู่ด้วย จึงจำเป็นต้องเรียก API เพิ่มเติมไปยัง Customer Directory ปัจจุบันมักละเว้นขั้นตอนนี้เนื่องจาก fingerprint ของบัตรสามารถจับคู่ได้เพียงพอในกรณีส่วนใหญ่
การรีเฟรช OAuth token อัตโนมัติ
Access token ของ Square OAuth จะหมดอายุภายในประมาณ 30 วัน สำหรับเส้นทางผ่าน Webhook ระบบจะตรวจสอบวันหมดอายุของ terminal.AccessToken ก่อนดึงข้อมูลรายละเอียด/ชื่อพนักงาน หากเหลือเวลาน้อยกว่า 60 วินาที จะอัปเดตอัตโนมัติด้วย RefreshToken(หลังอัปเดตจะบันทึกลงในเรคคอร์ดของเครื่องแบบถาวร)วิธีนี้ช่วยป้องกันไม่ให้เกิดการดึงรายละเอียดไม่สำเร็จในร้านค้าที่ขับเคลื่อนด้วย webhook เป็นระยะเวลานาน
ความคงเส้นคงวา (Idempotency)
แม้รายการขายเดียวกันจะมาถึงหลายครั้ง(เช่น การซิงค์ด้วยตนเองซ้ำในฝั่ง Square หรือ Webhook ซ้ำซ้อน)ระบบจะบันทึกซ้ำได้อย่างปลอดภัยด้วยการ upsert โดยใช้ Payment.id เป็นคีย์ จะไม่มีการออกใบเสร็จซ้ำในฝั่ง Receipt Roller
บทความที่เกี่ยวข้อง
- การตั้งค่าเชื่อมต่อ POS ของ Square — ขั้นตอนการเชื่อมต่อ
- การแมปข้อมูลการขายของ Smaregi — เวอร์ชัน Smaregi
- ข้อกำหนด PosTransactionDto (ฟิลด์อ้างอิง) — รายละเอียดของแต่ละฟิลด์
-
กลไกการทำงานของใบเสร็จอิเล็กทรอนิกส์อธิบายกลไกการทำงานของใบเสร็จอิเล็กทรอนิกส์ของ ReceiptRoller และวิธีเชื่อมต่อ POS กับ Square และสมาเรจิ
-
การลงทะเบียนข้อมูลพนักงานอธิบายขั้นตอนการลงทะเบียนพนักงานใหม่ ตั้งแต่แบบฟอร์มลงทะเบียนจากรายชื่อ ข้อมูลพื้นฐาน ร้านค้าที่มอบหมาย ข้อมูลบุคคล งานเพิ่มเติม ข้อมูลติดต่อฉุกเฉิน จนถึงการบันทึก พร้อมภาพหน้าจอประกอบ
-
การซิงค์ข้อมูลสมาชิกสองทางกับ Smaregi / Squareข้อมูลสมาชิกของ ReceiptRoller จะถูกซิงค์แบบสองทางกับ POS ที่เชื่อมต่อ เช่น Smaregi หรือ Square เมื่อลงทะเบียนสมาชิกที่ฝั่ง ReceiptRoller จะสะท้อนไปยังมาสเตอร์สมาชิกของ POS ด้วย และในทางกลับกัน ข้อมูลที่ลงทะเบียนสมาชิกที่ POS ก็จะถูกดึงเข้าสู่ CRM ของ ReceiptRoller เช่นกัน ไม่จำเป็นต้องจัดการข้อมูลสมาชิกซ้ำซ้อน บทความนี้อธิบายกลไกการซิงค์สองทาง รายการที่ซิงค์ จังหวะเวลา ลำดับความสำคัญเมื่อเกิดความขัดแย้ง POS ที่รองรับ และวิธีตั้งค่า
-
การแมปข้อมูลพนักงาน Square กับข้อมูลพนักงานของ Receipt Rollerอธิบายวิธีที่ Square Team Member เชื่อมโยงกับข้อมูลพนักงานของ Receipt Roller (StaffDto) ครอบคลุมวิธีใช้งานลิงก์แบบแมนนวล ตรรกะการระบุผู้รับผิดชอบในข้อมูลการขาย และฟิลด์หลักของ Team Member API
-
การตั้งค่าเชื่อมต่อ POS ของ Squareอธิบายขั้นตอนการเชื่อมต่อ POS ของ Square กับ ReceiptRoller เชื่อมต่อได้ง่ายด้วยการยืนยันตัวตนผ่าน OAuth และข้อมูลบัญชีจะถูกซิงค์แบบเรียลไทม์