(MVC) MVC (2016 год)

Опис двадцяти моїх дрібних фрілансерских проєктів 2016-го року.


1. Payment Gateway до системи Paymaster на VB.NET



Почнемо з проєкту Payment Gateway до системи Paymaster. Це звичайній Payment Gateway, яких я написав безліч за своє життя, наприклад ось тут якісь шлюзи описані в мене - Шлюзы к платежным системам интернет-денег. Незважаючи на те, что опис протокола цієї системи має 10 документів ( (12345678910 ), код системи простий, принаймні у мене він вийшов простий. Цей шлюз має чотири хандлера, головний смисловий хандлер виглядає ось так:

Зверніть увагу, що більшість кода тут - це отладка у зв'язку з неохідністю емуляції PHP-перетворювань:



   1:  <%@ WebHandler Language="VB" Class="Paymaster_Payment" %>
   2:   
   3:  Imports System
   4:  Imports System.Web
   5:   
   6:  'второй вход https://paymaster.ru/Partners/ru/docs/protocol
   7:  'Payment Notification - это единственный запрос, при обработке которого продавцу необходимо учитывать принятый платеж
   8:  '
   9:  'Paymaster_Payment : LMI_MERCHANT_ID=65a755b6-5d3d-4700-b4a1-13ac559d6fc2
  10:  '&LMI_PAYMENT_SYSTEM=3
  11:  '&LMI_CURRENCY=RUB
  12:  '&LMI_PAYMENT_AMOUNT=10.00
  13:  '&LMI_PAYMENT_NO=897ad9a1-64d8-40ce-ba3d-2459e066552a
  14:  '&LMI_PAYMENT_DESC=28d14d4f-362c-4638-a410-7cadce308479
  15:  '&LMI_SYS_PAYMENT_DATE=2016-06-20T12%3a34%3a01
  16:  '&LMI_SYS_PAYMENT_ID=53121046
  17:  '&LMI_PAYER_COUNTRY=RU
  18:  '&LMI_PAID_AMOUNT=10.00
  19:  '&LMI_PAID_CURRENCY=RUB
  20:  '&LMI_SIM_MODE=0
  21:  '&LMI_PAYER_IDENTIFIER=914578691977
  22:  '&LMI_PAYMENT_METHOD=Test
  23:  '&LMI_PAYER_PASSPORT_COUNTRY=RU
  24:  '&LMI_PAYER_IP_ADDRESS=87.249.23.92
  25:  '&LMI_PAYMENT_REF=836
  26:  '&LMI_HASH=LCZYl0HgP%2b0O455KgEYiFQ%3d%3d
  27:   
  28:   
  29:   
  30:  Public Class Paymaster_Payment : Implements IHttpHandler
  31:      
  32:      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
  33:          Try
  34:              Dim PostPRM As NameValueCollection = PaymasterGate.CommonRequestCheck(context)
  35:              If PostPRM Is Nothing Then
  36:                  'непонятный глюк - не удалось прочитать ни GET, ни POST-параметры
  37:                  Dim Err0 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
  38:                  "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Paymaster_Payment : NoPRM')")
  39:                  Err0.Select(New DataSourceSelectArguments)
  40:                  '
  41:                  context.Response.ContentType = "text/plain"
  42:                  context.Response.Write("Bad request")
  43:                  Exit Sub
  44:              End If
  45:              
  46:              '
  47:              Dim PaymentNumber As String = PostPRM("LMI_PAYMENT_NO").ToString
  48:              Dim UserID As String = PostPRM("LMI_PAYMENT_DESC").ToString
  49:              Dim SYS_PAYMENT_DATE As String = PostPRM("LMI_SYS_PAYMENT_DATE").ToString
  50:              Dim SYS_PAYMENT_ID As String = PostPRM("LMI_SYS_PAYMENT_ID").ToString
  51:              Dim SIM_MODE As String = "" 'PostPRM("LMI_SIM_MODE").ToString - рабочий режим
  52:              Dim PAID_AMOUNT As String = PostPRM("LMI_PAID_AMOUNT").ToString
  53:              Dim PAYMENT_AMOUNT As String = PostPRM("LMI_PAYMENT_AMOUNT").ToString 'заказанная
  54:              Dim CURRENCY As String = PostPRM("LMI_CURRENCY").ToString
  55:              Dim PAID_CURRENCY As String = PostPRM("LMI_PAID_CURRENCY").ToString
  56:              Dim PAYER_IDENTIFIER As String = PostPRM("LMI_PAYER_IDENTIFIER").ToString
  57:              Dim PAYER_COUNTRY As String = "NO"
  58:              If PostPRM("LMI_PAYER_COUNTRY") IsNot Nothing Then PAYER_COUNTRY = PostPRM("LMI_PAYER_COUNTRY").ToString()
  59:              Dim PAYER_PASSPORT_COUNTRY As String = "NO"
  60:              If PostPRM("LMI_PAYER_PASSPORT_COUNTRY") IsNot Nothing Then PAYER_PASSPORT_COUNTRY = PostPRM("LMI_PAYER_PASSPORT_COUNTRY").ToString()
  61:              Dim PAYER_IP_ADDRESS As String = "NO"
  62:              If PostPRM("LMI_PAYER_IP_ADDRESS") IsNot Nothing Then PAYER_IP_ADDRESS = PostPRM("LMI_PAYER_IP_ADDRESS").ToString()
  63:              Dim PAYMENT_REF As String = PostPRM("LMI_PAYMENT_REF").ToString
  64:              Dim PAYMENT_METHOD As String = PostPRM("LMI_PAYMENT_METHOD").ToString
  65:              Dim PAYMENT_SYSTEM As String = PostPRM("LMI_PAYMENT_SYSTEM").ToString
  66:              Dim HASH As String = PostPRM("LMI_HASH").ToString
  67:              '
  68:              Dim db1 As New ArendaDBDataContext
  69:              db1.PaymasterPay(New Guid(PaymentNumber), SYS_PAYMENT_DATE, SYS_PAYMENT_ID, "SIM_MODE", PAID_AMOUNT, PAID_CURRENCY, PAYER_IDENTIFIER, PAYER_COUNTRY, PAYER_PASSPORT_COUNTRY, PAYER_IP_ADDRESS, PAYMENT_METHOD, PAYMENT_REF)
  70:              '
  71:              'Инструмент проверки подписи https://paymaster.ru/Partners/ru/utility/generatesign
  72:              'Замечание для разработчиков на PHP: если $str - строка параметров, то хеш считается как base64_encode(md5($str, true)). Для PHP версии 4: base64_encode(pack("H*", md5($str)))
  73:              'Следующие параметры записываются в одну строчку, разделенные символом ‘;’: LMI_MERCHANT_ID, LMI_PAYMENT_NO, LMI_SYS_PAYMENT_ID, LMI_SYS_PAYMENT_DATE, LMI_PAYMENT_AMOUNT, LMI_CURRENCY, LMI_PAID_AMOUNT, LMI_PAID_CURRENCY, LMI_PAYMENT_SYSTEM, LMI_SIM_MODE
  74:              '
  75:              'Для URL http://localhost:50208/Arenda_4/Paymaster_Payment.ashx?LMI_MERCHANT_ID=65a755b6-5d3d-4700-b4a1-13ac559d6fc2&LMI_PAYMENT_SYSTEM=3&LMI_CURRENCY=RUB&LMI_PAYMENT_AMOUNT=10.00&LMI_PAYMENT_NO=897ad9a1-64d8-40ce-ba3d-2459e066552a&LMI_PAYMENT_DESC=28d14d4f-362c-4638-a410-7cadce308479&LMI_SYS_PAYMENT_DATE=2016-06-20T12%3a34%3a01&LMI_SYS_PAYMENT_ID=53121046&LMI_PAYER_COUNTRY=RU&LMI_PAID_AMOUNT=10.00&LMI_PAID_CURRENCY=RUB&LMI_SIM_MODE=0&LMI_PAYER_IDENTIFIER=914578691977&LMI_PAYMENT_METHOD=Test&LMI_PAYER_PASSPORT_COUNTRY=RU&LMI_PAYER_IP_ADDRESS=87.249.23.92&LMI_PAYMENT_REF=836&LMI_HASH=LCZYl0HgP%2b0O455KgEYiFQ%3d%3d
  76:              '1. Plan string - 65a755b6-5d3d-4700-b4a1-13ac559d6fc2;897ad9a1-64d8-40ce-ba3d-2459e066552a;53121046;2016-06-20T12:34:01;10.00;RUB;10.00;RUB;3;0;DFA02A86-B028-448D-A3BD-B9749C796626
  77:              '2. UTF8 bytes - 36 35 61 37 35 35 62 36 2D 35 64 33 64 2D 34 37 30 30 2D 62 34 61 31 2D 31 33 61 63 35 35 39 64 36 66 63 32 3B 38 39 37 61 64 39 61 31 2D 36 34 64 38 2D 34 30 63 65 2D 62 61 33 64 2D 32 34 35 39 65 30 36 36 35 35 32 61 3B 35 33 31 32 31 30 34 36 3B 32 30 31 36 2D 30 36 2D 32 30 54 31 32 3A 33 34 3A 30 31 3B 31 30 2E 30 30 3B 52 55 42 3B 31 30 2E 30 30 3B 52 55 42 3B 33 3B 30 3B 44 46 41 30 32 41 38 36 2D 42 30 32 38 2D 34 34 38 44 2D 41 33 42 44 2D 42 39 37 34 39 43 37 39 36 36 32 36
  78:              '3. Hash bytes - 2C 26 58 97 41 E0 3F ED 0E E3 9E 4A 80 46 22 15
  79:              '4. Base64 hash - LCZYl0HgP+0O455KgEYiFQ==
  80:              '
  81:              Dim Str1 As String = System.Configuration.ConfigurationManager.AppSettings("PaymasterID").ToString & ";" & _
  82:                                   PaymentNumber & ";" & _
  83:                                   SYS_PAYMENT_ID & ";" & _
  84:                                   SYS_PAYMENT_DATE & ";" & _
  85:                                   PAYMENT_AMOUNT & ";" & _
  86:                                   PAID_CURRENCY & ";" & _
  87:                                   PAID_AMOUNT & ";" & _
  88:                                   PAID_CURRENCY & ";" & _
  89:                                   PAYMENT_SYSTEM & ";" & _
  90:                                   SIM_MODE & ";" & _
  91:              System.Configuration.ConfigurationManager.AppSettings("PaymasterSecretKey").ToString()
  92:              Dim UTF8_ByteArr As Byte() = System.Text.UTF8Encoding.UTF8.GetBytes(Str1)
  93:            
  94:              Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider()
  95:              Dim bSignature As Byte() = md5.ComputeHash(UTF8_ByteArr)
  96:              
  97:              Dim sbSignature As New StringBuilder()
  98:              For Each b As Byte In bSignature
  99:                  sbSignature.AppendFormat("{0:x2}", b)
 100:              Next
 101:              '
 102:              Dim Msg2 As String = "Str1(" & Str1 & "), sbSignature(" & sbSignature.ToString() & "), Convert.ToBase64String(bSignature)(" & Convert.ToBase64String(bSignature) & "HASH(" & HASH & ")"
 103:              Dim Err2 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
 104:              "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Sign : " & Msg2 & "')")
 105:              Err2.Select(New DataSourceSelectArguments)
 106:              '
 107:              'Проверка цифровой подписи
 108:              If Convert.ToBase64String(bSignature) <> HASH Then
 109:                  context.Response.ContentType = "text/plain"
 110:                  context.Response.Write("bad sign")
 111:                  Exit Sub
 112:              End If
 113:              'Подпись совпала
 114:              Dim X = db1.PaymasterSuccess(New Guid(PaymentNumber)).ToList
 115:              If X.Count > 0 Then
 116:                  Dim Summ As Decimal
 117:                  Dim AccountNumber As Integer = X(0).AccountNumber
 118:                  Dim Accounttype As Integer = 1 'RUB 
 119:                  'Dim Culture As System.Globalization.CultureInfo = New Globalization.CultureInfo("ru-RU")
 120:                  'If Decimal.TryParse(PAYMENT_AMOUNT, Globalization.NumberStyles.Currency, Culture, Summ) Then
 121:                  If IsNumeric(GetDecimal(PAYMENT_AMOUNT)) Then
 122:                      Summ = GetDecimal(PAYMENT_AMOUNT)
 123:                      If CURRENCY = "RUB" Then
 124:                          'принимаются только рублевые платежи и зачисляются только на рублевый счет
 125:                          db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, Summ)
 126:                      Else
 127:                          db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, 2)
 128:                      End If
 129:                  Else
 130:                      db1.AddSumm(AccountNumber, New Guid(UserID), Accounttype, 1)
 131:                  End If
 132:   
 133:              End If
 134:              '
 135:              context.Response.ContentType = "text/plain"
 136:              context.Response.Write("OK")
 137:              '            
 138:          Catch ex As Exception
 139:              Dim Err1 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
 140:              "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Paymaster_Payment : " & ex.Message & "')")
 141:              Err1.Select(New DataSourceSelectArguments)
 142:              '
 143:              context.Response.ContentType = "text/plain"
 144:              context.Response.Write(ex.Message)
 145:          End Try
 146:   
 147:          '
 148:      End Sub
 149:   
 150:      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
 151:          Get
 152:              Return False
 153:          End Get
 154:      End Property
 155:   
 156:      Function GetDecimal(Str1 As String) As String
 157:          Return Str1.Replace(".00", "").Replace(" ", "")
 158:      End Function
 159:      
 160:  End Class

