lampac/Merchant/Streampay.cs
lampac-talks f843f04fd4 chore: initial commit 154.3
Signed-off-by: lampac-talks <lampac-talks@users.noreply.github.com>
2026-01-30 16:23:09 +03:00

200 lines
6.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Chaos.NaCl;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Shared;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using IO = System.IO.File;
namespace Merchant.Controllers
{
public class Streampay : MerchantController
{
static Streampay() { Directory.CreateDirectory("merchant/invoice/streampay"); }
[HttpGet]
[AllowAnonymous]
[Route("streampay/new")]
async public Task<ActionResult> Index(string email)
{
var init = AppInit.conf.Merchant.Streampay;
if (!init.enable || string.IsNullOrWhiteSpace(email))
return Content(string.Empty);
email = decodeEmail(email);
string transid = DateTime.Now.ToBinary().ToString().Replace("-", "");
if (memoryCache.TryGetValue($"streampay:{transid}", out string pay_link))
return Redirect(pay_link);
IO.WriteAllText($"merchant/invoice/streampay/{transid}", email);
var body = new
{
init.store_id,
customer = email,
external_id = transid,
description = $"Подписка на {AppInit.conf.Merchant.accessForMonths} {EndOfText("месяц", "месяца", "месяцев", AppInit.conf.Merchant.accessForMonths)}",
system_currency = "USDT",
payment_type = 2,
amount = AppInit.conf.Merchant.accessCost
};
var jsonBody = JsonSerializer.Serialize(body);
string signature = Sign(Encoding.UTF8.GetBytes(jsonBody + DateTime.UtcNow.ToString("yyyyMMdd:HHmm")), init.private_key);
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.streampay.org/api/payment/create")
{
Content = new StringContent(jsonBody, Encoding.UTF8, "application/json")
};
request.Headers.Add("signature", signature);
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string respData = await response.Content.ReadAsStringAsync();
string pay_url = Regex.Match(respData, "\"pay_url\":\"([^\"]+)\"").Groups[1].Value;
if (!string.IsNullOrEmpty(pay_url))
{
memoryCache.Set($"streampay:{transid}", pay_url, DateTime.Now.AddHours(2));
return Redirect(pay_url);
}
return Content(respData);
}
else if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
return Content("Invalid signature");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotAcceptable)
{
return Content("Invalid request data");
}
else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
return Content("Internal server error");
}
}
return Content("error");
}
[HttpGet]
[AllowAnonymous]
[Route("streampay/callback")]
public ActionResult Callback()
{
string transid = Request.Query["external_id"].ToString();
if (Request.Query["status"] != "awaiting_payment")
memoryCache.Remove($"streampay:{transid}");
var merchant = AppInit.conf.Merchant;
if (!merchant.Streampay.enable || !IO.Exists($"merchant/invoice/streampay/{transid}") || Request.Query["status"] != "success")
return Ok();
var now = DateTime.UtcNow;
var queryParams = Request.Query.OrderBy(x => x.Key).Select(x => $"{x.Key}={x.Value}").ToList();
string paramsStr = string.Join('&', queryParams);
byte[] paramsBuf = Encoding.UTF8.GetBytes(paramsStr);
string log = $"{paramsStr}\n{JsonSerializer.Serialize(Request.Headers)}";
for (int i = 0; i < 2; i++)
{
string tm = now.ToString("yyyyMMdd:HHmm");
var bufToSign = paramsBuf.Concat(Encoding.UTF8.GetBytes(tm)).ToArray();
bool verify = Verify(Request.Headers["Signature"], bufToSign, merchant.Streampay.public_key);
log += $"\nverify: {verify} | {tm} | signature: {Request.Headers["Signature"]}";
if (verify)
{
PayConfirm(IO.ReadAllText($"merchant/invoice/streampay/{transid}"), "streampay", transid);
WriteLog("streampay", log + "\nOK");
return Ok();
}
now = now.AddMinutes(-1);
}
WriteLog("streampay", log + "\nForbid");
return Forbid();
}
static string Sign(byte[] message, string privateKey)
{
var bytes = Ed25519.Sign(message, HexToBytes(privateKey));
StringBuilder hex = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
static bool Verify(string signature, byte[] message, string publicKey)
{
try
{
return Ed25519.Verify(HexToBytes(signature), message, HexToBytes(publicKey));
}
catch { return false; }
}
static byte[] HexToBytes(string key)
{
if (key.Length % 2 != 0)
return null;
int byteCount = key.Length / 2;
byte[] hexKey = new byte[byteCount];
for (int i = 0; i < byteCount; i++)
{
string byteString = key.Substring(i * 2, 2);
byte keyValue = Convert.ToByte(byteString, 16);
hexKey[i] = keyValue;
}
return hexKey;
}
static string EndOfText(string s1, string s2, string s3, int x)
{
int n = x % 100;
if ((n > 10) && (n < 20))
return s3;
switch (x % 10)
{
case 4: return s2;
case 0:
case 5:
case 6:
case 7:
case 8:
case 9: return s3;
default: return s1;
}
}
}
}