420 lines
17 KiB
C#

using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.PaymentGetway;
using Marco.Pms.Model.ViewModels.PaymentGetway;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Service;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Razorpay.Api;
using System.Security.Cryptography;
using System.Text;
namespace Marco.Pms.Services.Service
{
public class RazorpayService : IRazorpayService
{
private readonly ApplicationDbContext _context;
private readonly RazorpayClient _razorpayClient;
private readonly ILoggingService _logger;
private readonly IConfiguration _configuration;
private readonly IAesEncryption _aesEncryption;
private readonly string key;
private readonly string secret;
private readonly byte[] encryptionKey;
public RazorpayService(ApplicationDbContext context, IConfiguration configuration, ILoggingService logger, IAesEncryption aesEncryption)
{
_context = context;
_logger = logger;
_configuration = configuration;
_aesEncryption = aesEncryption;
key = configuration["Razorpay:Key"] ?? "";
secret = configuration["Razorpay:Secret"] ?? "";
_razorpayClient = new RazorpayClient(key, secret);
string stringKey = configuration["Encryption:PaymentKey"] ?? "";
encryptionKey = Convert.FromBase64String(stringKey);
}
public CreateOrderVM CreateOrder(double amount, Employee loggedInEmployee, Guid tenantId)
{
RazorpayClient client = new RazorpayClient(key, secret);
var receipt = $"rec_{Guid.NewGuid()}";
var length = receipt.Length;
Dictionary<string, object> options = new Dictionary<string, object>
{
{ "amount", amount * 100 }, // amount in paise
{ "currency", "INR" },
{ "receipt", receipt},
{ "payment_capture", 1 }
};
Order order = client.Order.Create(options);
var response = new CreateOrderVM
{
OrderId = order["id"],
Key = key
};
return response;
}
public string GetExpectedSignature(string payload)
{
string expectedSignature;
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
expectedSignature = Convert.ToHexString(hash).ToLower();
}
return expectedSignature;
}
/// <summary>
/// Fetch complete payment details from Razorpay including card details
/// </summary>
/// <param name="paymentId">Razorpay Payment ID</param>
/// <returns>Complete payment details with card information</returns>
public async Task<PaymentDetailsVM> GetPaymentDetails(string paymentId)
{
try
{
_logger.LogInfo("Fetching payment details from Razorpay for PaymentId: {PaymentId}", paymentId);
// Fetch payment details from Razorpay
Payment payment = _razorpayClient.Payment.Fetch(paymentId);
// Extract customer name from notes or fetch from customer API
string customerName = ExtractCustomerName(payment);
Guid paymentDetailsId = Guid.NewGuid();
// Map to custom model with all details
var paymentDetails = new RazorpayPaymentDetails
{
PaymentId = payment.Attributes["id"]?.ToString(),
OrderId = payment.Attributes["order_id"]?.ToString(),
Amount = Convert.ToDecimal(payment.Attributes["amount"]) / 100, // Convert from paise to rupees
Currency = payment.Attributes["currency"]?.ToString(),
Status = payment.Attributes["status"]?.ToString(),
Method = payment.Attributes["method"]?.ToString(),
Email = payment.Attributes["email"]?.ToString(),
Contact = payment.Attributes["contact"]?.ToString(),
Description = payment.Attributes["description"]?.ToString(),
CustomerName = customerName,
CreatedAt = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(payment.Attributes["created_at"])).DateTime,
// Card details (if payment method is card)
CardDetails = payment.Attributes["method"]?.ToString() == "card"
? ExtractCardDetails(payment)
: null,
// Bank details (if payment method is netbanking)
BankDetails = payment.Attributes["method"]?.ToString() == "netbanking"
? ExtractBankDetails(payment)
: null,
// UPI details (if payment method is upi)
UpiDetails = payment.Attributes["method"]?.ToString() == "upi"
? ExtractUpiDetails(payment)
: null,
// Wallet details (if payment method is wallet)
WalletDetails = payment.Attributes["method"]?.ToString() == "wallet"
? ExtractWalletDetails(payment)
: null,
// Additional details
Fee = payment.Attributes["fee"] != null
? Convert.ToDecimal(payment.Attributes["fee"]) / 100
: 0,
Tax = payment.Attributes["tax"] != null
? Convert.ToDecimal(payment.Attributes["tax"]) / 100
: 0,
ErrorCode = payment.Attributes["error_code"]?.ToString(),
ErrorDescription = payment.Attributes["error_description"]?.ToString(),
InternationalPayment = Convert.ToBoolean(payment.Attributes["international"] ?? false),
Captured = Convert.ToBoolean(payment.Attributes["captured"] ?? false)
};
var razorpayOrderDetails = GetOrderDetails(paymentDetails.OrderId ?? "");
var response = new PaymentDetailsVM
{
Id = paymentDetailsId,
RazorpayPaymentDetails = paymentDetails,
RazorpayOrderDetails = razorpayOrderDetails
};
string jsonString = JsonConvert.SerializeObject(response);
var data = _aesEncryption.Encrypt(jsonString, encryptionKey);
var paymentDetail = new PaymentDetail
{
Id = paymentDetailsId,
PaymentId = paymentDetails.PaymentId ?? "",
OrderId = paymentDetails.OrderId ?? "",
Status = paymentDetails.Status ?? "",
Method = paymentDetails.Method ?? "",
CreatedAt = DateTime.UtcNow,
EncryptedDetails = data.ciphertext,
Nonce = data.nonce,
Tag = data.tag
};
_context.PaymentDetails.Add(paymentDetail);
await _context.SaveChangesAsync();
_logger.LogInfo("Payment details fetched successfully from Razorpay for PaymentId: {PaymentId}", paymentId);
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching payment details from Razorpay for PaymentId: {PaymentId}", paymentId);
return new PaymentDetailsVM();
}
}
/// <summary>
/// Fetch order details from Razorpay
/// </summary>
/// <param name="orderId">Razorpay Order ID</param>
/// <returns>Order details</returns>
public RazorpayOrderDetails? GetOrderDetails(string orderId)
{
try
{
_logger.LogInfo("Fetching order details from Razorpay for OrderId: {OrderId}", orderId);
Order order = _razorpayClient.Order.Fetch(orderId);
var orderDetails = new RazorpayOrderDetails
{
OrderId = order.Attributes["id"]?.ToString(),
Amount = Convert.ToDecimal(order.Attributes["amount"]) / 100,
Currency = order.Attributes["currency"]?.ToString(),
Status = order.Attributes["status"]?.ToString(),
Receipt = order.Attributes["receipt"]?.ToString(),
CreatedAt = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(order.Attributes["created_at"])).DateTime,
AmountPaid = order.Attributes["amount_paid"] != null
? Convert.ToDecimal(order.Attributes["amount_paid"]) / 100
: 0,
AmountDue = order.Attributes["amount_due"] != null
? Convert.ToDecimal(order.Attributes["amount_due"]) / 100
: 0,
Attempts = Convert.ToInt32(order.Attributes["attempts"] ?? 0)
};
_logger.LogInfo("Order details fetched successfully from Razorpay for OrderId: {OrderId}", orderId);
return orderDetails;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching order details from Razorpay for OrderId: {OrderId}", orderId);
return null;
}
}
public async Task<PaymentDetailsVM> GetPaymentDetailsFromDataBase(Guid id)
{
var projectDetails = await _context.PaymentDetails.FirstOrDefaultAsync(pd => pd.Id == id);
if (projectDetails == null)
{
return new PaymentDetailsVM();
}
string decrypedData = _aesEncryption.Decrypt(projectDetails.EncryptedDetails ?? new byte[32], projectDetails.Nonce ?? new byte[12], projectDetails.Tag ?? new byte[16], encryptionKey);
// Deserialize JSON string to a Department object
PaymentDetailsVM? vm = JsonConvert.DeserializeObject<PaymentDetailsVM>(decrypedData);
if (vm == null)
{
return new PaymentDetailsVM();
}
else
{
return vm;
}
}
public async Task<List<PaymentDetailsVM>> GetPaymentDetailsListFromDataBase(List<Guid> paymentDetailsIds)
{
var projectDetails = await _context.PaymentDetails.Where(pd => paymentDetailsIds.Contains(pd.Id)).ToListAsync();
List<PaymentDetailsVM> response = new List<PaymentDetailsVM>();
foreach (var projectDetail in projectDetails)
{
string decrypedData = _aesEncryption.Decrypt(projectDetail.EncryptedDetails ?? new byte[32], projectDetail.Nonce ?? new byte[12], projectDetail.Tag ?? new byte[16], encryptionKey);
// Deserialize JSON string to a Department object
PaymentDetailsVM? vm = JsonConvert.DeserializeObject<PaymentDetailsVM>(decrypedData);
if (vm != null)
{
response.Add(vm);
}
}
return response;
}
/// <summary>
/// Extract card details from payment
/// </summary>
private CardDetails? ExtractCardDetails(Payment payment)
{
try
{
var cardObj = payment.Attributes["card"];
if (cardObj == null) return null;
string json = JsonConvert.SerializeObject(cardObj);
Dictionary<string, object>? cardDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
return new CardDetails
{
CardId = cardDict?["id"]?.ToString(),
Last4Digits = cardDict?["last4"]?.ToString(),
Network = cardDict?["network"]?.ToString(), // Visa, MasterCard, Amex, etc.
CardType = cardDict?["type"]?.ToString(), // credit, debit, prepaid
Issuer = cardDict?["issuer"]?.ToString(), // Bank name
International = Convert.ToBoolean(cardDict?["international"] ?? false),
Emi = Convert.ToBoolean(cardDict?["emi"] ?? false),
SubType = cardDict?["sub_type"]?.ToString() // consumer, business
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting card details");
return null;
}
}
/// <summary>
/// Extract bank details from payment
/// </summary>
private BankDetails? ExtractBankDetails(Payment payment)
{
try
{
return new BankDetails
{
Bank = payment.Attributes["bank"]?.ToString(),
BankCode = payment.Attributes["bank_code"]?.ToString()
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting bank details");
return null;
}
}
/// <summary>
/// Extract UPI details from payment
/// </summary>
private UpiDetails? ExtractUpiDetails(Payment payment)
{
try
{
var vpaObj = payment.Attributes["vpa"];
return new UpiDetails
{
Vpa = vpaObj?.ToString(), // UPI ID
Provider = payment.Attributes["provider"]?.ToString()
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting UPI details");
return null;
}
}
/// <summary>
/// Extract wallet details from payment
/// </summary>
private WalletDetails? ExtractWalletDetails(Payment payment)
{
try
{
return new WalletDetails
{
WalletName = payment.Attributes["wallet"]?.ToString() // paytm, phonepe, amazonpay, etc.
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting wallet details");
return null;
}
}
/// <summary>
/// Extract customer name from payment object
/// </summary>
private string ExtractCustomerName(Payment payment)
{
try
{
// Option 1: Get from notes (if you passed customer name during order creation)
var notesObj = payment.Attributes["notes"];
if (notesObj != null)
{
var notesDict = notesObj as Dictionary<string, object>;
if (notesDict != null && notesDict.ContainsKey("customer_name"))
{
return notesDict["customer_name"]?.ToString() ?? "CustomerName";
}
if (notesDict != null && notesDict.ContainsKey("name"))
{
return notesDict["name"]?.ToString() ?? "CustomerName";
}
}
// Option 2: Get from customer ID and fetch customer details
var customerId = payment.Attributes["customer_id"]?.ToString();
if (!string.IsNullOrEmpty(customerId))
{
try
{
Customer customer = _razorpayClient.Customer.Fetch(customerId);
return customer.Attributes["name"]?.ToString() ?? "CustomerName";
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching customer details for CustomerId: {CustomerId}", customerId);
}
}
// Option 3: Extract from card holder name (if available)
if (payment.Attributes["method"]?.ToString() == "card")
{
var cardObj = payment.Attributes["card"];
if (cardObj != null)
{
string json = JsonConvert.SerializeObject(cardObj);
Dictionary<string, object>? dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var cardName = dictionary?["name"]?.ToString();
if (!string.IsNullOrEmpty(cardName))
{
return cardName;
}
}
}
// Option 4: Use email as fallback
return payment.Attributes["email"]?.ToString() ?? "Customer";
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting customer name from payment");
return "Customer";
}
}
}
}