Shopify gửi một webhook đang thất bại 9 lần: lần đầu cộng 8 lần thử lại, lần cuối diễn ra 4 giờ sau sự kiện. Sau đó nó dừng. Trong thử nghiệm của chúng tôi, Shopify không gửi thêm gì cho những đơn hàng đó, kể cả khi server đã hoạt động trở lại. Đơn hàng vẫn nằm trong Shopify, còn hệ thống của bạn không bao giờ biết về chúng qua webhook.
Tài liệu của Shopify cho biết tổng số, 8 lần thử lại trong 4 giờ, nhưng không nói mỗi lần gửi diễn ra lúc nào. Vì vậy chúng tôi đã đo. Chúng tôi đặt bốn đơn hàng thử nghiệm trên một cửa hàng phát triển (development store) của Shopify, cho server nhận trả 503 trong gần sáu giờ và ghi lại mọi request.
TL;DR
- Các lần gửi đến vào khoảng 2 giây, 3 giây, 1 phút, 3 phút, 8 phút, 30 phút, 1 giờ, 2 giờ và 4 giờ. Cả bốn đơn đều theo đúng lịch này, lệch nhau không quá 1,4 giây
- Sau lần gửi thứ chín thì không còn gì. Server nhận hoạt động lại chưa đầy hai giờ sau đó, và không đơn nào trong bốn đơn tới được nó qua webhook
- “19 lần thử lại trong 48 giờ”, con số nhiều bài hướng dẫn vẫn nhắc, đã lỗi thời
- Một webhook thêm trong Shopify admin không bị xóa sau 36 lượt gửi thất bại liên tiếp. Trang admin không hiện lỗi nào, và chủ cửa hàng không nhận được email
- Muốn qua được sự cố ngừng hoạt động dài hơn bốn giờ cần hai thứ: một thành phần lưu sự kiện và trả lời Shopify khi server của bạn ngừng hoạt động, và một tác vụ đối soát với Admin API. Với webhook thêm trong admin hoặc một app nhỏ, thay đổi nhỏ nhất là dùng một dịch vụ trung chuyển như AnyHook: bạn đổi một URL
Lịch thử lại đã đo
| Lần gửi | Thời gian sau đơn hàng | Khoảng cách với lần trước |
|---|---|---|
| 1 | 2 giây | |
| 2 | 3 giây | 1 giây |
| 3 | 1 phút | 1 phút |
| 4 | 3 phút | 2 phút |
| 5 | 8 phút | 5 phút |
| 6 | 30 phút | 22 phút |
| 7 | 1 giờ | 30 phút |
| 8 | 2 giờ | 1 giờ |
| 9 | 4 giờ | 2 giờ |
| Sau đó | Không còn gì |
Thời gian tính từ X-Shopify-Triggered-At, dấu thời gian Shopify gắn vào webhook.
Năm trong chín lần gửi dồn vào tám phút đầu. Chúng đủ cho một lỗi thoáng qua nhưng ít giúp được khi deploy hỏng: lúc có người nhìn vào cảnh báo thì chỉ còn bốn lần.
Lịch này có vẻ cố định. Ở mỗi bước, bốn đơn đến cách nhau không quá 1,4 giây, nên không thấy độ lệch ngẫu nhiên (jitter) nào. Một loạt đơn cùng thất bại sẽ được thử lại thành cùng một loạt: đợt tăng đột biến của flash sale quay lại sau một phút, rồi ba phút, rồi tám phút.
Các lần thử lại của một sự kiện mang cùng một ID. Ở đơn chúng tôi so sánh, X-Shopify-Webhook-Id giống hệt nhau qua cả chín lần gửi, nên đây là khóa an toàn để chống trùng lặp.
Ranh giới quan trọng là bốn giờ. Khi endpoint của bạn hoạt động lại, mọi đơn hàng trong bốn giờ qua vẫn đang trên đường tới, ở lần gửi tiếp theo theo lịch. Mọi đơn cũ hơn thế sẽ không bao giờ tới.
“19 lần thử lại trong 48 giờ” đã lỗi thời
Bạn vẫn sẽ gặp con số này, kể cả trong các bài hướng dẫn mà nhà cung cấp webhook đăng năm nay. Nó đến từ chính sách cũ của Shopify. Shopify đã đổi cơ chế vào ngày 10 tháng 9 năm 2024 thành 8 lần thử lại trong 4 giờ, và tài liệu hiện tại cũng nói như vậy. Nhật ký của chúng tôi khớp: lần gửi cuối đến ở mốc bốn giờ, và hôm sau không có thêm gì.
Nếu runbook hoặc kế hoạch khôi phục của bạn giả định hai ngày, nó sai gấp mười hai lần.
Điều gì xảy ra với các đơn hàng
Trên webhook gửi thẳng, cả 36 lượt gửi (bốn đơn, mỗi đơn chín lần) đều nhận 503. Server nhận bắt đầu trả 200 trở lại 1 giờ 37 phút sau lần gửi cuối trong số đó, và Shopify không thử nữa. Không đơn nào trong bốn đơn tới được nó.
Sự cố này không có gì bất thường. Một lần deploy hỏng vào tối thứ Sáu, được sửa trước nửa đêm, cũng trông như vậy. Bạn mất đơn nào phụ thuộc vào việc server ngừng hoạt động bao lâu. Trong sự cố sáu giờ, các đơn của hai giờ đầu mất khỏi đường webhook, còn các đơn của bốn giờ cuối tới ở lần gửi tiếp theo theo lịch, muộn nhất hai giờ sau khi bạn hoạt động lại. Điều đó đúng với một webhook còn sống qua sự cố. Subscription tạo qua Admin API có thể bị xóa giữa chừng, và khi đó sẽ không có gì mới được gửi cho đến khi bạn tạo lại nó.
Chạy chuyện đó trong đợt sale Black Friday, lúc server chịu tải nặng nhất và mỗi giờ có nhiều đơn nhất, thì những giờ đầu phải được lấy bù (backfill) từ Admin API. Không có cách nào khác để lấy lại. Các bước nằm trong bài Shopify đã xóa webhook subscription của bạn.
Webhook vẫn còn đó, và không ai được báo
Tài liệu của Shopify nói một subscription bị xóa sau 8 lần thất bại liên tiếp chỉ khi nó được cấu hình qua Admin API, và cảnh báo được gửi tới emergency developer email của app. Tài liệu không nhắc tới webhook thêm thủ công trong Shopify admin, ở mục Settings → Notifications → Webhooks. Chúng không có app nào đứng sau, và thường là thứ nối một cửa hàng với ERP hoặc kho.
Vì vậy chúng tôi đã kiểm tra. Sau 36 lượt gửi thất bại liên tiếp, hôm sau:
- Webhook vẫn nằm trong danh sách, không đổi gì
- Trang admin không hiện lịch sử gửi và không có dấu hiệu lỗi. Mỗi dòng chỉ có sửa, gửi thử và xóa
- Không có email cảnh báo nào tới địa chỉ của chủ cửa hàng
Một webhook thất bại mọi lượt gửi suốt một ngày trông giống hệt một webhook khỏe. Triệu chứng duy nhất là đơn hàng thiếu ở phía sau. Subscription tạo qua Admin API thì hỏng theo cách khác: Shopify xóa nó.
Cách qua được sự cố ngừng hoạt động dài hơn bốn giờ
Bạn cần một thành phần lưu sự kiện và trả lời Shopify khi server của bạn ngừng hoạt động, cùng một tác vụ đối soát cho những gì vẫn lọt qua. Tài liệu của chính Shopify cũng khuyên xây tác vụ đó. Chúng tôi làm ra một trong các lựa chọn dưới đây, nên hãy cân nhắc ý kiến của chúng tôi cho phù hợp.
| Lựa chọn | Dùng được với webhook thêm trong admin | Phù hợp nhất với | Điểm cần lưu ý |
|---|---|---|---|
| Tác vụ đối soát trên Admin API | Có | Mọi cửa hàng mà một đơn bị thiếu là mất tiền | Chạy sau sự việc, theo lịch của bạn. Bạn tự xây và tự vận hành |
| Amazon EventBridge hoặc Google Pub/Sub | Không, form trong admin chỉ nhận một URL | App đã chạy trên đám mây đó | Chỉ cho subscription của app. Bạn tự xây consumer, thời gian lưu và cảnh báo |
| Hàng đợi riêng phía sau một endpoint nhỏ | Có | Team muốn giữ mọi thứ trong nhà | Endpoint đó cũng chỉ có năm giây để trả lời, và nó phải sống khi phần còn lại của hệ thống không sống |
| Hookdeck | Có | Team nền tảng cần biến đổi dữ liệu, quy trình làm việc theo nhóm và lưu lượng lớn | Nhiều sản phẩm hơn mức một tác vụ thử lại và phát lại cần |
| AnyHook | Có | Team nhỏ và webhook trong admin: đổi một URL, không cần code, bắt đầu miễn phí | Gửi theo kiểu ít nhất một lần, nên bạn vẫn phải chống trùng lặp. Gói Free ngừng thử lại ở phút thứ 35 và từ chối sự kiện vượt quá 1.000 mỗi ngày |
Nên chọn cái nào:
- Bạn thêm webhook thủ công, hoặc chạy một app nhỏ, và muốn đơn hàng sống sót qua một đêm tệ: đặt một dịch vụ trung chuyển trước endpoint của bạn. AnyHook được làm cho đúng trường hợp này. Một URL thay đổi và handler của bạn giữ nguyên
- Bạn chạy team nền tảng xử lý lưu lượng lớn: Hookdeck là sản phẩm đầy đủ hơn. Đây là so sánh giữa hai bên
- App của bạn đã chạy trên AWS hoặc Google Cloud: EventBridge hoặc Pub/Sub hợp với những gì bạn đang có
- Trong mọi trường hợp: giữ một tác vụ đối soát cho đơn hàng. Dịch vụ trung chuyển dời ranh giới từ bốn giờ sang khoảng thử lại và thời gian lưu của chính nó, và ranh giới vẫn còn đó
Cùng thử nghiệm đó, qua AnyHook
AnyHook là dịch vụ trung chuyển webhook (webhook relay): bạn trỏ webhook về nó, nó lưu sự kiện, trả lời bên gửi và chuyển tiếp tới server của bạn, thử lại khi server lỗi. Trong thử nghiệm này, mỗi đơn hàng còn đi tới một webhook thứ hai trỏ về AnyHook, và AnyHook chuyển tiếp tới cùng server nhận:
| Lần gửi | Qua AnyHook |
|---|---|
| 1 | 2 giây |
| 2 | 5 phút |
| 3 | 35 phút |
| 4 | 3 giờ 35 phút |
| 5 | 21 giờ 35 phút, đã tới nơi |
AnyHook trả 200 cho Shopify sau khoảng một giây, nên Shopify không có gì để thử lại, và lịch này là của riêng AnyHook tới cùng server nhận đang lỗi. Rất lâu sau khi các lần gửi của Shopify trên webhook gửi thẳng đã kết thúc, lần gửi thứ năm thấy server nhận đã khỏe và gửi ba đơn còn đang chờ. Bản sao của đơn đầu tiên đã tới từ phút thứ 35, do một sơ suất phía chúng tôi, được giải thích ở phần Cách chúng tôi thử nghiệm.
Bước chuyển tiếp làm lần gửi đầu chậm thêm 0,5 đến 0,9 giây. Chữ ký của Shopify vẫn giữ nguyên qua bước đó: server nhận xác minh X-Shopify-Hmac-SHA256 gốc trên các bản được chuyển tiếp và mọi lần xác minh đều đạt, nên code xác minh bạn đang có vẫn dùng được. AnyHook gửi email ở lượt gửi thất bại đầu tiên và một lần nữa khi thất bại năm lần liên tiếp. Shopify không gửi email nào.
Thử nghiệm này chạy trên tài khoản Enterprise của chính chúng tôi. Thời điểm thử lại giống nhau ở mọi gói. Thứ thay đổi là số lần gửi và thời gian bạn có thể phát lại:
| Gói | Giá | Số lần gửi | Lần gửi tự động cuối | Thời gian phát lại |
|---|---|---|---|---|
| Free | $0, không cần thẻ | 3 | 35 phút | 3 ngày |
| Pro | $20/tháng | 5 | 21 giờ 35 phút | 30 ngày |
| Scale | $90/tháng | 10 | 5 ngày 21 giờ | 90 ngày |
Trên gói Free, thử nghiệm này sẽ diễn ra khác: việc thử lại dừng ở phút thứ 35, trước khi server nhận hoạt động lại. Các đơn hàng vẫn nằm trong nhật ký, và bạn sẽ phát lại thủ công trong vòng ba ngày. Gói Free cũng từ chối sự kiện vượt quá 1.000 mỗi ngày, nên không phải gói để chạy một đợt sale. Gói Pro không có giới hạn theo ngày, và lần gửi thứ năm chạy như ở đây.
Một dịch vụ trung chuyển là thêm một bên nữa trên đường đi của đơn hàng. AnyHook lưu từng sự kiện, được mã hóa khi lưu trữ, trước khi trả lời. Nếu không lưu được một sự kiện, nó trả 503 thay vì 200, và cơ chế thử lại của chính Shopify sẽ tiếp quản.
Thiết lập
-
Trong AnyHook, tạo một App và đặt URL webhook hiện tại của bạn làm đích. Muốn xem thử mà chưa cần tài khoản, lệnh này trả về một
inbound_urlđang hoạt động, một API key và mộtclaim_url. Endpoint và các sự kiện của nó bị xóa sau 7 ngày, trừ khi bạn mở URL claim và đăng nhập:curl -X POST https://anyhook.net/api/v1/quickstart -
Trong Shopify admin, vào Settings → Notifications → Webhooks → Create webhook. Chọn sự kiện, đặt định dạng là JSON và dán inbound URL.
-
Sao chép signing secret (khóa bí mật dùng để ký) hiển thị trên trang đó vào source signing secret của App, rồi gửi thử từ menu của webhook. Nó phải xuất hiện trong nhật ký của App, và một request giả mạo sẽ nhận 401. Subscription do app của bạn tạo thì ký bằng client secret của app.
Một AI coding agent có thể làm bước đầu tiên bằng npx -y anyhook-mcp.
FAQ
Shopify thử lại webhook thất bại bao nhiêu lần?
Tám lần sau lần gửi đầu, tức tổng cộng chín lần gửi, trong bốn giờ. Chúng tôi đo được các lần gửi vào khoảng 2 giây, 3 giây, 1 phút, 3 phút, 8 phút, 30 phút, 1 giờ, 2 giờ và 4 giờ sau sự kiện.
Shopify có còn thử lại webhook trong 48 giờ không?
Không. 19 lần thử lại trong 48 giờ ở các bài hướng dẫn cũ có từ trước thay đổi Shopify thực hiện vào tháng 9 năm 2024. Trong thử nghiệm tháng 11 năm 2026 của chúng tôi, lần gửi cuối đến ở mốc bốn giờ.
Shopify có gửi lại webhook khi server của tôi hoạt động trở lại không?
Chỉ khi các lần gửi của sự kiện đó vẫn đang chạy. Một sự kiện phát sinh chưa tới bốn giờ trước khi bạn hoạt động lại sẽ tới ở lần gửi tiếp theo theo lịch. Sự kiện cũ hơn thì không được gửi lại, nên bạn lấy nó từ Admin API.
Thế nào được tính là một lượt gửi thất bại?
Theo tài liệu của Shopify, là mọi phản hồi ngoài dải 200, kể cả redirect 3xx, và không có phản hồi trong năm giây, trong đó một giây dành cho việc kết nối. Thử nghiệm của chúng tôi chỉ dùng phản hồi 503.
Shopify có xóa webhook cứ thất bại mãi không?
Theo tài liệu của Shopify, subscription tạo qua Admin API bị xóa sau 8 lần thất bại liên tiếp. Trong thử nghiệm của chúng tôi, một webhook thêm trong Shopify admin vẫn còn trong danh sách vào ngày hôm sau, sau 36 lần thất bại liên tiếp.
Shopify có cảnh báo khi webhook thất bại không?
Với subscription của app, email cảnh báo được gửi tới địa chỉ emergency developer của app. Với webhook chúng tôi thêm trong admin, không có email nào tới chủ cửa hàng và trang admin không hiện lỗi nào.
Làm sao lấy lại các đơn hàng mà Shopify đã ngừng thử lại?
Truy vấn Admin API cho khoảng thời gian xảy ra sự cố và cho kết quả chạy qua handler thông thường của bạn, với chống trùng lặp được bật. Các bước nằm trong bài Shopify đã xóa webhook subscription của bạn.
Cách tốt nhất để giữ webhook Shopify khi server của tôi ngừng hoạt động là gì?
Đặt một dịch vụ trung chuyển webhook hoặc hàng đợi trước endpoint của bạn: nó trả lời Shopify, lưu sự kiện và thử lại tới server của bạn theo một lịch dài hơn. Với webhook thêm trong admin hoặc một app nhỏ, AnyHook là thay đổi nhỏ nhất, chỉ một URL và có gói miễn phí, còn các gói trả phí thử lại lâu hơn và không có giới hạn theo ngày. Hookdeck hợp với team nền tảng cần biến đổi dữ liệu và quy trình làm việc theo nhóm. App chạy trên AWS hoặc Google Cloud có thể gửi subscription của mình tới EventBridge hoặc Pub/Sub. Trong mọi trường hợp, hãy giữ một tác vụ đối soát với Admin API.
Cách chúng tôi thử nghiệm
Chúng tôi chạy thử nghiệm này vào ngày 2 tháng 11 năm 2026, trên một cửa hàng phát triển của Shopify, API phiên bản 2026-10, topic orders/create. Cả hai webhook đều được thêm trong admin. Server nhận là một Cloudflare Worker ghi lại mọi request và trả 503 cho đến 17:43 UTC, sau đó trả 200. Bốn đơn hàng được đặt trong khoảng 11:46 đến 12:06 UTC.
Thời điểm đến chính xác trên webhook gửi thẳng, tính bằng giây sau X-Shopify-Triggered-At:
| Lần gửi | Trên bốn đơn hàng |
|---|---|
| 1 | 1,4 đến 1,9 |
| 2 | 2,3 đến 2,9 |
| 3 | 63,3 đến 64,2 |
| 4 | 183,6 đến 185,0 |
| 5 | 484,4 đến 485,8 |
| 6 | 1805,4 đến 1806,5 |
| 7 | 3606,2 đến 3607,4 |
| 8 | 7207,0 đến 7208,2 |
| 9 | 14407,7 đến 14409,0 |
Có hai điều không hoàn hảo. Một sơ suất phía chúng tôi khiến server nhận hoạt động trong vài phút quanh 12:20 UTC. Không lần gửi trực tiếp nào rơi vào khoảng đó, nhưng bản sao qua AnyHook của đơn đầu tiên thì có, nên đơn đó bị loại khỏi kết quả của AnyHook. Và server nhận chỉ bắt đầu kiểm tra chữ ký giữa chừng, nên các lần gửi đầu tiên trên cả hai đường chưa được kiểm tra.
Chúng tôi không thử timeout hay kết nối bị từ chối, chỉ thử một phản hồi 503 nhanh. Chúng tôi không thử subscription tạo qua Admin API, nên quy tắc xóa ở trên lấy từ tài liệu của Shopify. Chúng tôi không thử trên cửa hàng thật hay với lưu lượng lớn. Bốn đơn là mẫu nhỏ, dù một lịch đều đặn thế này khó có thể là ngẫu nhiên. Shopify có thể thay đổi bất cứ điều gì ở đây, nên hãy kiểm tra ngày ở trên.