(ASP.NET) ASP.NET (2009 год)

Шлюзы к платежным системам интернет-денег.

Для приема интернет-денег можно использовать анонимные кошельки. Но у них есть есть несколько недостатков:

1. Ваш плательщик может указать там произвольную сумму (а не четко требуемую к поступлению на ваш счет в нужной вам валюте), что может служить источником разногласий.

2. Он может указать непонятную цель платежа, что тоже может быть источником трений и разногласий.

3. Деньги со счета плательщика могут быть списаны, а к вам так и не поступить. Доказывать что-то по анонимному счету как правило бесполезно.

4. Деньги со своего анонимного кошелька вы все равно не сможете обналичить в банкомате (все равно это придется делать через чей-то авторизованный счет).

Указанных недостков лишены авторизованные интернет-кошельки (счета), которые имеют Merchant-интерфейс. Это кнопка, на которую можно кликнуть и, авторизовавшись в своем кошельке, окзаться на сайте интернет-банка уже с заполненной нужной вам суммой, нужным назначением платежа. Вместо цифрового номера вашего счета (куда клиент будет платить) как правило там будет указано символическое имя. А назначение платежа выбирается на вашем собственном сайте и на сайте интернет-банка его уже нельзя изменить.

После выполнения платежа вы мгновенно получите уведомление на свой сайт от интернет-банка и когда ваш пользователь нажмет кнопку "вернутся в магазин" ваш сайт сможет сразу же указать этому пользователю что выбранный им товар оплачен.


В теории все хорошо, вот только к сожалению все существующие интернет-банки имеют свои заморочки. Яндекс-деньги не позволяет использовать merchant-интерфейс физическим лицам, только юридическим - причем взимает за каждую транзакцию баснословные 5%.

Webmoney требует ацской и унизительной процедуры авторизации продавца, которая требует не только ксерокопии паспорта, но и собственноручной обязательной публикации в интернете всех личных данных, вплоть до домашнего адреса владельца предприятия (в отношении юридических лиц не возбуждаются уголовные дела). Ну как бы ваши личные данные невидимы неприоритетным логинам, но там есть десятки тысяч приоритетных логинов. Как мне объяснили в WebMoney в отношении всех остальных граждан (кроме россиян и украинцев) действует банковская тайна. На мой взгляд такая позиция интернет-банка противоречит закону о охране сведений о частной жизни, но кто видел чтобы у нас соблюдались какие-то законы? Банк действует видимо под крышей каких-то чекистов, возможно уклоняется от налогов (зарегистирован в Латвии), а в отношении нас крыша ему гарантирует возможность нарушать любые российские законы. Кроме того, собственноручное заявление об открытии счета с приложением копии паспорта заполяется ровно в той форме, какая требуется для открытия на вас какого-нибудь уголовного дела - фактически вы сами на себя заполняете бланки, которые вам потом покажет ваш следователь. Кроме того, конвертация денег из других платежных систем практически невозможна. Еще одним недостатком является замороченный и тупой сайт WebMoney в котором в самый нужный момент вываливается ошибка - сорри этот сервис недоступен. Кроме того, там еще миллион заморочек странных - например вы не можете открыть себе два счета - почему-то сразу деньги сразу же блокируются (становятся собственностью этого интернет-банка). Для удобства прослушивания они также требуют от вас при регистрации номер вашего мобильника. Чуствуется за этой шайкой крепкая криминально-чекистская крыша, которая позволяет им себя настолько беспредельно вести себя.

Еще более замкнутая платежная система (в плане приема денег из других платежных систем) англо-немецкий банк MoneyBooker. Правда там нет таких явных преступных намерений о сборе ваших личных данных, не нужно заполнять бланки для открытия уголовного дела в отношении себя, не требуется ксерокопия паспорта или номер вашего мобильника. Достаточно просто авторизовать свой почтовый адрес. Вам приходит на ваш домашний (или любой другой) адрес письмо - в нем несколько букв пароля. Вы их вбиваете на сайт и все - счет полностью авторизован. Вы получаете право перечислять по этому счету по $12 тыс. за каждые 90 дней (по $1400 за раз).

Проблемы обычно начинаются позже, когда вы сливаете свои деньги на счета российских чудо-банков, например альфа-банку на карточку Visa-Electron (чтобы снять деньги в банкомате). Альфа-банк видимо кому-то продает ведомости с перечислениями - меня лично трахают после каждого перевода не меньше месяца - звонят на домашний телефон всякие московские банки и предлагают взять у них кредиты. И еще возмущаются, когда я отказываюсь "у нас столько кредитных продуктов предлагается - как такое может быть, что вам не один наш продукт не нравится"? Хотя возможно, утечки происходят не в Альфа-банке. Все управление счетом происходит по SSL, а уведомления о каждом входе по https:// отправляются в Microsoft (как бы в рамках борьбы с фишингом) и вообще легко фильтруются на магистралях даже без этих уведомлений. Так что продавать сведения о платежах могут и все вот эти фельдмаршалы по борьбе с терроризмом, имеющие доступ к интернет-магистралям.

Для приема Яндекс-денег через Merchant-интерфейс физических лиц обычно используется Robokassa. У меня нет пока достаточного опыта общения с этой платежной системой, поэтому я от комментариев воздержусь. Однако относительно всех остальных систем сразу же бросилось в глаза отсутствие нормальной активации логина. Они это делают почему-то вручную по вашей письменной просьбе по электронной почте.

Платежная система для VISA, MasterCard и других пластиковых карточек - Assist. С ней я работал тоже. По логике работы ее мерчант-интерфейс является клоном интерфейса Яндекс-мани и Web-мани. С той небольшой заморочкой, что этот сервис написан на Яве и с ним гиморойно работать с платформы NET. Почему именно - я пояснил тут WCF_CLIENT - клиент Web-сервиса. Шлюз Ассиста является коммерческим know-how, поэтому тут его код опубликован не будет. Однако некоторые мои технологические подходы к программированию этого шлюза вы можете понять из этой моей заметки - Как парсить XML SOAP в MS SQL.

Очень уважаемые мною люди порекомендовали мне израильско-американскую интернет-платежную систему Plimus, который на первый взгляд лишен всех указанных заморочек. Но достаточного личного опыта работы с Плимусом у меня пока нет, поэтому я пока тоже воздержусь от критики.


Документация на Merchant-интерфейсы:


