PayPhi Payment Gateway Integration in .NET Core

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.

Payment gateway in .NET Core

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.