# مباحث مرتبط با توسعه وب > توسعه وب (Web Development) > آموزش: نحوه پیاده سازی کامل پرداخت آنلاین بانک ملی ایران (دامون) ، از ابتدا تا Verification نهایی

## Chabok

توجه : انتشار مطلب با ذکر نام نویسنده و لینک سایت برنامه نویس بلامانع است .

با سلام

در این مطلب به تشریح نحوه استفاده و پیاده سازی پرداخت آنلاین بانک ملی ایران (دامون) می پردازیم

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

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

*مقدمه :*
کلمه دامون مخفف عبارت "درگاه اینترنتی مرکزی وجوه نقدی" است که توسط شرکت خدمات انفورماتیک ایران تهیه شده است که بانک ملی ایران و بانک صادرات از آن پشتیبانی می کنند .


مراحل اخذ پذیرش در سیستم (نقل قول از سایت بانک) :



> شما به شعبه ای که در آن حساب دارید مراجعه کرده و فرم پذیرنده مجازی جهت پرداخت اینترنتی را تکمیل فرمایید.
> 
> درخواست شما به قسمت پرداخت اینترنتی ارایه میشود و یک پذیرنده برای شما تعریف شده و اطلاعاتی شامل شماره پذیرنده و ... برای شما ارسال میشود.همچنین یک نمونه پیاده سازی برای پذیرنده نیز برای شما ارسال میشود تا با استفاده از آن پیاده سازی لازم در برنامه خود را انجام دهید.
> 
> http://www.bmi.ir/Forums/ShowPost.aspx?PostID=2393
> 
> دریافت نمونه فرمهای عضویت :
> http://www.bmi.ir/fa/bmiservicesshow.aspx?sid=118


قابل ذکر است که این سیستم از سرویس پرداخت سایت AuthorizeNet.com الگو برداری شده است (روش SIM یا Simple Integration Method)

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

*طریقه انجام کار :*

*مرحله 1* : کاربر در سایت شما مراحل خرید را طی می کند و به مرحله پرداخت آنلاین می رسد

*مرحله 2* : در این مرحله یک سری اطلاعات بصورت متد POST به آدرس سایت بانک ارسال می شود(توسط تگ Form)
نکته خیلی مهم :(این اطلاعات باید در رکورد پرداخت ثبت شود . در آینده به آن ها احتیاج دارید )

*مرحله 3* : در صورت پرداخت موفق ، پول از حساب کاربر کسر می شود (نکته مهمی است) 

*مرحله 4* : این بار اطلاعاتی از سایت بانک بصورت POST به سایت فروشنده ارسال می شود که حاوی اطلاعاتی مبنی بر وضعیت پرداخت و یا خطا و همچنین اطلاعات ارسال شده اولیه به بانک است .
فروشنده آدرس برگشت از بانک را هنگام عقد قرارداد باید تعیین کند که به آن ReceiptPage گفته می شود .

نکته مهم و امنیتی : در صورتی که در این مرحله اطلاعات ارسالی به سایت فروشنده ، بیانگر صحت پرداخت بود نباید به آن اعتنا کرد
زیرا ممکن است شخص دیگری این اطلاعات را به صفحه وب سایت شما ارسال کرده باشد.

نکته بسیار مهم : ممکن است مرحله 4 بنا به دلایل متعددی رخ ندهد(بستن صفحه توسط کاربر ، قطع شدن اینترنت و ...)
*این در صورتی است که پول از حساب کاربر کسر شده است .*

در نتیجه پیاده سازی استعلام وضعیت پرداخت (Verification) ضروری می باشد .
(هم از لحاظ امنیتی و هم اینکه در صورت عدم پیاده سازی این قسمت ، بانک سیستم شما را فعال نخواهد کرد  :لبخند گشاده!: )


*پیاده سازی :*

در اولین قدم ، اطلاعات زیر باید به صورت متد POST به آدرس بانک ارسال شوند
آدرس صفحه درگاه دامون بانک ملی :
https://damoon.bankmelli-iran.com/DamoonPrePaymentController

این مقادیر به شرح زیر می باشند :
damoonTable1.JPG

توضیحات تکمیلی مهم :
*x_description* : طبق مستند بانک ، نام فروشنده می باشد و ارسال آن اختیاری می باشد .
ولی اولاً این که این عنوان در هیچ کجا نمایش داده نمی شود (من که ندیدم :لبخند گشاده!: )
و ثانیاً اجباری است . چون اگه ارسال نشه با خطا مواجه خواهید شد . پس حتماً با یک مقدار دلخواه آن را ارسال کنید

*x_login* : کد شناسایی فروشنده یا MerchantID است که توسط بانک تعیین و برای فروشنده ارسال می شود.البته در ابتدا یک MID بصورت تست برای فروشنده تعیین می شود
و در صورتی که مسئول پشتیبانی درگاه بانک ، پیاده سازی را تست و تایید کند ، کد MID اصلی فروشنده برای وی صادر و ارسال می شود

*x_fp_sequence* : شماره منحصر به فرد هر پرداخت می باشد . این شماره هنگام هر پرداخت باید تعیین شود و قابل استفاده مجدد نیست .
حتی در صورتی که پرداخت به هر دلیلی لغو شد ، دیگر این شماره قابل استفاده نبوده و باید شماره جدید ، جهت پرداخت جدید صادر شود(حتماً در پیاده سازی لحاظ شود)