Невеличкий допоміжний клас виглядає ось так, і знов-таки більшість кода в ньому - це отладка.


   1:  Imports Microsoft.VisualBasic
   2:   
   3:  Public Class PaymasterGate
   4:   
   5:      'Read and Parse Get/Post parameters and common check it
   6:      Public Shared Function CommonRequestCheck(ByVal context As HttpContext) As NameValueCollection
   7:          HttpContext.Current.Request.InputStream.Position = 0
   8:          Dim PostString As String = New System.IO.StreamReader(context.Request.InputStream).ReadToEnd()
   9:          Dim PostPRM As NameValueCollection = HttpUtility.ParseQueryString(PostString)
  10:          'сначала смотрим если есть PostStream
  11:          If PostPRM.Count > 0 Then
  12:              '
  13:              Dim Err0 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
  14:              "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Post : " & PostPRM.ToString & "')")
  15:              Err0.Select(New DataSourceSelectArguments)
  16:              '
  17:              Return Parse(PostPRM, context)
  18:          Else
  19:              'смотрим, есть ли GetStream
  20:              Dim GetPRM As NameValueCollection = HttpUtility.ParseQueryString(context.Request.Params.ToString)
  21:              '
  22:              Dim Err1 As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, _
  23:              "INSERT DEBUGLOG(CrDate,TXT)VALUES(GETDATE(),N'Get : " & GetPRM.ToString & "')")
  24:              Err1.Select(New DataSourceSelectArguments)
  25:              '
  26:              Return Parse(GetPRM, context)
  27:          End If
  28:   
  29:      End Function
  30:   
  31:   
  32:      Public Shared Function Parse(PostPRM As NameValueCollection, ByVal context As HttpContext) As NameValueCollection
  33:          If PostPRM.Count = 0 Then Return Nothing
  34:          Dim PaymasterID As String
  35:          Dim UserID As String
  36:          Dim PaymentNumber As String
  37:          Try
  38:              PaymasterID = PostPRM("LMI_MERCHANT_ID").ToString
  39:              UserID = PostPRM("LMI_PAYMENT_DESC").ToString
  40:              PaymentNumber = PostPRM("LMI_PAYMENT_NO").ToString
  41:          Catch ex As Exception
  42:              'есть GET-параметры, но в них только мусор
  43:              Return Nothing
  44:          End Try
  45:          'параметры найдены, есть смысл их проверять
  46:          If Not StringExtension.IsGuid(UserID) Or _
  47:              Not StringExtension.IsGuid(PaymentNumber) Or _
  48:              PaymasterID.ToString.Trim.ToLower <> System.Configuration.ConfigurationManager.AppSettings("PaymasterID").ToString.ToLower Then
  49:              context.Response.ContentType = "text/plain"
  50:              context.Response.Write("no")
  51:              context.Response.End()
  52:              Return Nothing
  53:          Else
  54:              Return PostPRM
  55:          End If
  56:      End Function
  57:   
  58:  End Class


