บทความทั้งหมด
Shopifywebhook debugginglocal testinge-commerce

ทดสอบ Shopify webhook ในเครื่องโดยไม่มีเซอร์ไพรส์

บั๊ก Shopify webhook ส่วนใหญ่ในการพัฒนาในเครื่องสรุปได้ที่บรรทัดเดียว: HMAC เข้ารหัสแบบ base64 ไม่ใช่ hex ถ้าคุณเรียก .digest('hex') แล้วเทียบกับ X-Shopify-Hmac-Sha256 การตรวจจะไม่มีวันผ่าน — แม้มี shared secret ที่ถูกต้อง เราเห็นวิศวกรซีเนียร์เสียทั้งบ่ายไปกับอักขระตัวเดียวนี้

กับดัก base64

Stripe ใช้ hex GitHub ใช้ hex (มี prefix sha256=) Shopify ใช้ base64 สามผู้ให้บริการ สามการเข้ารหัส เวอร์ชันแรกของตัวตรวจของคุณแทบแน่นอนจะคัดลอก snippet จากโปรเจกต์อื่นและล้มเหลวเงียบ ๆ

การตรวจควรเป็นแบบนี้:

const hmac = crypto
  .createHmac('sha256', SHOPIFY_API_SECRET)
  .update(rawBody)         // raw body, not JSON-parsed
  .digest('base64');       // base64, not hex

const valid = crypto.timingSafeEqual(
  Buffer.from(hmac),
  Buffer.from(req.headers['x-shopify-hmac-sha256']),
);

นอกจากการเข้ารหัส มีสองสิ่งสำคัญ body ต้องเป็นไบต์ดิบที่ยังไม่พาร์ส และการเทียบต้องเป็น timing-safe — การเทียบ string เท่ากันรั่วข้อมูลทีละไบต์

ทำไม tunnel ทำให้ง่ายขึ้น

คุณยังใช้คำสั่ง Shopify shopify app dev ได้ ซึ่งตั้ง tunnel ภายในให้ มันทำงานได้ แต่มันห่อเซิร์ฟเวอร์ dev ในกระบวนการของมันเอง กลืน log แบบเฉพาะ และไม่ให้ปุ่ม replay สำหรับการพัฒนาแอปเกิน "hello world" URL สาธารณะที่เสถียรพร้อม tunnel localhost ที่จับคำขอ ประหยัดเวลามากกว่าที่ CLI ประหยัดในการตั้งค่าเริ่มต้น

npx portpreview 3000

คุณวาง URL HTTPS ในการตั้งค่า webhook ของแอปในแดชบอร์ด Partners ทริกเกอร์อีเวนต์จากร้านทดสอบ และคำขอลงใน handler ในเครื่องพร้อม header ครบ

ร้านทดสอบ: ส่วนที่เอกสาร Shopify ผ่านเร็ว

สองสิ่งที่ควรรู้ก่อนต่ออะไร:

  • ร้าน development ยิงทุกอีเวนต์ webhook การสร้างคำสั่งซื้อ fulfillment คลังสินค้า — ทั้งหมด ไม่ต้องปลอมอะไร แค่ติดตั้งแอปบนร้าน dev แล้วคลิก
  • secret ของ webhook ต่างกันต่อแอปและต่อช่องทางส่ง ถ้าคุณ subscribe EventBridge หรือ Pub/Sub ด้วย พฤติกรรม HMAC ต่างกัน ที่นี่เราพูดถึงการส่ง webhook HTTPS แบบธรรมดา

การตั้งค่าทีละขั้น

  1. เริ่มแอปในเครื่องบนพอร์ตที่ใช้ (3000 เป็นที่นิยม)
  2. รัน npx portpreview 3000 และคัดลอก URL HTTPS ที่พิมพ์ออกมา
  3. ในแดชบอร์ด Shopify Partners เปิดแอปและไปที่ Configuration → Webhooks
  4. ตั้ง endpoint webhook เป็น https://your-tunnel.portpreview.dev/api/webhooks/shopify (หรือ path ที่แอปใช้)
  5. ติดตั้งแอปบนร้าน dev แล้วทริกเกอร์อีเวนต์ — สร้างคำสั่งซื้อร่าง fulfill รายการ เปลี่ยนคลัง
  6. ดูคำขอลงมา ตรวจ header และ body replay คำขอที่จับไว้หลังแก้ handler ทุกครั้ง

