ATOS SIPS Payment Gateway implementation in .NET

France based ATOS Worldline SIPS is  the leading secure payment solution in Europe. They process over 250 million transactions a year for  about 30,000 merchants.

Though they provide comprehensive technical docs and support to incorporate their gateway into your website, there is no existing example which demonstrates the whole process from start to finish.

This blog shows how to incorporate their gateway into an ASP.NET based website. It is assumed that you are a developer/programmer with experience in websites.

BASIC PAYMENT FLOW

The process flow is simple enough. Your website creates a checkout page from where the user is sent to the payment gateway. On successful/unsuccessful payment, the gateway sends control back to your website on a designated URL.

This is the classic Request/Response model used in all payment gateways. The website sends a payment request to the gateway, the gateway processes the request and sends back a response to the website.

This is shown in the diagram below:

ATOS SIPS supports two kinds of notifications in the Response stage:

  1. Manual Notification . This is the standard method used in most websites. The user clicks on a link to return back to the website after the payment process is over.
  2. Automatic Notification. This method sends an automatic notification to a designated website URL once payment is completed. This does not depend on the user to work. This method is also common in Paypal IPN method

In this blog, we are only going to look into the Manual Notification method.

TEST MODE AND LIVE MODE

Like most world class gateways, ATOS SIPS allows a gateway to be in test mode for development or testing purposes. The behavior of the gateway in live mode and test mode is exactly the same with only two differences:

  • In test mode No actual cards are debited or charged
  • The credentials used are different in test mode and live mode

So lets start off with the sample code implementation.

STEP 1 – Setup the credentials and gateway configuration

We store all gateway related credentials and settings in the web.config file. These are the following settings to be used:

  • Switch to toggle gateway between live and test mode
  • Gateway urls for test and live mode
  • The Return url
  • The Merchant id for test and live mode
  • The Secret Key for test and live mode

   <add key="PGTestMode" value="NO"></add>
    <add key="pgdomain2test" value="https://payment-webinit.simu.sips-atos.com/paymentServlet"></add>
    <add key="pgdomain2" value="https://payment-webinit.sips-atos.com/paymentInit"></add>
    <add key="PGURL2" value="response.aspx"></add>
    <add key="PG2MerchantId" value="xxxxxxxxxxxxxxx"/>
    <add key="PG2TestMerchantId" value="002020000000001"/>
    <add key="PG2SecretKey" value="xxxxxxxxxxxxxxxxxxxxxx"/>
    <add key="PG2TestSecretKey" value="002020000000001_KEY1"/>

The actual merchant id and secret key should be replaced by actual values.

The return url is the same for both the live and test modes in our sample. If you need different urls then you can setup two urls – one for test mode and one for live mode.

The merchant id and Secret Key for the test mode is predefined by ATOS SIPS and cannot be changed.

The Secret Key is the string which is used to generate a SHA256 cipher string . More on this later in this blog.

STEP 2 – Redirect to  the checkout page

The actual page from where the checkout will be initiated will differ as per your needs. In this sample. what we are doing is then when the user clicks on the Checkout button in this page, it saves the

  • Product id – the product code which identifies the product being bought
  • Amount – the amount of the purchase
  • Order Description – the name/title of the product being bought

in the Session and then redirects to the checkout page. The button method is shown below:

    protected void btnConfirm_Click(object sender, EventArgs e)
    {
       Session["merchant_reference_no"] = txtProductCode.Text ;
       Session["amount"] = txtAmount.Text;
       Session["order_desc"] = txtProductName.Text ;
        Response.Redirect("pgrequest.aspx", true);
    }

STEP 3 – The Checkout page (Request)

Here is where the process actually starts. The pgrequest.aspx is shown below:

<%@ Page Language="C#" AutoEventWireup="true" debug="true" CodeFile="pgrequest.aspx.cs" Inherits="pgrequest" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
<script language="javascript">
    function onLoadSubmit()
    {
        document.merchantForm.submit();
    }
</script>
</head>
<body onload="onLoadSubmit();">
	<br />&nbsp;<br />
	<center><font size="5" color="#3b4455">Transaction is being processed,<br/>Please wait ...</font></center>
	<form name="merchantForm" method="post" action="<% Response.Write(m_gatewayURL); %>">
    <input type="hidden" name="Data" value="<%= m_data %>"/>
    <input type="hidden" name="InterfaceVersion" value="HP_1.0"/>
    <input type="hidden" name="Seal" value="<%= m_seal %>"/> 
    
	

	<noscript>
		<br />&nbsp;<br />
		<center>
		<font size="3" color="#3b4455">
		JavaScript is currently disabled or is not supported by your browser.<br />
		Please click Submit to continue the processing of your transaction.<br />&nbsp;<br />
		<input type="submit" />
		</font>
		</center>
	</noscript>
	</form>
</body>    
    

</html>

The codebehind in pgrequest.aspx.cs is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Security.Cryptography;


public partial class pgrequest : System.Web.UI.Page
{


