اتصال درگاه پرداخت به فروشگاه؛ معماری امن و رفع خطا

پرداخت موفق در پنل بانک، سفارش موفق در فروشگاه نیست. فروشگاه باید مبلغ و سفارش را سمت سرور بسازد، نتیجه را از API درگاه راستی‌آزمایی کند، Callback تکراری را فقط یک‌بار پردازش کند و وضعیت‌های نامشخص را با Reconciliation حل کند. اعتماد به پارامتر بازگشت مرورگر می‌تواند به تحویل بدون پرداخت یا ثبت چندباره سفارش منجر شود.

این راهنما معماری اتصال درگاه پرداخت به فروشگاه ایرانی را از انتخاب ارائه‌دهنده تا امنیت، تست، مانیتورینگ و رفع خطا توضیح می‌دهد. برای طراحی فرم و تجربه خرید، راهنمای بهینه‌سازی Checkout را جداگانه بخوانید.

اتصال درگاه پرداخت دقیقاً شامل چیست؟

Payment gateway integration ارتباط امن میان فروشگاه، ارائه‌دهنده پرداخت و کاربر است. این ارتباط فقط «نصب افزونه و واردکردن Merchant ID» نیست؛ چرخه کامل سفارش، انتقال، تأیید، ثبت مالی، بازپرداخت و تطبیق را شامل می‌شود.

مسئولیت فروشگاه آن است که بدون اعتماد به Browser، بداند کدام Order با چه Amount و Currency واقعاً تأیید شده و آیا Fulfillment مجاز است.

درگاه مستقیم یا پرداخت‌یار؟

معیارPSP/قرارداد مستقیمپرداخت‌یار/واسط
فعال‌سازیطبق الزامات جاری ارائه‌دهنده و پذیرندگیفرایند و مدارک طبق قرارداد پرداخت‌یار
IntegrationAPI/Plugin همان PSPAPI واحد و گاهی چند مسیر تسویه
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

  1. کاربر سبد را تأیید می‌کند.
  2. Backend قیمت، تخفیف، موجودی، هزینه ارسال و Currency را دوباره محاسبه می‌کند.
  3. Order داخلی با شناسه یکتا و وضعیت pending_payment ساخته می‌شود.
  4. Backend از API درگاه Transaction می‌سازد و Amount، Order reference و Callback ثابت را می‌فرستد.
  5. درگاه Token/Payment URL برمی‌گرداند؛ فروشگاه آن را کنار Order ثبت می‌کند.
  6. کاربر به صفحه Hosted درگاه Redirect می‌شود.
  7. درگاه پرداخت را انجام می‌دهد و کاربر را به Return URL برمی‌گرداند؛ Webhook ممکن است مستقل ارسال شود.
  8. Backend نتیجه را با API درگاه Verify و Amount/Currency/Order/Status را تطبیق می‌دهد.
  9. Transition اتمیک به paid انجام و Fulfillment فقط یک‌بار Trigger می‌شود.
  10. Job تطبیق، وضعیت‌های نامشخص و Webhook ازدست‌رفته را بعداً بررسی می‌کند.

راهنمای OWASP برای اتصال درگاه ثالث نیز بر محاسبه سمت سرور، بی‌اعتمادی به Return، Verify با API، تطبیق مبلغ و شناسه و پردازش Idempotent تأکید می‌کند.

Order و Payment را جدا مدل کنید

یک Order ممکن است چند Payment attempt داشته باشد. اگر همه‌چیز در یک فیلد «پرداخت شد/نشد» خلاصه شود، Retry و Reconciliation سخت می‌شود.

Entityفیلدهای کلیدی
Orderorder_id، customer، items snapshot، amount، currency، fulfillment_status
Payment attemptattempt_id، provider، token، expected_amount، status، created_at
Gateway eventevent_id، type، signature_status، received_at، raw_hash
Transactiongateway_reference، verified_amount، paid_at، settlement reference
Refundrefund_id، amount، reason، provider_status، completed_at

Snapshot اقلام و مبلغ هنگام شروع پرداخت از تغییر قیمت بعدی جلوگیری می‌کند، اما موجودی و Expiry باید Policy روشن داشته باشند.

State machine پرداخت

وضعیتمعنااقدام مجاز
pending_paymentOrder ساخته، پرداخت شروع نشدهساخت Attempt
redirectedToken صادر و کاربر منتقل شدهانتظار/استعلام
processingنتیجه قطعی نیستPoll/Job، منع Fulfillment و Retry کنترل‌شده
paidVerify معتبر و مبلغ منطبقFulfillment یک‌باره
failedشکست قطعیAttempt جدید
canceledلغو کاربر/ارائه‌دهندهبازگشت به Checkout
expiredToken یا 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 همان نتیجه امن را برگرداند، نه اینکه سفارش یا اعتبار تازه بسازد.

  1. کلید یکتا برای Provider + gateway_reference تعریف کنید.
  2. رکورد را در Transaction دیتابیس Lock یا Compare-and-set کنید.
  3. اگر Order قبلاً paid است، نتیجه موجود را برگردانید.
  4. Transition و Outbox event را اتمیک ثبت کنید.
  5. Fulfillment consumer نیز idempotent باشد.
  6. ایمیل، 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 unpaidVerify، Mark paid کنترل‌شده یا بررسی دستی
Order paid / Provider ناموجودتوقف Fulfillment و Incident بحرانی
Amount متفاوتعدم تحویل و بررسی Fraud/Unit
دو تراکنش paidPolicy بازپرداخت و اطلاع کاربر
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 latencyP95 بالاتر از Baseline
unknown agingOrder نامشخص بیش از 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؛
  • دسترسی غیرمجاز به وضعیت سفارش دیگر.

انتشار امن

  1. Staging با Sandbox و داده ساختگی؛
  2. Backup و Rollback روشن؛
  3. Feature flag برای Provider جدید؛
  4. یک خرید واقعی کم‌مبلغ با هماهنگی؛
  5. Canary درصدی یا محدود به تیم؛
  6. Dashboard و Alert پیش از Rollout؛
  7. پشتیبانی با Error map و Runbook؛
  8. افزایش تدریجی و Reconciliation روز اول؛
  9. 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 پرداخت

  1. Incident را بر اساس اثر مالی و تعداد Order اعلام کنید.
  2. تغییر/Provider مشکل‌دار را محدود یا Rollback کنید.
  3. Fulfillment مشکوک را متوقف کنید.
  4. لیست Orderهای processing/paid mismatch را Snapshot بگیرید.
  5. با Provider و پشتیبانی کانال واحد داشته باشید.
  6. کاربر را با پیام وضعیت و شماره پیگیری مطلع کنید.
  7. Reconciliation، Refund و جبران را اجرا کنید.
  8. 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 ناشناس را بنویسید.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *