From d6eded539da5257f997a0d2137fd06152d2da913 Mon Sep 17 00:00:00 2001 From: KazanskiyMaks Date: Wed, 17 May 2023 18:25:47 +0300 Subject: [PATCH] Small fixes --- LiqPayIntegration/LiqPay.cs | 127 ++++-- LiqPayIntegration/PaymentResponse.cs | 366 +++++++++--------- Server/Controllers/PaymentsController.cs | 27 ++ Server/Program.cs | 2 + Server/Server.csproj | 5 +- Server/Services/IPaymentsService.cs | 12 + Server/Services/PaymentsService.cs | 23 ++ .../DataTransferObjects/PaymentDto.cs | 14 + auto.bus API.sln | 26 +- 9 files changed, 377 insertions(+), 225 deletions(-) create mode 100644 Server/Controllers/PaymentsController.cs create mode 100644 Server/Services/IPaymentsService.cs create mode 100644 Server/Services/PaymentsService.cs create mode 100644 SharedModels/DataTransferObjects/PaymentDto.cs diff --git a/LiqPayIntegration/LiqPay.cs b/LiqPayIntegration/LiqPay.cs index 6517416..8f4d888 100644 --- a/LiqPayIntegration/LiqPay.cs +++ b/LiqPayIntegration/LiqPay.cs @@ -1,4 +1,5 @@ using System.Buffers.Text; +using System.Dynamic; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; @@ -9,47 +10,99 @@ using static System.Net.WebRequestMethods; namespace LiqPayIntegration { - public class LiqPay - { - private readonly HttpClient _http; + public class LiqPay + { + private readonly HttpClient _http; - public const string API_URL = "https://www.liqpay.ua/api/request"; - public string PublicKey { get; init; } - public string PrivateKey { get; init; } - public LiqPay(string publicKey, string privateKey) - { - _http = new HttpClient(); - PublicKey = publicKey; - PrivateKey = privateKey; + public const string API_URL = "https://www.liqpay.ua/api/request"; + + public string PublicKey { get; private set; } + public string PrivateKey { get; private set; } + public string CallbackUrl { get; private set; } + public LiqPay(string publicKey, string privateKey) + { + _http = new HttpClient(); + PublicKey = publicKey; + PrivateKey = privateKey; + } + public LiqPay(string publicKey, string privateKey, string callbackUrl) : this (publicKey, privateKey) + { + CallbackUrl = callbackUrl; } - public async ValueTask PaymentArchive(DateTime from, DateTime to) - { - var json = JsonContent.Create(new - { - action = "reports", - version = 3, - public_key = PublicKey, - date_from = (long)from.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds, - date_to = (long)to.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds + public async ValueTask PaymentArchive(DateTimeOffset from, DateTimeOffset to) + { + var json = JsonContent.Create(new + { + action = "reports", + version = 3, + public_key = PublicKey, + date_from = from.ToUnixTimeMilliseconds(), + date_to = to.ToUnixTimeMilliseconds(), + //server_url = CallbackUrl + /*date_from = (long)from.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds, + date_to = (long)to.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds*/ }); - var data = Convert.ToBase64String(await json.ReadAsByteArrayAsync()); - var signString = PrivateKey + data + PrivateKey; - var signature = Convert.ToBase64String(SHA1.HashData(Encoding.UTF8.GetBytes(signString))); - var requestContent = new FormUrlEncodedContent(new[] { - new KeyValuePair("data", data), - new KeyValuePair("signature", signature) + var data = Convert.ToBase64String(await json.ReadAsByteArrayAsync()); + var signString = PrivateKey + data + PrivateKey; + var signature = Convert.ToBase64String(SHA1.HashData(Encoding.UTF8.GetBytes(signString))); + var requestContent = new FormUrlEncodedContent(new[] { + new KeyValuePair("data", data), + new KeyValuePair("signature", signature) + }); + var response = await _http.PostAsync(API_URL, requestContent); + var rawResponse = await response.Content.ReadAsStringAsync(); + var jsonResponse = JsonNode.Parse(rawResponse); + if (jsonResponse["result"] == null) + throw new NullReferenceException("jsonResponse[\"result\"]"); + var result = jsonResponse["result"].Deserialize(); + if (result != "success") + throw new Exception("result isn't success"); + var paymentResponses = jsonResponse["data"].Deserialize(); + return paymentResponses; + } + + public async ValueTask<(string data, string signature)> GetCheckout(double amount, string description, string order_id) + { + var json = JsonContent.Create(new + { + action = "pay", + version = 3, + public_key = PublicKey, + amount = amount, + currency = "UAH", + description = description, + order_id = order_id, + server_url = CallbackUrl }); - var response = await _http.PostAsync(API_URL, requestContent); - var rawResponse = await response.Content.ReadAsStringAsync(); - var jsonResponse = JsonNode.Parse(rawResponse); - if (jsonResponse["result"] == null) - throw new NullReferenceException("jsonResponse[\"result\"]"); - var result = jsonResponse["result"].Deserialize(); - if (result != "success") - throw new Exception("result isn't success"); - var paymentResponses = jsonResponse["data"].Deserialize(); - return paymentResponses; + var data = Convert.ToBase64String(await json.ReadAsByteArrayAsync()); + var signString = PrivateKey + data + PrivateKey; + var signature = Convert.ToBase64String(SHA1.HashData(Encoding.UTF8.GetBytes(signString))); + return (data, signature); + } + + public string GetPaymentUrl(string data, string signature) + { + return "https://www.liqpay.ua/api/3/checkout?data=" + data + "&signature=" + signature; } - } + + public async ValueTask GetPaymentUrl(double amount, string description, string order_id) + { + var vars = await GetCheckout(amount, description, order_id); + return "https://www.liqpay.ua/api/3/checkout?data=" + vars.data + "&signature=" + vars.signature; + + } + + /*public async void ReceiptOfReceipt(string orderId, string email) + { + var json = JsonContent.Create(new + { + action = "reports", + version = 3, + public_key = PublicKey, + date_from = (long)from.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds, + date_to = (long)to.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds + }); + }*/ + } } \ No newline at end of file diff --git a/LiqPayIntegration/PaymentResponse.cs b/LiqPayIntegration/PaymentResponse.cs index 42782dc..4f469c4 100644 --- a/LiqPayIntegration/PaymentResponse.cs +++ b/LiqPayIntegration/PaymentResponse.cs @@ -7,193 +7,193 @@ using System.Threading.Tasks; namespace LiqPayIntegration { - public class PaymentResponse - { - /// - /// Id платежу в системі LiqPay - /// - [JsonPropertyName("payment_id")] - public long PaymentId { get; set; } - /// - /// Тип операції. - /// Можливі значення: pay - платіж, - /// hold - блокування коштів на рахунку відправника, - /// paysplit - розщеплення платежу, - /// subscribe - створення регулярного платежу, - /// paydonate - пожертвування, - /// auth - предавторізація картки, - /// regular - регулярний платіж - /// - [JsonPropertyName("action")] - public string Action { get; set; } - /// - /// Статус платежу. - /// Можливі значення: - /// - [JsonPropertyName("status")] - public string Status { get; set; } - /// - /// Версія API. Поточне значення - 3 - /// - [JsonPropertyName("version")] - public int Version { get; set; } - /// - /// Тип платежу - /// - [JsonPropertyName("type")] - public string Type { get; set; } - /// - /// Спосіб оплати. - /// Можливі значення: - /// - [JsonPropertyName("paytype")] - public string PayType { get; set; } - /// - /// Публічний ключ магазину - /// - [JsonPropertyName("public_key")] - public string PublicKey { get; set; } - /// - /// ID еквайера - /// - [JsonPropertyName("acq_id")] - public int AcqId { get; set; } - /// - /// Order_id платежу - /// - [JsonPropertyName("order_id")] - public string OrderId { get; set; } - /// - /// Order_id платежу в системі LiqPay - /// - [JsonPropertyName("liqpay_order_id")] - public string LiqPayOrderId { get; set; } - /// - /// Коментар до платежу - /// - [JsonPropertyName("description")] - public string Description { get; set; } - /// - /// Карта відправника - /// - [JsonPropertyName("sender_card_mask2")] - public string SenderCardMask2 { get; set; } - /// - /// Банк відправника - /// - [JsonPropertyName("sender_card_bank")] - public string SenderCardBank { get; set; } - /// - /// Тип картки відправника MC/Visa - /// - [JsonPropertyName("sender_card_type")] - public string SenderCardType { get; set; } - /// - /// Країна картки відправника. Цифровий ISO 3166-1 код - /// - [JsonPropertyName("sender_card_country")] - public int SenderCardCountry { get; set; } + public class PaymentResponse + { + /// + /// Id платежу в системі LiqPay + /// + [JsonPropertyName("payment_id")] + public long PaymentId { get; set; } + /// + /// Тип операції. + /// Можливі значення: pay - платіж, + /// hold - блокування коштів на рахунку відправника, + /// paysplit - розщеплення платежу, + /// subscribe - створення регулярного платежу, + /// paydonate - пожертвування, + /// auth - предавторізація картки, + /// regular - регулярний платіж + /// + [JsonPropertyName("action")] + public string Action { get; set; } + /// + /// Статус платежу. + /// Можливі значення: + /// + [JsonPropertyName("status")] + public string Status { get; set; } + /// + /// Версія API. Поточне значення - 3 + /// + [JsonPropertyName("version")] + public int Version { get; set; } + /// + /// Тип платежу + /// + [JsonPropertyName("type")] + public string Type { get; set; } + /// + /// Спосіб оплати. + /// Можливі значення: + /// + [JsonPropertyName("paytype")] + public string PayType { get; set; } + /// + /// Публічний ключ магазину + /// + [JsonPropertyName("public_key")] + public string PublicKey { get; set; } + /// + /// ID еквайера + /// + [JsonPropertyName("acq_id")] + public int AcqId { get; set; } + /// + /// Order_id платежу + /// + [JsonPropertyName("order_id")] + public string OrderId { get; set; } + /// + /// Order_id платежу в системі LiqPay + /// + [JsonPropertyName("liqpay_order_id")] + public string LiqPayOrderId { get; set; } + /// + /// Коментар до платежу + /// + [JsonPropertyName("description")] + public string Description { get; set; } + /// + /// Карта відправника + /// + [JsonPropertyName("sender_card_mask2")] + public string SenderCardMask2 { get; set; } + /// + /// Банк відправника + /// + [JsonPropertyName("sender_card_bank")] + public string SenderCardBank { get; set; } + /// + /// Тип картки відправника MC/Visa + /// + [JsonPropertyName("sender_card_type")] + public string SenderCardType { get; set; } + /// + /// Країна картки відправника. Цифровий ISO 3166-1 код + /// + [JsonPropertyName("sender_card_country")] + public int SenderCardCountry { get; set; } /// /// /// - [JsonPropertyName("ip")] - public string Ip { get; set; } - /// - /// Сума платежу - /// - [JsonPropertyName("amount")] - public double Amount { get; set; } - /// - /// Валюта платежу - /// - [JsonPropertyName("currency")] - public string Currency { get; set; } - /// - /// Комісія з відправника у валюті платежу - /// - [JsonPropertyName("sender_commission")] - public double SenderCommission { get; set; } - /// - /// Комісія з одержувача у валюті платежу - /// - [JsonPropertyName("receiver_commission")] - public double ReceiverCommission { get; set; } - /// - /// Комісія агента в валюті платежу - /// - [JsonPropertyName("agent_commission")] - public double AgentCommission { get; set; } - /// - /// Сума транзакції debit у валюті currency_debit - /// - [JsonPropertyName("amount_debit")] - public double AmountDebit { get; set; } - /// - /// Сума транзакції credit в валюті amount_credit - /// - [JsonPropertyName("amount_credit")] - public double AmountCredit { get; set; } - /// - /// Комісія з відправника у валюті currency_debit - /// - [JsonPropertyName("commission_debit")] - public double CommissionDebit { get; set; } - /// - /// Комісія з одержувача у валюті currency_credit - /// - [JsonPropertyName("commission_credit")] - public double CommissionCredit { get; set; } - /// - /// Валюта транзакції debit - /// - [JsonPropertyName("currency_debit")] - public string CurrencyDebit { get; set; } - /// - /// Валюта транзакції credit - /// - [JsonPropertyName("currency_credit")] - public string CurrencyCredit { get; set; } - /// - /// Бонус відправника у валюті платежу - /// - [JsonPropertyName("sender_bonus")] - public double SenderBonus { get; set; } - /// - /// Бонус відправника у валюті платежу debit - /// - [JsonPropertyName("amount_bonus")] - public double AmountBonus { get; set; } - /// - /// Можливі значення: 5 - транзакція пройшла з 3DS (емітент і еквайєр підтримують технологію 3D-Secure), 6 - емітент картки платника не підтримує технологію 3D-Secure, 7 - операція пройшла без 3D-Secure - /// - [JsonPropertyName("mpi_eci")] - public string MpiEci { get; set; } - /// - /// Можливі значення: - /// true - транзакція пройшла з 3DS перевіркою, - /// false - транзакція пройшла без 3DS перевірки - /// - [JsonPropertyName("is_3ds")] - public bool Is3ds { get; set; } + [JsonPropertyName("ip")] + public string Ip { get; set; } + /// + /// Сума платежу + /// + [JsonPropertyName("amount")] + public double Amount { get; set; } + /// + /// Валюта платежу + /// + [JsonPropertyName("currency")] + public string Currency { get; set; } + /// + /// Комісія з відправника у валюті платежу + /// + [JsonPropertyName("sender_commission")] + public double SenderCommission { get; set; } + /// + /// Комісія з одержувача у валюті платежу + /// + [JsonPropertyName("receiver_commission")] + public double ReceiverCommission { get; set; } + /// + /// Комісія агента в валюті платежу + /// + [JsonPropertyName("agent_commission")] + public double AgentCommission { get; set; } + /// + /// Сума транзакції debit у валюті currency_debit + /// + [JsonPropertyName("amount_debit")] + public double AmountDebit { get; set; } + /// + /// Сума транзакції credit в валюті amount_credit + /// + [JsonPropertyName("amount_credit")] + public double AmountCredit { get; set; } + /// + /// Комісія з відправника у валюті currency_debit + /// + [JsonPropertyName("commission_debit")] + public double CommissionDebit { get; set; } + /// + /// Комісія з одержувача у валюті currency_credit + /// + [JsonPropertyName("commission_credit")] + public double CommissionCredit { get; set; } + /// + /// Валюта транзакції debit + /// + [JsonPropertyName("currency_debit")] + public string CurrencyDebit { get; set; } + /// + /// Валюта транзакції credit + /// + [JsonPropertyName("currency_credit")] + public string CurrencyCredit { get; set; } + /// + /// Бонус відправника у валюті платежу + /// + [JsonPropertyName("sender_bonus")] + public double SenderBonus { get; set; } + /// + /// Бонус відправника у валюті платежу debit + /// + [JsonPropertyName("amount_bonus")] + public double AmountBonus { get; set; } + /// + /// Можливі значення: 5 - транзакція пройшла з 3DS (емітент і еквайєр підтримують технологію 3D-Secure), 6 - емітент картки платника не підтримує технологію 3D-Secure, 7 - операція пройшла без 3D-Secure + /// + [JsonPropertyName("mpi_eci")] + public string MpiEci { get; set; } + /// + /// Можливі значення: + /// true - транзакція пройшла з 3DS перевіркою, + /// false - транзакція пройшла без 3DS перевірки + /// + [JsonPropertyName("is_3ds")] + public bool Is3ds { get; set; } /// /// /// - [JsonPropertyName("language")] - public string Language { get; set; } - /// - /// Дата створення платежу - /// - [JsonPropertyName("create_date")] - public long CreateDate { get; set; } - /// - /// Дата завершення/зміни платежу - /// - [JsonPropertyName("end_date")] - public long EndDate { get; set; } - /// - /// Id транзакції в системі LiqPay - /// - [JsonPropertyName("transaction_id")] - public long TransactionId { get; set; } - } + [JsonPropertyName("language")] + public string Language { get; set; } + /// + /// Дата створення платежу + /// + [JsonPropertyName("create_date")] + public long CreateDate { get; set; } + /// + /// Дата завершення/зміни платежу + /// + [JsonPropertyName("end_date")] + public long EndDate { get; set; } + /// + /// Id транзакції в системі LiqPay + /// + [JsonPropertyName("transaction_id")] + public long TransactionId { get; set; } + } } \ No newline at end of file diff --git a/Server/Controllers/PaymentsController.cs b/Server/Controllers/PaymentsController.cs new file mode 100644 index 0000000..eab8f0a --- /dev/null +++ b/Server/Controllers/PaymentsController.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; +using Server.Services; +using SharedModels.DataTransferObjects; + +[Route("api/payment")] +[ApiController] +public class PaymentController : ControllerBase +{ + private readonly IPaymentsService _paymentsService; + public PaymentController(IPaymentsService paymentsService) + { + _paymentsService = paymentsService; + } + + [HttpGet] + public async Task GetPaymentLink([FromQuery] PaymentDto payment) + { + var result = await _paymentsService.GetPaymentUrl(payment); + + if (!result.isSucceed) + { + return result.actionResult; + } + + return Ok(result.url); + } +} diff --git a/Server/Program.cs b/Server/Program.cs index 6a41d98..4c879c1 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -149,6 +149,8 @@ services.AddScoped(); services.AddScoped(); services.AddScoped(); +builder.Services.AddScoped(); + // Adding DB Context with PostgreSQL var connectionString = configuration.GetConnectionString("DefaultConnection"); services.AddDbContext(options => diff --git a/Server/Server.csproj b/Server/Server.csproj index e16e355..b2a5d31 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -36,8 +36,9 @@ + - + diff --git a/Server/Services/IPaymentsService.cs b/Server/Services/IPaymentsService.cs new file mode 100644 index 0000000..e003ffa --- /dev/null +++ b/Server/Services/IPaymentsService.cs @@ -0,0 +1,12 @@ +using LiqPayIntegration; +using Microsoft.AspNetCore.Mvc; +using SharedModels.DataTransferObjects; +using System.Dynamic; + +namespace Server.Services; + +public interface IPaymentsService +{ + Task<(bool isSucceed, IActionResult? actionResult, IEnumerable payments)> GetPayments(DateTime from, DateTime to); + Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(PaymentDto payment); +} \ No newline at end of file diff --git a/Server/Services/PaymentsService.cs b/Server/Services/PaymentsService.cs new file mode 100644 index 0000000..087981a --- /dev/null +++ b/Server/Services/PaymentsService.cs @@ -0,0 +1,23 @@ +using LiqPayIntegration; +using Microsoft.AspNetCore.Mvc; +using SharedModels.DataTransferObjects; + +namespace Server.Services; + +public class PaymentsService : IPaymentsService +{ + + // LiqpayIntegration + LiqPay liqPay = new LiqPay("sandbox_i23432845039", "sandbox_gymL9PdryqdfAznNQbb7ynLvASDQ5SJCCNJvF2iV"); + + public async Task<(bool isSucceed, IActionResult? actionResult, IEnumerable payments)> GetPayments(DateTime from, DateTime to) + { + return(true, null, (await liqPay.PaymentArchive(from, to)).AsEnumerable()); + } + + public async Task<(bool isSucceed, IActionResult? actionResult, string url)> GetPaymentUrl(PaymentDto payment) + { + return (true, null, (await liqPay.GetPaymentUrl(payment.Amount, payment.Description, payment.OrderId))); + } + +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/PaymentDto.cs b/SharedModels/DataTransferObjects/PaymentDto.cs new file mode 100644 index 0000000..c28437f --- /dev/null +++ b/SharedModels/DataTransferObjects/PaymentDto.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class PaymentDto +{ + [Required] + public double Amount { get; set; } + [Required] + public string Description { get; set; } + [Required] + [StringLength(maximumLength: 255, ErrorMessage = "Order Id is too long")] + public string OrderId { get; set; } +} diff --git a/auto.bus API.sln b/auto.bus API.sln index ab8a61e..a6b4dd5 100644 --- a/auto.bus API.sln +++ b/auto.bus API.sln @@ -1,8 +1,14 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{0DCB2130-10E7-4C98-AB94-5F8D68032B9F}" +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{0DCB2130-10E7-4C98-AB94-5F8D68032B9F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedModels", "SharedModels\SharedModels.csproj", "{34E01155-5DA4-45DA-8F58-63AEFCC56B4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedModels", "SharedModels\SharedModels.csproj", "{34E01155-5DA4-45DA-8F58-63AEFCC56B4B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiqPayIntegration", "LiqPayIntegration\LiqPayIntegration.csproj", "{D1DE3296-E918-4702-997D-A10B755FF2CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "..\auto.bus_api\Utils\Utils.csproj", "{B1AC0E7C-E77C-4F2F-82A0-A2AB2D126B71}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -18,5 +24,19 @@ Global {34E01155-5DA4-45DA-8F58-63AEFCC56B4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {34E01155-5DA4-45DA-8F58-63AEFCC56B4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {34E01155-5DA4-45DA-8F58-63AEFCC56B4B}.Release|Any CPU.Build.0 = Release|Any CPU + {D1DE3296-E918-4702-997D-A10B755FF2CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1DE3296-E918-4702-997D-A10B755FF2CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1DE3296-E918-4702-997D-A10B755FF2CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1DE3296-E918-4702-997D-A10B755FF2CB}.Release|Any CPU.Build.0 = Release|Any CPU + {B1AC0E7C-E77C-4F2F-82A0-A2AB2D126B71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1AC0E7C-E77C-4F2F-82A0-A2AB2D126B71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1AC0E7C-E77C-4F2F-82A0-A2AB2D126B71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1AC0E7C-E77C-4F2F-82A0-A2AB2D126B71}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {067793F9-205E-4946-A13B-5C70752D57A3} EndGlobalSection EndGlobal