Tất cả bài viết
Shopifywebhook debugginglocal testinge-commerce

Kiểm thử webhook Shopify cục bộ không bất ngờ

Hầu hết bug webhook Shopify trong phát triển cục bộ quy về một dòng: HMAC được mã hóa base64, không phải hex. Nếu bạn gọi .digest('hex') và so với X-Shopify-Hmac-Sha256, kiểm tra sẽ không bao giờ qua — kể cả với shared secret đúng. Chúng tôi đã thấy kỹ sư senior mất cả buổi chiều vì một ký tự đó.

Cái bẫy base64

Stripe dùng hex. GitHub dùng hex (với tiền tố sha256=). Shopify dùng base64. Ba nhà cung cấp, ba kiểu mã hóa. Phiên bản đầu của trình xác minh gần như chắc chắn sẽ copy một snippet từ dự án khác và thất bại âm thầm.

Đây là cách xác minh nên trông như sau:

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']),
);

Ngoài mã hóa, hai điều quan trọng. Body phải là byte thô, chưa parse. Và so sánh phải timing-safe — so bằng chuỗi rò rỉ thông tin từng byte một.

Vì sao một tunnel làm việc này dễ hơn

Bạn cũng có thể dùng lệnh Shopify shopify app dev, vốn dựng một tunnel nội bộ. Nó chạy được. Nhưng nó cũng bọc máy chủ dev của bạn trong tiến trình riêng, nuốt log theo một cách nhất định, và không cho bạn nút replay. Cho phát triển app vượt quá "hello world", một URL công khai ổn định cộng với tunnel localhost có bắt yêu cầu tiết kiệm nhiều thời gian hơn CLI tiết kiệm ở thiết lập ban đầu.

npx portpreview 3000

Bạn dán URL HTTPS vào cấu hình webhook của app trong dashboard Partners, kích hoạt một sự kiện từ một test store, và yêu cầu đáp xuống handler cục bộ với tất cả header còn nguyên.

Test store: phần tài liệu Shopify lướt qua

Hai điều cần biết trước khi nối bất cứ thứ gì:

  • Store development bắn tất cả sự kiện webhook. Tạo đơn, fulfillment, tồn kho — tất cả. Bạn không cần giả lập gì; chỉ cần cài app lên store dev và click loanh quanh.
  • Secret webhook khác nhau theo app và theo kênh giao. Nếu bạn cũng subscribe EventBridge hay Pub/Sub, hành vi HMAC khác. Ở đây ta nói về giao webhook HTTPS thuần.

Thiết lập từng bước

  1. Khởi động app cục bộ trên cổng bạn dùng (3000 phổ biến).
  2. Chạy npx portpreview 3000 và sao chép URL HTTPS nó in ra.
  3. Trong dashboard Shopify Partners, mở app và vào Configuration → Webhooks.
  4. Đặt endpoint webhook thành https://your-tunnel.portpreview.dev/api/webhooks/shopify (hoặc path app bạn dùng).
  5. Cài app lên store dev, rồi kích hoạt một sự kiện — tạo đơn nháp, fulfill một mục, đổi tồn kho.
  6. Xem yêu cầu đáp xuống. Kiểm tra header và body. Replay yêu cầu đã bắt sau mỗi lần sửa handler.

Những lỗi chúng tôi cứ thấy

Body bị parse trước khi xác minh

Nếu bạn đặt app.use(express.json()) trước route webhook, byte thô đã mất khi bạn cố xác minh. Mount một parser body thô chỉ trên path webhook, hoặc kéo body thủ công từ request stream trước bất kỳ parse JSON nào.

Nhầm lẫn secret

Secret app Partners không giống token Storefront API. HMAC webhook dùng secret app. Nếu bạn đang nhìn shp_xxx trong file env, bạn lấy nhầm rồi.

Sự kiện test trông giống hệt nhau

Nút "Send test notification" của Shopify giao một sự kiện tổng hợp với body giữ chỗ. Chữ ký trên sự kiện test đó là thật, nhưng payload cố định. Để test hình dạng payload thực tế, kích hoạt sự kiện từ chính store dev.

Replay không thể thương lượng cho dev app

Shopify thử lại webhook thất bại tới 48 giờ với backoff lũy thừa. Hào phóng, nhưng khi lặp bạn không muốn chờ lần thử kế. Bắt lần giao đầu tiên trong lịch sử yêu cầu của tunnel và replay theo nhu cầu vào handler cục bộ.

Khi cục bộ không còn đủ

Luồng đăng ký shop, webhook GDPR và thay đổi billing subscription dễ xác thực hơn trên môi trường đã deploy vì chúng tương tác với trạng thái tài khoản của chính Shopify. Còn mọi thứ khác — parse payload, xác minh chữ ký, logic nghiệp vụ — cục bộ nhanh hơn.

Về toán chữ ký nền tảng trên mọi nhà cung cấp, đọc hướng dẫn xác minh chữ ký webhook của chúng tôi. Tham gia danh sách chờ PortPreview nếu bạn muốn một tunnel tích hợp sẵn replay.

Câu hỏi thường gặp

Làm sao kiểm thử webhook Shopify trên localhost?
Khởi động một tunnel như PortPreview, lấy một URL HTTPS công khai, và đăng ký nó làm endpoint webhook trong dashboard Shopify Partners. Cài app lên một store development, kích hoạt sự kiện, và các lần giao tới handler cục bộ của bạn.
Vì sao xác minh HMAC Shopify của tôi luôn thất bại?
Hầu hết là do mã hóa. Shopify ký bằng HMAC SHA-256 và mã hóa kết quả base64. Nếu code của bạn dùng .digest('hex') thay vì .digest('base64'), mọi so sánh đều thất bại, kể cả khi secret đúng.
Tôi có cần shopify app dev hay dùng tunnel nào cũng được?
Tunnel nào cũng được. shopify app dev bao gồm tunnel riêng cho tiện, nhưng một tunnel đa dụng có bắt và replay yêu cầu hữu ích hơn cho phát triển lâu dài.