Усі процедури, що працюють с грошима, побудовані на транзакціях:



   1:   ALTER procedure [dbo].[AddSumm]
   2:    @Account integer,
   3:    @UserID uniqueIdentifier,
   4:    @AccountType integer,
   5:    @Summ money
   6:    as
   7:    BEGIN TRY
   8:          BEGIN TRANSACTION
   9:          update dbo.Account SET Value=Value+@Summ, SetValueDate=GETDATE() where i= @Account and ToUser=@UserID and ToValueType=@AccountType
  10:          insert dbo.AccountLog (ToAccount,Date,Value) values (@Account,GETDATE(),@Summ)
  11:          select * from dbo.Account where i= @Account and ToUser=@UserID and ToValueType=@AccountType
  12:          COMMIT
  13:          RETURN 0
  14:    END TRY      
  15:    BEGIN CATCH
  16:      DECLARE @ErrorMessage NVARCHAR(4000);
  17:      DECLARE @ErrorSeverity INT;
  18:      DECLARE @ErrorState INT;
  19:      SELECT @ErrorMessage = 'Abort transaction ' + ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
  20:      RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState );
  21:      RETURN 1
  22:   END CATCH


Payment Gateway викликався зі сторінки ASP.NET-сайт таким чином:



   1:  Partial Class Paymaster
   2:      Inherits System.Web.UI.Page
   3:   
   4:      Dim CurrentUser_ProviderUserKey As String
   5:      Dim CurrentUser_UserName As String
   6:   
   7:      Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
   8:          If Not IsPostBack Then
   9:              If User.Identity.IsAuthenticated Then
  10:                  CurrentUser_UserName = User.Identity.Name
  11:                  Dim User1 As MembershipUser = Membership.GetUser(CurrentUser_UserName)
  12:                  If User1 IsNot Nothing Then
  13:                      CurrentUser_ProviderUserKey = User1.ProviderUserKey.ToString
  14:                  Else
  15:                      GoTo defaultUser
  16:                  End If
  17:              Else
  18:  defaultUser:
  19:                  CurrentUser_UserName = System.Configuration.ConfigurationManager.AppSettings("Admin_Login").ToString
  20:                  Dim AdmUser1 As MembershipUser = Membership.GetUser(CurrentUser_UserName)
  21:                  If AdmUser1 IsNot Nothing Then
  22:                      CurrentUser_ProviderUserKey = AdmUser1.ProviderUserKey.ToString
  23:                  Else
  24:                      CurrentUser_ProviderUserKey = "28d14d4f-362c-4638-a410-7cadce308479"
  25:                  End If
  26:              End If
  27:              '
  28:              PaymasterWidgetLiteral.Text = "<script type='text/javascript' src='https://paymaster.ru/ru-RU/widget/Basic/1?LMI_MERCHANT_ID=" & _
  29:      System.Configuration.ConfigurationManager.AppSettings("PaymasterID") & _
  30:      "&LMI_PAYMENT_AMOUNT=10&LMI_PAYMENT_DESC=" & _
  31:      CurrentUser_ProviderUserKey.ToString & _
  32:      "&LMI_PAYER_EMAIL=" & _
  33:      CurrentUser_UserName & _
  34:      "&LMI_CURRENCY=RUB&LMI_PAYMENT_NO=" & _
  35:      Guid.NewGuid.ToString & _
  36:      "&LMI_SUCCESS_URL=" & _
  37:      HttpUtility.UrlEncode("http://arenda.votpusk.ru/User_Advance.aspx?User=" & CurrentUser_ProviderUserKey.ToString) & _
  38:      "&LMI_FAILURE_URL=" & _
  39:      HttpUtility.UrlEncode("http://arenda.votpusk.ru/User_Advance.aspx?User=" & CurrentUser_ProviderUserKey.ToString) & "'></script>"
  40:          End If
  41:      End Sub
  42:  End Class


