پرداخت موفق در پنل بانک، سفارش موفق در فروشگاه نیست. فروشگاه باید مبلغ و سفارش را سمت سرور بسازد، نتیجه را از API درگاه راستیآزمایی کند، Callback تکراری را فقط یکبار پردازش کند و وضعیتهای نامشخص را با Reconciliation حل کند. اعتماد به پارامتر بازگشت مرورگر میتواند به تحویل بدون پرداخت یا ثبت چندباره سفارش منجر شود.
این راهنما معماری اتصال درگاه پرداخت به فروشگاه ایرانی را از انتخاب ارائهدهنده تا امنیت، تست، مانیتورینگ و رفع خطا توضیح میدهد. برای طراحی فرم و تجربه خرید، راهنمای بهینهسازی Checkout را جداگانه بخوانید.
اتصال درگاه پرداخت دقیقاً شامل چیست؟
Payment gateway integration ارتباط امن میان فروشگاه، ارائهدهنده پرداخت و کاربر است. این ارتباط فقط «نصب افزونه و واردکردن Merchant ID» نیست؛ چرخه کامل سفارش، انتقال، تأیید، ثبت مالی، بازپرداخت و تطبیق را شامل میشود.
مسئولیت فروشگاه آن است که بدون اعتماد به Browser، بداند کدام Order با چه Amount و Currency واقعاً تأیید شده و آیا Fulfillment مجاز است.
درگاه مستقیم یا پرداختیار؟
| معیار | PSP/قرارداد مستقیم | پرداختیار/واسط |
|---|---|---|
| فعالسازی | طبق الزامات جاری ارائهدهنده و پذیرندگی | فرایند و مدارک طبق قرارداد پرداختیار |
| Integration | API/Plugin همان PSP | API واحد و گاهی چند مسیر تسویه |
| Settlement | برنامه تسویه قراردادی | برنامه، کارمزد و محدودیت قراردادی |
| پشتیبانی | پذیرندگی و تیم فنی PSP | پشتیبانی واسط و سپس شبکه پرداخت |
| قابلیتها | وابسته به سرویس مستقیم | ممکن است Link، Marketplace یا گزارش اضافه داشته باشد |
| ریسک تمرکز | وابستگی به یک PSP | وابستگی به واسط و PSPهای زیرساختی |
قوانین، مدارک، کارمزد و تسویه ممکن است تغییر کنند؛ ادعای ثابت «بدون اینماد» یا درصد کارمزد را از مقاله قدیمی نپذیرید. صفحه رسمی و قرارداد روز ارائهدهنده را بررسی کنید.
معیار انتخاب درگاه برای فروشگاه ایرانی
- پوشش نوع کسبوکار، شخص حقیقی/حقوقی و مدل Marketplace؛
- API و مستندات نسخهدار با Sandbox یا محیط Test؛
- Webhook امضاشده و API استعلام وضعیت؛
- Idempotency یا Transaction reference یکتا؛
- افزونه رسمی/نگهداریشده برای WooCommerce یا پلتفرم شما؛
- وضعیت عملیاتی، SLA و کانال Incident؛
- گزارش تراکنش، تسویه، Refund و Export؛
- تسویه، کارمزد، Reserve و اختلاف طبق قرارداد؛
- پشتیبانی پاسخگو با شناسه Ticket؛
- محدودیت مبلغ، IP، دامنه و Callback؛
- حفاظت داده و الزامات امنیتی؛
- تجربه صفحه پرداخت روی موبایل و شبکه ضعیف.
معماری مرجع پرداخت Redirect
- کاربر سبد را تأیید میکند.
- Backend قیمت، تخفیف، موجودی، هزینه ارسال و Currency را دوباره محاسبه میکند.
- Order داخلی با شناسه یکتا و وضعیت
pending_paymentساخته میشود. - Backend از API درگاه Transaction میسازد و Amount، Order reference و Callback ثابت را میفرستد.
- درگاه Token/Payment URL برمیگرداند؛ فروشگاه آن را کنار Order ثبت میکند.
- کاربر به صفحه Hosted درگاه Redirect میشود.
- درگاه پرداخت را انجام میدهد و کاربر را به Return URL برمیگرداند؛ Webhook ممکن است مستقل ارسال شود.
- Backend نتیجه را با API درگاه Verify و Amount/Currency/Order/Status را تطبیق میدهد.
- Transition اتمیک به
paidانجام و Fulfillment فقط یکبار Trigger میشود. - Job تطبیق، وضعیتهای نامشخص و Webhook ازدسترفته را بعداً بررسی میکند.
راهنمای OWASP برای اتصال درگاه ثالث نیز بر محاسبه سمت سرور، بیاعتمادی به Return، Verify با API، تطبیق مبلغ و شناسه و پردازش Idempotent تأکید میکند.
Order و Payment را جدا مدل کنید
یک Order ممکن است چند Payment attempt داشته باشد. اگر همهچیز در یک فیلد «پرداخت شد/نشد» خلاصه شود، Retry و Reconciliation سخت میشود.
| Entity | فیلدهای کلیدی |
|---|---|
| Order | order_id، customer، items snapshot، amount، currency، fulfillment_status |
| Payment attempt | attempt_id، provider، token، expected_amount، status، created_at |
| Gateway event | event_id، type، signature_status، received_at، raw_hash |
| Transaction | gateway_reference، verified_amount، paid_at، settlement reference |
| Refund | refund_id، amount، reason، provider_status، completed_at |
Snapshot اقلام و مبلغ هنگام شروع پرداخت از تغییر قیمت بعدی جلوگیری میکند، اما موجودی و Expiry باید Policy روشن داشته باشند.
State machine پرداخت
| وضعیت | معنا | اقدام مجاز |
|---|---|---|
| pending_payment | Order ساخته، پرداخت شروع نشده | ساخت Attempt |
| redirected | Token صادر و کاربر منتقل شده | انتظار/استعلام |
| processing | نتیجه قطعی نیست | Poll/Job، منع Fulfillment و Retry کنترلشده |
| paid | Verify معتبر و مبلغ منطبق | Fulfillment یکباره |
| failed | شکست قطعی | Attempt جدید |
| canceled | لغو کاربر/ارائهدهنده | بازگشت به Checkout |
| expired | Token یا Order منقضی | محاسبه و Attempt تازه |
| refund_pending | بازپرداخت شروع شده | پیگیری Provider |
| refunded | بازپرداخت تأیید شده | ثبت مالی و اطلاع کاربر |
Transitionها باید Allowlist و اتمیک باشند. Callback قدیمی نباید یک Order refunded یا canceled را بیقید به paid ببرد.
قیمت را از Client نپذیرید
Browser فقط ID کالا، Variant، تعداد و انتخاب کاربر را اعلام میکند. Backend باید:
- قیمت و تخفیف را از منبع معتبر بخواند؛
- مالکیت Coupon و محدودیت استفاده را بررسی کند؛
- هزینه ارسال و مالیات/عوارض قابلاعمال را محاسبه کند؛
- موجودی و Seller را اعتبارسنجی کند؛
- تومان/ریال و تبدیل واحد را یکبار و صریح انجام دهد؛
- Amount ارسالی را کنار Attempt ذخیره کند؛
- در Verify همان Amount و Currency را تطبیق دهد.
Hidden input، JavaScript و عدد Return شده از کاربر منبع حقیقت نیستند.
Return URL با Webhook فرق دارد
Return مرورگر
کاربر ممکن است Tab را ببندد، شبکه قطع شود، Back بزند یا پارامتر را تغییر دهد. Return برای نمایش نتیجه و شروع استعلام مفید است، اما Proof of payment نیست.
Webhook سروربهسرور
مستقل از مرورگر است، ولی میتواند تکراری، دیررس، جعلی یا خارج از ترتیب باشد. Signature/Secret، Timestamp، Event ID، Replay protection و Verify API لازماند.
API استعلام
Backend وضعیت را مستقیماً از ارائهدهنده میگیرد. برای تصمیم مالی، پاسخ معتبر API و تطبیق Order/Amount مرجع اصلی است. Timeout را مساوی شکست قطعی ندانید.
Callback امن
- فقط HTTPS و Host ثابت؛
- Route خارج از Page cache و CDN cache؛
- بدون Session یا Cookie کاربر برای Verify؛
- محدودیت Method و Content type طبق مستند؛
- اعتبارسنجی Signature/Secret در صورت پشتیبانی؛
- بررسی Timestamp و Replay window؛
- Lookup با Token/Reference ذخیرهشده؛
- Verify سروربهسرور؛
- تطبیق Amount، Currency، Merchant و Order؛
- Transaction اتمیک و Unique constraint؛
- پاسخ سریع و Queue برای کار سنگین؛
- Log بدون Secret و داده کارت.
Idempotency؛ یک پرداخت، یک Fulfillment
شبکه Retry میکند، Provider Webhook را تکرار میکند و کاربر Refresh میزند. Handler باید با دریافت دوباره همان Event/Transaction همان نتیجه امن را برگرداند، نه اینکه سفارش یا اعتبار تازه بسازد.
- کلید یکتا برای Provider + gateway_reference تعریف کنید.
- رکورد را در Transaction دیتابیس Lock یا Compare-and-set کنید.
- اگر Order قبلاً paid است، نتیجه موجود را برگردانید.
- Transition و Outbox event را اتمیک ثبت کنید.
- Fulfillment consumer نیز idempotent باشد.
- ایمیل، Inventory و Loyalty کلید Deduplication داشته باشند.
Idempotency فقط در Endpoint کافی نیست؛ کل زنجیره تا ارسال کالا و اعتبار کیف پول باید تکرار را تحمل کند.
Race condition و پرداخت همزمان
کاربر ممکن است دو Tab باز کند یا Retry سریع بزند. دو Attempt میتوانند همزمان موفق شوند. راهکار:
- یک Active attempt در هر لحظه یا Policy روشن برای چند Attempt؛
- قفل/Unique constraint هنگام Mark paid؛
- تشخیص تراکنش دوم و Queue بازپرداخت/بررسی؛
- عدم نمایش دکمه Retry در وضعیت processing؛
- Polling با Backoff و سقف؛
- Alert برای Order با بیش از یک Paid transaction.
Secret و دسترسی
- Merchant secret و API key در Secret manager یا Environment امن، نه کد/Git؛
- کلید جدا برای Test و Production؛
- کمترین Scope و IP allowlist در صورت امکان؛
- Rotation با Runbook و همپوشانی کنترلشده؛
- عدم چاپ Secret در Log، Error یا پنل؛
- دسترسی Production با MFA و Audit؛
- Webhook secret مستقل؛
- ابطال فوری کلید لورفته و بررسی تراکنشها.
کاهش دامنه PCI و داده کارت
در معماری Redirect یا Hosted payment page، داده کارت مستقیماً در صفحه ارائهدهنده وارد میشود و فروشگاه نباید PAN/CVV را ببیند یا ذخیره کند. اگر Script پرداخت داخل صفحه فروشگاه بارگذاری میشود، Scope و ریسک زنجیره تأمین متفاوت است.
- شماره کارت/CVV در Log، Analytics، Replay یا Ticket ذخیره نشود.
- فرم جعلی کارت روی دامنه فروشگاه نسازید مگر دامنه Compliance و تخصص لازم دارید.
- Scriptهای ثالث صفحه پرداخت حداقلی و کنترلشده باشند.
- الزامات جاری PCI DSS و قرارداد Provider را با متخصص تطبیق دهید.
- Redirect مقصد را Allowlist کنید.
افزونه درگاه WooCommerce را چگونه ارزیابی کنیم؟
- ناشر و منبع رسمی قابلتأیید؛
- سازگاری با نسخه جاری WordPress، WooCommerce و PHP؛
- تاریخ Release و Changelog؛
- استفاده از API جاری، نه Endpoint منسوخ؛
- Verify سمت سرور و Amount check؛
- Idempotency و مدیریت Callback تکراری؛
- HPOS و Checkout block در صورت استفاده؛
- عدم ذخیره Secret در Log یا خروجی HTML؛
- ترجمه خطا بدون افشای پاسخ حساس؛
- Hookهای درست برای Order note، stock و email؛
- Test coverage و مسیر گزارش آسیبپذیری؛
- مالک پشتیبانی مشخص.
تعداد نصب یا رایگانبودن بهتنهایی معیار امنیت نیست. افزونه را ابتدا در Staging و با نسخههای واقعی Stack تست کنید.
Cache، CDN و WAF
- Cart، Checkout، Return، Callback و Account از Full-page cache خارج باشند.
- Query/Body لازم توسط CDN یا WAF حذف نشود.
- WAF Rule اختصاصی و محدود برای Callback، نه Bypass کامل امنیت؛
- Rate limit با Retry قانونی Provider سازگار باشد.
- IP allowlist فقط اگر Provider Range رسمی و پایدار میدهد.
- صفحه نتیجه با
noindexو بدون داده حساس باشد. - Cache purge ارتباطی با وضعیت تراکنش نداشته باشد.
تومان و ریال
واحد نمایش و واحد API را Explicit نگه دارید. تبدیل مبهم میتواند مبلغ دهبرابر یا یکدهم بسازد.
- Money را Integer در کوچکترین واحد موردنیاز نگه دارید؛
- فیلد Currency/Unit کنار Amount؛
- تبدیل در یک تابع مرکزی و تستشده؛
- در Dashboard و Log واحد نمایش داده شود؛
- Expected amount و Verified amount هر دو ثبت شوند؛
- Mismatch هرگز خودکار paid نشود.
Timeout، لغو و وضعیت نامشخص
Timeout یعنی پاسخ دریافت نشده، نه اینکه پرداخت نشده است. تجربه و منطق مناسب:
- Order به processing/unknown برود؛
- API با Backoff و سقف Poll شود؛
- کاربر پیام «در حال بررسی» و شماره سفارش ببیند؛
- Retry پرداخت تا تعیین Policy محدود شود؛
- Job پسزمینه و Reconciliation ادامه دهد؛
- پس از قطعیت، پیام/ایمیل مناسب ارسال شود؛
- پشتیبانی Timeline و Reference را ببیند.
Reconciliation؛ حقیقت مالی روز بعد
حتی Integration خوب به تطبیق نیاز دارد. تراکنشهای داخلی، گزارش Provider و تسویه را مقایسه کنید:
| Mismatch | اقدام |
|---|---|
| Provider paid / Order unpaid | Verify، Mark paid کنترلشده یا بررسی دستی |
| Order paid / Provider ناموجود | توقف Fulfillment و Incident بحرانی |
| Amount متفاوت | عدم تحویل و بررسی Fraud/Unit |
| دو تراکنش paid | Policy بازپرداخت و اطلاع کاربر |
| Refund داخلی / Provider pending | پیگیری تا نتیجه قطعی |
| Settlement mismatch | تطبیق Fee، Reserve و Batch |
Job روزانه، Owner مالی/فنی، SLA و Queue بررسی دستی داشته باشید.
Refund و لغو
- Full و Partial refund مطابق قابلیت Provider؛
- Amount قابلبازپرداخت سمت سرور؛
- شناسه یکتا و Idempotency؛
- تفکیک «درخواست شد»، «پذیرفته شد» و «واریز شد»؛
- عدم بازگرداندن موجودی/اعتبار دو بار؛
- ثبت Reason و Actor؛
- اطلاع زمانبندی واقعبینانه به مشتری؛
- Reconciliation بازپرداخت و تسویه.
Logging و Observability پرداخت
Log باید Timeline بسازد، نه Secret افشا کند:
- order_id، attempt_id، provider، event_id و status transition؛
- زمان تهران و UTC؛
- Latency API و Error code نرمالشده؛
- Hash امن Payload برای تطبیق در صورت نیاز؛
- Trace/correlation ID از Checkout تا Fulfillment؛
- Mask کردن Token و اطلاعات حساس؛
- Retention و دسترسی محدود.
Dashboard و Alert را با راهنمای Observability سایت طراحی کنید.
شاخصها و هشدارها
| Signal | هشدار نمونه |
|---|---|
| initiation success | افت ناگهانی یک Provider/نسخه |
| verify success | فاصله غیرعادی با پرداخت شروعشده |
| callback latency | P95 بالاتر از Baseline |
| unknown aging | Order نامشخص بیش از SLA |
| duplicate paid | هر Order با دو Transaction موفق |
| amount mismatch | هر رخداد، شدت بحرانی |
| reconciliation mismatch | بالاتر از حد مطلق/درصدی |
| refund pending | قدیمیتر از SLA Provider |
تست قبل از انتشار
Happy path
پرداخت موفق، Verify، Order paid، کاهش موجودی، ایمیل و Fulfillment فقط یکبار.
Failure path
- لغو در درگاه؛
- موجودی/قیمت تغییرکرده؛
- Token منقضی؛
- API initiation timeout؛
- Verify timeout و پاسخ نامشخص؛
- پرداخت ناموفق؛
- Return بدون پارامتر یا دستکاریشده.
Retry و Duplicate
- Refresh صفحه Return؛
- Webhook تکراری و خارج از ترتیب؛
- دو Tab و دو Attempt؛
- Retry همزمان Job و Callback؛
- Fulfillment consumer تکراری؛
- Refund تکراری.
Security
- تغییر Amount/Order در Client؛
- Callback جعلی و Signature غلط؛
- Replay Event قدیمی؛
- Redirect مقصد خارجی؛
- Secret در Log/Error؛
- دسترسی غیرمجاز به وضعیت سفارش دیگر.
انتشار امن
- Staging با Sandbox و داده ساختگی؛
- Backup و Rollback روشن؛
- Feature flag برای Provider جدید؛
- یک خرید واقعی کممبلغ با هماهنگی؛
- Canary درصدی یا محدود به تیم؛
- Dashboard و Alert پیش از Rollout؛
- پشتیبانی با Error map و Runbook؛
- افزایش تدریجی و Reconciliation روز اول؛
- Post-release review پس از یک چرخه تسویه.
خرابی Provider و Failover
اضافهکردن درگاه دوم Availability را بالا میبرد، اما State و Reconciliation را پیچیده میکند:
- Health بر اساس API واقعی و Synthetic کنترلشده؛
- Circuit breaker و سقف Retry؛
- عدم جابهجایی Attempt در وضعیت نامشخص؛
- نمایش انتخاب ساده و قابلفهم به کاربر؛
- Mapping مستقل Error code؛
- Reconciliation برای هر Provider؛
- Runbook قطع، بازگشت و Orderهای بینابینی.
درگاه دوم بدون تست Race میتواند پرداخت مضاعف را بیشتر کند.
UX نتیجه پرداخت
صفحه فنی باید تجربه روشن بسازد:
- موفق: شماره سفارش، مبلغ، اقلام و پیگیری؛
- ناموفق: دلیل قابلاقدام بدون افشای فنی و Retry امن؛
- لغو: بازگشت به Checkout با حفظ سبد؛
- نامشخص: منع پرداخت دوباره، وضعیت زنده و پشتیبانی؛
- تکراری: توضیح و فرایند بازپرداخت؛
- منقضی: محاسبه تازه قیمت/موجودی.
Journey کامل فروشگاه در راهنمای UX فروشگاه اینترنتی آمده است.
Runbook Incident پرداخت
- Incident را بر اساس اثر مالی و تعداد Order اعلام کنید.
- تغییر/Provider مشکلدار را محدود یا Rollback کنید.
- Fulfillment مشکوک را متوقف کنید.
- لیست Orderهای processing/paid mismatch را Snapshot بگیرید.
- با Provider و پشتیبانی کانال واحد داشته باشید.
- کاربر را با پیام وضعیت و شماره پیگیری مطلع کنید.
- Reconciliation، Refund و جبران را اجرا کنید.
- Root cause و Control پیشگیرانه ثبت شود.
چکلیست اتصال درگاه پرداخت
- قرارداد، کارمزد، تسویه و محدودیتها از منبع روز بررسی شدهاند.
- Order و Payment attempt جدا مدل شدهاند.
- Amount و Currency سمت سرور محاسبه میشوند.
- Return مرورگر Proof of payment نیست.
- Verify API و تطبیق مبلغ/شناسه اجباری است.
- Callback/Webhook امضا، Replay و Idempotency را کنترل میکند.
- Fulfillment فقط پس از paid و فقط یکبار است.
- processing/unknown و Reconciliation پیاده شدهاند.
- Secret امن، قابل Rotation و خارج از Log است.
- صفحات پرداخت از Cache خارجاند.
- تومان/ریال و Retry/Duplicate تست شدهاند.
- Refund و تسویه State و Owner دارند.
- Monitoring، Alert و Runbook آمادهاند.
- Plugin/SDK در Staging و سپس Canary تست شده است.
سؤالات متداول اتصال درگاه پرداخت
Callback موفق یعنی سفارش را پرداختشده کنیم؟
خیر. پارامتر Return یا Callback بهتنهایی قابلاعتماد نیست. Backend باید وضعیت را با API درگاه Verify و Amount، Currency، Order و Reference را تطبیق دهد.
چرا پرداخت موفق است ولی سفارش ناموفق میماند؟
قطع Return، Cache، Timeout Verify، WAF، Callback اشتباه یا Race ممکن است علت باشد. Order را processing نگه دارید و با Job/Reconciliation نتیجه قطعی را بازیابی کنید.
Idempotency در پرداخت چیست؟
یعنی دریافت چندباره همان درخواست، Callback یا Event بیش از یک پرداخت/سفارش/ارسال ایجاد نکند. Unique key، Transaction اتمیک و Deduplication در Fulfillment لازم است.
درگاه مستقیم بهتر است یا پرداختیار؟
به مدارک، کارمزد و تسویه جاری، API، پشتیبانی، گزارش، SLA و مدل کسبوکار بستگی دارد. قرارداد و مستندات روز هر گزینه را با Pilot واقعی مقایسه کنید.
آیا نصب افزونه درگاه کافی است؟
خیر. باید صحت Verify، Amount، Callback تکراری، Cache، Refund، Secret، نسخههای WooCommerce و سناریوهای Timeout/Retry را در Staging و Production کنترلشده تست کنید.
جمعبندی
اتصال امن درگاه از Backend آغاز میشود: مبلغ معتبر، Order/Attempt مستقل، Verify سروربهسرور، State machine، Idempotency و Reconciliation. سپس Monitoring و UX وضعیتهای موفق، ناموفق و نامشخص آن را قابلعملیات میکنند.
برای Audit افزونه، طراحی Payment state machine یا رفع سفارشهای نامشخص، از فرم مشاوره فنی مایندیو استفاده کنید و پلتفرم، Provider، Error code و نمونه Order ناشناس را بنویسید.






