كيف أجريت نظام قبول للدفع Minecraft باستخدام PowerShell خالص


في هذه المقالة ، سنقوم بتثبيط الكعك الملله إلى خادم Minecraft vanilla باستخدام Powershell. ميزة الطريقة هي أن ماين كرافت ليست سوى حالة خاصة لتنفيذ المدفوعات التلقائية باستخدام أوامر وحدة التحكم. نستمع فقط إلى ما يرسله لنا نظام الدفع ولفه في فريق. والأهم من ذلك - لا ملحقات.
وسوف نقبل المدفوعات من خلال PayPal. الأهم من ذلك ، من أجل البدء في قبول المدفوعات ، لا تحتاج إلى تغيير الرمز ، سيرسل لنا PayPal كل ما تحتاجه. سنستخدم الأزرار الموجودة على الموقع ، بحيث يمكن للموقع القيام به باستخدام HTML خالص. نستخلص من تعقيدات نظام الدفع نفسه ونركز فقط على النقاط الرئيسية في الشفرة.

بالمناسبة ، سيكون المؤلف سعيدًا جدًا إذا نظرت من خلال جميع وحداته ووجدت أخطاء أطفال فيها تشير إليها أو تصححها. هنا رابط لمشروع جيثب .

بضع كلمات حول IPN


IPN


سنقبل المدفوعات من خلال الأزرار. لا تتطلب الأزرار أي خلفية منك ، فهي تعمل بتنسيق HTML خالص ، ولديها أيضًا حقولها الخاصة.

تعمل الأزرار على تشغيل IPN - إشعار الدفع الفوري ، حيث يتم إرسال البيانات إلى WebListener. سننظر في بنية IPN أدناه.

بالإضافة إلى ذلك ، يمكن لأي شخص لديه حساب PayPal إنشاء زر خاص به.
لا يمتلك IPN واجهة PayPal REST API الكاملة ، ولكن يمكن تنفيذ الوظائف الأساسية عليه. في الواقع ، IPN الذي نعتبره ليس REST API بالمعنى الكامل للكلمة لمجرد أن PayPal نفسها لا تتوقع أي شيء منا باستثناء الرمز 200.

رفع WebListener


لأسباب أمنية ، لا يرسل PayPal الطلبات عبر HTTP ، لذا نحتاج إلى إصدار شهادة للبدء. 

استخدم المؤلف WinAcme . يمكنك إصدار شهادة إلى أي مجال ، وتحتاج إلى وضع الشهادة في متجر شهادات محلي. بالمناسبة ، يقع WinAcme في جذر القرص في الصورة.

#     
Get-ChildItem -Path Cert:\LocalMachine\My 
 
#      443 .
netsh http add sslcert ipport=0.0.0.0:443 certhash=D106F5676534794B6767D1FB75B58D5E33906710 "appid={00112233-4455-6677-8899-AABBCCDDEEFF}"

يمكن لـ Powershell استخدام فئات من .net ، مما يجعلها تساوي تقريبًا .net. أولاً ، باستخدام فئة HttpListener ، قم برفع خادم الويب.

#   .net
$http = [System.Net.HttpListener]::new() 
 
#   
$http.Prefixes.Add("http://donate.to/")
$http.Prefixes.Add("https://donate.to/")
 
# 
$http.Start()

للتحقق من أن كل شيء على ما يرام ، قم بتشغيل netstat.



إذا بدأ البرنامج النصي بالاستماع إلى المنفذ 443 في القائمة ، فهذا يعني أنك فعلت كل شيء بشكل صحيح ، ويمكننا متابعة قبول الطلب. فقط لا تنسى جدار الحماية.

قبول الطلب


باستخدام IPN Simulator ، يمكننا إرسال طلب POST اختباري لنرى ما هو. ولكن لا يمكنك تضمين الحقول الخاصة بك في ذلك ، لذلك يوصي المؤلف بعمل زر وشراء شيء من نفسك على الفور. سيعرض IPN History طلبًا عاديًا من الزر الذي ستستخدمه. فعل المؤلف ذلك فقط عن طريق شراء فحم واحد مقابل روبل واحد.