Особливість системи у тому, що вона потрібно викликати скрипт, який сам сформує на сторінці кнопки, але скріпт сам теж формує тег <FORMS>, що робить сторінку ASP.NET непрацездатною:



Система пройшла всі тести і зараз чудово працює.



Ще трохи про мої пратежні шлюзи Шлюзы к платежным системам интернет-денег., My payment gateway for Inplat.ru, Liqpay payment gateway.

2. ASP.NET API до WHMCS.



Мій другий проєкт теж був пов'язаний з PHP. Треба було зробити сайт, який буде керувати WHMCS по API, тобто люди вже мали якийсь дизайн (на скрині нище), але не мали, власно-кажучі, діючих алгоритмів.



Тобто задумка цього сайту була у тому, щоб зробити діючу надбудову над WHMCS на ASP.NET. Щоб зробити API на ASP.NET, з якого можна було б керувати великою кількістю хостінг-сістем, побудованних на WHMCS, треба було подолати дуже багато проблем. Починаючи з того, что усі PHP-шні функції не мають аналогів на платформі ASP.NET (нема в них потреби, все робиться інакше), і закінчуючи тим, що взагалі всі функції мали описи на PHP, при чому це були важкі для емуляції функції, наприклад специфічна криптографія.

Я вирішив цю задачку за допомогою специфічної імплементації ось цього свого коду - WCF_CLIENT - клиент Web-сервиса (первая версия).



