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 options = new Dictionary { { "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; } /// /// Fetch complete payment details from Razorpay including card details /// /// Razorpay Payment ID /// Complete payment details with card information public async Task 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(); } } /// /// Fetch order details from Razorpay /// /// Razorpay Order ID /// Order details 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 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(decrypedData); if (vm == null) { return new PaymentDetailsVM(); } else { return vm; } } public async Task> GetPaymentDetailsListFromDataBase(List paymentDetailsIds) { var projectDetails = await _context.PaymentDetails.Where(pd => paymentDetailsIds.Contains(pd.Id)).ToListAsync(); List response = new List(); 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(decrypedData); if (vm != null) { response.Add(vm); } } return response; } /// /// Extract card details from payment /// private CardDetails? ExtractCardDetails(Payment payment) { try { var cardObj = payment.Attributes["card"]; if (cardObj == null) return null; string json = JsonConvert.SerializeObject(cardObj); Dictionary? cardDict = JsonConvert.DeserializeObject>(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; } } /// /// Extract bank details from payment /// 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; } } /// /// Extract UPI details from payment /// 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; } } /// /// Extract wallet details from payment /// 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; } } /// /// Extract customer name from payment object /// 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; 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? dictionary = JsonConvert.DeserializeObject>(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"; } } } }