    public string m_gatewayURL = "";
    public string m_data = "";
    public string m_seal = "";
    public string amount = "";
    public string merchantReferenceNo="";
    public string currencyCode = "";
    public string m_returnURL = "";
    public string m_productId = "";
    public string m_productName = "";
    public string m_merchantId = "";
    private string m_txnRef = "";


    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack) {


            if (System.Configuration.ConfigurationManager.AppSettings["PGTestMode"] == "YES")
                m_gatewayURL = System.Configuration.ConfigurationManager.AppSettings["pgdomain2test"];
            else
                m_gatewayURL = System.Configuration.ConfigurationManager.AppSettings["pgdomain2"];


            string action = Request.Url.Scheme + System.Uri.SchemeDelimiter + Request.Url.Host +
                        (Request.Url.IsDefaultPort ? "" : ":" + Request.Url.Port)  +
                        ((Request.Url.Host == "localhost") ? "/Jobsite" : "");

    
                // components of the DATA field
            amount = (string) Session["amount"];
            if (amount.EndsWith(".00"))
                amount = amount.Replace(".00", "00");
            currencyCode = "356";
            m_productId = (string)Session["merchant_reference_no"];
            m_productName = (string)Session["order_desc"];

            m_txnRef = DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() +
                DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString();

            if (System.Configuration.ConfigurationManager.AppSettings["PGTestMode"] == "YES")
                merchantReferenceNo = System.Configuration.ConfigurationManager.AppSettings["PG2TestMerchantId"];
            else
                merchantReferenceNo = System.Configuration.ConfigurationManager.AppSettings["PG2MerchantId"];

            m_returnURL = action + System.Configuration.ConfigurationManager.AppSettings["PGURL2"];


            m_data = "paymentMeanBrandList=VISA,MASTERCARD|amount=" + amount + "|currencyCode=" + currencyCode + "|merchantId=" + merchantReferenceNo +
            "|normalReturnUrl=" + m_returnURL + "|keyVersion=1|transactionReference=" + m_txnRef + "|customerLanguage=en|"+
            "orderId=" + m_productId ;


                // prepare the seal
            string secret = "";
            if (System.Configuration.ConfigurationManager.AppSettings["PGTestMode"] == "YES")
                secret = System.Configuration.ConfigurationManager.AppSettings["PG2TestSecretKey"];
            else
                secret = System.Configuration.ConfigurationManager.AppSettings["PG2SecretKey"];
            String sChaine = m_data + secret;
            UTF8Encoding utf8 = new UTF8Encoding();
            Byte[] encodedBytes = utf8.GetBytes(sChaine);

            byte[] shaResult;
            SHA256 shaM = new SHA256Managed();
            shaResult = shaM.ComputeHash(encodedBytes);

            m_seal = ByteArrayToHEX(shaResult);

        }

    }

    private string ByteArrayToHEX(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }

    public  pgrequest()
    {
        //Init += Page_Load;  
    }
}

What pgrequest.aspx does is prepare a FORM POST request to be sent to the gateway URL.

The code checks if the gateway is to be used in test or live mode and uses the correct values from web.config according to that. The amount field has to be without the decimal point. So if the amount is 100.00 it has to be passed as 10000 .  In the sample code, it is assumed that all amounts are without any decimal values. If you have amounts like 45.99 then you need to change the code a little to handle that. The best way would be to do

amount = amount.Replace(“.”, “”);

If you check the hidden form fields, all the relevant information is passed in the DATA field. The format for the DATA field is fixed i.e each name-value pair will be delimited by the vertical bar symbol.  Note that the name-value pairs are predefined and you cannot use your own name-value pair. The allowed names are given in the technical specs manual.

At around line 039 in the above source, there is a check for localhost. This is a check to be used when running this code from within Visual Studio. In the sample code , we assume the website application name is Jobsite. When run from Visual Studio, the code has to take care of the application name and the port in the URL.

Sealing the Data

The concept of sealing the data is to ensure the integrity of the data when it is received back in the response stage. Sealing is done by appending the secret key to the DATA string , converting it to bytes and then doing a SHA256 cipher. This returns an array of bytes which we convert to hexadecimal code. This hexadecimal code is passed as a hidden field in the FORM POST.

In the response page, the seal is again computed and compared with the SEAL passed from this page.

The paymentMeanBrandList specifies what payment brands to accept. If this is not passed, then it accepts the following brands:

  • VISA
  • Mastercard
  • Maestro
  • IDEAL
  • MiniTix
  • GIRO
  • INCASSO
  • REMBOURS

In the sample code, we are expecting only VISA and Mastercard users so we specify those two brands. This automatically excludes all other brands

The currency code is taken from the currency code list given in their technical specs. 356 used in the sample code stands for Indian Rupees. Euro is 978, USD is 840. Pound is 826.

STEP 4 – Getting the Response

Once the user has completed his payment , he will be redirected to the response page.  The source for response.aspx is given below:

<%@ Page Title="" Language="C#"  AutoEventWireup="true" CodeFile="response.aspx.cs" Inherits="PG_response" %>
<html>
<head>
<script language="javascript">
    function onLoadSubmit()
    {
        document.forms[0].submit();
    }
