Introduction
In the dynamic realm of online transactions, a smooth and secure payment gateway is indispensable. With .NET Core emerging as a go-to framework for web development, seamlessly integrating payment gateways like PayPhi has become essential. This article offers a concise guide to effortlessly integrating PayPhi with .NET Core. Let’s delve into the process, empowering developers to enhance their online payment systems efficiently.
Creation of a html/cshtml page
First and foremost, let's begin by setting up the groundwork. Create a new HTML page where we'll implement the PayPhi Payment Gateway integration with .NET Core. This HTML page will serve as the foundation for our project, allowing us to seamlessly incorporate PayPhi's functionality into our web application. We'll start by defining the structure of our page and then gradually integrate the necessary elements to facilitate secure and efficient online transactions. So, let's dive in and get started with the initial setup!
Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Payment Details</title>
<style>
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
box-sizing: border-box;
}
button {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<h2>Enter Payment Details</h2>
<form id="paymentForm">
<label for="customerName">Customer Name:</label>
<input type="text" id="customerName" name="customerName" value="John Doe" required>
<label for="mobile">Mobile Number:</label>
<input type="tel" id="mobile" name="mobile" value="1234567890" pattern="[0-9]{10}" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" value="[email protected]" required>
<label for="paymentAmount">Payment Amount:</label>
<input type="number" id="paymentAmount" name="paymentAmount" value="100.00" min="0" step="0.01" required>
<label for="invoiceNumber">Invoice/Merchant Unique Number:</label>
<input type="text" id="invoiceNumber" name="invoiceNumber" value="INV123456" required>
<button type="submit">Submit Payment</button>
</form>
<script>
document.getElementById('paymentForm').addEventListener('submit', function(event) {
event.preventDefault();
// Here you can handle the form submission, such as sending the data to your server
// You can access form fields using document.getElementById('elementId').value
// Example: var customerName = document.getElementById('customerName').value;
// Perform further processing or send data to the server using AJAX
});
</script>
</body>
</html>
Creation of request from client side to Server side
To initiate the transaction process, the client-side script will craft a request containing essential payment details. These details typically include the customer's name, contact information, payment amount, and any additional metadata such as invoice or merchant unique numbers. Using JavaScript or a similar client-side scripting language, the request is assembled and sent to the server-side endpoint designated to handle payment processing. This communication between the client and server is crucial for securely transmitting sensitive payment data and initiating the subsequent steps in the payment flow. Let's explore how this request is structured and transmitted to ensure a seamless integration of the PayPhi Payment Gateway with our .NET Core application.
Note. You can check my artile How to create a unique number for Paymant Gateway
Code
<script type="text/javascript">
$('.btnUnionBankPayOnline').click(function (e) {
$('.divPaymantGateway').toggleClass('d-none');
});
$('.btnPayPhi').click(function (e) {
function getMonthsInLet(mid) {
if (mid == 0) {
return "A";
} else if (mid == 1) {
return "B";
} else if (mid == 2) {
return "C";
} else if (mid == 3) {
return "D";
} else if (mid == 4) {
return "E";
} else if (mid == 5) {
return "F";
} else if (mid == 6) {
return "G";
} else if (mid == 7) {
return "H";
} else if (mid == 8) {
return "I";
} else if (mid == 9) {
return "J";
} else if (mid == 10) {
return "K";
} else if (mid == 11) {
return "L";
}
}
var Date1 = new Date();
var getMonLet = getMonthsInLet(Date1.getMonth());
var RandNo = 'PP' + Date1.getDate().toString().padStart(2, "0") + getMonLet + Date1.getFullYear().toString().substr(-2) + Date1.getHours() + Date1.getMinutes() + Date1.getSeconds() + Date1.getMilliseconds();
$('.TransNo').text(RandNo);
$('.hdnTransNo').val(RandNo);
var merchantorderno = $('.hdnTransNo').val();
var custphone = $('.hdnCustmob').val();
var custemail = $('.hdnCustEmail').val();
var firstname = $('.hdnCustname').val();
if (fstname == " " || fstname == null) {
$('.chkmessage').text("Please update your name.");
$('#CheckingModel').modal("show");
return false;
}
else if (custemail == " " || custemail == null) {
$('.chkmessage').text("Please update your email.");
$('#CheckingModel').modal("show");
return false;
}
else if (custphone == " " || custphone == null) {
$('.chkmessage').text("Please update your mobile no.");
$('#CheckingModel').modal("show");
return false;
}
else {
$('#CheckingModel').modal("hide");
}
var postdata = {
MerchantOrderNo: merchantorderno,
CustomerPaidAmount: actpaidAmt,
customer_phone: custphone,
customer_email: custemail,
customer_firstname: fstname,
};
swal({
title: "Ready to Pay with PayPhi?",
content: {
element: "div",
attributes: {
innerHTML: "<pre><b>Merchant Order No:</b>" + RandNo + "<br><b>Contract Number:</b>" + contractno + "<br><b>Amount:</b>" + actpaidAmt + "</pre>",
},
},
icon: "warning",
buttons: true,
dangerMode: true,
closeOnClickOutside: false,
closeOnEsc: false,
})
.then((willMakePayment) => {
if (willMakePayment) {
$('#ModalLoader').modal('show');
$.ajax({
type: "POST",
url: appRoot + "/OrderProcessing/MakePaymentPayPhiMoneyOnline",
data: JSON.stringify(postdata),
datatype: "json",
contentType: 'application/json',
traditional: true,
success: function (data) {
//console.log(data);
if (data != null && data.includes("http")) {
window.location.href = data;
}
else {
$('#ModalLoader').modal('hide');
swal({
text: data,
icon: "error",
buttons: true,
closeOnClickOutside: false,
});
$('.divPaymantGateway').addClass('d-none');
return false;
}
}
});
} else {
swal({
text: "Payment cancelled. Please review your details.",
icon: "error",
buttons: true,
closeOnClickOutside: false,
})
}
});
});
</script>
Note. For sweet Alter you need to be use the swal Plugin.
Creation of modal class:
Creating a modal class in .NET Core allows us to organize and manage payment details efficiently. This class serves as a blueprint for storing attributes like customer name, contact information, payment amount, and invoice/merchant unique number. By encapsulating these details, we ensure code clarity and facilitate seamless integration with the PayPhi Payment Gateway. Let's dive into crafting this modal class for streamlined payment data management in our .NET Core application.
using System;
using System.Collections.Generic;
using System.Text;
namespace Domain.Common
{
public class PayPhi
{
}
public class PayPhiPaymentRequest
{
public string addlParam1 { get; set; }
public string addlParam2 { get; set; }
public string chargeAmount { get; set; }
public string chargeHead1 { get; set; }
public string currencyCode { get; set; }
public string desc { get; set; }
public string dueDate { get; set; }
public string emailID { get; set; }
public string expiryPeriod { get; set; }
public string invoiceNo { get; set; }
public string merchantId { get; set; }
public string mobileNo { get; set; }
public string paymentReturnURL { get; set; }
public string udf21 { get; set; }
public string udf22 { get; set; }
public string udf23 { get; set; }
public string userName { get; set; }
public string secureHash { get; set; }
}
public class PayPhiResponse
{
public string responseCode { get; set; }
public string respDescription { get; set; }
public string merchantId { get; set; }
public string invoiceNo { get; set; }
public string txnID { get; set; }
public string redirectionURL { get; set; }
public string secureHash { get; set; }
}
public class PayPhiDvResponse
{
public string responseCode { get; set; }
public string respDescription { get; set; }
public string merchantId { get; set; }
public string invoiceNo { get; set; }
public string mobileNo { get; set; }
public string chargeAmount { get; set; }
public string addlParam1 { get; set; }
public string addlParam2 { get; set; }
public string invoiceStatus { get; set; }
public string paidAmount { get; set; }
public string refundedAmount { get; set; }
public List<PaymentChildDtls> payments { get; set; }
public string secureHash { get; set; }
}
public class PaymentChildDtls
{
public string txnID { get; set; }
public string paymentMode { get; set; }
public string paymentID { get; set; }
public string paymentDateTime { get; set; }
}
public class PayPhiConfiguration
{
public string PaymentReturnURL { get; set; }
public string MerchanKey { get; set; }
public string MerchantId { get; set; }
public string PayPhiRequestURL { get; set; }
public string CurrencyCode { get; set; }
public string ExpiryPeriod { get; set; }
public string StatusCheckURL { get; set; }
}
public class PayPhiBankGatewayResponse
{
public string BankCode { get; set; }
public string SecureHash { get; set; }
public string Amount { get; set; }
public string RespDescription { get; set; }
public string PaymentMode { get; set; }
public string CustomerEmailID { get; set; }
public string AddlParam2 { get; set; }
public string ResponseCode { get; set; }
public string CustomerMobileNo { get; set; }
public string PaymentSubInstType { get; set; }
public string MerchantId { get; set; }
public string PaymentID { get; set; }
public string MerchantTxnNo { get; set; }
public string ChargeAmount { get; set; }
public string AddlParam1 { get; set; }
public string PaymentDateTime { get; set; }
public string InvoiceStatus { get; set; }
public string InvoiceNo { get; set; }
public string TxnID { get; set; }
public string DisplayMessage { get; set; }
}
public class RequestForPayPhiDoulbeVerification
{
public string invoiceNo { get; set; }
public string merchantId { get; set; }
public string reqType { get; set; }
public string secureHash { get; set; }
}
}
Before going to write the controller code create a common class for generating the secureHash as HmacHelper.cs as below:
HmacHelper.cs
using System;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Summary description for HmacHelper
/// </summary>
public class HmacHelper
{
public static string GetHashValue(string key, string message, HMACTypes type)
{
string response = "";
UTF8Encoding encoding = new UTF8Encoding();
byte[] keyByte = encoding.GetBytes(key);
byte[] messageBytes = encoding.GetBytes(message);
switch (type)
{
case HMACTypes.HMAC_MD5:
using (var hmacmd = new HMACMD5(keyByte))
{
response = ByteToString(hmacmd.ComputeHash(messageBytes));
}
break;
case HMACTypes.HMAC_SHA1:
using (var hmacmd = new HMACSHA1(keyByte))
{
response = ByteToString(hmacmd.ComputeHash(messageBytes));
}
break;
case HMACTypes.HMAC_SHA256:
using (var hmacmd = new HMACSHA256(keyByte))
{
response = ByteToString(hmacmd.ComputeHash(messageBytes)).ToLower();
}
break;
case HMACTypes.HMAC_SHA384:
using (var hmacmd = new HMACSHA384(keyByte))
{
response = ByteToString(hmacmd.ComputeHash(messageBytes));
}
break;
case HMACTypes.HMAC_SHA512:
using (var hmacmd = new HMACSHA512(keyByte))
{
response = ByteToString(hmacmd.ComputeHash(messageBytes));
}
break;
}
return response;
}
private static string ByteToString(byte[] buff)
{
return buff.Aggregate("", (current, t) => current + t.ToString("X2"));
}
}
public enum HMACTypes
{
HMAC_MD5 = 0,
HMAC_SHA1,
HMAC_SHA256,
HMAC_SHA384,
HMAC_SHA512
}
Write the Controller code for Payment Initiation
[HttpPost]
public async Task<IActionResult> MakePaymentPayPhiMoneyOnline([FromBody] Payment payment)
{
try
{
LoggedInUser profile = HttpContext.Session.Get<LoggedInUser>(KeyHelper.UserKey);
if (profile == null)
{
return RedirectToAction("Logout", "Account");
}
else
{
var guidTokenNo = Guid.NewGuid().ToString();
string amount = Convert.ToString(payment.CustomerPaidAmount);
string customerName = payment.customer_firstname;
string email = payment.customer_email;
string phone = payment.customer_phone;
string productinfo = payment.ContractNumber.Trim();
/*Getting the priceperMT and Paid Qty*/
// Split the string by '|' character
string[] parts = payment.ItemsIndPriceDetails?.Split('|');
string pricePerMT = (parts?.Length > 1) ? parts[1].Trim() : "0.00";
string paidQty = (parts?.Length > 2) ? parts[2].Trim() : "0.00";
/*Added by Prakash For Mines Name,MaterialName & Contract Qty*/
PrintOnlineReceipt pt = new PrintOnlineReceipt();
pt.ContractNumber = productinfo;
pt.Action = "D";
var mnres = _orderProcessingRepository.GetMinesByContract(pt).Result.FirstOrDefault();
/*END*/
if (mnres != null)
{
var getPayPhiConfig = _commonRepository.GetPayPhiCongiguration().Result.FirstOrDefault();
if (getPayPhiConfig != null)
{
PayPhiPaymentRequest payPhireq = new PayPhiPaymentRequest();
payPhireq.addlParam1 = productinfo.Trim();
payPhireq.addlParam2 = mnres.MineName.Trim();
payPhireq.chargeAmount = amount.Trim();
payPhireq.chargeHead1 = amount.Trim();
payPhireq.currencyCode = getPayPhiConfig.CurrencyCode.Trim();
payPhireq.desc = "Payment For Contract No : " + productinfo.Trim();
payPhireq.dueDate = DateTime.Now.ToString("dd/MM/yyyy").Trim();
payPhireq.emailID = email.Trim();
payPhireq.expiryPeriod = mnres.DayCountInHours.Trim();
payPhireq.invoiceNo = payment.MerchantOrderNo.Trim();
payPhireq.merchantId = getPayPhiConfig.MerchantId.Trim();
payPhireq.mobileNo = phone.Trim();
payPhireq.paymentReturnURL = getPayPhiConfig.PaymentReturnURL.Trim();
payPhireq.udf21 = mnres.MaterialName.Trim();
payPhireq.udf22 = paidQty.Trim();
payPhireq.udf23 = pricePerMT.Trim();
payPhireq.userName = customerName.Trim();
// Set the property accoing to alphabetical order
string hashSequence = $"{payPhireq.addlParam1}{payPhireq.addlParam2}{payPhireq.chargeAmount}{payPhireq.chargeHead1}{payPhireq.currencyCode}{payPhireq.desc}{payPhireq.dueDate}{payPhireq.emailID}{payPhireq.expiryPeriod}{payPhireq.invoiceNo}{payPhireq.merchantId}{payPhireq.mobileNo}{payPhireq.paymentReturnURL}{payPhireq.udf21}{payPhireq.udf22}{payPhireq.udf23}{payPhireq.userName}";
var SecureHash = HmacHelper.GetHashValue(getPayPhiConfig.MerchanKey, hashSequence, HMACTypes.HMAC_SHA256);
payPhireq.secureHash = SecureHash;
string errorMessage = "";
if (string.IsNullOrEmpty(payPhireq.chargeAmount))
{
errorMessage = "chargeAmount is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.chargeHead1))
{
errorMessage = "chargeHead1 is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.currencyCode))
{
errorMessage = "currencyCode is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.desc))
{
errorMessage = "desc is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.dueDate))
{
errorMessage = "dueDate is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.invoiceNo))
{
errorMessage = "invoiceNo is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.paymentReturnURL))
{
errorMessage = "paymentReturnURL is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.merchantId))
{
errorMessage = "merchantId is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.secureHash))
{
errorMessage = "secureHash is missing or empty.";
}
else if (string.IsNullOrEmpty(payPhireq.emailID))
{
errorMessage = "emailID is missing or empty.";
}
if (!string.IsNullOrEmpty(errorMessage))
{
return Json(errorMessage);
}
var Payment_Request_String = JsonConvert.SerializeObject(payPhireq).ToString();
if (Payment_Request_String != null)
{
// Write the Payment Json Request in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "MakePaymentPayPhiMoneyOnline",
Error_Message = "",
Request_String = Payment_Request_String,
Response_Message = "",
Response_Statuscode = "",
Response_String = ""
});
using (HttpClient client = new HttpClient())
{
// Post the request to the PayPhi endpoint directly using the full URL
HttpResponseMessage response = await client.PostAsync(
getPayPhiConfig.PayPhiRequestURL,
new StringContent(Payment_Request_String, Encoding.UTF8, "application/json")
);
// Read the response content
string responseContent = await response.Content.ReadAsStringAsync();
// Deserialize the JSON response
var jsonResponse = JObject.Parse(responseContent);
// Extract the response code
string responseCode = jsonResponse["responseCode"].ToString();
// Check the response code
if (responseCode == "0000")
{
// If the response code is "0000", the API was successful
// Write the Payment Json response in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "",
Error_Message = "",
Request_String = "",
Response_Message = response.ReasonPhrase,
Response_Statuscode = responseCode,
Response_String = responseContent
});
// Check if the response is successful
if (response.IsSuccessStatusCode)
{
//Deserialize the response JSON to a PayPhiResponse object
var payPhiResponse = JsonConvert.DeserializeObject<PayPhiResponse>(responseContent);
if (payPhiResponse.redirectionURL != null)
{
/*Insert to to temp table*/
payment.Action = "I";
payment.CreatedBy = profile.UserId;
payment.MerchantId = getPayPhiConfig.MerchanKey;
payment.PaymentGatewayBankName = "PayPhi";
var res = _orderProcessingRepository.AddPaymentOnline(payment).Result;
/*Insert to to temp table*/
// Redirect the user to the redirectionURL received in the response
return Json(payPhiResponse.redirectionURL);
}
}
else
{
// You can access other fields from the JSON response for error handling
string respDescription = jsonResponse["respDescription"].ToString();
// Handle unsuccessful response
return Json(respDescription);
}
}
else
{
// Handle unsuccessful response
// If the response code is not "0000", handle the error accordingly
// You can access other fields from the JSON response for error handling
string respDescription = jsonResponse["respDescription"].ToString();
//Write the Payment Json response in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "",
Error_Message = respDescription,
Request_String = "",
Response_Message = "",
Response_Statuscode = responseCode,
Response_String = responseContent
});
return Json(respDescription);
}
}
}
}
}
}
}
catch (Exception ex)
{
await _commonRepository.InsertErrorLog(ex.Message, DateTime.Now, "MakePaymentPayPhiMoneyOnline", ex.Source, " StckTrace = " + ex.StackTrace);
return Json(ex.Message);
}
return View();
}
Write the Code for Paymant Status Received
public async Task<IActionResult> PayPhiResponse()
{
try
{
// Initialize PayPhi object to store response data
PayPhiBankGatewayResponse PayPhiRes = new PayPhiBankGatewayResponse();
var guidTokenNo = Guid.NewGuid().ToString();
// Create a dictionary to hold all parameters and values received from the gateway
Dictionary<string, string> gatewayParams = new Dictionary<string, string>();
// Concatenate all values from Request.Form including the secureHash parameter
string allFormValues = "";
var sortedKeys = Request.Form.Keys.OrderBy(key => key);
foreach (var key in sortedKeys)
{
var value = Request.Form[key];
// Skip the secureHash parameter
if (key != "secureHash")
{
//Do something with key-value pair (parameter and its value)
allFormValues += value;
}
// Add the parameter and its value to the dictionary
gatewayParams[key] = value;
}
// Write the Payment Json Request in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "PayPhiResponse",
Error_Message = "",
Request_String = Request.Form["invoiceNo"].ToString().Trim('{', '}'),
Response_Message = "",
Response_Statuscode = "",
Response_String = ""
});
// Serialize the dictionary containing all parameters and values into JSON format
string gatewayParamsJson = JsonConvert.SerializeObject(gatewayParams);
if (gatewayParamsJson != null)
{
// Write the Payment Json response in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "",
Error_Message = "",
Request_String = "",
Response_Message = Request.Form["respDescription"].ToString().Trim('{', '}'),
Response_Statuscode = Request.Form["responseCode"].ToString().Trim('{', '}'),
Response_String = gatewayParamsJson
});
var getPayPhiConfig = _commonRepository.GetPayPhiCongiguration().Result.FirstOrDefault();
if (getPayPhiConfig != null)
{
// Calculate hash from concatenated values (excluding secureHash)
string calculatedSecureHash = HmacHelper.GetHashValue(getPayPhiConfig.MerchanKey, allFormValues, HMACTypes.HMAC_SHA256);
// Retrieve secure hash from Request.Form
string receivedSecureHash = Request.Form["secureHash"];
// Compare calculated hash with received hash
if (calculatedSecureHash != receivedSecureHash)
{
// Log the secure hash mismatch error in API log table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "PayPhiResponse",
Error_Message = $"Secure hash mismatch. Calculated: {calculatedSecureHash}, Received: {receivedSecureHash}",
Request_String = "",
Response_Message = Request.Form["respDescription"].ToString().Trim('{', '}'),
Response_Statuscode = Request.Form["responseCode"].ToString().Trim('{', '}'),
Response_String = gatewayParamsJson
});
PayPhiRes.DisplayMessage = $"Secure hash mismatch.\nCalculated: {calculatedSecureHash}\nReceived: {receivedSecureHash}";
return View(PayPhiRes);
}
else
{
//Write the code for Payment success
PayPhiRes.AddlParam1 = Request.Form["addlParam1"].ToString().Trim('{', '}');
PayPhiRes.AddlParam2 = Request.Form["addlParam2"].ToString().Trim('{', '}');
PayPhiRes.ChargeAmount = Request.Form["chargeAmount"].ToString().Trim('{', '}');
PayPhiRes.CustomerEmailID = Request.Form["customerEmailID"].ToString().Trim('{', '}');
PayPhiRes.InvoiceNo = Request.Form["invoiceNo"].ToString().Trim('{', '}');
PayPhiRes.MerchantId = Request.Form["merchantId"].ToString().Trim('{', '}');
PayPhiRes.CustomerMobileNo = Request.Form["customerMobileNo"].ToString().Trim('{', '}');
PayPhiRes.BankCode = Request.Form["bankCode"].ToString().Trim('{', '}');
PayPhiRes.SecureHash = Request.Form["secureHash"].ToString().Trim('{', '}');
PayPhiRes.Amount = Request.Form["amount"].ToString().Trim('{', '}');
PayPhiRes.RespDescription = Request.Form["respDescription"].ToString().Trim('{', '}');
PayPhiRes.PaymentMode = Request.Form["paymentMode"].ToString().Trim('{', '}');
PayPhiRes.ResponseCode = Request.Form["responseCode"].ToString().Trim('{', '}');
PayPhiRes.PaymentSubInstType = Request.Form["paymentSubInstType"].ToString().Trim('{', '}');
PayPhiRes.PaymentID = Request.Form["paymentID"].ToString().Trim('{', '}');
PayPhiRes.MerchantTxnNo = Request.Form["merchantTxnNo"].ToString().Trim('{', '}');
PayPhiRes.PaymentDateTime = Request.Form["paymentDateTime"].ToString().Trim('{', '}');
PayPhiRes.InvoiceStatus = Request.Form["invoiceStatus"].ToString().Trim('{', '}');
PayPhiRes.TxnID = Request.Form["txnID"].ToString().Trim('{', '}');
string transactionDateTimeString = PayPhiRes.PaymentDateTime;
DateTime transactionDateTime;
// Parse the string to DateTime
if (DateTime.TryParseExact(transactionDateTimeString, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out transactionDateTime))
{
PayPhiRes.PaymentDateTime = transactionDateTime.ToString();
}
//0000 Transaction successful
//039 Transaction Rejected
//020 Cancelled by user
//R1000 Request submitted successfully
if (PayPhiRes.ResponseCode == "0000")
{
if (PayPhiRes.InvoiceStatus == "Paid")
{
PayPhiRes.InvoiceStatus = "SUCCESS";
}
var res = _orderProcessingRepository.AddPayPhiResponse(PayPhiRes).Result;
return View(PayPhiRes);
}
else
{
//"respDescription": "payment_cancelled",
//0000 Transaction successful
//039 Transaction Rejected
//020 Cancelled by user
//R1000 Request submitted successfully
PayPhiRes.InvoiceStatus = (PayPhiRes.ResponseCode == "039" || PayPhiRes.ResponseCode == "020") ? "FAIL" : PayPhiRes.InvoiceStatus;
var res = _orderProcessingRepository.AddPayPhiResponse(PayPhiRes).Result;
return View(PayPhiRes);
}
}
}
}
}
catch (Exception ex)
{
await _commonRepository.InsertErrorLog(ex.Message, DateTime.Now, "PayPhiResponse", ex.Source, " StckTrace = " + ex.StackTrace);
return Json(ex.Message);
}
return View();
}
Write the Code for Paymant Status Check API for Pay Phi
[HttpPost]
public async Task<IActionResult> PayPhiDoubleVerification([FromBody] CustomerPayment cpayment)
{
try
{
var guidTokenNo = Guid.NewGuid().ToString();
RequestForPayPhiDoulbeVerification rdvd = new RequestForPayPhiDoulbeVerification();
var getPayPhiConfig = _commonRepository.GetPayPhiCongiguration().Result.FirstOrDefault();
if (getPayPhiConfig != null)
{
rdvd.invoiceNo = cpayment.MerchantOrderNo;
rdvd.merchantId = getPayPhiConfig.MerchantId;
rdvd.reqType = "status";
rdvd.secureHash = "";
string errorMessage = "";
if (string.IsNullOrEmpty(rdvd.invoiceNo))
{
errorMessage = "invoiceNo is missing or empty.";
}
else if (string.IsNullOrEmpty(rdvd.merchantId))
{
errorMessage = "merchantId is missing or empty.";
}
else if (string.IsNullOrEmpty(rdvd.reqType))
{
errorMessage = "reqType is missing or empty.";
}
if (!string.IsNullOrEmpty(errorMessage))
{
return Json(errorMessage);
}
// Set the property accoing to alphabetical order
string hashSequence = $"{rdvd.invoiceNo}{rdvd.merchantId}{rdvd.reqType}";
var SecureHash = HmacHelper.GetHashValue(getPayPhiConfig.MerchanKey, hashSequence, HMACTypes.HMAC_SHA256);
rdvd.secureHash = SecureHash;
var Payment_Request_String = JsonConvert.SerializeObject(rdvd).ToString();
if (Payment_Request_String != null)
{
// Write the Payment Json Request in API Log Table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "PayPhiDoubleVerification",
Error_Message = "",
Request_String = Payment_Request_String,
Response_Message = "",
Response_Statuscode = "",
Response_String = ""
});
using (HttpClient client = new HttpClient())
{
// Post the request to the PayPhi endpoint directly using the full URL
HttpResponseMessage response = await client.PostAsync(getPayPhiConfig.StatusCheckURL, new StringContent(Payment_Request_String, Encoding.UTF8, "application/json"));
// Read the response content
string responseContent = await response.Content.ReadAsStringAsync();
// Deserialize the JSON string into an object
var JsonresObj = JsonConvert.DeserializeObject<PayPhiDvResponse>(responseContent);
// Deserialize the JSON string into a JObject
JObject responseObject = JObject.Parse(responseContent);
// Get the properties of the JObject in alphabetical order
var properties = responseObject.Properties().OrderBy(p => p.Name);
// Initialize a StringBuilder to concatenate the values
StringBuilder concatenatedValues = new StringBuilder();
foreach (var property in properties)
{
// Exclude "secureHash" property
if (property.Name == "secureHash")
{
continue; // Skip this property
}
// Get the value of the property
var value = property.Value;
// Convert the value to string with the appropriate formatting
string valueString;
valueString = value.ToString();
if (value.Type == JTokenType.Float)
{
// If the value is a float, format it as a string to preserve the decimal part
valueString = ((double)value).ToString("0.0##################");
}
else
{
// For other types, convert to string normally
valueString = value.ToString();
}
if (property.Name == "payments")
{
string cleanedResponse = valueString.Replace("\r\n", "").Replace(" ", "");
valueString = cleanedResponse.Trim();
}
// Concatenate the value to the StringBuilder
concatenatedValues.Append(valueString);
}
// Get the concatenated values as a string
string concatenatedString = concatenatedValues.ToString();
// Calculate hash from concatenated values (excluding secureHash)
string calculatedSecureHash = HmacHelper.GetHashValue(getPayPhiConfig.MerchanKey, concatenatedString, HMACTypes.HMAC_SHA256);
// Retrieve secure hash from Request.Form
string receivedSecureHash = JsonresObj.secureHash;
// Compare calculated hash with received hash
if (calculatedSecureHash != receivedSecureHash)
{
// Log the secure hash mismatch error in API log table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "",
Error_Message = $"Secure hash mismatch. Calculated: {calculatedSecureHash}, Received: {receivedSecureHash}",
Request_String = "",
Response_Message = JsonresObj.respDescription,
Response_Statuscode = JsonresObj.responseCode,
Response_String = responseObject.ToString()
});
string outPutRes = $"Secure hash mismatch.\nCalculated: {calculatedSecureHash}\nReceived: {receivedSecureHash}";
return Json(outPutRes);
}
else
{
// Log the Payment Response received in API log table
await _commonRepository.WriteAPILogDetail(new WriteAPILog
{
TokenNo = guidTokenNo,
API_Name = "",
Error_Message = "",
Request_String = "",
Response_Message = JsonresObj.respDescription,
Response_Statuscode = JsonresObj.responseCode,
Response_String = responseObject.ToString()
});
// Check if the response is successful
if (response.IsSuccessStatusCode)
{
if (JsonresObj.responseCode == "0000")
{
if(JsonresObj.invoiceStatus == "Paid")
{
JsonresObj.invoiceStatus = "SUCCESS";
var res = _orderProcessingRepository.AddDoubleVerificationResponsePayPhi(JsonresObj).Result;
if (res.Remark != null)
{
return Json(res.Remark);
}
else
{
return Json("Oops! Update Failed");
}
}
else if(JsonresObj.invoiceStatus == "Rejected" || JsonresObj.invoiceStatus == "Expired")
{
JsonresObj.invoiceStatus = "FAIL";
var res = _orderProcessingRepository.AddDoubleVerificationResponsePayPhi(JsonresObj).Result;
if (res.Remark != null)
{
return Json(res.Remark);
}
else
{
return Json("Oops! Update Failed");
}
}
else if(JsonresObj.invoiceStatus == "Open")
{
return Json("Invoice Status: " + JsonresObj.invoiceStatus + ", " + JsonresObj.respDescription);
}
else
{
return Json("Invoice Status: " + JsonresObj.invoiceStatus + ", " + JsonresObj.respDescription);
}
}
else
{
return Json("Invoice Status: " + JsonresObj.invoiceStatus + ", " + JsonresObj.respDescription);
}
}
else
{
// Handle unsuccessful response
return Json("Invoice Status: " + JsonresObj.invoiceStatus + ", " + JsonresObj.respDescription);
}
}
}
}
}
}
catch (Exception ex)
{
await _commonRepository.InsertErrorLog(ex.Message, DateTime.Now, "PayPhiDoubleVerification", ex.Source, " StckTrace = " + ex.StackTrace);
return Json(ex.Message);
}
return View();
}
Interface for creating API Log
#region--------------------------------Write API Log----------------------------------------
/// <summary>
/// Insert API Log Details
/// </summary>
/// <param name="objAPILog"></param>
/// <returns></returns>
Task<CustomerResult> WriteAPILogDetail(WriteAPILog objAPILog);
#endregion-----------------------------------------------------------------------------------
public Task<CustomerResult> WriteAPILogDetail(WriteAPILog objAPILog)
{
object[] objArray = new object[] {
"TokenNo", objAPILog.TokenNo!=null?objAPILog.TokenNo:"",
"Error_Message", objAPILog.Error_Message!=null?objAPILog.Error_Message:"",
"Request_String", objAPILog.Request_String!=null?objAPILog.Request_String:"",
"Response_String", objAPILog.Response_String!=null?objAPILog.Response_String:"",
"Response_Statuscode", objAPILog.Response_Statuscode!=null?objAPILog.Response_Statuscode:"",
"Response_Message", objAPILog.Response_Message!=null?objAPILog.Response_Message:"",
"API_Name", objAPILog.API_Name!=null?objAPILog.API_Name:"",
"ACTION", "A",
};
DynamicParameters _param = new DynamicParameters();
_param = objArray.ToDynamicParameters("PAR_OUT");
var result = Connection.Query<string>("USP_T_COP_API_LOG_DETAILS", _param, commandType: System.Data.CommandType.StoredProcedure);
string response = _param.Get<string>("PAR_OUT");
CustomerResult customerResult1 = new CustomerResult() { Remark = response };
return Task.FromResult(customerResult1);
}
Create Tables and Procedures for write API Log
This SQL script creates a table named APILog
to store API log entries, including fields for request date, method, endpoint, request body, response status, and response body.
Create a transaction error table also like below.
CREATE TABLE [dbo].[T_Transaction_Error](
[intErrorId] [int] IDENTITY(1,1) NOT NULL,
[vchErrorMessage] [varchar](512) NULL,
[dtmErrorDate] [datetime] NULL,
[vchProcedureName] [varchar](80) NULL,
[vchLineNumber] [varchar](250) NULL,
[vchErrorProcedure] [varchar](512) NULL,
CONSTRAINT [PK_T_Transaction_Error] PRIMARY KEY CLUSTERED
(
[intErrorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[T_COP_API_LOG_DETAILS](
[INT_API_LOG_ID] [int] IDENTITY(1,1) NOT NULL,
[VCH_REQUEST_DATETIME] [varchar](50) NULL,
[VCH_REQUEST_STRING] [nvarchar](max) NULL,
[VCH_RESPONSE_DATETIME] [varchar](50) NULL,
[VCH_RESPONSE_STRING] [nvarchar](max) NULL,
[VCH_RESPONSE_STATUSCODE] [varchar](10) NULL,
[VCH_RESPONSE_MESSAGE] [varchar](250) NULL,
[VCH_ERRORMESSAGE] [varchar](250) NULL,
[VCH_APINAME] [varchar](150) NULL,
[VCH_TOKENNO] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_T_COP_API_LOG_DETAILS] PRIMARY KEY CLUSTERED
(
[INT_API_LOG_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
UNIQUE NONCLUSTERED
(
[VCH_TOKENNO] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER PROCEDURE [dbo].[USP_T_COP_API_LOG_DETAILS]
(
@Request_String nvarchar(max) =NULL,
@Response_String nvarchar(max) =NULL,
@Response_Statuscode varchar(10) =NULL,
@Response_Message varchar(250) =NULL,
@Error_Message varchar(250) =NULL,
@API_Name varchar(150) =NULL,
@ACTION CHAR(1) =NULL,
@TokenNo NVARCHAR(50),
@PAR_OUT VARCHAR(24) =NULL OUT
)
AS
BEGIN
BEGIN TRY
BEGIN TRAN
IF(@ACTION='A')
BEGIN
IF EXISTS(SELECT * FROM T_COP_API_LOG_DETAILS WHERE VCH_TOKENNO=@TokenNo)
BEGIN
IF (@Error_Message IS NULL OR LEN(@Error_Message) = 0)
BEGIN
UPDATE T_COP_API_LOG_DETAILS
SET
VCH_RESPONSE_DATETIME = FORMAT(GETDATE(), 'dd-MMM-yyyy h:mm:ss tt'),
VCH_RESPONSE_STRING = @Response_String,
VCH_RESPONSE_STATUSCODE = @Response_Statuscode,
VCH_RESPONSE_MESSAGE = @Response_Message,
VCH_ERRORMESSAGE = @Error_Message
WHERE
[VCH_TOKENNO] = @TokenNo
END
ELSE
BEGIN
UPDATE T_COP_API_LOG_DETAILS
SET
VCH_RESPONSE_DATETIME = FORMAT(GETDATE(), 'dd-MMM-yyyy h:mm:ss tt'),
VCH_ERRORMESSAGE = @Error_Message,
VCH_RESPONSE_STRING = @Response_String,
VCH_RESPONSE_STATUSCODE = @Response_Statuscode,
VCH_RESPONSE_MESSAGE = @Response_Message
WHERE [VCH_TOKENNO] = @TokenNo
END
END
ELSE
BEGIN
INSERT INTO T_COP_API_LOG_DETAILS
(
VCH_REQUEST_DATETIME,
VCH_REQUEST_STRING,
VCH_APINAME,
[VCH_TOKENNO]
)
VALUES
(
FORMAT(GETDATE(), 'dd-MMM-yyyy h:mm:ss tt'),
@Request_String,
@API_Name,
@TokenNo
);
END
SET @PAR_OUT='1';
END
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
INSERT INTO T_TRANSACTION_ERROR
(
VCHERRORMESSAGE
,DTMERRORDATE
,VCHPROCEDURENAME
,[vchLineNumber]
)
VALUES
(
ERROR_MESSAGE()
,GETDATE()
,'USP_T_COP_API_LOG_DETAILS'
,ERROR_LINE()
);
SET @PAR_OUT='0';
END CATCH
END
Sample Request Body for PayPhi
{
"addlParam1": "Product Name",
"addlParam2": "Product Desc",
"chargeAmount": "2032201.00",
"chargeHead1": "2032201.00",
"currencyCode": "356",
"desc": "Payment For Prduct Desc",
"dueDate": "17/04/2024",
"emailID": "[email protected]",
"expiryPeriod": "720",
"invoiceNo": "PP17D2412242979",
"merchantId": "T_22334",
"mobileNo": "9876555533",
"paymentReturnURL": "http://localhost:54277/Payment/PayPhiResponse",
"udf1": "",
"udf2": "",
"userName": "RAM Chandra",
"secureHash": "63bdbe7b6e526c913a1885e78bdd6c4039d53a46c89a4b270d3c81bb73b3fc97"
}
Sample Response Body from PayPhi
{
"responseCode": "0000",
"respDescription": "Order/Invoice created successfully",
"merchantId": "T_23423",
"invoiceNo": "PP17D2412242979",
"txnID": "PP17D2412242979",
"redirectionURL": "http://qa.payph.in/4UddduV4/6Mddd31",
"secureHash": "26628d43620982a4f6bc3e0b4f1a179a808cb4c2151650e9a87a55e0ed861b3f"
}
Conclusion
Integrating the PayPhi Payment Gateway with .NET Core offers businesses a robust and secure solution for processing online transactions. Throughout this guide, we've explored the step-by-step process of integrating PayPhi into .NET Core applications, from setting up the initial HTML page to handling payment initiation on the server side. By leveraging the power of .NET Core alongside PayPhi's features, developers can create seamless payment experiences for their customers while ensuring the security and reliability of their payment infrastructure.
Moving forward, it's essential to continue exploring and implementing best practices for payment gateway integration, such as error handling, data validation, and security measures to safeguard sensitive payment information. Additionally, staying updated with the latest developments in both .NET Core and PayPhi will enable businesses to adapt and optimize their payment systems to meet evolving customer needs and industry standards.
With the integration of PayPhi Payment Gateway, businesses can streamline their online payment processes, enhance customer satisfaction, and drive growth in the digital marketplace. By following the guidance provided in this guide and continually refining their payment integration strategies, businesses can unlock the full potential of PayPhi and .NET Core to achieve success in the competitive landscape of e-commerce and online transactions.