*x_fp_timestamp* : زمان سپری شده از 1/1/1970 تا لحظه جاری (GMT) بر حسب ثانیه که برای مثال در #C بصورت زیر بدست می آید
((int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds).ToString();

*x_amount* : مبلغ پرداخت بر حسب ریال (توضیح خاصی ندارد)

*x_currency_code* : واحد مبلغ که ظاهراً فقط مقدار Rial پشتیبانی می شود

*x_fp_hash* : این پارامتر یک مقدار hash شده است که توسط تابع HMAC_MD5 بر روی مقادیر مشخص شده و با کلید خصوصی تعیین شده (کليد تراکنش یا Transaction Key یا x_tran_key ) تولید می شود .

نمونه کد تابع HMAC_MD5 در #C :

    string HMACMD5(string text, string key)
    {
        if (text != null && key != null)
        {
            var Encoder = new System.Text.ASCIIEncoding();

            using (var MyHMD5 = new System.Security.Cryptography.HMACMD5(Encoder.GetBy  tes(key)))
            {
                return String.Concat(
                       Array.ConvertAll(
                       MyHMD5.ComputeHash(Encoder.GetBytes(text))
                       , x => x.ToString("X2")));
            }
        }
        else return null;
    }
کد تابع HMAC_MD5 برای سایر زبان ها در نمونه کدهای ضمیمه موجود است . در غیر این صورت با پشتیبانی درگاه تماس بگیرید تا کد لازم در زبان مورد نظر شما رو ارسال کنند .

این تابع دارای 2 ورودی است.
ورودی اول باید حاصل عبارت زیر باشد . یعنی مقدار پارامتر های مشخص شده (ترتیب مهم است) که توسط کارکتر ^ به یکدیگر متصل شده اند .
x_login^x_fp_sequence^x_fp_timestamp^x_amount^x_currency_code
برای مثال چنین عبارتی :
MerTest22^122^2255054^250000^Rial
پارامتر دوم تابع HMAC_Md5 همان گونه که ذکر شد ، کلید خصوصی تعریف شده از بانک برای فروشنده است (کليد تراکنش یا Transaction Key یا x_tran_key ) که محرمانه است و فقط فروشنده و بانک از آن اطلاع دارند و فقط هنگام استفاده از تابع HMAC_MD5 به عنوان کلید Hash استفاده می شود و در هیچ بخشی نباید ارسال یا نمایش داده شود .

نمونه استفاده از تابع :
Fingerprint = HMAC-MD5 ("MerTest22^122^2255054^250000^Rial", "YMRxIohOmZ35O2NZ")
به خروجی تابع HMAC_MD5 ، امضا یا اثرانگشت گفته می شود که برای مثال در اینجا در متغیر Fingerprint ذخیره کرده ایم .

به ادامه بحث بر می گردیم .
مقدار پارامتر x_fp_hash که جرو پارامترهایی است که باید POST شود را برابر مقدار Fingerprint  تولید شده قرار می دهیم .

برای مثال در نهایت باید چنین فرمی آماده شود :

    <form method="POST" action="https://damoon.bankmelli-iran.com/DamoonPrePaymentController">

    <input type="hidden" name="x_description" value="EShop">
    <input type="hidden" name="x_login" value="Test22">
    <input type="hidden" name="x_fp_sequence" value="122">
    <input type="hidden" name="x_fp_timestamp" value="1322657054">
    <input type="hidden" name="x_amount" value="250000">
    <input type="hidden" name="x_currency_code" value="Rial">
    <input type="hidden" name="x_fp_hash" value="8DFD28DED63548629F5E21CF734C3ED9">

    <input type="submit" value="VerifyDamoon">

    </form>
طریقه Submit کردن این فرم و ارسال آن به بانک ، به روش های مختلفی امکان پذیر است که از حوصله این مبحث خارج است و بر اساس سلیقه و نظر برنامه نویس پیاده سازی می شود.

*نکته بسیار بسیار مهم :*

هنگام پیاده سازی استعلام پرداخت (Verification) باید دقیقاً همین مقادیری که در این مرحله به بانک ارسال شد را مجدداً به بانک ارسال کنید .
پس واجب است که در یکی از فیلدهای رکورد پرداخت ، این عبارت را با همین مقادیر با فرمت QueryString ذخیره کنید.
x_description=value&x_login=value&x_fp_sequence=va  lue&x_fp_timestamp=value&x_amount=value&x_currency  _code=value&x_fp_hash=value
عبارت value یعنی همان مقدار تعیین شده قبلی . دقیقاً همان مقادیری که قرار است برای بانک ارسال شود .
پس این عبارت را بسازید و در رکورد پرداخت ذخیره کنید . از آن در قسمت Verification استفاده خواهیم کرد .
نام این رشته اطلاعاتی را PaymentInfoString می گذاریم . یادتون باشه . بعداً باهاش کار داریم  :چشمک: .

حال در مرحله ای هستیم که مقادیر به بانک ارسال شده و رکورد پرداخت در سیستم ذخیره شده است .
در این مرحله در صورت پرداخت موفقیت آمیز ، مبلغ مورد نظر از حساب کاربر کسر خواهد شد و بنا به مستندات بانک در پایان ساعت 24 به حساب فروشنده واریز می شود.

در هر حالت ، کاربر مجدداً توسط بانک به سایت فروشنده باز خواهد گشت و اطلاعاتی از بانک بصورت POST به صفحه سایت ارسال خواهد شد.
(گرچه ممکن است این مرحله بنا به دلایل متعددی رخ ندهد که در مقدمه اشاره شد)

این صفحه بازگشت ، هنگام عقد قرار داد با بانک تحت عنوان ReceiptPage مشخص می شود .

هنگام بازگشت کاربر از صفحه بانک به صفحه ReceiptPage این اطلاعات نیز بصورت POST ارسال خواهند شد :
damoonTable2.jpg

*توضیحات :*
*x_trans_id* : شماره رسید الکترونیکی (شماره خرید ، شماره پیگیری ، ...) که از طرف بانک تولید می شود
(این شماره باید در رکورد پرداخت ذخیره شود)

*x_response_code* : کد پاسخ که نشان دهنده وضعیت پرداخت می باشد 
1 = پرداخت موفق
2 = لغو پرداخت
3 = خطا در پرداخت
*
x_response_subcode
x_response_reason_code
x_response_reason_text*
این 3 پارامتر نیز اطلاعات بیشتری در مورد وضعیت پرداخت می دهند که در صورت لزوم می توانید آن ها را ثبت و ذخیره کنید

حال با داشتن این پارامترها ، کارهای زیادی میشه کرد . یعنی میشه همه چیز رو چک کرد و از صحت اطلاعات ارسالی مطمئن شد .
ولی یک روش تصمیم گیری می تواند به این صورت باشد :




> توسط x_fp_sequence که همان شماره پرداخت بود ، رکورد پرداخت را Fetch کنید .
> 
> اگر (رکورد پرداخت وجود داشت)
>     {
>       اگر (x_response_code برابر 1 بود && مقدار x_trans_id خالی نبود) // پرداخت موفق بوده است
>       {
>             انجام عمل Verification و استعلام صحت پرداخت از بانک // ممکن است اطلاعات فعلی از سمت بانک ارسال نشده باشد
> 
>             اگر (x_response_code دریافتی از verification برابر 1 بود && مقدار x_trans_id دریافتی از verification خالی نبود)
> ...


جهت اطمینان بیشتر ، می توانید برابر بودن مقدار مبلغ ارسال شده توسط بانک (x_amount) را با مقداری که در رکورد پرداخت ثبت کرده اید ، چک کنید.

*حال به تشریح پیاده سازی استعلام پرداخت (Verification) می پردازیم :*

آدرسی که باید اطلاعات پرداخت به آن ارسال شود و پاسخ استعلام وضعیت پرداخت از آن دریافت شود آدرس زیر است :
https://Damoon.bankmelli-iran.com/DamoonVerificationController

رشته اطلاعاتی که قبلاً توضیح داده شد که ذخیره کنید (PaymentInfoString) باید به این آدرس با متد POST ارسال شود تا پاسخ استعلام را مشاهده کنید
*ولی* این بار دیگه نباید صفحه کاربر را توسط Form به این صفحه ارسال کنیم . بلکه باید این آدرس را در کد نویسی خود فراخوانی کنیم و از خروجی آن مقادیر مورد نظر را استخراج کرده و تصمیم مناسب در مورد وضعیت پرداخت بگیریم .

در صورتی که مقادیر PaymentInfoString به درستی به این آدرس ارسال شود ، خروجی مانند زیر تولید خواهد شد :

x_trans_id=123165546&x_response_code=1&x_response_  subcode=3&x_response_reason_code=&x_response_reaso  n_text=&x_login=Test21&x_fp_sequence=256
&x_fp_timestamp=1322657054&x_amount=10000&x_curren  cy_code=Rial&x_fp_hash=f16696fac8329d1defb9bf09f79  687cc
<html>
<head>
<title>
PaymentVerification
</title>
</head>
<body>
</body>
</html>
<!--damoon.jsp.PaymentVerification.jsp-->
همان گونه که مشاهده می کنید در خط اول پاسخ مقادیر استعلام وجود دارد . ولی در خطهای بعدی تگ های HTML بی مصرفی مشاهده می شوند که به هیچ دردی نمیخورن  :گیج: 
اینجا حتماً طراح صفحه میخواسته اگه کاربری مستقیم این صفحه رو فراخوانی کرد صفحه Title داشته باشه . این هم یکی دیگه از شاهکارهای این سیستم پرداخت است  :لبخند گشاده!: . بگذریم  :اشتباه: 

کاری که باید برنامه نویس انجام دهد ، این است که مقادیر لازم از رشته ی در خط اول را استخراج کند و از آن ها استفاده کند .

روشی که من در #ASP.net , C از آن استفاده کردم به صورت زیر است :
(برنامه نویسان در سایر زبان ها با بخش پشتیبانی درگاه تماس گرفته و نمونه کد جهت Verify کردن را درخواست کنند)

var webClient = new System.Net.WebClient();

string PostData = PaymentInfoString;//x_description=value&x_login=value&x_fp_sequence=va  lue&x_fp_timestamp=value&x_amount=value&x_currency  _code=value&x_fp_hash=value

webClient.Encoding = System.Text.UnicodeEncoding.UTF8;
webClient.Headers["Content-type"] = "application/x-www-form-urlencoded";

string bankResponse = webClient.UploadString("https://Damoon.bankmelli-iran.com/DamoonVerificationController?" + PostData, "POST", PostData);

همان گونه که متوجه شدید (اگه نشدید دقت کنید  :لبخند گشاده!: ) ، میبینید که مقدار متغیر PostData (که همان PaymentInfoString است که گفتم حتماً ذخیره کنید) هم به صورت GET در انتهای آدرس بانک قرار گرفته است و هم به صورت POST به همین آدرس ارسال شده است .
این هم یکی دیگر از شاهکارهای این سیستم پرداخت است  :لبخند گشاده!:  که در این آدرس مقادیر بصورت GET باید تحت QueryString به آدرس ارسال شوند . ولی خود آدرس بصورت POST فراخوانی شود .
البته اگر اطلاعاتی نیز POST نشوند مشکلی ندارد و همین که صفحه بصورت POST فراخوانی شود ، جواب خواهد داد .
ولی ما برای اطمینان هم به صورت GET و هم به صورت POST ارسال میکنیم که اگه سیستم صفحه بانک اصلاح شد ، مشکلی پیش نیاد .

بهتر است این کد را در بلوک TryCatch فراخوانی کنید تا در صورت وجود مشکل در فراخوانی صفحه بانک ، خطایی رخ ندهد .

به هر حال اگه مشکلی در سیستم بانک و شبکه و اینترنت و ... وجود نداشته باشه ، خروجی صفحه در متغیر bankResponse قرار خواهد گرفت .

حال همان متنی که در بالاتر اشاره کردیم را در یک متغیر String داریم .(خروجی استعلام به همراه کمی تگ HTML مزاحم  :خیلی عصبانی: )
اینجا هم باز بنا به سلیقه و تکنیک و تجربه برنامه نویس کارهای مختلفی قابل انجام است .

من ابتدا تگ های HTML مزاحم را توسط RegularExpressions حذف میکنم  :شیطان: .

bankResponse = System.Text.RegularExpressions.Regex.Replace(bankR  esponse, @"\<(.|\r|\n)*\>", "").Trim();
این کار باعث میشه تا کلیه مقادیر بین اولین کارکتر > و آخرین کارکتر < به همراه خود آن ها حذف شوند(و فقط متن مورد نظر باقی خواهد ماند).

باز هم می توانید از روش های مختلفی برای پردازش این رشته و استخراج اطلاعات لازم از آن استفاده کنید

ولی از آنجا که این متن فرمت QueryString دارد ، در #C می توانیم از تابع ParseQueryString موجود در کلاس HttpUtility استفاده کنیم .
این تابع یک رشته به فرمت QueryString را می گیرد و توسط متد Get کلید مورد نظر را گرفته و value آن را خروجی میدهد .

value = HttpUtility.ParseQueryString(query).Get(key);
برای مثال برای بدست آوردن مقدار x_trans_id به این صورت فراخوانی کنید :

string nx_trans_id = HttpUtility.ParseQueryString(bankResponse).Get("x_  trans_id");
به همین صورت مقادیر همه پارامترها را از این رشته استخراج کنید و تصمیم مورد نظر را بر اساس آن بگیرید .




> اگر (x_response_code دریافتی از verification برابر 1 بود && مقدار x_trans_id دریافتی از verification خالی نبود)
> {
>             ثبت پرداخت به عنوان تایید شده و پرداخت موفق و ...
>             ذخیره اطلاعات مورد نیاز در رکورد پرداخت مانند x_trans_id و x_response_code و x_response_subcode و x_response_reason_code و  x_response_reason_text و...
>             خروجی مناسب به کاربر مبنی بر پرداخت موفق
> }
> در غیر این صورت
> {
>             ثبت پرداخت به بصورت پرداخت نشده ، خطا در پرداخت ، لغو پرداخت و ...
> }


*متد استعلام پاسخ از بانک در بخش های زیر استفاده می شود :*
1 . هنگام ارسال پاسخ بانک به صفحه ReceiptPage فروشنده . جهت مطمئن شدن از صحت پرداخت (حتماً پیاده سازی شود)

2 . در سیستم پنل مدیریت ، جهت مطمئن شدن از صحت پرداخت . (حتماً پیاده سازی شود . ممکن است کاربر پرداخت را انجام داده باشد ، ولی اطلاعات به سایت فروشنده ارسال نشده باشد)

زیرا هنگام تست و تایید سیستم پرداخت سایت فروشنده توسط مسئول پشتیبانی  درگاه ، از شما امکان Verify کردن بصورت دستی ، در صورت عدم ارسال اطلاعات به  صفحه ReceiptPage را تحویل میگیرند (یکی از دلایل این است که هنگام پرداخت ،  مبلغ از حساب کاربر کسر می شود)

3 . در پنل کاربری اعضا سایت نیز می توانید امکان Verify کردن پرداخت توسط کاربر را فراهم کنید (در صورتی که کاربر پرداخت انجام داده باشد ، اطلاعات به سایت ارسال نشده باشد و مدیر نیز جهت استعلام پرداخت در دسترس نباشد . (اختیاری است))

*جمع بندی نکات مهم :*
*1 .*
مقادیر x_login و x_tran_key از طرف بانک برای فروشنده ارسال خواهد شد.
(x_tran_key محرمانه است و به عنوان کلید hash در تابع HMAC_MD5 استفاده می شود)

*2.*
هر پرداخت باید ID یکتا داشته باشد (x_fp_sequence)
برای مثال : در صورتی که پرداخت شماره 100 ثبت شد و به بانک ارسال شد ، ولی موفق نبود ، دیگر قابل استفاده نیست و باید پرداخت 101 ثبت شود و مجدداً به بانک ارسال شود.
(می توانید ترتیبی اتخاذ کنید که کدهای سوخته پس از بررسی ، حذف شوند)

*3.*
هنگام ثبت پرداخت ، تمامی مقادیری که باید به بانک بصورت POST ارسال شود را در رکورد پرداخت ذخیره کنید
زیرا هنگام پیاده سازی Verification باید دقیقاً از همین اطلاعات استفاده کنید

*4.*
در تعداد فراخوانی و استعلام وضعیت پرداخت (Verification) محدودیتی وجود ندارد و می توان چندین بار این استعلام را انجام داد

*نکته :*
در مستندات ، صحبت از سیستم SilentResponse شده است و یعنی این که بانک به محض پرداخت کاربر ، اطلاعات پرداخت را به صورت آسنکرون به آدرس فروشنده ارسال می کند
در صورتی که ممکن است کاربر هنوز در سایت بانک باشد و یا صفحه بانک را بعد از پرداخت ببندد .
ولی ظاهراً این مکانیزم هنوز توسط بانک اجرایی نشده است .
گرچه پیاده سازی آن در وب سایت فروشنده هیچ تفاوتی با صفحه ReceiptPage ندارد .


*تلفن تماس پشتیبانی درگاه دامون :*
02129982793
02129982782

*آدرس صفحات بانک جهت ارسال اطلاعات :*

درگاه پرداخت :
https://damoon.bankmelli-iran.com/DamoonPrePaymentController
استعلام پرداخت (Verification) :
https://Damoon.bankmelli-iran.com/DamoonVerificationController
**************
آدرس جهت تست صحت اطلاعات پرداخت :
https://Damoon.bankmelli-iran.com/MerchantsIntegrationTestController

آدرس جهت تست صحت اطلاعات استعلام (Verification) :
https://Damoon.bankmelli-iran.com/VerificationTestController

بهتر است در حین پیاده سازی و جهت تست ، ابتدا آدرس های خود را بر روی آدرس های تست که در بالا ذکر شده تنظیم کنید
تا از صحت مقادیر و فرمت اطلاعات ارسالی مطمئن شوید و پس از انجام تست از آدرس های اصلی استفاده نمایید .

در صورت ارسال صحیح اطلاعات به صفحات تست ، چنین پیغامی نمایش داده خواهد شد :

damoonTest.JPG

در پایان مجدداً متذکر می شوم که قصد من از ارائه این مطلب ، پوشش دادن بهتر و رفع سوالاتی است که در زمینه پرداخت الکترونیکی سیستم دامون مطرح می شوند .

هدف من این بود که تمامی پارامترها،مقادیر و اطلاعات ارسالی و دریافتی بین بانک و سایت فروشنده به صورت روشن و واضح بیان شود تا کد نویسی برای این عملیات ، مشخص و هدفمند باشد .
تا شما بدانید دقیقاً چه چیزهایی باید به بانک ارسال شود و بانک چه مقادیری برای سایت شما ارسال می کند.

به نظر من مستندات و نمونه کدهای موجود ، روشن و شفاف نبودند و بصورت پراکنده مطالبی مطرح شده بود.

به همین دلیل نمونه کدهایی که مطرح شده است فقط جنبه آموزشی دارند و بیشتر سعی کردم با ذکر مثال بحث را باز کنم . زیرا به این سخن انیشتین اعتقاد دارم که :

مثال زدن یک راه آموزش نیست . تنها راه آموزش است  :چشمک: 

موفق و پیروز باشید

----------


## irGeek

شایعاتی  درباره  تغییر پرداخت بانک ملی شنیدم میخواستم ببینم کسی هست که الان هم از این سیستم استفاده می کنه یا نه؟

----------


## Chabok

این سیستم آخرین و جدیدترین سیستم پرداخت آنلاین بانک ملی بوده و حدود 2-3 سال است که شروع به کار کرده
ولی جهت رفع ابهام می تونید با شماره پشتیبانی تماس بگیرید و در این مورد ازشون سوال کنید .

جهت اطلاع عرض کنم که بنده شخصاً 2 هفته نیست که این سیستم پرداخت رو پیاده سازی و اتمام کردم .
داره بدون مشکل کار میکنه و پشتیبانی نسبتاً خوبی هم داره

موفق باشید

----------


## mahdi_farhani

یک نکته جالب : برای اینکه برنامتون را تست کنید میتونید با مبلغ *صفر ریال* برنامه رو تست کنید کلیه مراحل انجام میشه ، پرداخت هم از سمت درگاه تایید میشه و trans_id به شما داده میشه و هیچ مبلغی هم از شما کسر نمیشه

برعکس بانک پارسیان که زیر 1000 تومان اصلاً تایید نمیکنه

----------


## crazy_1892

دوست عزیز من شنیدم یک سیستم دسگه داره به نام سداد این چیه با این دامون چه فرقی داره؟

----------


## Chabok

ظاهراً اینجور که شنیده ام درگاه سداد رو به همه نمیدن . بیشتر به شرکت های بزرگ و دانشگاه ها و موسسات حقوقی میدن . یعنی مسئول پشتیبانی اینجور به من گفت
ولی دامون رو به اشخاص حقیقی هم میدن (در صورتی که شعبه ، شخص و حسابش رو تایید بکنه . شاید جواز کسب و ... هم لازم باشه)

----------


## raziee

> یک نکته جالب : برای اینکه برنامتون را تست کنید میتونید با مبلغ *صفر ریال* برنامه رو تست کنید کلیه مراحل انجام میشه ، پرداخت هم از سمت درگاه تایید میشه و trans_id به شما داده میشه و هیچ مبلغی هم از شما کسر نمیشه
> 
> برعکس بانک پارسیان که زیر 1000 تومان اصلاً تایید نمیکنه


یک شرط داره.
این که بانک شحاب شما و بانک مربوطه یکی باشه.

----------


## sm

باتشکر از دوستان
اگر محسن خان محبت کنن و درباره submit کردن هم توضیح بدن ممنون میشم.البته بادرنظر گرفتن صحبت انیشتن خان!!!




> طریقه Submit کردن این فرم و ارسال آن به بانک ، به روش های مختلفی امکان پذیر است که از حوصله این مبحث خارج است و بر اساس سلیقه و نظر برنامه نویس پیاده سازی می شود.


موفق باشید

----------


## hamed2592

با سلام و تشکر 

ای کاش با VB هم نمونه کد گذاشته بودین

----------


## رضا قربانی

مزخرف ترین درگاه اینترنتی همین بانک ملی سیستم دامون بوده - من با php نوشتمش ولی دو روز کشید و با بقیه بانکها فرق میکنه (برنامه نویسش قدیمی فکر میکرده)، مخصوصا این قسمت Verification و تابع چک کردن زمانی که کاربر تایید نهایی رو نزد. خلاصه ...
بانک ملت - سرمایه و... کجا و این کجا

----------


## naeeme

از نظر پیاده سازی ساده است اما متناسب با وضعیت اینترنت ایران نیست و متاسفانه خیلی تغییرات توش میدن که گاهی باعث دردسر میشه.

----------


## assa_912004

سلام 
من سایتم با جوملاست و ماژول ملی را براش نصب کردم اینم لینکش Www.emailsend.ir
ولی از این طریقه verification چیزی متوجه نشدم در ضمن اصلا این کد ها را باید کجای سایت بزاریم؟

----------


## rana-writes

سلام
ممنون از راهنمايي خيلي خوبتون
ميشه طريقه Submit كردنش رو هم توضيح بدين چون يه جا گفته بودين كه اين اطلاعات بايد توي يه ركورد توي بانكتون ثبت بشه
حالا با يه باتني كه مربوط به HTML  ميشه چطوري ميشه كدنويسي ‍C#‎‎‎  انجام داد
توي نمونه كدي هم كه گذاشتين يه فقط يه تابع بود كه توش هيچ كاري انجام نشده بود


در ضمن ميشه طريقه Verification رو هم توضيح بدين؟

هر كاري ميكنم نمي تونم پياده سازيش كنم. لطفا يه راهنمايي كنين 

ممنونم

بعدا اضافه شده:

با استفاده از آموزشهاي اين تاپيك، تونستم پياده سازي رو به طور كامل انجام بدم
لينكش رو ميذارم اينجا به همراه يه نمونه پروژه آماده شايد به درد دوستان ديگه بخوره

showthread.php?353544

----------


## Morteza

سلام
میشه راجع به کتابخانه SOAP که در PHP هست یه توضیحی بدید، من تو بعضی از سایت ها دیدم که برای پرداخت آنلاین از این کتابخانه استفاده میکنن و اینکه آیا برای پرداخت دامون هم نیازی به این کتابخانه هست یا نه؟

ممنون

----------


## mohsentranslator

سلام

بنده از سیستم whmcs استفاده می کنم و نیاز به درگاه پرداخت بانک ملی   (سداد) با قابلیت ها زیر را دارم. لطفا هزینه و مدت زمان تحویل   رو اعلام کنید.

 نصب آسان تنها با آپلود کردن چند فایل قابلیت بازگشت وجه به پرداخت کننده در  			صورت بروز مشکل در هنگام پرداخت ارائه Admin panel 			برای ماژول در قسمت مدیریتی WHMCSبرای  			مشاهده وضعیت تراکنش ها ثبت رسید دیجیتالی صادر شده توسط بانک  			در WHMCS



نیاز ضرورری

باتشکر

----------


## VeRJiL

با سلام ممنون از پست فوق العاده مفید و جالبتون فقط میخواستم بدونم امکانش هست یکی از روشهایی رو که گفتین رو به صورت کد نمایش بدین آخه من تازه وب سایت نویسی رو شروع کردم و تقریبا گیج شدم در ضمن من دارم با PHP کار میکنم این کدها هم PHP بودن ؟
اگه امکانش هست کمکم کنین

----------


## rezaonline.net

کلاس دامون بانک صادرات . (بانک ملی هم شبیه همینه فقط آدرسها رو عوض کنید .)
درضمن از امسال بانک ملی فقط درگاه سداد میده ، دیگه دامون نمیده . پس بررسی کنید درگاه سداد هستید یا دامون
<?php


class bsi  
{
      private    $loginID = '1235';
      private    $transactionKey = '123456789aaddvv';
        
    
        /**
    *     method send request to bank and get authority!
    *
    * @param $price int , toman تومان
    * @param $order_id int , unique id
    * @param $callback string , callback site
    * @return $au string
    **/
    public function request($price = NULL , $order_id = NULL , $callback = NULL)
    {
        $amount = $price * 10;
        $sequence    = (int) rand(1, 1000);
        $timeStamp    = (int) time ();
        $au = "{$order_id}-{$sequence}-{$timeStamp}-tmp";
        $url    = "https://Damoon.bsi.ir/DamoonPrePaymentController";
        $loginID        = $this->loginID;
        $transactionKey = $this->transactionKey;
        
        
        $currency = "Rial";
        $testMode        = "false";
        
        if( phpversion() >= '5.1.2' )
        {    $fingerprint = hash_hmac("md5", $loginID . "^" . $sequence . "^" . $timeStamp . "^" . $amount . "^" . $currency, $transactionKey); }
        else 
        { $fingerprint = bin2hex(mhash(MHASH_MD5, $loginID . "^" . $sequence . "^" . $timeStamp . "^" . $amount . "^" . $currency, $transactionKey)); }


        $c = "<FORM method='post' action='$url' name='myform'  >";
        
        $c .= "    <INPUT type='hidden' name='x_login' value='{$this->loginID}' />";
        $c .= "    <INPUT type='hidden' name='x_amount' value='$amount' />";
        $c .= "    <INPUT type='hidden' name='x_currency_code' value='Rial' />";
        $c .= "    <INPUT type='hidden' name='x_description' value='ORDER_ID={$order_id}' />";
        $c .= "    <INPUT type='hidden' name='x_invoice_num' value='$order_id' />";
        $c .= "    <INPUT type='hidden' name='x_fp_sequence' value='$sequence' />";
        $c .= "    <INPUT type='hidden' name='x_fp_timestamp' value='$timeStamp' />";
        $c .= "    <INPUT type='hidden' name='x_fp_hash' value='$fingerprint' />";
        $c .= "    <INPUT type='hidden' name='x_test_request' value='$testMode' />";
        $c .= "    <INPUT type='hidden' name='x_show_form' value='PAYMENT_FORM' />";
        $c .= "    <INPUT type='hidden' name='x_action' value='$url' />";
        $c .= "    <INPUT type='hidden' name='x_fp_receiptpage' value='$callback' />";
        
        $c .= "</FORM> <script language='javascript'>document.myform.submit()</script>";
        
        self::$c = $c;
        
        return $au;
    }
    
    static public $c;
    
    /* go to bank by au */
    public function go2bank($au='')
    {
        ?>
                <!DOCTYPE html>
<html>
  <head>
   <meta charset='utf-8' />
  </head>
  <body>
  <meta charset='utf-8' />
  <center dir=rtl style='font-family:tahoma;font-size:11px'>
لطفا صبر کنید ... <br>
<img src='<?php echo Yii::app()->baseUrl .'/images/loading.gif' ?>' />
</center>
<?php echo self::$c; ?>

  </body>
</html>
        
        
        <?php
        
    }
    
    
        /**
    * method to check verify transaction
    *
    * @param $price int , تومان
    * @param $order_id int , unique order id 
    * @param $au string , authority code
    * @return bool true/false 
    **/
    public $SaleReferenceId = '';
    public function verify($price = NULL ,$order_id = NULL , $au = NULL)
    {
        
        $_ = explide('-',$au);
        $x_description = "ORDER_ID={$order_id}";
        $x_currency_code = 'Rial';
        
        $result = $this->verificationTransactions($_[1],$_[2],$price*10,$x_currency_code,$x_description);
        if($result===false)
            return false;
        
        $result = explode("&",$result);
            foreach ($result AS $temp) 
            {
                $temp = explode("=",$temp);
                $resultArray[$temp[0]] = $temp[1];
            }
            //return $this->checkReceipt($resultArray,true);
            $post = $resultArray;
            
        if( isset($post['x_trans_id']) )
            {
            $this->SaleReferenceId = $post['x_trans_id'];
                $x_trans_id = $post['x_trans_id'];
                $x_response_code = $post['x_response_code'];
                $x_response_subcode = $post['x_response_subcode'];
                $x_response_reason_code = $post['x_response_reason_code'];
                $x_response_reason_text = $post['x_response_reason_text'];
                $x_login = $post['x_login'];
                $x_fp_sequence = $post['x_fp_sequence'];
                $x_fp_timestamp = $post['x_fp_timestamp'];
                $x_amount = $post['x_amount'];
                $x_currency_code = $post['x_currency_code'];
                $x_fp_hash = $post['x_fp_hash'];

                if($x_login != $this->loginID)
                {
                    //Logs::model()->add('برگشت صادرات','عدم تطابق loginID');
                    return false;
                }
                
                if($x_amount!= $price * 10)
                {
                    //Logs::model()->add('برگشت صادرات','عدم تطابق مبلغ دریافتی و ارسالی');
                    return false;
                }
                
                //$x_currency_code = $this->_CurrencyCode;    

                //$this->_TRANSKEY = Configuration::get($this->name . '_TRANSKEY');
                $fingerprint = $this ->hmac($this->transactionKey, $x_trans_id . "^" . $x_response_code . "^" .$x_response_subcode . "^" .$x_response_reason_code . "^" .  $x_response_reason_text . "^" . $x_login . "^" . $x_fp_sequence . "^" . $x_fp_timestamp . "^" . $x_amount . "^" . $x_currency_code);

                $x_fp_hash=rtrim($x_fp_hash);
                if($fingerprint != $x_fp_hash)
                {

                    Logs::model()->add('برگشت صادرات','Error in Fingerprint.');  
                    return false;
                }
                else
                {
                    //if($x_response_code != 4)
                        //$this->saveBankResponse($x_fp_sequence,$x_trans_id,$x_re  sponse_code,$x_response_subcode,$x_response_reason  _code,$x_response_reason_text,$x_amount);
                        
                    if($x_response_code == 1 )
                    {
                        //$this->_html .= '<h2>' . $this->l('Payment Succesfull') . '</h2>';
                        //$this->_postMessages[] = $this->l('Payment Succesfull');
                        //$this->_html .=$this->l('Sequence Number = ') .$this->checkOrderInfo($x_fp_sequence).'<br />';
                        $result = true;
                        
                        return true;
                    }
                    else
                    {
                        return false;
                        /*$result = false;
                        switch($x_response_code)
                        {
                            case "2":
                                $this->_postErrors[] = $this->l('RCP-ERR-Transaction Declined');
                                break;    
                            case "3":
                                $this->_postErrors[] = $this->l('RCP-ERR-Error in payment');
                                break;
                            case "4":
                                $this->_postErrors[] = $this->l('RCP-ERR-Ambiguous-Wait for certain response');
                                break;
                        }*/
                    }
                }
            }
            else 
            {
                return false;
            }
    }
    
    private function verificationTransactions($x_fp_sequence,$x_fp_time  stamp,$x_amount,$x_currency_code,$x_description)    {
            $x_tran_key = $this->transactionKey;
            $x_login = $this->loginID;

            $x_fp_hash = $this -> hmac ($x_tran_key, $x_login . "^" . $x_fp_sequence . "^" . $x_fp_timestamp . "^" . $x_amount . "^" . $x_currency_code);
            
            $ch = curl_init();
            
                curl_setopt($ch, CURLOPT_URL, 'https://Damoon.bsi.ir/DamoonVerificationController');
            


            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt ($ch, CURLOPT_POST, 1);

            $post="x_fp_timestamp=$x_fp_timestamp&x_fp_sequenc  e=$x_fp_sequence&x_fp_hash=$x_fp_hash&x_login=$x_l  ogin&x_description=$x_description&x_amount=$x_amou  nt&x_currency_code=$x_currency_code";

            curl_setopt ($ch, CURLOPT_POSTFIELDS, $post);
            $estore = curl_exec ($ch);
            curl_close ($ch);
            if(!$estore){
                //$this->_postErrors[] = $this->l('Error in verification');
                //$this->displayErrors();
                return false;
            }
            $tagPos = strpos($estore,'<');
            return substr($estore,0,$tagPos);
            return $estore;
        }
    
    public function _verify($price = NULL ,$order_id = NULL , $au = NULL)
    {
        $post = $_POST;
         
        if( isset($_POST['x_trans_id']) )
            {
            $this->SaleReferenceId = $post['x_trans_id'];
                $x_trans_id = $post['x_trans_id'];
                $x_response_code = $post['x_response_code'];
                $x_response_subcode = $post['x_response_subcode'];
                $x_response_reason_code = $post['x_response_reason_code'];
                $x_response_reason_text = $post['x_response_reason_text'];
                $x_login = $post['x_login'];
                $x_fp_sequence = $post['x_fp_sequence'];
                $x_fp_timestamp = $post['x_fp_timestamp'];
                $x_amount = $post['x_amount'];
                $x_currency_code = $post['x_currency_code'];
                $x_fp_hash = $post['x_fp_hash'];

                if($x_login != $this->loginID)
                {
                    Logs::model()->add('برگشت صادرات','عدم تطابق loginID');
                    return false;
                }
                
                if($x_amount!= $price * 10)
                {
                    Logs::model()->add('برگشت صادرات','عدم تطابق مبلغ دریافتی و ارسالی');
                    return false;
                }
                
                //$x_currency_code = $this->_CurrencyCode;    

                //$this->_TRANSKEY = Configuration::get($this->name . '_TRANSKEY');
                $fingerprint = $this ->hmac($this->transactionKey, $x_trans_id . "^" . $x_response_code . "^" .$x_response_subcode . "^" .$x_response_reason_code . "^" .  $x_response_reason_text . "^" . $x_login . "^" . $x_fp_sequence . "^" . $x_fp_timestamp . "^" . $x_amount . "^" . $x_currency_code);

                $x_fp_hash=rtrim($x_fp_hash);
                if($fingerprint != $x_fp_hash)
                {

                    //Logs::model()->add('برگشت صادرات','Error in Fingerprint.');  
                    return false;
                }
                else
                {
                    //if($x_response_code != 4)
                        //$this->saveBankResponse($x_fp_sequence,$x_trans_id,$x_re  sponse_code,$x_response_subcode,$x_response_reason  _code,$x_response_reason_text,$x_amount);
                        
                    if($x_response_code == 1 )
                    {
                        //$this->_html .= '<h2>' . $this->l('Payment Succesfull') . '</h2>';
                        //$this->_postMessages[] = $this->l('Payment Succesfull');
                        //$this->_html .=$this->l('Sequence Number = ') .$this->checkOrderInfo($x_fp_sequence).'<br />';
                        $result = true;
                        
                        return true;
                    }
                    else
                    {
                        return false;
                        /*$result = false;
                        switch($x_response_code)
                        {
                            case "2":
                                $this->_postErrors[] = $this->l('RCP-ERR-Transaction Declined');
                                break;    
                            case "3":
                                $this->_postErrors[] = $this->l('RCP-ERR-Error in payment');
                                break;
                            case "4":
                                $this->_postErrors[] = $this->l('RCP-ERR-Ambiguous-Wait for certain response');
                                break;
                        }*/
                    }
                }
            }
            else 
            {
                return false;
            }
    }
    
    private function hmac ($key, $data)    
    {
        return (hash_hmac("md5", $data, $key)); 
    }

    
}

----------


## ashkan

با اینکه خیلی گذشته گفتم یه توضیحات تکمیلی هم بنویسم.

1- برای درگاه دامون بانک ملی فایلد description نمیشه تهی یا همون خالی باشه وگرنه پیغام خطای سیستمی بانک نشون میده
درضمن دقت کنید که تو این فیلد چی مینویسید چون برای verify کردن لازمه.از نظر من این متغیر کلا اضافه هست.

2-برای این کلاس که برای دامون صادرات هست و این دوستمون نوشتند چند متغیر اضافه مثلا action و x_fp_receiptpage و...دیدم که در ملی نیست
گویا دامون بانک صادرات با متغیر x_fp_receiptpage اجازه میده صفحه بازگشت رو خودمون تعیین کنیم در صورتی که دامون بانک ملی ثابت هست.

اگر کسی صفحه بازگشت دلخواه رو تونسته در دامون بانک ملی بنویسه یه توضیحی بده.

----------


## alireza_rashvand

سلام 
*الان شیوه پیاده سازی نسبت به گفته های این تاپیک فرق کرده یا نه ؟
*
لطفا دوستانی که اطلاعات دارند کمک کنند که ایا منی که تا به حال پرداخت انلاین انجمن ندادم از همین روش تاپیک پیروی کنم یا شیوه نسبت با سال های قبل, تغییر کرده 

*سپاس.*

----------