سنقبل باستخدام حلقة while. أثناء تشغيل خادم الويب ، يمكننا قراءة دفق البيانات الواردة.

while ($http.IsListening) {
 
  $context = $http.GetContext()
 
  if ($context.Request.HttpMethod -eq 'POST' -and $context.Request.RawUrl -eq '/') {
 
    #  POST 
    $Reader = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd()
    
    #  .
    $DecodedContent = [System.Web.HttpUtility]::UrlDecode($Reader)
 
    #   .
    $Payment | Format-Table
 
    #  200 OK   .
    $context.Response.Headers.Add("Content-Type", "text/plain")
    $context.Response.StatusCode = 200
    $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
    $context.Response.ContentLength64 = $ResponseBuffer.Length
    $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
    $context.Response.Close()
  }
}

إذا حصلت على شعيرية مثل هذه ، فقم بتطبيق:

$Payment = $DecodedContent -split "&" | ConvertFrom-StringData



بعد ذلك ، ستتلقى أخيرًا كائنًا عاديًا ، حيث تكون كل القيمة عبارة عن سلسلة.



يمكنك إنهاء القراءة هنا إذا كنت لا تريد التعمق في الشفرة ، ولكنك ترغب فقط في قبول الطلبات من واجهة برمجة تطبيقات شخص ما.

إليك الشفرة التي تعمل فورًا خارج الصندوق ، انسخها واستخدمها:

# 
$http = [System.Net.HttpListener]::new() 
 
# ,   
$http.Prefixes.Add("http://localhost/")
$http.Prefixes.Add("https://localhost/")
 
$http.Start()
 
while ($http.IsListening) {
 
  $context = $http.GetContext()
 
  if ($context.Request.HttpMethod -eq 'POST' -and $context.Request.RawUrl -eq '/') {
 
    #  POST 
    $Reader = [System.IO.StreamReader]::new($context.Request.InputStream).ReadToEnd()
    
    #  .
    $DecodedContent = [System.Web.HttpUtility]::UrlDecode($Reader)
          
    #  IPN   
    $Payment = $DecodedContent -split "&" | ConvertFrom-StringData
 
    #   .
    $Payment | Format-Table
 
    #  200 OK   .
    $context.Response.Headers.Add("Content-Type", "text/plain")
    $context.Response.StatusCode = 200
    $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
    $context.Response.ContentLength64 = $ResponseBuffer.Length
    $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
    $context.Response.Close()
  }
}

Minecraft الفروق الدقيقة


لذلك اكتشفنا كيف يمكننا تلقي تنبيهات حول المدفوعات ، والآن يمكننا أن ننسب لهم. ولكن هنا أيضا ليست بهذه البساطة. المشكلة هي أن اللعبة لا تعطي عناصر أو تغير حالة اللاعبين الذين ليسوا على الخادم. أي أننا بحاجة إلى الانتظار حتى يدخل الشخص إلى الخادم ليعطيه ما دفع ثمنه.

لذلك ، يتم عرض انتباهك على المفهوم العام للمدخن ، لقيد المدفوعات.



يتم استلام المدفوعات من خلال المستمع أعلاه ؛ تمت إضافة سطر واحد فقط لكتابة الكائن إلى الملف. ينظر الدفع الكامل (المعالج) إلى اللقب ويطابقه مع اسم الملف. إذا وجد ملفًا ، يجمع أمرًا لـ rcon وينفذه.

ابدأ ماين كرافتحول التي كتبها المؤلف في مقال سابق تم تعديله بشكل طفيف. يستمع الآن إلى الاستنتاج ، وينظر إلى الأسماء المستعارة للاعبين ويمررها إلى معالج الدفع.

إجراء عمليات رد حقيقية


بدون استخدام الإضافات ، سنقوم بإجراء عمليات رد حقيقية. لهذا ، تم تعديل Start-Minecraft. الآن لا يعرف فقط كيفية إضافة StdOut إلى ملف ، ولكن أيضًا يمشي على طول كل سطر بجدول منتظم. لحسن الحظ ، تترك لعبة ماين كرافت رسالة محددة للغاية عندما يدخل اللاعب إلى الخادم.

