اگر تا حالا سایتی را به درگاه پرداخت وصل کردهاید، حتماً این کلمه را دیدهاید: Webhook، یا همان «کالبک». بعضیها آن را ساده میگیرند و بعضی تا آخر راهاندازی از آن میترسند. خوشبختانه ایدهاش ساده است.
مثال روزمره
فرض کنید پیتزا سفارش دادهاید. دو راه برای فهمیدن زمان رسیدن هست: هر پنج دقیقه به مغازه زنگ بزنید و بپرسید «آماده شد؟»، یا بگذارید خودشان وقتی آماده شد زنگ بزنند. راه اول polling است و راه دوم وبهوک.
در پرداخت چه معنایی دارد؟
سایت شما تراکنشی میسازد و منتظر میماند. وقتی مشتری پرداخت کرد و سرور پرداخت آن را تایید کرد، به یک آدرس از سایت شما که از قبل تعیین شده، یک درخواست میفرستد: «سفارش فلان پرداخت شد». سایت شما با دریافت این پیام، سفارش را کامل میکند.
مزیتها واضح است: لازم نیست مدام وضعیت را بپرسید، تاخیر کم میشود، و اگر مشتری مرورگر را ببندد هم ماجرا خراب نمیشود، چون اطلاع از سرور به سرور میرسد و به مرورگر مشتری وابسته نیست.
یک درخواست وبهوک شبیه چیست؟
یک درخواست HTTP از نوع POST با بدنهی JSON، مثلاً شامل شناسهی تراکنش، شمارهی سفارش شما، مبلغ و وضعیت. معمولاً یک هدر امضا هم دارد. در مستندات سرویس، نمونهی دقیق و نام فیلدها آمده است.
مهمترین قدم: بررسی امضا
آدرس وبهوک شما یک آدرس عمومی است. هر کسی که آن را بداند میتواند به آن درخواست بفرستد و ادعا کند سفارشی پرداخت شده. اگر سایت شما بدون بررسی باور کند، فروشگاه شما در عمل رایگان شده است.
راهحل، امضای درخواست است: سرور پرداخت با یک کلید مخفی که فقط شما و او میدانید، یک مهر روی درخواست میزند. شما همان محاسبه را انجام میدهید و اگر نتیجه برابر نبود، درخواست را رد میکنید. دو نکتهی ریز:
- مقایسهی امضا را با تابعی انجام دهید که زمان ثابت است (مثل
hash_equalsدر PHP) تا از حملهی زمانی در امان بمانید. - زمان درخواست را هم بررسی کنید تا یک درخواست قدیمی دوباره پخش نشود (Replay).
تکراری بودن درخواستها
گاهی وبهوک دو بار میرسد: مثلاً سرور شما دیر جواب داده و فرستنده دوباره امتحان کرده است. برنامهی شما باید این را تحمل کند. قاعدهی ساده: قبل از انجام کار، وضعیت سفارش را بخوانید. اگر قبلاً «پرداختشده» بود، کاری نکنید و فقط «باشه» جواب دهید. به این ویژگی میگویند idempotent بودن، و جلوی ارسال دو بار کالا یا شارژ دو بار کیف پول را میگیرد.
چه جوابی بدهیم؟
معمولاً کد ۲۰۰ یعنی «گرفتم». اگر چیزی غیر از ۲۰۰ برگردانید، فرستنده فرض میکند پیام نرسیده و بعداً دوباره میفرستد. پس هر کاری که زمانبر است (ارسال ایمیل، ساخت فایل) را بعد از جواب دادن یا در صف انجام دهید؛ وبهوک باید سریع جواب بدهد.
تست روی سیستم محلی
سرور پرداخت نمیتواند به localhost شما درخواست بفرستد. برای تست از ابزارهای تونل مثل ngrok استفاده میکنند، یا حالت آزمایشی سرویس را باز میکنند تا تراکنشهای تستی بسازید و ببینید وبهوک چطور میرسد.
یک نمونهی ساده با PHP
تصور کنید وبهوک شما فایلی به نام hook.php است. در سادهترین شکل، کارش این است: بدنهی خام درخواست را بخواند، امضا را با کلید مخفی بسنجد، اگر درست بود سفارش را پیدا کند، اگر قبلاً پرداختشده نبود وضعیتش را عوض کند و کد ۲۰۰ برگرداند. شاید بیست خط کد بیشتر نشود. دقت کنید امضا را روی بدنهی خام حساب کنید، نه روی دادهی بعد از json_decode، چون تغییر کوچکی در فاصلهها یا ترتیب کلیدها امضا را بیاعتبار میکند.
خطاهای رایج در راهاندازی
- فایروال یا افزونهی امنیتی: گاهی هاست درخواستهای خارجی را مسدود میکند و وبهوک هرگز نمیرسد. لاگ سرور را ببینید.
- ریدایرکت: اگر آدرس وبهوک به آدرس دیگری ریدایرکت شود (مثلاً از http به https)، درخواست POST ممکن است گم شود. آدرس نهایی را وارد کنید.
- زمانبر بودن: پردازش سنگین قبل از پاسخ دادن، باعث timeout میشود.
- ثبت لاگ نکردن: بدون لاگ، وقتی مشکلی پیش بیاید نمیدانید درخواست رسیده یا نه. هر وبهوک را با زمان ثبت کنید.
چه موقع وبهوک را دوباره بفرستیم؟
خوب است پنلی داشته باشید که تلاشهای ناموفق ارسال را نشان دهد و بتوانید دستی دوباره بفرستید. این قابلیت وقتی سرور شما چند دقیقه از دسترس خارج بود، بسیار نجاتدهنده است. اگر چنین ابزاری ندارید، حداقل یک روش برای پرسش از وضعیت تراکنش از طریق API داشته باشید تا بتوانید سفارشهای معلق را بررسی کنید.
اگر نمیخواهید وبهوک بنویسید
روش جایگزین این است که وضعیت را با API بپرسید (polling). این راه سادهتر و کمی کندتر است؛ برای فروشگاه کمحجم کاملاً قابل قبول است. جزئیات اتصال به وردپرس و سایتهای دیگر را در راهنمای اتصال بخوانید. و اگر دربارهی ربات تلگرام میپرسید، این نوشته مثال دارد.