Для мене це була важка задача, щоб зрозуміти поведінку PHP-середовища, мені довелося поставити собі на комп'ютер XAMPP та Netbeans.



Це була для мене важка справа, бо по-перше, я не PHP-програміст, а по-друге, на моему девелоперскому комп'ютері вже працювали десяток чи більше різноманітних версій PHP, MySQL, Apache. Вони займали усі більш-менш стандартні порти та каталоги для цієї роботи. Але мені вдалось налаштувати собі середовище PHP-програміста



та-таки розпочати отладку цієї проги:



3. Об'єктна membership-надбудова.



Наступна задачка була у мене для того ж самого замовника. Вона була, мабуть, найпростішою з цього пакета, що я описую, бо не було потреби змінювати якось своє середовище роботи, це була задачка на ASP.NET MVC. Замовник забажав не тільки готові DLL, але й source code на C#, тому мені довелося перетранслювати увесь свій VB-код на С#-код. На першому скріні якісь табли до цієї задачки, на другому - мій оригінальний код, на останньому скрині - юніт тести.



4. Сайт з peer-to-peer video-комунікаціями.



Далі я виконав дуже цікаву роботу. Вона стосується обміну даними peer-to-peer. Що сталося з самим сайтом, я не знаю, я його віддав замовнику, у мене залишився перший тест цього двигуна, сподіваюсь він ще працює :


Це дуже простий сайт, але він має под собою достатньо могутню теорію. Починаючи з основ, звичайно ми маємо справу з сервером та браузером, тобто всі комуникації (навіть між кліентами), виконуються за допомогою сервера та через сервер. Але існує і інша можливість, працювати без сервера, як TORRENT, чи SKYPE - тобто кліенти спілкуються між собою напрямки. Взагалі, звичайно так працюють FLEX та JAVA. Але і у JAVASCRIPT таке теж можливо. Тут взагалі багато цікавих питань, починаючи з можливості кросдоменного постбека на JavaScript у різноманітних середовищах:



Щоб зрозуміти щю проблему, яку можно вирішити або можливостями браузера або можливостями JavaScript - прочитайте ось цю сторінку - Cross-Domain Browser Window Messaging with HTML5 and Javascript.

Але кросдомений постбек на JavaScript- це лише один з можливих варіантів peer-to-peer комунікацій. Більш поширені варіанта - Flex та JAVA. Взагалі на Flex я цю можливість використовував у декількох проєктах.

На цих можливостях побудовані два цікавих публічних сервіса.

Перший сервіс мені подобаться більше, але він не дозволяє робити великі кімнати для спілкування peer-to-peer. А ось appear.in дозволяє це зробити, тому у цьому проєкті я використав саме його. Існують і інші варіанти peer-to-peer комунікацій, якийсь перелік їх є ось на цій сторінці, але ж всі ці peer-to-peer комунікації не виконуються безпосередньо з браузера. Також існує багато суто апаратних рішень для виконання peer-to-peer комунікацій, наприклад на обладнанні CISCO.

Отже у цьому проєкті я побудував сістему комунікацій на сервісі appear.in. Кліент мав можливість зробити переговорну кімнату з унікальним ID. Десь на сторінці розташовано літерал:

<asp:Literal ID="FrameLiteral1" runat="server"></asp:Literal>

І на сторінці утворюється унікальний кабінет:

Dim NewSessionID As Guid = SetSessionID(Video1.CommandName)
FrameLiteral1.Text = "<iframe src='https://appear.in/" & NewSessionID.ToString & "' width='800' height='640' frameborder='0'></iframe>"

Спочатку у цей кабінет заходить "секретар", розмовляє с клієнтом, можливо перевіряє оплату, а потім запрошує до цього кабінету "зірку", спілкування з якою заказав кліент. Схема достатньо універсальна і має багато практичних застосувань.


5. Десктопна прога автоматизації роботи фірми.



Наступна прога, яку я зробив (exhibition) - wе десктопна прога роботи с базами даних, призначення цієї проги - планування виставок у виставкових центрах для різних фірм. Має багато різних особливостей, наприклад графікі обзвону кліентів.



Усі сіточні формицієї проги я побудував однаково, десь приблизно по технології, яку я описав тут - Проги під заказ і проги по натхненню. Найбільша проблема цього проєкту була в тому, що замовник передал мені дані у такому форматі, що я витратив чимало часу, поки зумів прочитати ці дані та трансформувати їх у MS SQL.




6. Розсилка SMS за допомогою GSM-передавачів GoIP та за допомогою интернет-комунікацій (WhatsApp на емуляторі Android).



