FutureForgeFutureForge

خدمات FutureForge

  • طراحی و ساخت محصولاز شناخت نیاز تا انتشار محصول، یک مسیر روشن پیش رو دارید.
  • توسعه فول‌استکوب‌اپلیکیشن، API و داده را هماهنگ و یکپارچه توسعه دهید.
  • ممیزی مهندسیپیش از هزینه‌ی بیشتر، از وضعیت واقعی محصول‌تان باخبر شوید.
  • مشاوره معماریپیش از یک تصمیم فنی پرهزینه، گزینه‌ها را درست بسنجید.
  • زیرساخت و استقرارمحصول را با خیال راحت منتشر کنید و پایدار نگه دارید.
  • هوش مصنوعی در محصولهوش مصنوعی را فقط جایی به کار می‌گیریم که مسئله‌ای را حل کند.
نمونه‌کارهایادداشت‌هاقیمت‌گذاریدرباره ما
شروع گفتگو
  1. خانه
  2. /یادداشت‌ها
  3. /یادداشت‌ها
FutureForge

استودیوی مهندسی محصول — طراحی، ساخت و استقرار نرم‌افزار.

خدمات

طراحی سایت با وردپرستوسعه اختصاصیمهندسی محصولزیرساخت و استقرارمشاوره معماریهوش مصنوعی در محصول

راهنمای انتخاب

یادداشت‌هاقیمت‌گذاریتخمین هزینه پروژهمقایسه روش‌هانمونه‌کارهادرباره ماتماس

ارتباط

hello@futureforge.ir

ایده‌ای برای ساخت دارید؟

نیازتان را مطرح کنید تا درباره مسیر توسعه و قدم بعدی صحبت کنیم.

شروع گفتگو

© 2026 FutureForge. همه حقوق محفوظ است.

حریم خصوصیشرایط استفاده
خانهنمونه‌کارشروعتماس
معماری نرم‌افزار

اتصال درگاه پرداخت اینترنتی به‌صورت قابل‌تعویض

سا
سهیل ابراهیم‌پور
·۳ آبان ۱۴۰۵·6 دقیقه مطالعه
درگاه پرداختproviderverifycallbackفروشگاه اینترنتی
توسعه‌دهنده در حال تست جریان پرداخت و بازگشت از درگاه روی لپ‌تاپ

یک واسط داخلی برای provider پرداخت تعریف کنید، مبلغ را از دیتابیس verify کنید، verify تکراری را درست مدیریت کنید و تطبیق مالی بسازید؛ نمونه از مستند رسمی زرین‌پال.

اتصال مستقیم کد فروشگاه به یک درگاه پرداخت سریع‌ترین راه برای فروش اولین سفارش است، و پرهزینه‌ترین راه برای روزی که باید درگاه را عوض کنید، یا یک درگاه دوم برای پشتیبان اضافه کنید، یا تراکنشی را که «پولش کم شد ولی سفارش ثبت نشد» پیگیری کنید. این مقاله یک قرارداد داخلی کوچک برای «ارائه‌دهندهٔ پرداخت» پیشنهاد می‌دهد و نشان می‌دهد نمونهٔ واقعی (مستندات رسمی زرین‌پال) چطور در آن می‌نشیند.

چند مرز صریح: ما درگاه خاصی را توصیه نمی‌کنیم و زرین‌پال فقط به‌عنوان نمونهٔ یک جریان مستند آمده است؛ نام پارامترها و آدرس‌ها را از مستند روز همان درگاه بگیرید. دربارهٔ الزامات قراردادی، مجوز، مالیات و حقوقی درگاه‌ها هیچ ادعایی نمی‌کنیم. بخش‌هایی که «توصیهٔ نویسنده» نامیده‌ایم، تجربهٔ مهندسی عمومی‌اند و منبع مستقل ندارند.

جریان رایج: درخواست، ریدایرکت، بازگشت، تأیید

مستند زرین‌پال جریان را چنین می‌گوید: ابتدا پذیرنده از سرور خود درخواست پرداخت می‌فرستد و code و authority می‌گیرد؛ سپس خریدار را به آدرس StartPay همراه authority هدایت می‌کند؛ پس از پایان کار، درگاه خریدار را به callback_url بازمی‌گرداند و یک Status با مقدار OK یا NOK در QueryString می‌آید. NOK یعنی تراکنش ناموفق بوده یا خریدار لغو کرده است و متد verify فقط وقتی فراخوانی می‌شود که Status برابر OK باشد.

این الگوی «درخواست سمت سرور، ریدایرکت مرورگر، بازگشت مرورگر، تأیید سمت سرور» در بسیاری از درگاه‌ها مشابه است، هرچند جزئیات فرق دارد. نکتهٔ مهم برای معماری: بازگشت مرورگر مدرک پرداخت نیست؛ تأیید سمت سرور مدرک است.

قرارداد داخلی provider

