420 lines
17 KiB
C#
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";
|
|
}
|
|
}
|
|
}
|
|
}
|