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 00000000..49be77ad Binary files /dev/null and b/src/Parbad.Gateway/Tara/src/icon.png differ diff --git a/src/Parbad.Gateway/Tara/src/readme.txt b/src/Parbad.Gateway/Tara/src/readme.txt new file mode 100644 index 00000000..3966eeda --- /dev/null +++ b/src/Parbad.Gateway/Tara/src/readme.txt @@ -0,0 +1,189 @@ +Tara Gateway for Parbad +======================= + +This package provides integration with Tara payment gateway (https://pay.tara360.ir) for the Parbad payment library. + +## How to use + +### Installation +Install the package via NuGet: +``` +dotnet add package Parbad.Gateway.Tara +``` + +### Configuration +Add Tara gateway to your services: + +```csharp +services.AddParbad() + .ConfigureGateways(gateways => + { + 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 +