У цьому проєкті я працював не тільки як програміст, а і як менеджер проєкту. Тому і розповідь про цей проєкт буде трошки глобальніше, ніж звичайно. Цей дуже цікавий проєкт занурив мене у дуже цікавий бізнес розсилки SMS. Існує багато сайтів (наприклад https://sms-reg.com/,   http://virtualsim.net/index.php?l=ru,  http://onlinesim.ru/,  http://sms-activate.ru,  https://smslike.ru/), які надають ці послуги для різноманітних цілій - починаючи з простої розсилки реклами до послуг реєстрації телефона у на різноманітних сайтах.



Взагалі, у цьому бізнесі існує два протилежних підхіда. Перший підхід - це використання GSM-шлюза с змінним IMEA - http://www.dbltek.com/pdf/GoIP%20Series%20User%20Manual%20V1.4B.pdf - а його змінювати потрібно, бо телекомуникаційні компанії вважають, що це їх власний бізнес і намагаються видавити усі інші компанії з цього бізнесу. Наприклад, ось повідомлення, як спецслужби, виконуючи заказ телекомунікаційних компаній, розгромили подібний бізнес, та ще й звинуватили цих дрібних бізнесменів у тероризмі. Для охорони бізнесу від зажерливості телекомунікаційних компаній краще розподіляти головні компоненти бізнесу по різним країнам.

Щоб рухатися таким шляхом потрібно по-перше купити передавач, тобто мобільник, восьмиканальний коштує $600 http://www.ebay.com/ А по-друге, купити SIM-банк, наприклад на 32-SIM-ки він коштує 700 долларов, а на 128 SIM-ок вже 2000 доларів - http://www.ebay.com/. А далі, якщо використовувати рішення GoIP, то потрібно самому робити шлюз, який буде звертатися до цього обладнання. Існує і більш дорогі та комплексні рішення, які вже мають шлюз, та апаратно прошитий Asterisk - SMSEagle NPE-9300 GSM/GPRS.

Інший напрямок дієї діяльності - це термінірованіе трафіку. Я багато разів стикався з такими Системами, ось тут, наприклад, я дещо описав про таку сістему - Elastix - сервер VOIP-телефонии.


Головне, що потрібно зрозуміти, рухаючись по першому шляху (з окремими передавачами) - що можно розсилати SMS-ки на звичайні мобільники. Можливо також мати зворотній зв'язок від абонентів з звичайними мобільниками. Існує і протилежний шлях - чисто інтернет-коммунікації, без використання GSM та радіо-єфіру взагалі. Таким чином можливо зв'язатися лише з сучасними комунікаторами, які тримають зв'язок по 3G/4G, або, навіть зі стаціонарними комп'ютерами. Основа такої комунікації - WhatsApp. У цьому бізнесі теж присутні SIM-банкі, але (оскільки це звичайні інтернет-комунікації), то працюють з ними зі звичайного комп'ютера, який і посилає у інтернет-мережу пакети для клієнтів.

Якщо рухатися по шляху інтернет-комунікацій, то можна звернутися до послуг сторонніх сайтів типу WhatsApp Bulk Sender, або побудувати сістему самостійно. Якщо будувати самостійно, то WhatsApp - то перше, що потрібно поставити собі на комп'ютер. Він ставиться на ємулятор системи Andriod, тобто це звичайний код операційної системи Андроід, який відтрансльований для процесора X86, та запущена ця Система на окремої віртуалці на звичайному комп'ютері. Існує готовий повний пакет https://www.genymotion.com/, який має в собі і сконфігурований VirtualBox і Андроід. Для одного ємулятора ця Система безкоштовна. Ця Система вже має добре сконфігурований VirtualBox, у якому лише потрібно налаштувати IP-адресу - і до того віртуального Андроіда вже можливо конектитись.



Наступна задача - встановити на наш віртуальний Андроід Google Play service (щоб з нього встановити WhatsApp). Для цього з одного боку на локальний комп'ютер потрібно на локальний комп'ютер встановити ADB, за допомогою якого можна керувати віртуальним приладом на Andriod. У даному випадку я також встановив Google USB Driver, який потрібен для роботи ADB.



А з іншого боку на наш віртуальний Андроід потрібно встановити ARM-транслятор, який дозволить виконуватися на машинах X86 оригінальному коду для мобільних приладів.



Потім потрібно встановити на Андроід Phone ID changer та ще декілька програм, не буду все це перераховувати до кінця, покажу на іншому комп'ютері вже процесс отладки софта, який я написав як заплатку до QueenSoft.Vn.AutoWART, щоб не звертатися до стороннього сервісу за каналами до SIM-карт, а звертатися до власного SIM-банку.




7. Проєкт Шаровоз.



Це був мій самій великий проєкт з описанного тут пакету дрібних проєктів, нажаль замовник за нього мені нічого не заплатив. Тому розповідаю про цей проєкт з усіма деталями, ніяких добрих побажань до людини, яку не заплатила мені гроші за місяць моєї роботи - у мене немає. Взагалі, власник цього бізнесу звільнив усіх своїх програмістів, остання більш-менш постійна людина пішла від нього геть на початку 2016-року, тому можливо, при такому відношенню до програмістів, цього проекту вже і не існує зовсім. Хоча цей інтернет-проєкт з непоганими грошима та з великою кількістю власних серверів, має 70 тысяч постійних користувачів, які перераховують цьому проєкту гроші - і можливість платити нормальні гроші програмістам принципово існує (але у власника цього бізнесу, здається, не існує бажання платити гроши навіть за добре виконану роботу).

Проєкт побудований таким чином - є центральний сервер во Франції, є велика кількість проксі-серверів, е декілька окремих доменів, які так чи інакше, через проксі-сервера, дивляться на центральний сервер во Франції. Також існує велика кількість серверів, на яких встановлені card-sharing сервери, які керуються з центрального сервера. Є також декілька платіжних шлюзів, через які можливо приймати гроші як за пакети в цілому, так і за окремі фільми. Працездатність такої великої кількості серверів контролюється системой Zabbix. Також в проекті активно використовується віртуалізація на базі XEN.

Коли я ближче познайомився з сістемою кард-шарінга, мені будо дуже цікаво, що клієнтом шарінга можуть бути не тільки класичні супутникові приймачи, але й персональні комп'ютери та різноманітні емулятори справжніх супутникових ресиверів. До речі, на сайті цього проєкту можливо отримати не тільки вся параметри всіх супутників, повні описи усіх пакетів, но і всі налаштування не тільки для класичних приймачів, але й для різноманітних емуляторів справжніх приймачів.

Але повернемося від загального опису проекту до центрально сервера, на якому мене наняли навести порядок. Коли я подивився на цей сервер, мене охопив жах:

Значна частина цього програмного аду не працювала і взагалі незрозуміло що вона повинна була робити. Ось такий буває результат, коли власник бізнесу, навіть с непоганими грошима таким чином ставиться до програмістів, які тікають від нього як від беса. А між тим, це простий сайт, я взагалі спочатку йому запропонував побудувати його з нуля за декілька місяців - можливо це було навіть легче, ніж розібратися у всьому цьому пеклі. Але власник відмовився і ми зупинилися на першочергових діях, зробити три окремі машини - DEV (девлоперську), SQL (для SQL-серверу), та WEB (сайти з цієї машини бачать через інтернет кліенти). І по-друге ми домовились у першу чергу відремонтувати ти важливі сервіси, що не працювали - починаючи з сервісів роботи з грошима. До речі, і першу і другу частку домовленостей я виконав, тобто зробив три нові окремі машини та переробив наново сервіс роботи з веб-мані - але грошей я так і не отримав. Після виконання всіх робот, прямо у процесі налаштування одного з 24-х сайтів адмін просто закрив мені доступ до серверу та мене забанили у скайпі.

Нище ви бачите скрини, як я поставив три окремі машини - DEV, SQL, WEB. А також я накатів всі обновляшки на всі операціонки, сконфігурував їх правильно, установив і зареестрував VS2015, налаштував безліч сервісів, починаючи від FTP до GIT.



Ніякого бекапа даних у цьому проекті не велось взагалі ніколи, я першій, кто налаштував SQL-бекап вже на новому сервері, який сам поставив.



Коли я переніс SQL-сервер на окремий сервер, то Entity-framework відмовився працювати нормально, мені довелося зробити окрему латочку.



Ось тут вже процесс розгортання сайту на новому WEB-сервері, який я сам зробив для цього проекту. Коли я вперше переніс проєкт зі старого сервера на новий і спробував його откомпіліровати, я отримав перші 1100 помилок. Тільки на вирішення цієї проблеми (на першому скрині) я витратив декілька днів.



Я також повністю переробив сервіс роботи з вебмані-кишенею.



На протязі цієї роботи власник бізнесу прохав мене декілька разів ще вівести якісь необхідні дані з бази. Я роблю це просто, буквально в пару кліків мишкою, якщо не запаморочуватися на нових технологіях, а використовувати звичайні Linq-to-SQL та VB.NET - все ці маленькі сайтіки я теж зробив.



Багато ще чого я зробив корисного у цьому проєкті, але, як я розповідав вище, цей проєкт закінчився для мене несподівано.


8. Сайт для мобільників.



А третя прога грудня - це сайт для мобильників на класіке ASP.NET. У ньому я застосував було багато цікавих, але більш-менш стандартних рішень, і якби я мав вільний час, я б зробив кращій опис цього цікавого проєкту, але мабуть це вже не вийде, бо часу не вистачає катастрофічно. Отже, друзі, маю час лише на малесенький перелік рішень, які я застосував у цьому проєкті (для моїх старих друзів, для яких я пишу вже більше 15-ти років).

Отже, друзі, спочатку про ідею цього проєкту. У мене є чудовий проєкт https://arenda.votpusk.ru/default.aspx, але він не мав мобільної версії. Як я додав до цього проєкту мобільну версію, я зараз розповім. Більш повний опис цієї методики знаходиться ось тут - Як будуються адаптивні сайти для мобільників та десктопів на Classic ASP.NET..

По-перше я зробив приблизно ось такий класс, який перевіряє чи мобільний браузер застосовує клієнт, чи ні. Зрозуміло, що публікую тут лише загальну схему, у реальному проєкти все трохи складніше.



Цей клас відпрацьовує у дуже специфічній момент Page_PreInit, до того, як будуть взагалі працювати всі сторінки сайту. Цей клас оцінює реквест, чи є у ньому мобільна кука, чи немає, якщо куки мобільного прилада немає, то клас аналізує реквест - чи нас запрошує мобільний прилад?

Після цього клас пише куку мобильного прилада у поток браузера, якщо прилад мобільний, то змінює мастер-пейдж. І головне, далі все сторінки будуть мати свойство - чи був це реквест мобільний, чи ні. У трошки скороченому вигляди цей код виглядає ось так


   1:  Imports Microsoft.VisualBasic
   2:   
   3:  Public Class MobilePage
   4:      Inherits System.Web.UI.Page
   5:   
   6:      Property IsMobile As Boolean
   7:          Get
   8:              Return ViewState("_IsMobile")
   9:          End Get
  10:          Set(value As Boolean)
  11:              ViewState("_IsMobile") = value
  12:          End Set
  13:      End Property
  14:   
  15:      Private Sub Page_PreInit(sender As Object, e As System.EventArgs) Handles Me.PreInit
  16:          If Request.Cookies("Mobile") Is Nothing Then
  17:              GoTo CheckDeviceAndSetCookie
  18:          Else
  19:              If Request.Cookies("Mobile").Value = "" Then
  20:  CheckDeviceAndSetCookie:
  21:                  ViewState("_IsMobile") = Is_Mobile()
  22:                  If IsMobile Then
  23:                      ViewState("_IsMobile") = True
  24:                      Response.Cookies("Mobile").Value = "yes"
  25:                      Response.Cookies("Mobile").Expires = Now.AddDays(1)
  26:                      Response.Cookies("Mobile").Domain = "votpusk.ru"
  27:                  Else
  28:                      ViewState("_IsMobile") = False
  29:                      Response.Cookies("Mobile").Value = "no"
  30:                      Response.Cookies("Mobile").Expires = Now.AddDays(1)
  31:                      Response.Cookies("Mobile").Domain = "votpusk.ru"
  32:                  End If
  33:              Else
  34:                  If Request.Cookies("Mobile").Value = "yes" Then
  35:                      ViewState("_IsMobile") = True
  36:                  Else
  37:                      ViewState("_IsMobile") = False
  38:                  End If
  39:              End If
  40:              '
  41:              If Me.IsMobile Then
  42:                  If Not Request.RawUrl.Contains("Message.aspx") Then
  43:                      Me.MasterPageFile = "M1_mobile.master"
  44:                  End If
  45:              End If
  46:   
  47:          End If
  48:      End Sub
  49:   
  50:      Function Is_Mobile() As Boolean
  51:          Dim u As String = Request.ServerVariables("HTTP_USER_AGENT")
  52:          Dim b As New Regex("(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino", RegexOptions.IgnoreCase)
  53:          Dim v As New Regex("1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-", RegexOptions.IgnoreCase)
  54:          If b.Match(u).Success Or v.Match(Left(u, 4)).Success Then Return True Else Return False
  55:      End Function
  56:  End Class

Якщо сторінки мають таке свойство - запит з мобільного юнита, то вся сторінки можна переробити ось таким чином.



Приблизно таким же чином можна було б переробити і код сторінок, але тут ставимо кому.



Page-Load можливо переробити так, але що робити з контролами? Вони ж усі трошки різні у мобільному та немобільному варіанті.



Тому я зробив базові класи для мобільних та немобільних приладів.



Таким чином всі звичайні процедури біндінга контролів мені вдалося переробити на Дженеріках, тобто саме так я зберіг увесь раніше побудований код та уникнув дублів коду для мобільних та немобільних контролів.



Це не єдине цікаве, але досить стандартне рішення, яке я застосував у цьому сайті. Ще одне досить стандартне, але теж цікаве рішення описано ось тут SPA-page на Classic ASP.NET та jQuery.

9. Ще п'ять моїх проєктів (адміністрування серверів, конвертация даних MySQL, виготовлення відео та проект на Visual Basic 6).



Я виконав ще безліч якихось інших робот, до деяких у мене збереглось по одному скріну, до деяких - жодного скріна. На першому скріні - я добре пам'ятаю що я робив у цьому проєкті. На другому скрині - пригадую важко, якась адміністративна робота с сервером, щось там було з ILO. На третьому скрині - якась робота по конвертації бази даних з MySQL. На наступному скрині я зробив собі зведення команд FFMPEG, бо у цей період я готував дуже багато відео для своїх проєктів. А на останньому скріні - правив якусь прогу на VB6. Таких дрібних робот було просто безліч, до більшості з них не збереглось навіть скринів.



10. Два моїх останніх проєкта 2016-го року для USA (прога для медицини та торговельный робот).



Це мабуть буде виглядати як невеличкий додаток-апдейт до сторінки, але я викладу тут ще три мої проекта, які я пишу вже у грудні. Перший проєкт - це прога керування якимось медичним обладнанням, яку я написав по специфікації, прога працю. добре, заплатили всі гроші. Друга прога - це моя остання прога у 2016-му році, щось вона йде дуже туго, у мене є інші плани на кінець року, можливо вона так і не буде завершена. Це цікавий специфічній торговельний робот для біржі, але тут по-перше дуже багато речей, які до кінця не зрозуміло як зробити, тобто прога вимагає набагато більше часу і можливо ії не має сенсу закінчувати за такий час і такі гроші, а по друге у мене є багато планів на різноманітні поїздки, тому з цим проєктом є велике питання що з ним буде далі.



11. Декілька тестових проєктів для різноманітних фірм.



У цьому році я шукав гарну грошову роботу у непоганій фірмі, але так і не зайшов її поки що. Але мені кожного разу давали якийсь тестові завдання, які теж з'їли значну частку моїх сил та часу. Різноманітних тестових проєктів було чимало, і тому я написав про них детально на окремих сторінках.





12. Проги по натхненню.



Ще декілька своїх прог я описав на ціеї сторінці. Ці проги я зробив без замовлення, особисто для себе.



Маю ще багато цікавих власних задумок, але, нажаль не маю часу на їх реалізацію.



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/FreelanceProjects/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>