توصیهٔ نویسنده: کد فروشگاه نباید نام authority یا StartPay را بشناسد. یک واسط داخلی تعریف کنید که چهار کار انجام دهد:

عملیات داخلیورودیخروجینگاشت در نمونهٔ زرین‌پال
start(order)شناسهٔ سفارش، مبلغ، آدرس بازگشتشناسهٔ مرجع پرداخت + آدرس ریدایرکتدرخواست پرداخت ← authority + آدرس StartPay
parse_callback(request)درخواست بازگشت مرورگرشناسهٔ مرجع + نتیجهٔ اولیه (موفق/ناموفق/لغو)Authority و Status از QueryString
verify(ref, amount)شناسهٔ مرجع، مبلغ مورد انتظارنتیجهٔ نهایی، شمارهٔ پیگیری، نشانگر «قبلاً تأیید شده»verify ← code 100 / 101 + ref_id
inquire(ref)شناسهٔ مرجعوضعیت فعلیطبق مستند همان درگاه؛ ما بررسی نکردیم

خروجی را به وضعیت‌های خودتان نگاشت کنید (paid، failed، canceled، pending، already_verified) تا بقیهٔ سیستم هرگز کد خطای خام درگاه را نبیند. وضعیت‌های سفارش و پرداخت را هم با ماشین حالت جدا نگه دارید.

فروشگاه، واسط داخلی و دو provider
نمودار مفهومی.

تأیید سمت سرور: مبلغ را از دیتابیس بخوانید

مستند SDK زرین‌پال تأکید می‌کند که amount در verify باید با مبلغ اصلی تراکنش مطابقت داشته باشد و از دیتابیس استخراج شود، نه از ورودی کاربر. دلیلش روشن است: کسی که پارامترهای بازگشت را دستکاری می‌کند نباید بتواند مبلغ کمتر را «تأیید» کند. پارامترهای verify شامل merchant_id، amount (به ریال) و authority است و پاسخ code، ref_id، card_pan (ماسک‌شده)، card_hash و fee را دربرمی‌گیرد.

پس رفتار صحیح این است:

  1. سفارش را با شناسهٔ مرجع پرداخت از دیتابیس پیدا کنید.
  2. مبلغ مورد انتظار را از همان رکورد بخوانید.
  3. verify را با آن مبلغ صدا بزنید.
  4. فقط در صورت نتیجهٔ موفق، سفارش را تأیید کنید و شمارهٔ پیگیری (ref_id) را ذخیره کنید.

verify تکراری: کد ۱۰۱ را خطا نگیرید

در مستند زرین‌پال، با verify موفق فقط یک بار code=100 برمی‌گردد و در دفعات بعدی همان تراکنش code=101 می‌آید که یعنی «موفق و قبلاً verify شده». اگر کد شما ۱۰۱ را خطا بگیرد، کاربری که صفحهٔ بازگشت را رفرش می‌کند پیام خطا می‌بیند، در حالی که پولش کسر شده است. اگر ۱۰۱ را موفقیت جدید بگیرد، ممکن است سفارش را دوباره پردازش کنید و موجودی را دوبار کم کنید. راه درست: ۱۰۱ را «موفق، بدون اثر جدید» بنگارید و وضعیت سفارش را از دیتابیس خودتان بخوانید.

همین منطق در Stripe هم هست: کلید idempotency اجازه می‌دهد درخواست پس از خطای اتصال بی‌خطر تکرار شود و پاسخ اولین درخواست برمی‌گردد. در کد خودتان عملیات «تأیید سفارش» را با شرط وضعیت انجام دهید: فقط اگر سفارش الان در انتظار پرداخت است، به پرداخت‌شده تغییر کند، در همان تراکنش دیتابیس.

تعویض provider در عمل

چه چیزهایی باید برای تعویض یا افزودن درگاه دوم آماده باشد (توصیهٔ نویسنده):

  • جدول پرداخت‌ها با ستون provider و provider_reference، تا هر تراکنش به درگاهش وصل باشد.
  • پیکربندی هر provider (کلیدها، آدرس‌ها) جدا از کد و قابل‌تغییر بدون انتشار.
  • تست قرارداد: یک مجموعه تست مشترک که هر provider باید پاس کند (شروع، بازگشت موفق، بازگشت لغو، verify تکراری، مبلغ ناهماهنگ).
  • نمونهٔ مستند زرین‌پال برای تست از sandbox استفاده می‌کند؛ هر درگاه را در محیط آزمایشی خودش تست کنید، اگر دارد.
  • سوئیچ آگاهانه: انتخاب provider برای هر سفارش در لحظهٔ start ثبت شود تا سفارش‌های در جریان با درگاه قبلی تمام شوند.

تطبیق مالی

