Chiến lược thử lại webhook trong hệ thống thanh toán
Qvard thử lại webhook thanh toán thế nào, điều gì tính là gửi thất bại và cách xây trình xử lý chịu được thử lại, lặp lại và ngừng hoạt động mà không thực hiện đơn hai lần.

Webhook thanh toán là một thông điệp không được mất và không được xử lý hai lần. Mạng lỗi, máy chủ khởi động lại, nên bên gửi thử lại; vì vậy trình xử lý của bạn phải được viết với tính toán cho việc lặp lại. Bài này mô tả cách Qvard thử lại, điều gì nó coi là thất bại và cách xây trình xử lý cùng quy trình khôi phục xoay quanh hành vi đó.
Khi nào webhook được gửi
Qvard gửi webhook khi thanh toán hoàn tất. Đó là một POST tới callback URL đã lưu trong dự án, với một JSON nhỏ chứa id của bạn (mã định danh bạn đưa khi tạo thanh toán) và status, có chữ ký trong header X-Qvard-Signature.
Khoản thanh toán thiếu, bị từ chối hoặc còn đang chờ không sinh ra webhook hoàn tất. Nếu cần biết về chúng, hãy xem trong bảng điều khiển; đừng chờ một thông điệp không được gửi.
Điều gì được coi là đã giao
Qvard coi một lần gửi là thành công chỉ khi endpoint của bạn trả lời đúng HTTP 200. Mọi thứ còn lại là thất bại:
- bất kỳ mã trạng thái nào khác, gồm
201,202,204và mọi4xx,5xx; - quá thời gian, vì máy chủ của bạn chậm hoặc không truy cập được;
- chuyển hướng: Qvard không theo
301hay302, nên quy tắc chuyểnhttpsanghttpshoặc thêm dấu gạch chéo cuối khiến lần gửi thất bại.
Sự chính xác này là có chủ đích. 204 No Content trông như một câu trả lời hoàn toàn tốt nhưng vẫn là lần gửi thất bại. Hãy trả 200, nếu muốn thì với nội dung trống.
Cách việc thử lại hoạt động
Sau một lần gửi thất bại, Qvard thử lại. Tổng cộng có năm lần thử trong khoảng sáu phút. Hai hệ quả:
- Gián đoạn ngắn được bao phủ. Nếu máy chủ khởi động lại hoặc một lần triển khai mất một hai phút, một lần thử sau đó thành công và bạn không mất gì.
- Gián đoạn dài hơn thì không. Sau lần thử cuối, Qvard dừng. Nếu máy chủ của bạn ngừng quá sáu phút đó, thông báo không tự đến; hãy gửi lại cho khoản thanh toán đó từ bảng điều khiển.
Vì có thử lại, trình xử lý của bạn đôi khi sẽ thấy cùng một sự kiện nhiều lần, ví dụ khi máy chủ đã làm xong việc nhưng câu trả lời bị mất trên đường về. Ví dụ bên dưới được xây cho điều đó: bản cập nhật chỉ thay đổi đơn chưa thanh toán, việc thực hiện chỉ được xếp hàng khi có một dòng thay đổi, và câu trả lời luôn là 200.
import os
from fastapi import FastAPI, Request, Response
from qvard import Qvard
app = FastAPI()
SECRET = os.environ["QVARD_SECRET_KEY"]
qvard = Qvard(SECRET)
@app.post("/webhooks/qvard")
async def qvard_webhook(request: Request):
try:
body = qvard.webhooks.construct_event(await request.body(), request.headers.get("X-Qvard-Signature", ""), SECRET)
except ValueError:
return Response(status_code=400)
order_id, status = body.get("id"), body.get("status")
# Idempotent: the UPDATE changes a row only for an order that is not paid yet.
if status == "completed":
changed = await db.execute(
"UPDATE orders SET state = 'paid' WHERE id = :id AND state <> 'paid'", {"id": order_id}
)
if changed:
await queue.enqueue("fulfil", order_id) # slow work after the answer
return Response(status_code=200) # exactly 200, also for repeats and unknown ordersTrình xử lý của bạn phải làm gì
Bốn quy tắc bao phủ gần như mọi vấn đề:
- Xác minh chữ ký. Kiểm tra header
X-Qvard-Signature(HMAC-SHA256 bằng khóa bí mật của bạn trên dấu thời gian và nội dung thô) theo thời gian không đổi và trả400nếu không khớp. Hãy làm bước này trước khi xử lý thanh toán. Xem cách bảo mật webhook thanh toán crypto. - Idempotent. Làm cho việc đổi trạng thái có điều kiện, «đánh dấu đã trả nếu chưa trả», lý tưởng là trong một câu lệnh cơ sở dữ liệu, để hai lần gửi song song không thể cùng thắng.
- Trả lời nhanh. Kiểm tra, cập nhật, xếp hàng, trả lời đúng 200. Làm email, tạo tệp và gọi hệ thống khác từ hàng đợi, để một dịch vụ chậm không biến thành quá thời gian của webhook.
- Trả 200 cho những gì bạn sẽ không bao giờ xử lý. Đơn không rõ hoặc một lần lặp cũng phải trả 200; nếu không Qvard cứ thử lại một thông điệp bạn đã xử lý hoặc sẽ không bao giờ xử lý.
Những lỗi trông như lỗi của Qvard
Phần lớn báo cáo «webhook không đến» có nguyên nhân ở phía nhận:
- Tường lửa hoặc WAF chặn yêu cầu từ địa chỉ lạ, hoặc trang «sắp ra mắt» hay bảo trì.
- Một chuyển hướng từ
httpsanghttps, từwwwsang tên miền trần hoặc sang dấu gạch chéo cuối. - Lỗi framework sau khi phản hồi đã được quyết định, ví dụ một ngoại lệ trong middleware biến 200 của bạn thành 500.
- Trình xử lý chậm làm việc trước khi trả lời và vướng quá thời gian.
- Bộ nhớ đệm hoặc CDN đặt trước route.
- URL sai được lưu trong dự án, hoặc gõ nhầm route.
Hãy thử endpoint từ ngoài mạng của bạn bằng một yêu cầu mang header X-Qvard-Signature hợp lệ và xem nhật ký truy cập của máy chủ để biết mã trả lời, không chỉ việc yêu cầu có đến hay không.
Quy trình khôi phục cho thông báo bị bỏ lỡ
Việc thử lại bao phủ gián đoạn ngắn; với phần còn lại, hãy thêm một lưới an toàn.
- Ghi log mọi lần gửi với
id,status, quyết định của bạn (đã áp dụng, lặp lại, đơn không rõ, khóa bị từ chối) và thời gian. Một dòng log bị thiếu là cách bạn nhận ra khoảng trống. - Đối soát theo lịch. Mỗi ngày một lần, so sánh các đơn bạn vẫn coi là chưa trả với các khoản thanh toán và số dư trong bảng điều khiển Qvard. Bất cứ thứ gì trong bảng điều khiển đã hoàn tất mà bên bạn chưa trả đều là thông báo bị bỏ lỡ.
- Gửi lại từ bảng điều khiển cho những khoản đó và để trình xử lý idempotent áp dụng chúng.
- Cảnh báo khi một loạt yêu cầu nhận lỗi, để biết endpoint hỏng trong vài phút chứ không phải cuối ngày.
Phần còn lại của việc tích hợp, gồm tạo thanh toán, nằm trong cách nhận thanh toán crypto trên website và API thanh toán crypto cho nhà phát triển.
FAQ
Qvard thử lại webhook bao nhiêu lần?
Tổng cộng năm lần trong khoảng sáu phút. Sau đó Qvard dừng, và bạn có thể gửi lại thông báo của khoản thanh toán từ bảng điều khiển.
Endpoint của tôi phải trả mã trạng thái nào?
Đúng 200. Mọi trạng thái khác, gồm 204, quá thời gian hay chuyển hướng đều tính là gửi thất bại và được thử lại.
Cùng một webhook có thể đến hai lần không?
Có, ví dụ khi máy chủ đã xử lý lần gửi đầu nhưng câu trả lời bị mất. Hãy làm trình xử lý idempotent và vẫn trả 200 cho các lần lặp.
Tôi có nhận webhook cho khoản thanh toán thiếu hoặc bị từ chối không?
Không. Webhook được gửi khi thanh toán hoàn tất. Hãy kiểm tra các khoản khác trong bảng điều khiển.