</script>

</head>
<body onload="onLoadSubmit();">
<form method="post" action="JSPaymentResponse.aspx">
<input type="hidden" name="amount" value="<%= m_amount %>"/>
<input type="hidden" name="productId" value="<%= m_orderId %>"/>
<input type="hidden" name="productName" value="<%= m_productName %>"/>
<input type="hidden" name="txnDate" value="<%= m_dateTime %>"/>
<input type="hidden" name="txnReference" value="<%= m_txnRef %>"/>
<input type="hidden" name="responseCode" value="<%= m_responseCode %>"/>

</form>
</body>
</html>

The codebehind is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Security.Cryptography;


public partial class PG_response : System.Web.UI.Page
{
    public string m_amount = "";
    public string m_captureMode = "";
    public string m_orderId = "";
    public string m_dateTime = "";
    public string m_txnRef = "";
    public string m_paymentBrand = "";
    public string m_responseCode = "";
    public string m_productId = "";
    public string m_productName = "";


    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            if (Request.Form["data"] == null || Request.Form["data"] == "" ||
                Request.Form["seal"] == null || Request.Form["seal"] == "")
            {
                Response.Write("No parameters have been passed.Invalid Payment confirmation data");
                Response.End();
            }

            string data = Request.Form["data"];
            string seal = Request.Form["seal"];

                // check params
            if (data == null || data == "" || seal == null || seal == "") {
                Response.Write("Invalid Payment confirmation data");
                Response.End();
            }

                // verify validity of data
            string secret = "";
            if (System.Configuration.ConfigurationManager.AppSettings["PGTestMode"] == "YES")
                secret = System.Configuration.ConfigurationManager.AppSettings["PG2TestSecretKey"];
            else
                secret = System.Configuration.ConfigurationManager.AppSettings["PG2SecretKey"];
            String sChaine = data + secret;
            UTF8Encoding utf8 = new UTF8Encoding();
            Byte[] encodedBytes = utf8.GetBytes(sChaine);

            byte[] shaResult;
            SHA256 shaM = new SHA256Managed();
            shaResult = shaM.ComputeHash(encodedBytes);

            string checkSeal = ByteArrayToHEX(shaResult);

            if (seal != checkSeal) {
                Response.Write("Data Validity is incorrect. This is not a valid payment transaction");
                Response.End();
            }

            // extract fields
            string[] arr = data.Split('|');
            foreach (string s in arr) { 
                string [] arr2 = s.Split('=');
                if (arr2[0] == "amount")
                {
                    m_amount = arr2[1];
                    if (m_amount.IndexOf(".") == -1)
                        m_amount = m_amount.Substring(0, m_amount.Length - 2) + ".00";
                }
                if (arr2[0] == "orderId")
                {
                     m_orderId = arr2[1];
                }
                if (arr2[0] == "transactionDateTime")
                    m_dateTime = arr2[1];
                if (arr2[0] == "transactionReference")
                    m_txnRef = arr2[1];
                if (arr2[0] == "responseCode")
                    m_responseCode = arr2[1];
            }

      

        }
    }

    private string ByteArrayToHEX(byte[] ba)
    {
        StringBuilder hex = new StringBuilder(ba.Length * 2);
        foreach (byte b in ba)
            hex.AppendFormat("{0:x2}", b);
        return hex.ToString();
    }

}

There are two validation checks happening before the data is accepted:

  1. Check if the DATA field and the SEAL field have been passed in the FORM POST
  2. Verify the computed seal value with the passed seal value.

If the above two checks pass then the transaction is considered valid . One important point to note here is in pgresponse.aspx the page is redirecting to another page called JSPaymentResponse.aspx . The name of this page is not important – you can use any page of your liking. JSPaymentResponse.aspx is the page which the user will see as the confirmation page. It will show the details of the transaction he made.

The reason why we do not let the user see all the details in pgresponse.aspx is because then if the user refreshes the page by purpose or accidentally, the transaction will again be passed into the gateway page and it will generate an error. So we pass all the data to another page, whose job is to only show the confirmation details. On that page, even if the user presses refresh, it will simply show the details again, without trying to execute the gateway page again.

So the detailed payment flow becomes as shown in the diagram

13 Comments

  1. It was gr8 to see this page. Even I’ve heard that ATOS is Launching SIPS in INDIA.

    Can you provide this details in ASP , PHP ??

    Thank you in Advance 🙂

  2. looking for php tutorial for sips. but still very helpful.

    if anybody in here have any idea about integrating sips using php please do share.

  3. Helpful suggestions , I learned a lot from the specifics , Does anyone know where my company might be able to grab a template IRS 706 copy to type on ?

2 Trackbacks / Pingbacks

  1. Wiredelta & Comm des Entrepreneurs - Digital fundraising - Blog
  2. Wiredelta & Comm des Entrepreneurs - Digital fundraising

Leave a Reply

Your email address will not be published.


*