ความผิดพลาดที่เราเห็นซ้ำ

body ถูกพาร์สก่อนตรวจ

ถ้าคุณวาง app.use(express.json()) ก่อน route webhook ไบต์ดิบหายไปแล้วตอนคุณพยายามตรวจ mount raw-body parser เฉพาะ path webhook หรือดึง body เองจาก request stream ก่อนการพาร์ส JSON ใด ๆ

สับสน secret

secret ของแอป Partners ไม่ใช่ token Storefront API HMAC ของ webhook ใช้ secret ของแอป ถ้าคุณจ้อง shp_xxx ในไฟล์ env คุณหยิบผิดตัว

อีเวนต์ทดสอบที่ดูเหมือนกัน

ปุ่ม "Send test notification" ของ Shopify ส่งอีเวนต์สังเคราะห์ที่มี body ตัวยึด ลายเซ็นบนอีเวนต์ทดสอบนั้นเป็นของจริง แต่ payload คงที่ สำหรับทดสอบรูปร่าง payload จริง ให้ทริกเกอร์อีเวนต์จากร้าน dev เอง

replay ต่อรองไม่ได้สำหรับ dev แอป

Shopify ลอง webhook ที่ล้มเหลวซ้ำได้ถึง 48 ชั่วโมงด้วย exponential backoff ใจกว้าง แต่ตอน iterate คุณไม่อยากรอ retry ครั้งถัดไป จับการส่งครั้งแรกใน ประวัติคำขอของ tunnel และ replay ตามต้องการกับ handler ในเครื่อง

เมื่อในเครื่องไม่พออีกต่อไป

flow การลงทะเบียนร้าน webhook GDPR และการเปลี่ยนการเรียกเก็บเงิน subscription ตรวจง่ายกว่าบนสภาพแวดล้อมที่ deploy เพราะมันโต้ตอบกับสถานะบัญชีของ Shopify เอง สำหรับที่เหลือ — การพาร์ส payload การตรวจลายเซ็น ตรรกะธุรกิจ — ในเครื่องเร็วกว่า

สำหรับคณิตศาสตร์ลายเซ็นพื้นฐานข้ามทุกผู้ให้บริการ อ่าน คู่มือการตรวจลายเซ็น webhook ของเรา เข้าร่วมรายชื่อรอ PortPreview ถ้าคุณอยากได้ tunnel ที่มี replay ในตัว

คำถามที่พบบ่อย

ฉันทดสอบ Shopify webhook บน localhost อย่างไร?
เริ่ม tunnel อย่าง PortPreview รับ URL HTTPS สาธารณะ และลงทะเบียนเป็น endpoint webhook ในแดชบอร์ด Shopify Partners ติดตั้งแอปบนร้าน development ทริกเกอร์อีเวนต์ และการส่งจะถึง handler ในเครื่องของคุณ
ทำไมการตรวจ HMAC ของ Shopify ล้มเหลวเสมอ?
ส่วนใหญ่คือการเข้ารหัส Shopify เซ็นด้วย HMAC SHA-256 และเข้ารหัสผลลัพธ์เป็น base64 ถ้าโค้ดของคุณใช้ .digest('hex') แทน .digest('base64') การเทียบทุกครั้งล้มเหลว แม้ secret ถูกต้อง
ต้องใช้ shopify app dev หรือใช้ tunnel ใดก็ได้?
tunnel ใดก็ได้ shopify app dev มี tunnel ของตัวเองเพื่อความสะดวก แต่ tunnel อเนกประสงค์ที่มีการจับและ replay คำขอมีประโยชน์กว่าสำหรับการพัฒนาต่อเนื่อง