[04:20:00 INFO]: UUID of player XXPROHUNTERXX is 23e93d2e-r34d-7h15 -5h17-a9192cd70b48

من السهل جدًا اختيار لقب من هذا السطر. إليك كل الشفرة التي نحتاجها للحصول على البيانات من سلاسل Stdout.

$Regex = [Regex]::new("of player ([^ ]+)")
 
powershell.exe -file ".\Start-MinecraftHandler.ps1" -type $type -MinecraftPath $MinecraftPath | Tee-Object $LogFile -Append | ForEach-Object {
 
     Write-host $_
        
    $Player = $Regex.Matches($_).value -replace "of player "
        
    if ($true -eq $Regex.Matches($_).Success) {
        #   
    }
}


يتم إدخال خط جديد في خط الأنابيب $ _ ، نكتبه في نافذة وحدة التحكم ونتصفحه بانتظام. يخطرنا النظام العادي نفسه عندما يعمل ، وهو أمر مريح للغاية.

من هنا يمكننا استدعاء أي رمز. على سبيل المثال ، باستخدام نفس RCON ، يمكننا تحية اللاعب في PM ، باستخدام برنامج التتبُّع في الخلاف لإعلام أن شخصًا ما قام بتسجيل الدخول إلى الخادم ، وحظر كش ملك ، وما إلى ذلك.

تسديد المدفوعات


منذ أن بدأنا في معالجة المدفوعات ، نود أن نحصل على الأقل على بيانات كاملة تمامًا حول العملية وتاريخ العمليات التي تم إجراؤها ، لأننا نتحدث عن أرقام ذات صفرين ، إذا جاز التعبير.

يرغب المؤلف في ترك كل شيء بسيط للغاية وعدم محاكاة قاعدة حتى الآن. دعونا نلقي نظرة على نهج NoSQL. لنقم بإنشاء فصل خاص بنا ، والذي سيقوم باستيراد جميع المدفوعات المقبولة إلى / المدفوعات / المجلد في ملفات Json.

    class Payment {
        #  .
        [datetime]$Date = [datetime]::ParseExact($i.payment_date, "HH:mm:ss MMM dd, yyyy PDT", [System.Globalization.CultureInfo]::InvariantCulture)
        # 
        [string]$Item = $i.item_name
        # 
        [UInt16]$Quantity = $i.Quantity
        #    
        [UInt16]$AmountPaid = $AmountPaid -as [UInt16]
        #     
        [string]$Currency = $i.mc_currency
        # ,   
        [string]$Player = $i.option_selection1
    
        [bool]$Completed = $false
        [UInt16]$ItemId = $i.item_number
    }
/source>

    , , ,          .

 ,    <b>option_selection1</b> –   .      input,   ,     .
     <b>option_selection1</b>,<b>option_selection2</b>   .

      ,     ,      .

<source lang="powershell"> #     Payment,        .
    $Payment = [Payment]::new()
    $Payment | Format-Table
    #  ,   ----
    $FileName = $Payment.Player + "-" + $Payment.date.Hour + "-" + $Payment.date.Minute + "-" + $Payment.date.Day + "-" + $Payment.date.Month + "-" + $Payment.date.Year + ".json"
 
# ,      
    $JsonPath = Join-Path $MinecraftPath \payments\Pending $FileName
    
    #   
    $Payment | ConvertTo-Json | Out-File $JsonPath

هذا كل ما كان مطلوبًا من مستمعنا. تلقي البيانات من PayPal والكتابة إلى ملف.

نقوم بمعالجة الدفع


سيتم استدعاء المعالج العادي الذي كتب عنه في وقت سابق. نقوم بنقل لقب اللاعب إلى الوحدة النمطية وهذا كل شيء. بعد ذلك ، يتم تشغيل برنامج نصي جديد ، يبحث عن الملف ، وإذا كان هناك ملف ، فإنه يمنح اللاعب العنصر المكتوب في الملف.