Теперь перейдем к обсуждению технических аспектов merchant-шлюза в ASP.NET специфике. Все платежные шлюзы устроены совершенно одинаково, собственно даже многие фрагменты кода можно просто копировать из одного шлюза в другой. Процесс начинается с того, то на своем сайте вы формируете итоговую форму (по результатам того, что нащелкал юзер на вашем сайте) и оправляете ее постбеком на сервер платежей. В качестве данных там будет сумма, номер счета назначения, все необходимые комментарии. Вместо цифрового номера счета там будет символичское имя вашего магазина, комментарии и суммы изменить уже будет нельзя. У плимуса страничка, куда попадает ваш клиент после нажатия кнопки "оплатить" на вашем сайте не одинаковая, как в остальных платежных системах (так она выглядит в Яндексе, а вот так в WebMoney), а вы ее конструируете сами (для каждого своего продукта или назначения платежа). В MoneyBooker тоже есть некая настройка странички куда попадет ваш покупатель, но не настолько продвинутая, как в Plimus.

Для выполнения этого первичного постбека с запросом о платеже есть два пути - первый формирование POST на нужный адрес на сервере. Это, конечно, более защищенный путь - относительно отправки первичного реквеста о платеже непосредственно из браузера клиента вашего сайта. Но, увы, это не получается со всеми платежными системами. Так получается работать например с Яндекс-деньгами (пример вы можете посмотреть на rzd.gisis.ru. С WebMoney получается как-то странно - реквесты проходят иногда несколько подряд, иногда через один, иногда каждый второй. Чуствуются какие-то проблемы у этих чекистов. Ну не хочет, видимо, на них работать ни один нормальный программист.

Поэтому для WebMoney (и других) можно работать менее защищенным, но более универсальным способом - отправкой первичного реквеста о платеже непосредственно с клиента. Такой путь я применил, например, на сайте www.asp-net.ru. Фишка, однако, заключается в том, что ASP.NET в приниципе не позволяет отправлять постбеки на другую страничку - те в рамках пиридавой разрекламированной билагейтсвоской чудо-технологии ASP.NET эта простенькая задачка не решаема в принципе. Однако она решается элементарно в рамках простого ASP, где постбек на странички можно отправить куда угодно. Правда тут есть некоторые технические проблемы, например с той же кодировкой.

Итак, если мы хотим сформировать первичный реквест в рамках технологии ASP.NET и более защищенным от всяких атак и подделок способом, то нам надо взять примерно вот такой код, который лежит где-то в библиотеке:


   1:      'Запрос странички методом POST (молча, ошибки обрабатываются извне этого кода)
   2:      Public Function PostRequest(ByVal URL As String, ByVal POST_Data As String) As String
   3:          '========== System.NotSupportedException The URI prefix is not recognized.
   4:          Dim request As Net.HttpWebRequest = Net.HttpWebRequest.Create(URL)
   5:          request.Method = "POST"
   6:          Dim byteArray As Byte() = System.Text.Encoding.ASCII.GetBytes(POST_Data)
   7:          request.ContentType = "application/x-www-form-urlencoded"
   8:          request.ContentLength = byteArray.Length
   9:          '========== System.Net.WebExceptionStatus.Timeout Unable to connect to the remote server
  10:          Dim POST_Stream As IO.Stream = request.GetRequestStream()
  11:          POST_Stream.Write(byteArray, 0, byteArray.Length)
  12:          POST_Stream.Close()
  13:          '
  14:          'ждем
  15:          '========== System.Net.WebException.Timeout
  16:          '========== System.Net.WebException = "The remote server returned an error: (404) Not Found."
  17:          Dim response As Net.HttpWebResponse = request.GetResponse()
  18:          Dim GET_Stream As IO.Stream = response.GetResponseStream()
  19:          Dim reader As IO.StreamReader
  20:          reader = New IO.StreamReader(GET_Stream, System.Text.ASCIIEncoding.GetEncoding("windows-1251"))
  21:          Dim HTML As String = reader.ReadToEnd
  22:          reader.Close()
  23:          GET_Stream.Close()
  24:          response.Close()
  25:          Return HTML
  26:      End Function

И вызвать его прямо на сервере примерно вот так:


   1:      'первый реквест в Яндекс
   2:      Public Shared Sub YaMoneyFirstRequest(ByVal Summ As String, ByVal CustomerNumber As String)
   3:          Dim Replay As String = InternetTransfer.PostRequest(System.Configuration.ConfigurationManager.AppSettings("YaMoneyURL"), "scid=1234567&ShopID=" & System.Configuration.ConfigurationManager.AppSettings("ShopID").ToString & "&Sum=" & Summ & "&CustomerNumber=" & CustomerNumber & "&BuyButton=Submit")
   4:          Replay = Replay.Replace("action=""//demomoney.yandex.ru", "action=""https://demomoney.yandex.ru")
   5:          Replay = Replay.Replace("action=""/store-request.xml", "action=""https://demomoney.yandex.ru/store-request.xml")
   6:          Replay = Replay.Replace("action=""http://127.0.0.1:8129/wallet", "action=""https://demomoney.yandex.ru:8129/wallet")
   7:          HttpContext.Current.Response.Write(Replay)
   8:      End Sub

Как вы понимаете, это не вполне корректный вызов, скорее напоминающий технологии фишингования. Корректный способ перехода на сервера платежных систем я описал здесь - WCF_CLIENT - клиент Web-сервиса.

Здесь вы видите пример для демо-денег (ибо вам все равно придется отлаживать свой шлюз на них). Параметр ShopID и YaMoneyURL я вынес в конфиг, вам их дадут когда вы зарегистрируетесь в Яндексе.

<add key="ShopID" value="12345678"/>
<add key="YaMoneyURL" value="https://demomoney.yandex.ru/eshop.xml"/>

Обратите внимание, что при получении ответа от Яндекса вам придется поменять ссылки на рисунки (ибо они указаны относительными адресами). Параметр CustomerNumber - это тот самый номер заказа, который сформировал ваш сайт. Именно чтобы его сокрыть - выгодно отправлять первичный реквест с сервера, а не с клиента. Это защитит ваш сайт от любых возможных атак. Ведь во всех последующих ответах от яндекса (или от того, кто пытается выдать себя за Яндекс) - нужно будет указывать этот CustomerNumber. А если его и не показывать клиенту изначально (это просто GUID обычно у меня), то тема атак на ваш сервер учета платежей вообще отсутвует. К тому же это решение самое что ни на есть в рамках ASP.NET. Но... как я уже говорил, более глюкавые интернет-банки почему-то принимают такие серверные реквесты через раз.

Поэтому более универсальный, но менее защищенный шлюз должен сформировать этот постбек на клиенте. Для этого придется выйти за рамки ASP.NET. Делается это так:

В итоге у вас получается верстка странички со всеми вашими контролами и мастер-пейджами. Вы можете сразу выбросить из нее все скрипты ASp.NET, но главное:

Для merchant.webmoney.ru это изменение будет выглядеть так:

    <form name="aspnetForm" method="post" accept-charset="cp1251" action="https://merchant.webmoney.ru/lmi/payment.asp"  id="aspnetForm">
    <input type="hidden" id="LMI_PAYMENT_AMOUNT" name="LMI_PAYMENT_AMOUNT" value='<%=Request.Querystring("Sum") %>' />
    <input type="hidden" id="LMI_PAYMENT_DESC" name="LMI_PAYMENT_DESC" value='<%=Request.Querystring("Type") %> : <%=Request.Querystring("Comment") %>' />
    <input type="hidden" id="LMI_PAYMENT_NO" name="LMI_PAYMENT_NO" value="0" />
    <input type="hidden" id="LMI_PAYEE_PURSE" name="LMI_PAYEE_PURSE" value="Z397687856249" />
    <input type="hidden" id="LMI_SIM_MODE" name="LMI_SIM_MODE" value="0" />

Как вы поняли, я тут заморачиваться не стал, а вызвал эту ASP-овскую страничку из ASP.NET-овской просто прокинув ей параметры в Реквесте:


   1:      Protected Sub ImageButton3_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles ImageButton3.Click
   2:          Response.Redirect("WM_Payment.asp?Sum=" & txSumm.Text.Replace(",", ".") & "&Type=" & Server.UrlEncode(DropDownList1.SelectedItem.Text) & "&Comment=" & Server.UrlEncode(txComment.Text))
   3:          Response.End()
   4:      End Sub

Обратите внимание на кодировку в POST. Здесь все стоит именно так, чтобы в кошельке у вашего покупателя введеный им текст на страничке ASP.NET оказался в руской кодировке. Для этого надо еще задать кодировку самой ASP-вской странички (чтобы она приняла правильно параметры из ASP.NET-овской):

<META http-equiv="Content-Type" content="text/html; charset=UTF-8">

В приниципе, сохранение правильной кодировка при переходе из странички ASP.NET на страничку ASP, а затем постбек на страничку платежной системы - это основная заморочка здесь. Комментарий, введенный вашим покупателем на ASP.NET страничке на русском языке - должен отобрадаться в платежной системе тоже на русском языке после прохождения этой цепочки. Никаких других проблем, над которыми тут можно задуматься больше секунды - тут просто нет. Для тестирования кодировок вы можете воспользоваться моим публичным сервисом //www.vb-net.com/PostTest.ashx, отправив для начала постбеки на мой хандлер.


В связи с тем, что я описываю беспредельно простой магазин - для WebMoney тот самый CustomerNumber (номер заказа) здесь имеет имя LMI_PAYMENT_NO и в этом магазине он у меня тупо ноль всегда. LMI_SIM_MODE - это режим тестовый/боевой (в Яндексе применяют вместо этого переключателя префикс к URL - money.yandex.ru или DEMOmoney.yandex.ru)

Аналогичная страничка для отправки первичного постбека формируется и для moneybooker:

    <form name="aspnetForm" method="post" accept-charset="cp1251" action="http://www.moneybookers.com/app/test_payment.pl"  id="aspnetForm">
    <input type="hidden" id="pay_to_email" name="pay_to_email" value="[email protected]" />
    <input type="hidden" id="language" name="language" value="RU" />
    <input type="hidden" id="transaction_id" name="transaction_id" value="0" />
    <input type="hidden" id="amount" name="amount" value='<%=Request.Querystring("Sum") %>' />
    <input type="hidden" id="currency" name="currency" value="USD" />
    <input type="hidden" id="detail1_description" name="detail1_description" value='<%=Request.Querystring("Type") %>' />
    <input type="hidden" id="detail1_text" name="detail1_text" value='<%=Request.Querystring("Comment") %>' />
    <input type="hidden" id="status_url" name="status_url"  value='//www.vb-net.com/Status3.ashx' />

Совершенно аналогично формируется первичный постбек и для robokassa.ru

    <form name="aspnetForm" method="post" accept-charset="cp1251" action="https://merchant.roboxchange.com/Index.aspx"  id="aspnetForm">
    <input type="hidden" id="MrchLogin" name="MrchLogin" value="asp-net" />
    <input type="hidden" id="OutSum" name="OutSum" value='<%=Request.Querystring("Sum") %>'  />
    <input type="hidden" id="nInvId" name="nInvId" value='0'  />
    <input type="hidden" id="sInvDesc" name="sInvDesc" value='<%=Request.Querystring("Type") %> : <%=Request.Querystring("Comment") %>'  />
    <input type="hidden" id="sSignatureValue" name="sSignatureValue"  value='<%=Request.Querystring("Hash") %>'  />
    <input type="hidden" id="sIncCurrLabel" name="sIncCurrLabel" value='PCR'  />
    <input type="hidden" id="sCulture" name="sCulture" value='ru'  />
    <input type="hidden" id="sEncoding" name="sEncoding" value='Windows-1251'  />

Вот только вызов ее будет сложнее из-за необходимости отправлять MD5-хеш:


   1:      Protected Sub ImageButton2_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles ImageButton2.Click, ImageButton7.Click, ImageButton8.Click
   2:          Dim Signature As New StringBuilder
   3:          Dim MD5 As New System.Security.Cryptography.MD5CryptoServiceProvider
   4:          'sMrchLogin, sOutSum, nInvId, sMrchPass1
   5:          Dim MD5Hash() As Byte = MD5.ComputeHash(Encoding.ASCII.GetBytes("тутвыпишитесвойлогин" & ":" & txSumm.Text.Replace(",", ".") & ":" & "0" & ":" & "тутвыпишитесвойпароль"))
   6:          For Each One As Byte In MD5Hash
   7:              Signature.AppendFormat("{0:x2}", One)
   8:          Next
   9:          Response.Redirect("Robo_Payment.asp?Sum=" & txSumm.Text.Replace(",", ".") & "&Type=" & Server.UrlEncode(DropDownList1.SelectedItem.Text) & "&Comment=" & Server.UrlEncode(txComment.Text) & "&Hash=" & Signature.ToString)
  10:          Response.End()
  11:      End Sub

Здесь роль индивидуального номера заказа играет параметр nInvId, так же как CustomerNumber в Яндексе, LMI_PAYMENT_NO в WebMoney, transaction_id в Moneybooker.

Немного иной принцип у plimis.com - вы создаете в конструкторе на сайте плимуса несколько нужных вам настроенных страниц, на которых подробно можете описать все свои продукты, сервисы. Загружаете нужные картинки, программы. Получаете индивидуальный номер созданного описания. И в простейшем случае ставите со своей странички просто линк с номером вашей странички с описанием товара, например такой:

<a href="https://www.plimus.com/jsp/buynow.jsp?contractId=2393616" target="_blank">Payment my product</a>

И ваш покупатель попадает на нужную страничку, на которой вы сформировали описание своего товара или услуги.


Теперь перейдем к следующему этапу. Когда пользователь вашего сайта залогинится и выполнит платеж со своего кошелька в ваш адрес, вам поступит уведомление об оплате. Все интернет-банки имеют некоторые параметры настройки - каким образом удостоверится в корректности параметров платежа, каким образом пеередать вам уведомление об оплате, как вам передать сводный месячный отчет и так далее. Если запрос на выполнение платежа всегда выполняется одинакого, то ответы от интернет-банка зависят от этих настроек. Поэтому я дальше покажу свои любимые настройки в каждом из этих интернет-банков, ибо без них весь дальнейший код совершенно теряет смысл. Например если вы установили режим получения уведомления по почте, то какой смысл будет в хандлере на вашем сайте, который я опишу ниже для приема уведомлений о платеже?

Итак, вы входите в WebMoney и выполняете там настройки своего кошелька. Совершенно аналогичным образом устанавливаются настройки (имена хандлеров на вашем сайте) для Яндекс-кошелька или Робокассы (фактически надстройки над Яндекс-кошельками для физических лиц). Для MoneyBooker видимо имена этих хандлеров тоже можно где-то задать в настройках счета, но я их передаю непосредственно в постбеке параметром status_url. В Яндексе и других магазинах их тоже по идее так же можно передавать. Plimus, насколько я понимаю, делает уведомления об оплате только по E-mail (и я пока не понимаю, как это обрабатывать). Другие платежные системы тоже могут высылать уведомления по Email, но тогда после возврата из платежной системы в ваш магазин вы не узнаете - оплачен ли уже ваш товар. Мыло ведь может и через день дойти. Поэтому, на мой взгляд, есть смысл выбирать уведомления по E-mail только для месячных реестров, а в остальных случаях видеть факт оплаты в онлайне.

Хандлеры, которые мы рассмотрим дальше и являются онлайновыми обработчиками уведомлений об оплате. В каждой платежной системе есть описание, в какой именно форме (обычно это XML) платежные системы вам передают данные об успешной оплате (и от кого зачислены вам деньги).

Для Яндекс-денег таких хандлера должно быть четыре - Aviso.ashx, Fail.asxh, Check.asxh, Success.ashx. Fail.ashx вызывается когда ваш покупатель нажал кнопку оплатить товар, залогинился в свой кошелек, что-то тыкал в нем, но платеж в итоге не прошел. Поэтому обрабатывать осмысленно эти ложные тыкания смысла нет.

А вот Check.ashx должен содержать вполне осмысленный код. Этот ханжлер должен сформировать XML, который подтвердит Яндексу, что такой продукт вы продать готовы и деньги за него ваш электронный магазин приять готов. Этот хандлер должен выглядеть так:


   1:  <%@ WebHandler Language="VB" Class="Check" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class Check : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          Dim InvoiceId As String = ""
   8:          Dim GisisID As String = Gateway.YaCheckSave
   9:          If GisisID <> "" Then
  10:              InvoiceId = Gateway.YaCheck(GisisID)
  11:              If InvoiceId <> "" Then
  12:                  Dim D As DateTime = Now
  13:                  Dim Performed_S As String '2005-01-21T13:20:07+03:00
  14:                  Performed_S = String.Format("{0:0000}-{1:00}-{2:00}T{3:00}:{4:00}:{5:00}+03:00", D.Year, D.Month, D.Day, D.Hour, D.Minute, D.Second)
  15:                  Dim ResultXmlString As String = "<?xml version=""1.0"" encoding=""windows-1251""?>" & vbCrLf & _
  16:                  "<response performedDatetime=""" & Performed_S & """>" & vbCrLf & _
  17:                  "<result code=""0"" action=""Check"" shopId=""" & System.Configuration.ConfigurationManager.AppSettings("ShopID").ToString & """ invoiceId=""" & InvoiceId & """/>" & vbCrLf & _
  18:                  "</response>"
  19:                  Dim Coder As System.Text.Encoding
  20:                  Coder = System.Text.Encoding.GetEncoding(1251)
  21:                  context.Response.ContentEncoding = Coder
  22:                  context.Response.ContentType = "text/xml"
  23:                  context.Response.BinaryWrite(Coder.GetBytes(ResultXmlString))
  24:              End If
  25:          End If
  26:      End Sub

Хандлер вызывает две функции шлюза. Первая из них просто ведет статистику всех обращений Яндекса за проверками (этого можно и не делать, чтобы не росла база и не требовала лишнего обслуживания) и возвращает CustomerNumber - тот самый номер заказа, который мы отправляли Яндексу в первичном постбеке с запросом платежа.


   1:      'Сохранение ответа из Яндекса на запрос платежа
   2:      Public Shared Function YaCheckSave() As String
   3:          '
   4:          Dim CN As System.Data.SqlClient.SqlConnection
   5:          Dim CMD As Data.SqlClient.SqlCommand
   6:          '
   7:          Try
   8:              CN = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
   9:              CN.Open()
  10:              CMD = New Data.SqlClient.SqlCommand("[YaCheckSave]", CN)
  11:              CMD.CommandType = Data.CommandType.StoredProcedure
  12:              CMD.Parameters.AddWithValue("requestDatetime", HttpContext.Current.Request.Form("requestDatetime"))
  13:              CMD.Parameters.AddWithValue("md5", HttpContext.Current.Request.Form("md5"))
  14:              CMD.Parameters.AddWithValue("shopId", HttpContext.Current.Request.Form("shopId"))
  15:              CMD.Parameters.AddWithValue("invoiceId", HttpContext.Current.Request.Form("invoiceId"))
  16:              CMD.Parameters.AddWithValue("customerNumber", HttpContext.Current.Request.Form("customerNumber"))
  17:              CMD.Parameters.AddWithValue("orderCreatedDatetime", HttpContext.Current.Request.Form("orderCreatedDatetime"))
  18:              CMD.Parameters.AddWithValue("orderSumAmount", HttpContext.Current.Request.Form("orderSumAmount"))
  19:              CMD.Parameters.AddWithValue("orderSumCurrencyPaycash", HttpContext.Current.Request.Form("orderSumCurrencyPaycash"))
  20:              CMD.Parameters.AddWithValue("orderSumBankPaycash", HttpContext.Current.Request.Form("orderSumBankPaycash"))
  21:              CMD.Parameters.AddWithValue("shopSumAmount", HttpContext.Current.Request.Form("shopSumAmount"))
  22:              CMD.Parameters.AddWithValue("orderIsPaid", HttpContext.Current.Request.Form("orderIsPaid"))
  23:              CMD.Parameters.AddWithValue("paymentType", HttpContext.Current.Request.Form("paymentType"))
  24:              CMD.Parameters.AddWithValue("paymentPayerCode", HttpContext.Current.Request.Form("paymentPayerCode"))
  25:              CMD.Parameters.AddWithValue("action", HttpContext.Current.Request.Form("action"))
  26:              CMD.Parameters.AddWithValue("ErrorTemplate", HttpContext.Current.Request.Form("ErrorTemplate"))
  27:              CMD.Parameters.AddWithValue("shn", HttpContext.Current.Request.Form("shn"))
  28:              CMD.Parameters.AddWithValue("secureparam2", HttpContext.Current.Request.Form("secureparam2"))
  29:              CMD.Parameters.AddWithValue("secureparam5", HttpContext.Current.Request.Form("secureparam5"))
  30:              CMD.Parameters.AddWithValue("BuyButton", HttpContext.Current.Request.Form("BuyButton"))
  31:              CMD.Parameters.AddWithValue("isViaWeb", HttpContext.Current.Request.Form("isViaWeb"))
  32:              CMD.Parameters.AddWithValue("SuccessTemplate", HttpContext.Current.Request.Form("SuccessTemplate"))
  33:              CMD.Parameters.AddWithValue("scid", HttpContext.Current.Request.Form("scid"))
  34:              CMD.Parameters.AddWithValue("ncrnd", HttpContext.Current.Request.Form("ncrnd"))
  35:              CMD.Parameters.AddWithValue("WAPaymentPreferences", HttpContext.Current.Request.Form("WAPaymentPreferences"))
  36:              CMD.ExecuteNonQuery()
  37:          Catch ex As Exception
  38:              Dim CN1 As System.Data.SqlClient.SqlConnection
  39:              Dim CMD1 As Data.SqlClient.SqlCommand
  40:              CN1 = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  41:              CN1.Open()
  42:              CMD1 = New Data.SqlClient.SqlCommand("INSERT [CheckDump] ([Raw]) VALUES (@Raw)", CN1)
  43:              CMD1.CommandType = Data.CommandType.Text
  44:              CMD1.Parameters.AddWithValue("Raw", ex.Message)
  45:              CMD1.ExecuteNonQuery()
  46:              Return ""
  47:          End Try
  48:          '
  49:          If CN IsNot Nothing Then
  50:              If CN.State.Open Then
  51:                  CN.Close()
  52:              End If
  53:          End If
  54:          Return HttpContext.Current.Request.Form("customerNumber")
  55:      End Function

Вторая функция осуществляет собственно проверку - может ли жлектронный магазин принимать деньги за указанный товар. У меня эта проверка вынесена на уровень SQL и свою проверку я публиковать не буду, ибо в вашем магазине она все равно будет другая.


   1:      'Проверка корректности ответа из Яндекса на запрос платежа (возвращает InvoiceId)
   2:      Public Shared Function YaCheck(ByVal TicketID As String) As String
   3:          '
   4:          Dim CN As System.Data.SqlClient.SqlConnection
   5:          Dim CMD As Data.SqlClient.SqlCommand
   6:          Dim InvoiceId As String = ""
   7:          '
   8:          Try
   9:              CN = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  10:              CN.Open()
  11:              CMD = New Data.SqlClient.SqlCommand("[YaCheck]", CN)
  12:              CMD.CommandType = Data.CommandType.StoredProcedure
  13:              CMD.Parameters.AddWithValue("TicketId", TicketID)
  14:              Dim RDR1 As Data.SqlClient.SqlDataReader = CMD.ExecuteReader(System.Data.CommandBehavior.SingleRow)
  15:              If RDR1.Read Then
  16:                  If Not IsDBNull(RDR1("invoiceId")) Then
  17:                      InvoiceId = RDR1("invoiceId")
  18:                  End If
  19:              End If
  20:          Catch ex As Exception
  21:              Dim CN1 As System.Data.SqlClient.SqlConnection
  22:              Dim CMD1 As Data.SqlClient.SqlCommand
  23:              CN1 = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  24:              CN1.Open()
  25:              CMD1 = New Data.SqlClient.SqlCommand("INSERT [CheckDump] ([Raw]) VALUES (@Raw)", CN1)
  26:              CMD1.CommandType = Data.CommandType.Text
  27:              CMD1.Parameters.AddWithValue("Raw", ex.Message)
  28:              CMD1.ExecuteNonQuery()
  29:              Return ""
  30:          End Try
  31:          '
  32:          If CN IsNot Nothing Then
  33:              If CN.State.Open Then
  34:                  CN.Close()
  35:              End If
  36:          End If
  37:          Return InvoiceId
  38:      End Function

Вторая функция возвращает invoiceId - номер платежной транзакции Яндекса - он нужен для формирования результирующего XML, который я сформировал прямо в хандлере со всем требуемым Яндексом форматированием.

Хандлер Aviso.ashx вызывает Яндекс, когда происходит платеж. Он тоже не может быть пустым, потому что в ответе надо сформировать XML. Я делаю этот хандлер так:


   1:  <%@ WebHandler Language="VB" Class="Aviso" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class Aviso : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          Dim InvoiceId As String = gateway.YaSetAviso
   8:          If InvoiceId<>"" then
   9:                  Dim D As DateTime = Now
  10:                  Dim Performed_S As String '2005-01-21T13:20:07+03:00
  11:                  Performed_S = String.Format("{0:0000}-{1:00}-{2:00}T{3:00}:{4:00}:{5:00}+03:00", D.Year, D.Month, D.Day, D.Hour, D.Minute, D.Second)
  12:                  Dim ResultXmlString As String = "<?xml version=""1.0"" encoding=""windows-1251""?>" & vbCrLf & _
  13:                  "<response performedDatetime=""" & Performed_S & """>" & vbCrLf & _
  14:                  "<result code=""0"" action=""PaymentSuccess"" shopId=""" & System.Configuration.ConfigurationManager.AppSettings("ShopID").ToString & """ invoiceId=""" & InvoiceId & """/>" & vbCrLf & _
  15:                  "</response>"
  16:                  Dim Coder As System.Text.Encoding
  17:                  Coder = System.Text.Encoding.GetEncoding(1251)
  18:                  context.Response.ContentEncoding = Coder
  19:                  context.Response.ContentType = "text/xml"
  20:                  context.Response.BinaryWrite(Coder.GetBytes(ResultXmlString))
  21:          End If
  22:      End Sub
  23:   
  24:   
  25:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
  26:          Get
  27:              Return False
  28:          End Get
  29:      End Property
  30:   
  31:  End Class

Все осмысленные действия по факту платежа я делаю на уровне SQL и тут публиковать не буду, соответсвенно функция YaSetAviso этого хандлера - это просто вызов процедуры:


   1:      'Проверка корректности ответа из Яндекса на запрос платежа (возвращает InvoiceId)
   2:      Public Shared Function YaSetAviso() As String
   3:          '
   4:          Dim CN As System.Data.SqlClient.SqlConnection
   5:          Dim CMD As Data.SqlClient.SqlCommand
   6:          Dim InvoiceId As String = ""
   7:          '
   8:          Try
   9:              CN = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  10:              CN.Open()
  11:              CMD = New Data.SqlClient.SqlCommand("[YaSetAviso]", CN)
  12:              CMD.CommandType = Data.CommandType.StoredProcedure
  13:              CMD.Parameters.AddWithValue("id", HttpContext.Current.Request.Form("customerNumber"))
  14:              CMD.Parameters.AddWithValue("Price", HttpContext.Current.Request.Form("orderSumAmount"))
  15:              CMD.ExecuteNonQuery()
  16:              InvoiceId = HttpContext.Current.Request.Form("invoiceId")
  17:          Catch ex As Exception
  18:              Dim CN1 As System.Data.SqlClient.SqlConnection
  19:              Dim CMD1 As Data.SqlClient.SqlCommand
  20:              CN1 = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  21:              CN1.Open()
  22:              CMD1 = New Data.SqlClient.SqlCommand("INSERT [CheckDump] ([Raw]) VALUES (@Raw)", CN1)
  23:              CMD1.CommandType = Data.CommandType.Text
  24:              CMD1.Parameters.AddWithValue("Raw", ex.Message)
  25:              CMD1.ExecuteNonQuery()
  26:              Return ""
  27:          End Try
  28:          '
  29:          If CN IsNot Nothing Then
  30:              If CN.State.Open Then
  31:                  CN.Close()
  32:              End If
  33:          End If
  34:          Return InvoiceId
  35:      End Function

И, наконец, хандлер Success.ashx вызывается Яндексом по кнопке "возврат в магазин". Хдесь вы должны просто сдедать переход на свою страничку, на которой будет установлено, что товар покупателем уже оплачен. Грубо говоря, этот хандлер содержит единственную строку с Response.redirect на список товаров, который отбирал пользователь до нажатия кнопки "оплатить", только в Aviso вы уже изменили статус этого товара на "уже оплачен".

Совершенно аналогично устроен платежный шлюз WebMoney, он состоят из хандлеров Check и Success. Здесь у меня более простой магазин и деньги он принимает без проверки - всегда отвечая "Да".


   1:  <%@ WebHandler Language="VB" Class="Check1" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class WM_Check : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          If context.Request.Form("LMI_PREREQUEST") IsNot Nothing Then
   8:              'это запрос Check - всегда отвечаем YES
   9:              context.Response.ContentType = "text/plain"
  10:              context.Response.Write("YES")
  11:          Else
  12:              '            
  13:          End If
  14:      End Sub
  15:   
  16:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
  17:          Get
  18:              Return False
  19:          End Get
  20:      End Property
  21:   
  22:  End Class

А хандлер Success ведет учет поступивших платежей (фиксируя фозможные ошибки):


   1:  <%@ WebHandler Language="VB" Class="Success1" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class WM_Success : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          '
   8:          Dim CN As System.Data.SqlClient.SqlConnection
   9:          Dim CMD As Data.SqlClient.SqlCommand
  10:          '
  11:          Try
  12:              CN = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  13:              CN.Open()
  14:              CMD = New Data.SqlClient.SqlCommand("AddVmAviso", CN)
  15:              CMD.CommandType = Data.CommandType.StoredProcedure
  16:              CMD.Parameters.AddWithValue("PAYEE_PURSE", HttpContext.Current.Request.Form("LMI_PAYEE_PURSE"))
  17:              CMD.Parameters.AddWithValue("PAYMENT_AMOUNT", HttpContext.Current.Request.Form("LMI_PAYMENT_AMOUNT"))
  18:              CMD.Parameters.AddWithValue("PAYMENT_NO", HttpContext.Current.Request.Form("LMI_PAYMENT_NO"))
  19:              CMD.Parameters.AddWithValue("MODE", HttpContext.Current.Request.Form("LMI_MODE"))
  20:              CMD.Parameters.AddWithValue("SYS_INVS_NO", HttpContext.Current.Request.Form("LMI_SYS_INVS_NO"))
  21:              CMD.Parameters.AddWithValue("SYS_TRANS_NO", HttpContext.Current.Request.Form("LMI_SYS_TRANS_NO"))
  22:              CMD.Parameters.AddWithValue("PAYER_PURSE", HttpContext.Current.Request.Form("LMI_PAYER_PURSE"))
  23:              CMD.Parameters.AddWithValue("PAYER_WM", HttpContext.Current.Request.Form("LMI_PAYER_WM"))
  24:              CMD.Parameters.AddWithValue("PAYMER_NUMBER", HttpContext.Current.Request.Form("LMI_PAYMER_NUMBER"))
  25:              CMD.Parameters.AddWithValue("PAYMER_EMAIL", HttpContext.Current.Request.Form("LMI_PAYMER_EMAIL"))
  26:              CMD.Parameters.AddWithValue("EURONOTE_NUMBER", HttpContext.Current.Request.Form("LMI_EURONOTE_NUMBER"))
  27:              CMD.Parameters.AddWithValue("EURONOTE_EMAIL", HttpContext.Current.Request.Form("LMI_EURONOTE_EMAIL"))
  28:              CMD.Parameters.AddWithValue("TELEPAT_PHONENUMBER", HttpContext.Current.Request.Form("LMI_TELEPAT_PHONENUMBER"))
  29:              CMD.Parameters.AddWithValue("TELEPAT_ORDERID", HttpContext.Current.Request.Form("LMI_TELEPAT_ORDERID"))
  30:              CMD.Parameters.AddWithValue("PAYMENT_CREDITDAYS", HttpContext.Current.Request.Form("LMI_PAYMENT_CREDITDAYS"))
  31:              CMD.Parameters.AddWithValue("ATM_WMTRANSID", HttpContext.Current.Request.Form("LMI_ATM_WMTRANSID"))
  32:              CMD.Parameters.AddWithValue("HASH", HttpContext.Current.Request.Form("LMI_HASH"))
  33:              CMD.Parameters.AddWithValue("SYS_TRANS_DATE", HttpContext.Current.Request.Form("LMI_SYS_TRANS_DATE"))
  34:              CMD.Parameters.AddWithValue("SECRET_KEY", HttpContext.Current.Request.Form("LMI_SECRET_KEY"))
  35:              CMD.Parameters.AddWithValue("Comment", HttpContext.Current.Request.Form("Comment"))
  36:              CMD.ExecuteNonQuery()
  37:          Catch ex As Exception
  38:              Dim CN1 As System.Data.SqlClient.SqlConnection
  39:              Dim CMD1 As Data.SqlClient.SqlCommand
  40:              CN1 = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  41:              CN1.Open()
  42:              CMD1 = New Data.SqlClient.SqlCommand("INSERT [WebMoneyAviso]([PAYEE_PURSE], [PAYMENT_AMOUNT]) VALUES (@Raw, @Raw1)", CN1)
  43:              CMD1.CommandType = Data.CommandType.Text
  44:              CMD1.Parameters.AddWithValue("Raw", ex.Message)
  45:              '
  46:              Dim Buf1(10000) As Byte
  47:              If HttpContext.Current.Request.InputStream.Length < Buf1.Length Then
  48:                  '
  49:                  Dim ASCII As New System.Text.ASCIIEncoding
  50:                  Dim ASCII_STR1 As String
  51:                  Dim UNIC As New System.Text.UnicodeEncoding
  52:                  Dim UNIC_STR1 As String
  53:                  Dim Raw1 As String
  54:                  '
  55:                  While HttpContext.Current.Request.InputStream.CanRead
  56:                      Dim CountReadingBytes As Integer = HttpContext.Current.Request.InputStream.Read(Buf1, 0, HttpContext.Current.Request.InputStream.Length)
  57:                      If CountReadingBytes = 0 Then Exit While
  58:                      ReDim Preserve Buf1(HttpContext.Current.Request.InputStream.Length)
  59:                      '
  60:                      Raw1 &= ("PostTest" & vbCrLf & vbCrLf)
  61:                      Raw1 &= ("Request.ContentEncoding = " & HttpContext.Current.Request.ContentEncoding.ToString & vbCrLf)
  62:                      Raw1 &= ("Request.ContentType     = " & HttpContext.Current.Request.ContentType & vbCrLf)
  63:                      Raw1 &= ("Request.ContentLength   = " & HttpContext.Current.Request.ContentLength & vbCrLf & vbCrLf)
  64:                      Raw1 &= ("Request.InputStream     = ")
  65:                      For i As Integer = 0 To HttpContext.Current.Request.ContentLength
  66:                          Raw1 &= (String.Format(" {0:X2}", Buf1(i)))
  67:                      Next
  68:                      Raw1 &= (vbCrLf & vbCrLf)
  69:                      '
  70:                      ASCII_STR1 &= ASCII.GetString(Buf1)
  71:                      UNIC_STR1 &= UNIC.GetString(Buf1)
  72:                      '
  73:                      ReDim Buf1(10000)
  74:                  End While
  75:                  Raw1 &= "ASCII   = " & ASCII_STR1 & vbCrLf
  76:                  Raw1 &= "UNICODE = " & UNIC_STR1 & vbCrLf
  77:                  '
  78:                  Raw1 &= (vbCrLf & "Request.Params " & vbCrLf & vbCrLf)
  79:                  For i As Integer = 0 To HttpContext.Current.Request.Params.Count - 1
  80:                      Raw1 &= ("(" & i.ToString & ") " & HttpContext.Current.Request.Params.Keys(i) & " = " & HttpContext.Current.Request.Params(HttpContext.Current.Request.Params.Keys(i)) & vbCrLf)
  81:                  Next
  82:                  '
  83:                  CMD1.Parameters.AddWithValue("Raw1", Raw1)
  84:              End If
  85:              CMD1.ExecuteNonQuery()
  86:          End Try
  87:          '
  88:          If CN IsNot Nothing Then
  89:              If CN.State.Open Then
  90:                  CN.Close()
  91:              End If
  92:          End If
  93:          '
  94:          context.Current.Response.Redirect("Default.aspx")
  95:      End Sub
  96:   
  97:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
  98:          Get
  99:              Return False
 100:          End Get
 101:      End Property
 102:   
 103:  End Class

Примерно так же устроен и шлюз робокассы. Там тоже требуется два осмысленных хандлера - Result и Success. Небольшая заморочка заключается в проверке MD5-хеша в хандлере Result. Это я делаю так:


   1:  <%@ WebHandler Language="VB" Class="Result2" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class Robo_Result : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          'уведомления об исполнении операции
   8:          Dim sMrchPass2 As String = "JujWLopJW87%@),762KDXkjwe6"
   9:          Dim OutSum As String
  10:          If context.Current.Request.Form("OutSum") IsNot Nothing Then
  11:              If context.Current.Request.Form("OutSum") <> "" Then
  12:                  OutSum = context.Current.Request.Form("OutSum")
  13:              End If
  14:          End If
  15:          Dim InvId As String
  16:          If context.Current.Request.Form("InvId") IsNot Nothing Then
  17:              If context.Current.Request.Form("InvId") <> "" Then
  18:                  InvId = context.Current.Request.Form("InvId")
  19:              End If
  20:          End If
  21:          Dim sCrc As String
  22:          If context.Current.Request.Form("SignatureValue") IsNot Nothing Then
  23:              If context.Current.Request.Form("SignatureValue") <> "" Then
  24:                  sCrc = context.Current.Request.Form("SignatureValue")
  25:              End If
  26:          End If
  27:          Dim sCrcBase As String = String.Format("{0}:{1}:{2}", OutSum, InvId, sMrchPass2)
  28:          '
  29:          Dim md5 As System.Security.Cryptography.MD5CryptoServiceProvider = New System.Security.Cryptography.MD5CryptoServiceProvider()
  30:          Dim bSignature() As Byte = md5.ComputeHash(Encoding.ASCII.GetBytes(sCrcBase))
  31:          '
  32:          Dim sbSignature = New StringBuilder()
  33:          For Each One As Byte In bSignature
  34:              sbSignature.AppendFormat("{0:x2}", One)
  35:          Next
  36:          If sbSignature.ToString.ToUpper <> sCrc.ToUpper Then
  37:              context.Response.ContentType = "text/plain"
  38:              HttpContext.Current.Response.Write("bad sign")
  39:              Exit Sub
  40:          Else
  41:              context.Response.ContentType = "text/plain"
  42:              HttpContext.Current.Response.Write(String.Format("OK{0}", InvId))
  43:              'писать в базу ничего не будем, запишем в Success    
  44:          End If
  45:      End Sub
  46:   
  47:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
  48:          Get
  49:              Return False
  50:          End Get
  51:      End Property
  52:   
  53:  End Class

При успешной оплате робокасса вызывает согласно моим настройкам хандлер Success, который у меня выглядит вот так:


   1:  <%@ WebHandler Language="VB" Class="Success2" %>
   2:  Imports System
   3:  Imports System.Web
   4:  Public Class Robo_Success : Implements IHttpHandler
   5:      
   6:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
   7:          'уведомления об исполнении операции
   8:          Dim sMrchPass2 As String = "3_4jhs&do$i2kjgMsaW(EghAas"
   9:          Dim OutSum As String
  10:          If context.Current.Request.Form("OutSum") IsNot Nothing Then
  11:              If context.Current.Request.Form("OutSum") <> "" Then
  12:                  OutSum = context.Current.Request.Form("OutSum")
  13:              End If
  14:          End If
  15:          Dim InvId As String
  16:          If context.Current.Request.Form("InvId") IsNot Nothing Then
  17:              If context.Current.Request.Form("InvId") <> "" Then
  18:                  InvId = context.Current.Request.Form("InvId")
  19:              End If
  20:          End If
  21:          Dim sCrc As String
  22:          If context.Current.Request.Form("SignatureValue") IsNot Nothing Then
  23:              If context.Current.Request.Form("SignatureValue") <> "" Then
  24:                  sCrc = context.Current.Request.Form("SignatureValue")
  25:              End If
  26:          End If
  27:          Dim sCrcBase As String = String.Format("{0}:{1}:{2}", OutSum, InvId, sMrchPass2)
  28:          '
  29:          Dim md5 As System.Security.Cryptography.MD5CryptoServiceProvider = New System.Security.Cryptography.MD5CryptoServiceProvider()
  30:          Dim bSignature() As Byte = md5.ComputeHash(Encoding.ASCII.GetBytes(sCrcBase))
  31:          '
  32:          Dim sbSignature = New StringBuilder()
  33:          For Each One As Byte In bSignature
  34:              sbSignature.AppendFormat("{0:x2}", One)
  35:          Next
  36:          If sbSignature.ToString.ToUpper <> sCrc.ToUpper Then
  37:              context.Response.ContentType = "text/plain"
  38:              HttpContext.Current.Response.Write("bad sign")
  39:              Exit Sub
  40:          Else
  41:              context.Response.ContentType = "text/plain"
  42:              HttpContext.Current.Response.Write("Thank you for using our service")
  43:          End If
  44:          '
  45:          Dim CN As System.Data.SqlClient.SqlConnection
  46:          Dim CMD As Data.SqlClient.SqlCommand
  47:          '
  48:          Try
  49:              CN = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  50:              CN.Open()
  51:              CMD = New Data.SqlClient.SqlCommand("AddRoboAviso", CN)
  52:              CMD.CommandType = Data.CommandType.StoredProcedure
  53:              CMD.Parameters.AddWithValue("Sum", HttpContext.Current.Request.Form("OutSum"))
  54:              CMD.Parameters.AddWithValue("Id", HttpContext.Current.Request.Form("OutSum"))
  55:              CMD.Parameters.AddWithValue("Comment", HttpContext.Current.Request.Form("Comment"))
  56:              CMD.ExecuteNonQuery()
  57:          Catch ex As Exception
  58:              Dim CN1 As System.Data.SqlClient.SqlConnection
  59:              Dim CMD1 As Data.SqlClient.SqlCommand
  60:              CN1 = New System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString)
  61:              CN1.Open()
  62:              CMD1 = New Data.SqlClient.SqlCommand("INSERT [RoboAviso]([Sum], [Id], [Comment]) VALUES ('0','0', @Raw)", CN1)
  63:              CMD1.CommandType = Data.CommandType.Text
  64:              CMD1.Parameters.AddWithValue("Raw", ex.Message)
  65:              CMD1.ExecuteNonQuery()
  66:          End Try
  67:          '
  68:          If CN IsNot Nothing Then
  69:              If CN.State.Open Then
  70:                  CN.Close()
  71:              End If
  72:          End If
  73:      End Sub
  74:   
  75:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
  76:          Get
  77:              Return False
  78:          End Get
  79:      End Property
  80:   
  81:  End Class

Для MoneyBooker требуется один хандлер - Success. В нем надо формировать XML с ответом - ровно так же, как я показал выше для шлюза с Яндексом.

Что касается Plimus, то на данный момент я к сожалению пока не знаю, как принять онлайновое уведомление о платеже из этой платежной системы. Если Плимус высылает только мыльные сообщения, то для приема оповещений о платежах нужен собственный SMTP-сервер на Web-портале. Для такого SMTP-шлюза придется взять любой хорошо отлаженный исходный код почтового сервера, например LumiSoft Mail Server и прикрутить его к Web-порталу. Но по этому вопросу окончательного мнения у меня пока нет.

Зато я точно знаю как надо организовывать таймаут (для любого из указанных платежных систем). Организация таймаута нужна в тех случаях, когда нельзя держать купленый в онлайне товар бесконечное время забронированным для некоторого покупателя. Обычно логика сайта подразумевает оплату заказанного товара в течении допустим получаса. Если оплата не произошла, то выбранный товар считается разбронированным. Как это делается, я описал здесь - Реализация таймаута на динамически создаваемых SQL JOB, вызывающих SQL CLR сборку.



Comments ( )
Link to this page: //www.vb-net.com/PaymentGateway/index.htm
< THANKS ME>