ref_id، یعنی شمارهٔ پیگیری درگاه، به نوشتهٔ مستند برای پیگیری‌های مالی است؛ آن را کنار سفارش ثبت کنید. توصیهٔ نویسنده: یک گزارش دوره‌ای بسازید که سفارش‌های پرداخت‌شدهٔ شما را با گزارش تسویهٔ درگاه مقایسه کند و تفاوت‌ها (پرداخت‌شده بدون سفارش، سفارش بدون پرداخت، مبلغ نامساوی) را نشان دهد. جزئیات تسویه و کارمزد مطابق قرارداد شما با درگاه است و این مقاله بررسی نکرده است. برای پایش و هشدار روی خطاهای verify، Monitoring چیست؟ را ببینید.

سناریوهای شکست برای تست

  • کاربر پس از پرداخت مرورگر را می‌بندد و callback هرگز نمی‌رسد. (راه‌حل: کار دوره‌ای که سفارش‌های در انتظار را استعلام می‌کند، اگر درگاه استعلام دارد.)
  • callback دوبار می‌رسد.
  • Status=NOK یا مقدار نامعتبر.
  • authority ناشناخته یا قبلاً مصرف‌شده.
  • verify با timeout روبه‌رو می‌شود. (نتیجه ناشناخته است؛ سفارش را «نامشخص» علامت بزنید و دوباره استعلام کنید، نه «ناموفق».)
  • مبلغ سفارش بین شروع و تأیید تغییر کرده است.

چک‌لیست

  1. کد فروشگاه فقط با واسط داخلی کار می‌کند.
  2. بازگشت مرورگر به‌تنهایی سفارش را تأیید نمی‌کند.
  3. مبلغ verify از دیتابیس می‌آید.
  4. verify تکراری (مثل ۱۰۱) بی‌اثر و بی‌خطاست.
  5. هر پرداخت provider و شمارهٔ مرجع دارد.
  6. تست‌های قرارداد برای هر provider وجود دارد.
  7. تطبیق دوره‌ای با گزارش درگاه و هشدار تفاوت.
  8. الزامات حقوقی و قراردادی از منبع رسمی روز بررسی شده، نه از این مقاله.

برای تصمیم‌های معماری بزرگ‌تر، انتخاب معماری اپلیکیشن وب را ببینید.

منابع و مراجع

  • راهنمای اتصال به درگاه اینترنتی زرین‌پال — ZarinPal (مستندات رسمی).
  • درگاه پرداخت (اطلاعات فنی سیستم) — ZarinPal (مستندات رسمی).
  • تأیید پرداخت (Payment Verification) — SDK Node.js — ZarinPal (مستندات رسمی).
  • Idempotent requests (Stripe API) — Stripe.

نویسنده

سا

سهیل ابراهیم‌پور بنیان‌گذار FutureForge است. روی طراحی محصول، معماری و استقرار نرم‌افزار سفارشی کار می‌کند.

یادداشت‌های مرتبط

یادداشت‌های مرتبط

دسته‌بندی‌ها

خدمات مرتبط

از یادداشت تا پروژه

اگر با مسئله‌ای شبیه این روبه‌رو هستید، درباره‌ی شرایط پروژه‌تان با هم صحبت کنیم.

برای انتخاب مسیر فنی پروژه‌تان کمک می‌خواهید؟

از مسئله و محدودیت‌هایتان بگویید؛ اگر بتوانیم همراهی‌تان کنیم، برای گفت‌وگو هماهنگ می‌کنیم.

سهیل ابراهیم‌پور
یادداشت‌ها
اتصال موجودی انبار به فروشگاه: منبع حقیقت، همزمانی و idempotency
خواندن EXPLAIN ANALYZE در PostgreSQL
طراحی pagination API: offset، cursor و ترتیب پایدار
تفاوت Nginx و Apache: چارچوب تصمیم
SQL یا NoSQL: انتخاب بر اساس workload، query و سازگاری

معماری نرم‌افزار

اتصال موجودی انبار به فروشگاه: منبع حقیقت، همزمانی و idempotency

۳ آبان ۱۴۰۵

معماری نرم‌افزار

خواندن EXPLAIN ANALYZE در PostgreSQL

۲ آبان ۱۴۰۵

معماری نرم‌افزار

طراحی pagination API: offset، cursor و ترتیب پایدار

۲ آبان ۱۴۰۵

معماری نرم‌افزار

تفاوت Nginx و Apache: چارچوب تصمیم

۲ آبان ۱۴۰۵

معماری نرم‌افزار

SQL یا NoSQL: انتخاب بر اساس workload، query و سازگاری

۲ آبان ۱۴۰۵
همه یادداشت‌ها296
معماری نرم‌افزار24
واژه‌نامه37
عملیات و استقرار90
مهندسی محصول117
راهنمای وب28
طراحی و ساخت محصول
توسعه فول‌استک
ممیزی مهندسی
مشاوره معماری
زیرساخت و استقرار
شروع گفتگو
ابزارهای رایگان
شروع گفتگو