powershell.exe -file "C:\mc.fern\Start-MinecraftHandler.ps1" -type $type -MinecraftPath $MinecraftPath | Tee-Object $LogFile -Append | ForEach-Object {
       
        #     ,      .
        Write-host $_   
 
        # Regex     
        if ($true -eq $Regex.Matches($_).Success) {
            
            #       
            $Player = $Regex.Matches($_).value -replace "of player "
            
            #  ,       
            Complete-Payment -Player $Player
        }
    }

عندما يتم تشغيل الانتظام ، يتم تشغيل وحدة تكمل الدفع ، أي أنها تمنح اللاعب عنصرًا. للقيام بذلك ، في المجلد / Payments / Pending / ، يبحث البرنامج النصي عن الملفات التي تحتوي على لقب اللاعب الذي دخل اللعبة ويقرأ محتوياتها.

الآن تحتاج إلى جمع الأمر للخادم وإرساله إلى هناك. سيتم جمعها من ملف. نحن نعلم لقب اللاعب ، واسم العنصر ومعرفه الذي تم تسجيله ، وعدد القطع التي تم تسجيلها أيضًا ، ويبقى فقط لإرسال أمر إلى خادم اللعبة. لهذا سنستخدم mcrcon .

#    
    $JsonPath = Join-Path $MinecraftPath\payments\Pending -ChildPath $Player*
    $i = $JsonPath | Get-Item | Where-Object { !$_.PSIsContainer } | Get-Content | ConvertFrom-Json -ErrorVariable Errored
 
    #      
    if ($null -ne $i) {
 
        #  
        $Command = '"' + "give " + $i.Player + " " + $i.Item + " " + $i.Quantity + '"'
        Write-host $Command -ForegroundColor Green
    
        #   
        Start-Process -FilePath mcrcon.exe -ArgumentList "-H localhost -p 123 -w 5 $Command"
    
        # ,      
        $JsonPath = Join-Path $MinecraftPath\payments\Pending -ChildPath $FileName
        
        #   
        $i | ConvertTo-Json | Out-File $JsonPath
    
        #     
        Move-Item  -Path $JsonPath -Destination $MinecraftPath\payments\Completed
    }

نحن نصنع كل شيء في وحدة مناسبة


تتطلب عملية Java وعملية WebListener مؤشرات ترابط مختلفة ، ولكن المؤلف غير راضٍ عن الحاجة إلى تشغيل WebListener بشكل منفصل والخادم بشكل منفصل. يريد المؤلف كل شيء دفعة واحدة مع فريق واحد.

لذلك ، باستخدام Powershell 7 ، سنطلق هذا وذاك. وسوف يساعدنا:

ForEach-Object -Parallel {}

يعمل الأمر cmdlet مع inputObject ، لذلك نقوم بإطعامه صفيفًا غير معقد ومشاركة التدفقات باستخدام المحول.

"A", "B" | ForEach-Object -Parallel {
 
    Import-Module ".\Start-Minecraft.ps1"
 
    Import-Module ".\Start-WebListener.ps1"
 
    switch ($_) {
        "A" {
            Start-WebListener -Path "C:\mc\"
        }
        "B" {
            Start-Minecraft -Type Vanilla -LogFile ".\stdout.txt" -MinecraftPath "C:\mc\"
        }
        
    }
}

لذلك ، بطريقة عكسية ، بدأنا عمليتين مختلفتين من محطة واحدة ولم نفقد حتى المدخلات. ولكن كانت هناك مشكلة أخرى. يقوم WebListener بتأمين وحدة التحكم بعد إيقاف تشغيل الخادم بشكل منتظم ولا يريد الانتقال إلى أي مكان.

لكي لا يتم إعادة تشغيل الجهاز الطرفي في كل مرة ، تمت إضافة مفتاح عشوائي إلى Start-MinecraftHandler.ps1 وإلى Start-WebListener.ps1 ، مما سيؤدي إلى إيقاف الخادم عبر POST على WebListener.

Start-MinecraftHandler.ps1 ، عندما يسجل إكمال ناجح ، ينفذ الأمر:

Invoke-WebRequest -Method Post -Uri localhost -Body $StopToken | Out-Null

يحتوي StopToken $ على قيمة رقمية عشوائية يتم تمريرها مسبقًا بواسطة البرنامج النصي لبدء التشغيل إلى كل من المستمع والمعالج. ينظر المستمع إلى ما استلمه في الطلب وينطفئ إذا كان نص الطلب يطابق $ StopToken.

if ($DecodedContent -eq $StopToken) {
        Write-Host "Stopping WebListener"
        $context.Response.Headers.Add("Content-Type", "text/plain")
        $context.Response.StatusCode = 200
        $ResponseBuffer = [System.Text.Encoding]::UTF8.GetBytes("")
        $context.Response.ContentLength64 = $ResponseBuffer.Length
        $context.Response.OutputStream.Write($ResponseBuffer, 0, $ResponseBuffer.Length)
        $context.Response.Close()
        $http.Close()
        break
      }

إنه آمن بما فيه الكفاية ، فقط ذاكرة الوصول العشوائي تعرف عن الرمز المميز ولا أحد غيره. يتم إطلاق جميع الوحدات من تحت PowerShell 7 ، ويختلف المسار إلى الوحدات النمطية لـ PowerShell 7 عن المسار في Windows Powershell. تم تكديس كل شيء هنا. ضع في اعتبارك عند كتابة الخاصة بك.

C:\Program Files\PowerShell\7\Modules

نصنع ملف التكوين


حتى يمكن استخدام كل هذا الخزي بدون صداع شديد ، تحتاج إلى إنشاء ملف تكوين عادي. سيحتوي الملف على متغيرات وليس أكثر. يتمسك التكوينات باستخدام المعيار:

Import-Module $MinecraftPath\config.ps1 -Force

نحن بحاجة إلى الإشارة إلى أهم شيء. النطاق الذي يتم النقر عليه هو النطاق العادي الذي يبحث عن لقب اللاعب ، فقد يختلف الناتج من إصدار إلى آخر ، وكلمة المرور من rcon.

تبدو هكذا:

#,    
$DomainName = "localhost"
 
# ,      
# ,  
$RegExp = "of player ([^ ]+)"
#    ,   ,  .
$RegExpCut = "of player "
 
#  rcon,     server.properties
$rconPassword = "123"

من المستحسن وضع التكوين في مجلد الخادم ، لأن البرنامج النصي يبحث عنه في الجذر MinecraftPath

كيف تستخدم كل هذا؟


بادئ ذي بدء ، يتم تثبيت هذه البرامج النصية وجاهزة للاستخدام في سوق Ruvds ، ولكن إذا لم يكن لديك عميل بعد أو لم تجرب الصورة ، فإليك رابطًا لجميع الملفات الموجودة في المستودع ، فلا تتردد في الالتزام. 

  1. تنزيل وتثبيت PowerShell 7
  2. قم بتنزيل أرشيف الوحدات وفك ضغطه


الآن ظهرت جميع الوحدات والأوامر اللازمة. ماذا يفعلون؟

ابدأ ماين كرافت


الخيارات: -

اكتب
Forge أو Vanilla. يبدأ الخادم إما من Server.Jar أو Forge ، باختيار أحدث إصدار موجود في المجلد.

-MinecraftPath يشير
إلى المجلد الذي سيتم تشغيل الخادم منه.

-LogFile
طريقة بديلة لجمع السجلات. يشير إلى ملف سيتم فيه كتابة كل شيء يظهر في وحدة التحكم.

-StartPaymentListener
إلى جانب الخادم ، يبدأ ويقبل المدفوعات. قبول الدفع نفسه متاح كوحدة منفصلة. يستبدل cmdlet Start-Weblistener

بدء قائمة الويب


تبدأ وحدة قبول الدفع.

-MinecraftPath يشير
إلى المجلد مع ملف التكوين.

-StopToken
يحدد -Body HTTP POST طلب لإيقاف WebListener'a.

استنتاج:


حسنًا ، تحدث المعجزات.


All Articles