From 414dc631544a3a429c173689676f96dc20844300 Mon Sep 17 00:00:00 2001 From: Pooria Shariatzadeh Date: Sun, 7 Dec 2025 11:40:45 +0330 Subject: [PATCH] feat : add tara gateway --- .../Models/TaraAuthenticateRequest.cs | 9 + .../Models/TaraAuthenticateResponse.cs | 12 + .../src/Internal/Models/TaraCallbackResult.cs | 15 + .../Internal/Models/TaraGetTokenRequest.cs | 18 + .../Internal/Models/TaraGetTokenResponse.cs | 11 + .../src/Internal/Models/TaraInquiryRequest.cs | 14 + .../Internal/Models/TaraInquiryResponse.cs | 19 ++ .../src/Internal/Models/TaraInvoiceItem.cs | 49 +++ .../src/Internal/Models/TaraServiceAmount.cs | 19 ++ .../src/Internal/Models/TaraTrackPurchase.cs | 22 ++ .../src/Internal/Models/TaraVerifyRequest.cs | 9 + .../src/Internal/Models/TaraVerifyResponse.cs | 17 + .../Tara/src/Internal/TaraHelper.cs | 316 ++++++++++++++++++ .../Tara/src/Parbad.Gateway.Tara.csproj | 37 ++ .../Tara/src/Parbad.Gateway.Tara.xml | 315 +++++++++++++++++ src/Parbad.Gateway/Tara/src/TaraGateway.cs | 118 +++++++ .../Tara/src/TaraGatewayAccount.cs | 32 ++ .../Tara/src/TaraGatewayAccountExtensions.cs | 70 ++++ .../Tara/src/TaraGatewayBuilderExtensions.cs | 55 +++ .../Tara/src/TaraGatewayDiagnostics.cs | 52 +++ .../TaraGatewayInvoiceBuilderExtensions.cs | 198 +++++++++++ .../Tara/src/TaraGatewayOptions.cs | 60 ++++ .../Tara/src/TaraGatewayResultTranslator.cs | 43 +++ .../Tara/src/TaraRequestAdditionalData.cs | 52 +++ src/Parbad.Gateway/Tara/src/icon.png | Bin 0 -> 14067 bytes src/Parbad.Gateway/Tara/src/readme.txt | 189 +++++++++++ 26 files changed, 1751 insertions(+) create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateRequest.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateResponse.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraCallbackResult.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenRequest.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenResponse.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryRequest.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryResponse.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraInvoiceItem.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraServiceAmount.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraTrackPurchase.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyRequest.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyResponse.cs create mode 100644 src/Parbad.Gateway/Tara/src/Internal/TaraHelper.cs create mode 100644 src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.csproj create mode 100644 src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.xml create mode 100644 src/Parbad.Gateway/Tara/src/TaraGateway.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayAccount.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayAccountExtensions.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayBuilderExtensions.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayDiagnostics.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayInvoiceBuilderExtensions.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayOptions.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraGatewayResultTranslator.cs create mode 100644 src/Parbad.Gateway/Tara/src/TaraRequestAdditionalData.cs create mode 100644 src/Parbad.Gateway/Tara/src/icon.png create mode 100644 src/Parbad.Gateway/Tara/src/readme.txt diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateRequest.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateRequest.cs new file mode 100644 index 00000000..e459d081 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateRequest.cs @@ -0,0 +1,9 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraAuthenticateRequest + { + public string username { get; set; } + public string password { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateResponse.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateResponse.cs new file mode 100644 index 00000000..56d36ee3 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraAuthenticateResponse.cs @@ -0,0 +1,12 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraAuthenticateResponse + { + public string accessToken { get; set; } + public string result { get; set; } + public string description { get; set; } + public string doTime { get; set; } + public long expireTime { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraCallbackResult.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraCallbackResult.cs new file mode 100644 index 00000000..9e14effe --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraCallbackResult.cs @@ -0,0 +1,15 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraCallbackResult + { + public bool IsSucceed { get; set; } + public string Token { get; set; } + public string Result { get; set; } + public string Description { get; set; } + public string ChannelRefNumber { get; set; } + public string AdditionalData { get; set; } + public long OrderId { get; set; } + public string Message { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenRequest.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenRequest.cs new file mode 100644 index 00000000..1a8b5f4e --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenRequest.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraGetTokenRequest + { + public string ip { get; set; } + public List serviceAmountList { get; set; } + public List taraInvoiceItemList { get; set; } + public string additionalData { get; set; } + public string callBackUrl { get; set; } + public string amount { get; set; } + public string mobile { get; set; } + public long orderId { get; set; } + public long vat { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenResponse.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenResponse.cs new file mode 100644 index 00000000..c7b76356 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraGetTokenResponse.cs @@ -0,0 +1,11 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraGetTokenResponse + { + public string token { get; set; } + public string result { get; set; } + public string description { get; set; } + public string doTime { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryRequest.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryRequest.cs new file mode 100644 index 00000000..d959f396 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryRequest.cs @@ -0,0 +1,14 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraInquiryRequest + { + public string ip { get; set; } + public string token { get; set; } + } +} + + + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryResponse.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryResponse.cs new file mode 100644 index 00000000..3c756f9f --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInquiryResponse.cs @@ -0,0 +1,19 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraInquiryResponse + { + public string result { get; set; } + public string description { get; set; } + public string doTime { get; set; } + public List trackPurchaseList { get; set; } + public string orderId { get; set; } + } +} + + + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInvoiceItem.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInvoiceItem.cs new file mode 100644 index 00000000..8985282e --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraInvoiceItem.cs @@ -0,0 +1,49 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + /// + /// Represents an invoice item in Tara gateway request. + /// + public class TaraInvoiceItem + { + /// + /// Item name + /// + public string name { get; set; } + + /// + /// Item code + /// + public string code { get; set; } + + /// + /// Item count/quantity + /// + public long count { get; set; } + + /// + /// Unit type + /// + public long unit { get; set; } + + /// + /// Item fee/price + /// + public long fee { get; set; } + + /// + /// Item group + /// + public string group { get; set; } + + /// + /// Item group title + /// + public string groupTitle { get; set; } + + /// + /// Additional data for this item + /// + public string data { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraServiceAmount.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraServiceAmount.cs new file mode 100644 index 00000000..0d9d2b7b --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraServiceAmount.cs @@ -0,0 +1,19 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + /// + /// Represents a service amount in Tara gateway request. + /// + public class TaraServiceAmount + { + /// + /// Service identifier + /// + public long serviceId { get; set; } + + /// + /// Amount for this service + /// + public long amount { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraTrackPurchase.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraTrackPurchase.cs new file mode 100644 index 00000000..9808fbf1 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraTrackPurchase.cs @@ -0,0 +1,22 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraTrackPurchase + { + public string token { get; set; } + public string result { get; set; } + public string description { get; set; } + public string doTime { get; set; } + public List serviveAmountList { get; set; } + public string amount { get; set; } + public string rrn { get; set; } + public string type { get; set; } + } +} + + + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyRequest.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyRequest.cs new file mode 100644 index 00000000..29e423fe --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyRequest.cs @@ -0,0 +1,9 @@ +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraVerifyRequest + { + public string ip { get; set; } + public string token { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyResponse.cs b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyResponse.cs new file mode 100644 index 00000000..2c266fa6 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/Models/TaraVerifyResponse.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara.Internal.Models +{ + internal class TaraVerifyResponse + { + public string token { get; set; } + public string result { get; set; } + public string description { get; set; } + public string doTime { get; set; } + public List serviceAmountList { get; set; } + public string amount { get; set; } + public string rrn { get; set; } + public string type { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/Internal/TaraHelper.cs b/src/Parbad.Gateway/Tara/src/Internal/TaraHelper.cs new file mode 100644 index 00000000..a15530e4 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Internal/TaraHelper.cs @@ -0,0 +1,316 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Parbad.Abstraction; +using Parbad.Gateway.Tara; +using Parbad.Gateway.Tara.Internal.Models; +using Parbad.Http; +using Parbad.Internal; +using Parbad.Net; +using Parbad.Options; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace Parbad.Gateway.Tara.Internal +{ + internal static class TaraHelper + { + public const string AuthorizationHeader = "Authorization"; + public const string SuccessResult = "0"; + + #region Authentication + + public static TaraAuthenticateRequest CreateAuthenticateRequest(TaraGatewayAccount account) + { + return new TaraAuthenticateRequest + { + username = account.Username, + password = account.Password + }; + } + + public static async Task AuthenticateAsync( + HttpClient httpClient, + TaraGatewayOptions options, + TaraGatewayAccount account, + CancellationToken cancellationToken = default) + { + var request = CreateAuthenticateRequest(account); + + // Use staging URL if account is in test mode + var authUrl = TaraGatewayOptions.GetEnvironmentUrl(options.AuthenticationUrl, account.IsTest); + + TaraGatewayDiagnostics.LogRequest("Authentication", authUrl, JsonConvert.SerializeObject(request)); + + // Clear any existing headers and set fresh ones + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Accept.Clear(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + // Use Parbad's standard PostJsonAsync extension method + var response = await httpClient.PostJsonAsync(authUrl, request, cancellationToken) + .ConfigureAwaitFalse(); + + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwaitFalse(); + + TaraGatewayDiagnostics.LogResponse("Authentication", (int)response.StatusCode, responseContent); + + if (!response.IsSuccessStatusCode) + { + throw new Exception($"Authentication HTTP error {response.StatusCode}: {responseContent}"); + } + + var authResponse = JsonConvert.DeserializeObject(responseContent); + + if (authResponse == null) + { + throw new Exception($"Authentication failed: Unable to parse response: {responseContent}"); + } + + if (authResponse.result != SuccessResult) + { + throw new Exception($"Authentication failed (code: {authResponse.result}): {authResponse.description}"); + } + + if (string.IsNullOrEmpty(authResponse.accessToken)) + { + throw new Exception("Authentication succeeded but no access token was returned"); + } + + return authResponse.accessToken; + } + + #endregion + + #region Request + + public static TaraGetTokenRequest CreateGetTokenRequest(TaraGatewayAccount account, Invoice invoice) + { + var additionalData = invoice.GetTaraRequestAdditionalData(); + + // If no additional data is provided, use default service amount with the invoice amount + var serviceAmountList = additionalData?.ServiceAmountList?.Count > 0 + ? additionalData.ServiceAmountList + : new List + { + new TaraServiceAmount { serviceId = 1, amount = (long)invoice.Amount } + }; + + var invoiceItemList = additionalData?.InvoiceItemList ?? new List(); + var mobile = additionalData?.Mobile ?? ""; + var additionalDataString = additionalData?.AdditionalData ?? ""; + var vat = additionalData?.Vat ?? 0; + + return new TaraGetTokenRequest + { + ip = account.Ip, + serviceAmountList = serviceAmountList, + taraInvoiceItemList = invoiceItemList, + additionalData = additionalDataString, + callBackUrl = invoice.CallbackUrl, + amount = invoice.Amount.ToString(), + mobile = mobile, + orderId = invoice.TrackingNumber, + vat = vat + }; + } + + public static async Task CreateRequestResult( + HttpClient httpClient, + HttpContext httpContext, + TaraGatewayOptions options, + TaraGatewayAccount account, + Invoice invoice, + MessagesOptions messagesOptions, + CancellationToken cancellationToken = default) + { + try + { + // Step 1: Authenticate to get access token + var accessToken = await AuthenticateAsync(httpClient, options, account, cancellationToken) + .ConfigureAwaitFalse(); + + // Step 2: Get payment token + var getTokenRequest = CreateGetTokenRequest(account, invoice); + + // Clear headers and set authorization + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Accept.Clear(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + + // Use staging URL if account is in test mode + var getTokenUrl = TaraGatewayOptions.GetEnvironmentUrl(options.GetTokenUrl, account.IsTest); + + TaraGatewayDiagnostics.LogRequest("GetToken", getTokenUrl, JsonConvert.SerializeObject(getTokenRequest)); + + var response = await httpClient.PostJsonAsync(getTokenUrl, getTokenRequest, cancellationToken) + .ConfigureAwaitFalse(); + + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwaitFalse(); + + TaraGatewayDiagnostics.LogResponse("GetToken", (int)response.StatusCode, responseContent); + + if (!response.IsSuccessStatusCode) + { + return PaymentRequestResult.Failed(responseContent, account.Name); + } + + var tokenResponse = JsonConvert.DeserializeObject(responseContent); + + if (tokenResponse.result != SuccessResult) + { + var message = TaraGatewayResultTranslator.Translate(tokenResponse.result, messagesOptions); + return PaymentRequestResult.Failed(message, account.Name); + } + + // Step 3: Create form data to redirect to payment page + var form = new Dictionary + { + { "username", account.Username }, + { "token", tokenResponse.token } + }; + + // Use staging payment URL if account is in test mode + var paymentUrl = TaraGatewayOptions.GetEnvironmentUrl(options.PaymentUrl, account.IsTest); + + return PaymentRequestResult.SucceedWithPost(account.Name, httpContext, paymentUrl, form); + } + catch (Exception exception) + { + return PaymentRequestResult.Failed(exception.Message, account.Name); + } + } + + #endregion + + #region Callback + + public static async Task CreateCallbackResultAsync( + HttpRequest httpRequest, + InvoiceContext context, + MessagesOptions messagesOptions, + CancellationToken cancellationToken = default) + { + var resultParam = await httpRequest.TryGetParamAsync("result", cancellationToken).ConfigureAwaitFalse(); + var descParam = await httpRequest.TryGetParamAsync("desc", cancellationToken).ConfigureAwaitFalse(); + var tokenParam = await httpRequest.TryGetParamAsync("token", cancellationToken).ConfigureAwaitFalse(); + var channelRefNumberParam = await httpRequest.TryGetParamAsync("channelRefNumber", cancellationToken).ConfigureAwaitFalse(); + var additionalDataParam = await httpRequest.TryGetParamAsync("additionalData", cancellationToken).ConfigureAwaitFalse(); + var orderIdParam = await httpRequest.TryGetParamAsync("orderId", cancellationToken).ConfigureAwaitFalse(); + + var result = resultParam.Value.ToString(); + var desc = descParam.Value.ToString(); + var token = tokenParam.Value.ToString(); + var channelRefNumber = channelRefNumberParam.Value.ToString(); + var additionalData = additionalDataParam.Value.ToString(); + var orderId = orderIdParam.Value.ToString(); + + var isSucceed = !result.IsNullOrEmpty() && result.Equals(SuccessResult, StringComparison.OrdinalIgnoreCase); + + var message = isSucceed + ? messagesOptions.PaymentSucceed + : TaraGatewayResultTranslator.Translate(result, messagesOptions); + + return new TaraCallbackResult + { + IsSucceed = isSucceed, + Token = token, + Result = result, + Description = desc, + ChannelRefNumber = channelRefNumber, + AdditionalData = additionalData, + OrderId = orderId.IsNullOrEmpty() ? 0 : long.Parse(orderId), + Message = message + }; + } + + #endregion + + #region Verify + + public static TaraVerifyRequest CreateVerifyRequest(string ip, string token) + { + return new TaraVerifyRequest + { + ip = ip, + token = token + }; + } + + public static async Task CreateVerifyResult( + HttpClient httpClient, + TaraGatewayOptions options, + TaraGatewayAccount account, + TaraCallbackResult callbackResult, + MessagesOptions messagesOptions, + CancellationToken cancellationToken = default) + { + try + { + // Authenticate to get access token + var accessToken = await AuthenticateAsync(httpClient, options, account, cancellationToken) + .ConfigureAwaitFalse(); + + // Create verify request + var verifyRequest = CreateVerifyRequest(account.Ip, callbackResult.Token); + + // Clear headers and set authorization + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Accept.Clear(); + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + + // Use staging URL if account is in test mode + var verifyUrl = TaraGatewayOptions.GetEnvironmentUrl(options.VerifyUrl, account.IsTest); + + TaraGatewayDiagnostics.LogRequest("Verify", verifyUrl, JsonConvert.SerializeObject(verifyRequest)); + + var response = await httpClient.PostJsonAsync(verifyUrl, verifyRequest, cancellationToken) + .ConfigureAwaitFalse(); + + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwaitFalse(); + + TaraGatewayDiagnostics.LogResponse("Verify", (int)response.StatusCode, responseContent); + + if (!response.IsSuccessStatusCode) + { + return PaymentVerifyResult.Failed(responseContent); + } + + var verifyResponse = JsonConvert.DeserializeObject(responseContent); + + var isSucceed = !verifyResponse.result.IsNullOrEmpty() && + verifyResponse.result.Equals(SuccessResult, StringComparison.OrdinalIgnoreCase); + + var message = isSucceed + ? messagesOptions.PaymentSucceed + : TaraGatewayResultTranslator.Translate(verifyResponse.result, messagesOptions); + + var result = new PaymentVerifyResult + { + Status = isSucceed ? PaymentVerifyResultStatus.Succeed : PaymentVerifyResultStatus.Failed, + TransactionCode = verifyResponse.rrn, + Message = message + }; + + result.DatabaseAdditionalData.Add("token", verifyResponse.token); + result.DatabaseAdditionalData.Add("type", verifyResponse.type); + + return result; + } + catch (Exception exception) + { + return PaymentVerifyResult.Failed(exception.Message); + } + } + + #endregion + } +} diff --git a/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.csproj b/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.csproj new file mode 100644 index 00000000..5a00e807 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.csproj @@ -0,0 +1,37 @@ + + + netstandard2.0;netstandard2.1;netcoreapp3.0;net5.0;net6.0;net7.0;net8.0,net9.0 + 1.0.0 + latest + Pooria Shariatzadeh + Copyright © Sina Soltani 2016 + https://github.com/Sina-Soltani/Parbad + icon.png + https://raw.githubusercontent.com/Sina-Soltani/Parbad/master/images/Parbad-128x128.png + https://github.com/Sina-Soltani/Parbad + LGPL-3.0-or-later + true + «تارا» اپلیکیشن خرید و پرداخت اعتباری است.با استفاده از تارا می‌توانید به صورت اعتباری، قسطی و نقدی -بدون پرداخت در لحظه- خرید حضوری یا آنلاین انجام دهید و هزینه را به صورت قسطی از ماه بعد پرداخت کنید. + true + https://github.com/Sina-Soltani/Parbad/releases/tag/v3.11.0 + Tara Gateway for Parbad project.For more information see: https://www.nuget.org/packages/Parbad/ + + + 1701;1702;1591 + Parbad.Gateway.Tara + + + Parbad.Gateway.Tara.xml + + + Parbad.Gateway.Tara.xml + + + + + + + + + + \ No newline at end of file diff --git a/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.xml b/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.xml new file mode 100644 index 00000000..0d5c6055 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/Parbad.Gateway.Tara.xml @@ -0,0 +1,315 @@ + + + + Parbad.Gateway.Tara + + + + + Represents an invoice item in Tara gateway request. + + + + + Item name + + + + + Item code + + + + + Item count/quantity + + + + + Unit type + + + + + Item fee/price + + + + + Item group + + + + + Item group title + + + + + Additional data for this item + + + + + Represents a service amount in Tara gateway request. + + + + + Service identifier + + + + + Amount for this service + + + + + + + + + + + + + + + + + Gets or sets the username for Tara gateway authentication. + + + + + Gets or sets the password for Tara gateway authentication. + + + + + Gets or sets the IP address to use for API calls. + + + + + Gets or sets whether to use the staging/test environment. + When true, uses https://stage-pay.tara360.ir/pay instead of production. + + + + + Sets the username for Tara gateway authentication. + + + Username + + + + Sets the password for Tara gateway authentication. + + + Password + + + + Sets the IP address for Tara gateway API calls. + + + IP Address + + + + Marks this account as a test account to use the staging environment. + This will automatically switch to https://stage-pay.tara360.ir/pay + + + Whether to use staging environment + + + + Adds Tara gateway to Parbad services. + + + + + + Configures the accounts for . + + + Configures the accounts. + + + + Configures the options for Tara Gateway. + + + Configuration + + + + Diagnostic helpers for troubleshooting Tara gateway integration. + + + + + Enables detailed logging for Tara gateway requests. + Logs will be written to Debug output. + + + + + The invoice will be sent to Tara gateway. + + + + + + Sets additional data for Tara gateway request. + + + + + + + Sets additional data for Tara gateway request using a configuration action. + + + Action to configure the additional data + + + + Adds a service amount to the Tara gateway request. + + + Service identifier + Amount for this service + + + + Adds multiple service amounts to the Tara gateway request. + + + List of service amounts + + + + Adds an invoice item to the Tara gateway request. + + + Invoice item to add + + + + Adds multiple invoice items to the Tara gateway request. + + + List of invoice items + + + + Sets the mobile number for the Tara gateway request. + + + Mobile number + + + + Sets the additional data string for the Tara gateway request. + + + Additional data string + + + + Sets the VAT amount for the Tara gateway request. + + + VAT amount + + + + Production base URL for Tara API. + + + + + Staging/Test base URL for Tara API. + + + + + Gets or sets the base URL for Tara API. + + + + + Gets or sets the authentication URL. + + + + + Gets or sets the get token URL. + + + + + Gets or sets the payment request URL. + + + + + Gets or sets the payment verification URL. + + + + + Gets or sets the payment inquiry URL. + + + + + Gets the appropriate URL based on test mode. + Replaces production URL with staging URL if isTest is true. + + The configured URL + Whether to use staging environment + Production or staging URL + + + + Additional data for Tara gateway payment request. + + + + + Initializes a new instance of . + + + + + List of service amounts for the payment request. + + + + + List of invoice items for the payment request. + + + + + Additional data string. + + + + + Mobile number of the customer. + + + + + VAT (Value Added Tax) amount. + + + + diff --git a/src/Parbad.Gateway/Tara/src/TaraGateway.cs b/src/Parbad.Gateway/Tara/src/TaraGateway.cs new file mode 100644 index 00000000..d36d048e --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGateway.cs @@ -0,0 +1,118 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Parbad.Abstraction; +using Parbad.Gateway.Tara.Internal; +using Parbad.GatewayBuilders; +using Parbad.Internal; +using Parbad.Net; +using Parbad.Options; +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Parbad.Gateway.Tara +{ + [Gateway(Name)] + public class TaraGateway : GatewayBase + { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly HttpClient _httpClient; + private readonly TaraGatewayOptions _gatewayOptions; + private readonly MessagesOptions _messageOptions; + + public const string Name = "Tara"; + + public TaraGateway( + IHttpContextAccessor httpContextAccessor, + IHttpClientFactory httpClientFactory, + IGatewayAccountProvider accountProvider, + IOptions gatewayOptions, + IOptions messageOptions) : base(accountProvider) + { + _httpContextAccessor = httpContextAccessor; + _httpClient = httpClientFactory.CreateClient(this); + _gatewayOptions = gatewayOptions.Value; + _messageOptions = messageOptions.Value; + } + + /// + public override async Task RequestAsync(Invoice invoice, CancellationToken cancellationToken = default) + { + if (invoice == null) throw new ArgumentNullException(nameof(invoice)); + + var account = await GetAccountAsync(invoice).ConfigureAwaitFalse(); + + return await TaraHelper.CreateRequestResult( + _httpClient, + _httpContextAccessor.HttpContext, + _gatewayOptions, + account, + invoice, + _messageOptions, + cancellationToken).ConfigureAwaitFalse(); + } + + /// + public override async Task FetchAsync(InvoiceContext context, CancellationToken cancellationToken = default) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + + var callbackResult = await TaraHelper.CreateCallbackResultAsync( + _httpContextAccessor.HttpContext.Request, + context, + _messageOptions, + cancellationToken).ConfigureAwaitFalse(); + + IPaymentFetchResult result; + + if (callbackResult.IsSucceed) + { + result = PaymentFetchResult.ReadyForVerifying(); + } + else + { + result = PaymentFetchResult.Failed(callbackResult.Message); + } + + return result; + } + + /// + public override async Task VerifyAsync(InvoiceContext context, CancellationToken cancellationToken = default) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + + var callbackResult = await TaraHelper.CreateCallbackResultAsync( + _httpContextAccessor.HttpContext.Request, + context, + _messageOptions, + cancellationToken).ConfigureAwaitFalse(); + + if (!callbackResult.IsSucceed) + { + return PaymentVerifyResult.Failed(callbackResult.Message); + } + + var account = await GetAccountAsync(context.Payment).ConfigureAwaitFalse(); + + return await TaraHelper.CreateVerifyResult( + _httpClient, + _gatewayOptions, + account, + callbackResult, + _messageOptions, + cancellationToken).ConfigureAwaitFalse(); + } + + /// + public override Task RefundAsync(InvoiceContext context, Money amount, CancellationToken cancellationToken = default) + { + // Tara gateway doesn't support refund operation according to the documentation + return Task.FromResult(PaymentRefundResult.Failed("Refund operation is not supported by Tara gateway.")); + } + } +} diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayAccount.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayAccount.cs new file mode 100644 index 00000000..dfc2edfa --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayAccount.cs @@ -0,0 +1,32 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Parbad.Abstraction; + +namespace Parbad.Gateway.Tara +{ + public class TaraGatewayAccount : GatewayAccount + { + /// + /// Gets or sets the username for Tara gateway authentication. + /// + public string Username { get; set; } + + /// + /// Gets or sets the password for Tara gateway authentication. + /// + public string Password { get; set; } + + /// + /// Gets or sets the IP address to use for API calls. + /// + public string Ip { get; set; } + + /// + /// Gets or sets whether to use the staging/test environment. + /// When true, uses https://stage-pay.tara360.ir/pay instead of production. + /// + public bool IsTest { get; set; } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayAccountExtensions.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayAccountExtensions.cs new file mode 100644 index 00000000..c9333408 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayAccountExtensions.cs @@ -0,0 +1,70 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Parbad.Builder; +using Parbad.GatewayBuilders; +using System; + +namespace Parbad.Gateway.Tara +{ + public static class TaraGatewayAccountExtensions + { + /// + /// Sets the username for Tara gateway authentication. + /// + /// + /// Username + public static IGatewayAccountBuilder SetUsername( + this IGatewayAccountBuilder builder, + string username) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.AddInMemory(account => account.Username = username); + } + + /// + /// Sets the password for Tara gateway authentication. + /// + /// + /// Password + public static IGatewayAccountBuilder SetPassword( + this IGatewayAccountBuilder builder, + string password) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.AddInMemory(account => account.Password = password); + } + + /// + /// Sets the IP address for Tara gateway API calls. + /// + /// + /// IP Address + public static IGatewayAccountBuilder SetIp( + this IGatewayAccountBuilder builder, + string ip) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.AddInMemory(account => account.Ip = ip); + } + + /// + /// Marks this account as a test account to use the staging environment. + /// This will automatically switch to https://stage-pay.tara360.ir/pay + /// + /// + /// Whether to use staging environment + public static IGatewayAccountBuilder UseTestMode( + this IGatewayAccountBuilder builder, + bool isTest = true) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.AddInMemory(account => account.IsTest = isTest); + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayBuilderExtensions.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayBuilderExtensions.cs new file mode 100644 index 00000000..1b810f5d --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayBuilderExtensions.cs @@ -0,0 +1,55 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.DependencyInjection; +using Parbad.GatewayBuilders; + +namespace Parbad.Gateway.Tara +{ + public static class TaraGatewayBuilderExtensions + { + /// + /// Adds Tara gateway to Parbad services. + /// + /// + public static IGatewayConfigurationBuilder AddTara(this IGatewayBuilder builder) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder + .AddGateway() + .WithHttpClient(clientBuilder => { }) + .WithOptions(options => { }); + } + + /// + /// Configures the accounts for . + /// + /// + /// Configures the accounts. + public static IGatewayConfigurationBuilder WithAccounts( + this IGatewayConfigurationBuilder builder, + Action> configureAccounts) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.WithAccounts(configureAccounts); + } + + /// + /// Configures the options for Tara Gateway. + /// + /// + /// Configuration + public static IGatewayConfigurationBuilder WithOptions( + this IGatewayConfigurationBuilder builder, + Action configureOptions) + { + builder.Services.Configure(configureOptions); + + return builder; + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayDiagnostics.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayDiagnostics.cs new file mode 100644 index 00000000..b9197df8 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayDiagnostics.cs @@ -0,0 +1,52 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; + +namespace Parbad.Gateway.Tara +{ + /// + /// Diagnostic helpers for troubleshooting Tara gateway integration. + /// + public static class TaraGatewayDiagnostics + { + /// + /// Enables detailed logging for Tara gateway requests. + /// Logs will be written to Debug output. + /// + public static bool EnableDetailedLogging { get; set; } + + internal static void Log(string message) + { + if (EnableDetailedLogging) + { + Debug.WriteLine($"[Tara Gateway] {DateTime.Now:HH:mm:ss.fff} - {message}"); + Console.WriteLine($"[Tara Gateway] {DateTime.Now:HH:mm:ss.fff} - {message}"); + } + } + + internal static void LogRequest(string endpoint, string url, string jsonPayload) + { + if (EnableDetailedLogging) + { + Log($"=== {endpoint} REQUEST ==="); + Log($"URL: {url}"); + Log($"Payload: {jsonPayload}"); + Log("========================="); + } + } + + internal static void LogResponse(string endpoint, int statusCode, string response) + { + if (EnableDetailedLogging) + { + Log($"=== {endpoint} RESPONSE ==="); + Log($"Status Code: {statusCode}"); + Log($"Response: {response}"); + Log("=========================="); + } + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayInvoiceBuilderExtensions.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayInvoiceBuilderExtensions.cs new file mode 100644 index 00000000..fcdd8d7f --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayInvoiceBuilderExtensions.cs @@ -0,0 +1,198 @@ +using Parbad.Abstraction; +using Parbad.Gateway.Tara.Internal.Models; +using Parbad.InvoiceBuilder; +using System; +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara +{ + public static class TaraGatewayInvoiceBuilderExtensions + { + private const string RequestAdditionalDataKey = "TaraRequestAdditionalData"; + + /// + /// The invoice will be sent to Tara gateway. + /// + /// + public static IInvoiceBuilder UseTara(this IInvoiceBuilder builder) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.SetGateway(TaraGateway.Name); + } + + /// + /// Sets additional data for Tara gateway request. + /// + /// + /// + public static IInvoiceBuilder SetTaraData(this IInvoiceBuilder builder, TaraRequestAdditionalData additionalData) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (additionalData == null) throw new ArgumentNullException(nameof(additionalData)); + + return builder.AddOrUpdateProperty(RequestAdditionalDataKey, additionalData); + } + + /// + /// Sets additional data for Tara gateway request using a configuration action. + /// + /// + /// Action to configure the additional data + public static IInvoiceBuilder SetTaraData(this IInvoiceBuilder builder, Action configureData) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (configureData == null) throw new ArgumentNullException(nameof(configureData)); + + var data = new TaraRequestAdditionalData(); + configureData(data); + + return builder.AddOrUpdateProperty(RequestAdditionalDataKey, data); + } + + /// + /// Adds a service amount to the Tara gateway request. + /// + /// + /// Service identifier + /// Amount for this service + public static IInvoiceBuilder AddTaraServiceAmount(this IInvoiceBuilder builder, long serviceId, long amount) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.ServiceAmountList.Add(new TaraServiceAmount + { + serviceId = serviceId, + amount = amount + }); + }); + } + + /// + /// Adds multiple service amounts to the Tara gateway request. + /// + /// + /// List of service amounts + public static IInvoiceBuilder AddTaraServiceAmounts(this IInvoiceBuilder builder, IEnumerable serviceAmounts) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (serviceAmounts == null) throw new ArgumentNullException(nameof(serviceAmounts)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.ServiceAmountList.AddRange(serviceAmounts); + }); + } + + /// + /// Adds an invoice item to the Tara gateway request. + /// + /// + /// Invoice item to add + public static IInvoiceBuilder AddTaraInvoiceItem(this IInvoiceBuilder builder, TaraInvoiceItem item) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (item == null) throw new ArgumentNullException(nameof(item)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.InvoiceItemList.Add(item); + }); + } + + /// + /// Adds multiple invoice items to the Tara gateway request. + /// + /// + /// List of invoice items + public static IInvoiceBuilder AddTaraInvoiceItems(this IInvoiceBuilder builder, IEnumerable items) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + if (items == null) throw new ArgumentNullException(nameof(items)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.InvoiceItemList.AddRange(items); + }); + } + + /// + /// Sets the mobile number for the Tara gateway request. + /// + /// + /// Mobile number + public static IInvoiceBuilder SetTaraMobile(this IInvoiceBuilder builder, string mobile) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.Mobile = mobile; + }); + } + + /// + /// Sets the additional data string for the Tara gateway request. + /// + /// + /// Additional data string + public static IInvoiceBuilder SetTaraAdditionalData(this IInvoiceBuilder builder, string additionalData) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.AdditionalData = additionalData; + }); + } + + /// + /// Sets the VAT amount for the Tara gateway request. + /// + /// + /// VAT amount + public static IInvoiceBuilder SetTaraVat(this IInvoiceBuilder builder, long vat) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + + return builder.ChangeProperties(properties => + { + var data = GetOrCreateTaraData(properties); + data.Vat = vat; + }); + } + + internal static TaraRequestAdditionalData GetTaraRequestAdditionalData(this Invoice invoice) + { + if (invoice == null) throw new ArgumentNullException(nameof(invoice)); + + if (invoice.Properties.TryGetValue(RequestAdditionalDataKey, out var additionalData)) + { + return (TaraRequestAdditionalData)additionalData; + } + + return null; + } + + private static TaraRequestAdditionalData GetOrCreateTaraData(IDictionary properties) + { + if (properties.TryGetValue(RequestAdditionalDataKey, out var existingData)) + { + return (TaraRequestAdditionalData)existingData; + } + + var data = new TaraRequestAdditionalData(); + properties[RequestAdditionalDataKey] = data; + return data; + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayOptions.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayOptions.cs new file mode 100644 index 00000000..509ea419 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayOptions.cs @@ -0,0 +1,60 @@ +namespace Parbad.Gateway.Tara +{ + public class TaraGatewayOptions + { + /// + /// Production base URL for Tara API. + /// + public const string ProductionBaseUrl = "https://pay.tara360.ir/pay"; + + /// + /// Staging/Test base URL for Tara API. + /// + public const string StagingBaseUrl = "https://stage-pay.tara360.ir/pay"; + + /// + /// Gets or sets the base URL for Tara API. + /// + public string BaseUrl { get; set; } = ProductionBaseUrl; + + /// + /// Gets or sets the authentication URL. + /// + public string AuthenticationUrl { get; set; } = $"{ProductionBaseUrl}/api/v2/authenticate"; + + /// + /// Gets or sets the get token URL. + /// + public string GetTokenUrl { get; set; } = $"{ProductionBaseUrl}/api/getToken"; + + /// + /// Gets or sets the payment request URL. + /// + public string PaymentUrl { get; set; } = $"{ProductionBaseUrl}/api/ipgPurchase"; + + /// + /// Gets or sets the payment verification URL. + /// + public string VerifyUrl { get; set; } = $"{ProductionBaseUrl}/api/purchaseVerify"; + + /// + /// Gets or sets the payment inquiry URL. + /// + public string InquiryUrl { get; set; } = $"{ProductionBaseUrl}/api/purchaseInquiry"; + + /// + /// Gets the appropriate URL based on test mode. + /// Replaces production URL with staging URL if isTest is true. + /// + /// The configured URL + /// Whether to use staging environment + /// Production or staging URL + public static string GetEnvironmentUrl(string url, bool isTest) + { + return isTest + ? url.Replace(ProductionBaseUrl, StagingBaseUrl) + : url; + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraGatewayResultTranslator.cs b/src/Parbad.Gateway/Tara/src/TaraGatewayResultTranslator.cs new file mode 100644 index 00000000..14e82e06 --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraGatewayResultTranslator.cs @@ -0,0 +1,43 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Parbad.Options; + +namespace Parbad.Gateway.Tara +{ + internal static class TaraGatewayResultTranslator + { + public static string Translate(string result, MessagesOptions messagesOptions) + { + return result switch + { + "0" => "موفق", + "1" => "درخواست از IP غیر مجاز", + "2" => "نام کاربری یا رمز عبور نامعتبر است", + "3" => "کاربر دسترسی ندارد", + "4" => "پذیرنده یافت نشد", + "5" => "هدایت به صفحه پرداخت", + "6" => "تراکنش یافت نشد", + "7" => "شماره سرویس نامعتبر است", + "8" => "توکن تکراری است", + "9" => "مبالغ یکسان نیست", + "10" => "کانال یافت نشد", + "11" => "مبلغ بیشتر از حد مجاز", + "12" => "مبلغ کمتر از حد مجاز", + "13" => "مبلغ نمی تواند خالی باشد", + "14" => "IP نمی تواد خالی باشد", + "15" => "مبلغ نامعتبر می باشد", + "16" => "لیست مبالغ سرویس خالی میباشد", + "17" => "شناسه سرویس نامعتبر", + "18" => "فرمت آدرس برگشتی صحیح نمی‌باشد", + "19" => "خطای عمومی", + "20" => "توکن یافت نشد", + "21" => "شماره پیگیری به پذیرنده تعلق ندارد", + "22" => "خطای عمومی", + "23" => "تراکنش اصلی موفق نبوده است", + _ => messagesOptions.PaymentFailed + }; + } + } +} + diff --git a/src/Parbad.Gateway/Tara/src/TaraRequestAdditionalData.cs b/src/Parbad.Gateway/Tara/src/TaraRequestAdditionalData.cs new file mode 100644 index 00000000..511814fb --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/TaraRequestAdditionalData.cs @@ -0,0 +1,52 @@ +// Copyright (c) Parbad. All rights reserved. +// Licensed under the GNU GENERAL PUBLIC License, Version 3.0. See License.txt in the project root for license information. + +using Parbad.Gateway.Tara.Internal.Models; +using System.Collections.Generic; + +namespace Parbad.Gateway.Tara +{ + /// + /// Additional data for Tara gateway payment request. + /// + public class TaraRequestAdditionalData + { + /// + /// Initializes a new instance of . + /// + public TaraRequestAdditionalData() + { + ServiceAmountList = new List(); + InvoiceItemList = new List(); + } + + /// + /// List of service amounts for the payment request. + /// + public List ServiceAmountList { get; set; } + + /// + /// List of invoice items for the payment request. + /// + public List InvoiceItemList { get; set; } + + /// + /// Additional data string. + /// + public string AdditionalData { get; set; } + + /// + /// Mobile number of the customer. + /// + public string Mobile { get; set; } + + /// + /// VAT (Value Added Tax) amount. + /// + public long Vat { get; set; } + } +} + + + + diff --git a/src/Parbad.Gateway/Tara/src/icon.png b/src/Parbad.Gateway/Tara/src/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..49be77ad544051430e27ccaf026e1993521ec7bd GIT binary patch literal 14067 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 + { + gateways.AddTara() + .WithAccounts(accounts => + { + accounts.AddInMemory(account => + { + account.Username = "your-username"; + account.Password = "your-password"; + account.Ip = "your-ip-address"; + account.IsTest = false; // Set to true for staging environment + }); + }); + }); +``` + +### Test/Staging Environment + +Tara provides a staging environment for testing. To use it: + +```csharp +accounts.AddInMemory(account => +{ + account.Username = "test-username"; + account.Password = "test-password"; + account.Ip = "your-ip"; + account.IsTest = true; // Automatically uses stage-pay.tara360.ir +}); + +// OR use the extension method: +accounts.AddInMemory(account => +{ + account.Username = "test-username"; + account.Password = "test-password"; + account.Ip = "your-ip"; +}) +.UseTestMode(); // Automatically switches to staging +``` + +When `IsTest = true`, all API calls automatically use: +- **Production**: `https://pay.tara360.ir/pay` +- **Staging**: `https://stage-pay.tara360.ir/pay` + +### Basic Usage +Use Tara gateway in your payment requests: + +```csharp +var result = await _onlinePayment.RequestAsync(invoice => +{ + invoice + .SetAmount(10000) + .SetCallbackUrl("https://your-site.com/callback") + .UseTara(); +}); +``` + +### Advanced Usage - Tara-Specific Features + +Tara gateway supports additional features like service amounts, invoice items, mobile number, VAT, and additional data. + +#### Option 1: Using SetTaraData with configuration action +```csharp +var result = await _onlinePayment.RequestAsync(invoice => +{ + invoice + .SetAmount(10000) + .SetCallbackUrl("https://your-site.com/callback") + .SetTaraData(data => + { + data.ServiceAmountList.Add(new TaraServiceAmount + { + serviceId = 1, + amount = 10000 + }); + + data.InvoiceItemList.Add(new TaraInvoiceItem + { + name = "Product 1", + code = "P001", + count = 1, + unit = 1, + fee = 10000, + group = "Electronics", + groupTitle = "Electronic Items" + }); + + data.Mobile = "09123456789"; + data.Vat = 900; + data.AdditionalData = "Order #12345"; + }) + .UseTara(); +}); +``` + +#### Option 2: Using individual extension methods +```csharp +var result = await _onlinePayment.RequestAsync(invoice => +{ + invoice + .SetAmount(10000) + .SetCallbackUrl("https://your-site.com/callback") + .AddTaraServiceAmount(serviceId: 1, amount: 5000) + .AddTaraServiceAmount(serviceId: 2, amount: 5000) + .AddTaraInvoiceItem(new TaraInvoiceItem + { + name = "Product 1", + code = "P001", + count = 2, + fee = 5000 + }) + .SetTaraMobile("09123456789") + .SetTaraVat(900) + .SetTaraAdditionalData("Order #12345") + .UseTara(); +}); +``` + +#### Option 3: Adding multiple items at once +```csharp +var result = await _onlinePayment.RequestAsync(invoice => +{ + var serviceAmounts = new List + { + new TaraServiceAmount { serviceId = 1, amount = 7000 }, + new TaraServiceAmount { serviceId = 2, amount = 3000 } + }; + + var items = new List + { + new TaraInvoiceItem + { + name = "Product 1", + code = "P001", + count = 1, + fee = 7000 + }, + new TaraInvoiceItem + { + name = "Product 2", + code = "P002", + count = 1, + fee = 3000 + } + }; + + invoice + .SetAmount(10000) + .SetCallbackUrl("https://your-site.com/callback") + .AddTaraServiceAmounts(serviceAmounts) + .AddTaraInvoiceItems(items) + .UseTara(); +}); +``` + +### Available Extension Methods + +- **SetTaraData(TaraRequestAdditionalData)** - Set all Tara data at once +- **SetTaraData(Action)** - Configure with action +- **AddTaraServiceAmount(long serviceId, long amount)** - Add single service amount +- **AddTaraServiceAmounts(IEnumerable)** - Add multiple service amounts +- **AddTaraInvoiceItem(TaraInvoiceItem)** - Add single invoice item +- **AddTaraInvoiceItems(IEnumerable)** - Add multiple invoice items +- **SetTaraMobile(string)** - Set customer mobile number +- **SetTaraAdditionalData(string)** - Set additional data string +- **SetTaraVat(long)** - Set VAT amount + +### Note +If you don't provide service amounts, the gateway will automatically create a default service amount with serviceId = 1 and the invoice amount. + +For more information about Parbad, visit: https://github.com/Sina-Soltani/Parbad +