From 1d54af7c00df077075ddbca59c35979a57446c17 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Fri, 24 Oct 2025 17:01:22 +0530 Subject: [PATCH] Added create order and verify payment API for razor pay --- Marco.Pms.Helpers/Marco.Pms.Helpers.csproj | 1 + .../Dtos/PaymentGetway/CreateOrderDto.cs | 7 ++ .../PaymentVerificationRequest.cs | 9 +++ .../ViewModels/PaymentGetway/CreateOrderVM.cs | 8 +++ .../Controllers/PaymentController.cs | 71 +++++++++++++++++++ Marco.Pms.Services/Helpers/PaymentHelper.cs | 57 +++++++++++++++ Marco.Pms.Services/Marco.Pms.Services.csproj | 1 + Marco.Pms.Services/Program.cs | 8 ++- .../appsettings.Development.json | 6 +- 9 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 Marco.Pms.Model/Dtos/PaymentGetway/CreateOrderDto.cs create mode 100644 Marco.Pms.Model/Dtos/PaymentGetway/PaymentVerificationRequest.cs create mode 100644 Marco.Pms.Model/ViewModels/PaymentGetway/CreateOrderVM.cs create mode 100644 Marco.Pms.Services/Controllers/PaymentController.cs create mode 100644 Marco.Pms.Services/Helpers/PaymentHelper.cs diff --git a/Marco.Pms.Helpers/Marco.Pms.Helpers.csproj b/Marco.Pms.Helpers/Marco.Pms.Helpers.csproj index e12ac6c..3c8937b 100644 --- a/Marco.Pms.Helpers/Marco.Pms.Helpers.csproj +++ b/Marco.Pms.Helpers/Marco.Pms.Helpers.csproj @@ -8,6 +8,7 @@ + diff --git a/Marco.Pms.Model/Dtos/PaymentGetway/CreateOrderDto.cs b/Marco.Pms.Model/Dtos/PaymentGetway/CreateOrderDto.cs new file mode 100644 index 0000000..50b8e2f --- /dev/null +++ b/Marco.Pms.Model/Dtos/PaymentGetway/CreateOrderDto.cs @@ -0,0 +1,7 @@ +namespace Marco.Pms.Model.Dtos.PaymentGetway +{ + public class CreateOrderDto + { + public double Amount { get; set; } + } +} diff --git a/Marco.Pms.Model/Dtos/PaymentGetway/PaymentVerificationRequest.cs b/Marco.Pms.Model/Dtos/PaymentGetway/PaymentVerificationRequest.cs new file mode 100644 index 0000000..f0bfe49 --- /dev/null +++ b/Marco.Pms.Model/Dtos/PaymentGetway/PaymentVerificationRequest.cs @@ -0,0 +1,9 @@ +namespace Marco.Pms.Model.Dtos.PaymentGetway +{ + public class PaymentVerificationRequest + { + public string? OrderId { get; set; } + public string? PaymentId { get; set; } + public string? Signature { get; set; } + } +} diff --git a/Marco.Pms.Model/ViewModels/PaymentGetway/CreateOrderVM.cs b/Marco.Pms.Model/ViewModels/PaymentGetway/CreateOrderVM.cs new file mode 100644 index 0000000..0b863e0 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/PaymentGetway/CreateOrderVM.cs @@ -0,0 +1,8 @@ +namespace Marco.Pms.Model.ViewModels.PaymentGetway +{ + public class CreateOrderVM + { + public string? OrderId { get; set; } + public string? Key { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/PaymentController.cs b/Marco.Pms.Services/Controllers/PaymentController.cs new file mode 100644 index 0000000..5418380 --- /dev/null +++ b/Marco.Pms.Services/Controllers/PaymentController.cs @@ -0,0 +1,71 @@ +using Marco.Pms.Model.Dtos.PaymentGetway; +using Marco.Pms.Model.Utilities; +using Marco.Pms.Services.Helpers; +using MarcoBMS.Services.Helpers; +using Microsoft.AspNetCore.Mvc; + +namespace Marco.Pms.Services.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PaymentController : ControllerBase + { + private readonly UserHelper _userHelper; + private readonly PaymentHelper _paymentHelper; + private readonly Guid tenantId; + private readonly Guid organizaionId; + public PaymentController(UserHelper userHelper, PaymentHelper paymentHelper) + { + _userHelper = userHelper; + _paymentHelper = paymentHelper; + tenantId = userHelper.GetTenantId(); + organizaionId = userHelper.GetCurrentOrganizationId(); + } + + [HttpPost("create-order")] + public async Task CreateOrder([FromBody] CreateOrderDto model) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + try + { + var response = _paymentHelper.CreateOrder(model.Amount, loggedInEmployee, tenantId); + return Ok(ApiResponse.SuccessResponse(response, "Payment created successfully", 200)); + } + catch (Exception ex) + { + return StatusCode(500, ApiResponse.ErrorResponse("Error occured While creating the payment", new + { + Message = ex.Message, + StackTrace = ex.StackTrace, + Source = ex.Source, + InnerException = new + { + Message = ex.InnerException?.Message, + StackTrace = ex.InnerException?.StackTrace, + Source = ex.InnerException?.Source, + } + }, 500)); + } + } + + [HttpPost("verify-payment")] + public IActionResult VerifyPayment([FromBody] PaymentVerificationRequest request) + { + string payload = request.OrderId + "|" + request.PaymentId; + string actualSignature = request.Signature ?? ""; + string expectedSignature = _paymentHelper.GetExpectedSignature(payload); + + if (actualSignature == expectedSignature) + { + // Payment is verified, process accordingly e.g. update tenant payment details + return Ok(new { status = "success" }); + } + else + { + return BadRequest(new { status = "failure", message = "Invalid signature" }); + } + } + } + +} + diff --git a/Marco.Pms.Services/Helpers/PaymentHelper.cs b/Marco.Pms.Services/Helpers/PaymentHelper.cs new file mode 100644 index 0000000..fce3f8f --- /dev/null +++ b/Marco.Pms.Services/Helpers/PaymentHelper.cs @@ -0,0 +1,57 @@ +using Marco.Pms.Model.Employees; +using Marco.Pms.Model.ViewModels.PaymentGetway; +using Razorpay.Api; +using System.Security.Cryptography; +using System.Text; + +namespace Marco.Pms.Services.Helpers +{ + public class PaymentHelper + { + private readonly IConfiguration _configuration; + private readonly string key = "YOUR_RAZORPAY_KEY"; + private readonly string secret = "YOUR_RAZORPAY_SECRET"; + public PaymentHelper(IConfiguration configuration) + { + _configuration = configuration; + key = configuration["Razorpay:Key"] ?? ""; + secret = configuration["Razorpay:Secret"] ?? ""; + } + + 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; + } + } +} diff --git a/Marco.Pms.Services/Marco.Pms.Services.csproj b/Marco.Pms.Services/Marco.Pms.Services.csproj index 71be88e..c248667 100644 --- a/Marco.Pms.Services/Marco.Pms.Services.csproj +++ b/Marco.Pms.Services/Marco.Pms.Services.csproj @@ -34,6 +34,7 @@ + diff --git a/Marco.Pms.Services/Program.cs b/Marco.Pms.Services/Program.cs index 8c3a28c..de0921e 100644 --- a/Marco.Pms.Services/Program.cs +++ b/Marco.Pms.Services/Program.cs @@ -1,6 +1,6 @@ -using Marco.Pms.CacheHelper; using FirebaseAdmin; using Google.Apis.Auth.OAuth2; +using Marco.Pms.CacheHelper; using Marco.Pms.DataAccess.Data; using Marco.Pms.Helpers; using Marco.Pms.Helpers.CacheHelper; @@ -55,7 +55,7 @@ builder.Services.AddCors(options => policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() - .WithExposedHeaders("Authorization"); + .WithExposedHeaders("Authorization", "X-Request-ID", "X-Correlation-ID"); }); // A stricter policy for production (loaded from config) @@ -65,7 +65,8 @@ builder.Services.AddCors(options => { policy.WithOrigins(allowedOrigins) .AllowAnyMethod() - .AllowAnyHeader(); + .AllowAnyHeader() + .WithExposedHeaders("Authorization", "X-Request-ID", "X-Correlation-ID"); }); }); #endregion @@ -190,6 +191,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index e7fdcee..541cc61 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -9,7 +9,7 @@ "Title": "Dev" }, "ConnectionStrings": { - "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1" + "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMSStage" }, "SmtpSettings": { "SmtpServer": "smtp.gmail.com", @@ -50,5 +50,9 @@ "SerilogDatabaseUrl": "mongodb://localhost:27017/DotNetLogs", "ConnectionString": "mongodb://localhost:27017/MarcoBMS_Caches?socketTimeoutMS=500&serverSelectionTimeoutMS=500&connectTimeoutMS=500", "ModificationConnectionString": "mongodb://devuser:DevPass123@147.93.98.152:27017/MarcoBMSLocalDev?authSource=admin&eplicaSet=rs01&directConnection=true" + }, + "Razorpay": { + "Key": "rzp_test_RXCzgEcXucbuAi", + "Secret": "YNAVBXxRsDg8Oat4M1C3m09W" } }