ATOS SIPS Payment Gateway implementation in .NET

February 11th, 2013 by amit 5 comments »



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

What I got for being a DZone contributor

February 2nd, 2013 by amit 4 comments »



DZone informed me in late 2012,that I was one of the top contributors in 2012, and as a mark of appreciation, they were sending me some free gifts.

I got the parcel today and I was very pleased to receive it. Consider the fact that I am in India and DZone took the trouble of sending me the parcel all the way from USA, at their own cost. That fact itself speaks very highly of them.

 

 

What I got:

  • A black T-shirt with <dev> written in large white letters and the DZone logo on the sleeve.
  • A stack of DZone monogrammed notes
  • Foam darts with two shooters, as part of a game called ICBM (Inter Cubicle Ballistic Missiles) to fire at your colleagues in office
  • A PDA/smartphone holder from Data Nerd
  • Hard copies of various Ref cards

Thanks a lot , DZone. I hope others also get motivated to submit more posts after reading this article.

 

URL masking by encrypting query string

September 14th, 2012 by sanjay 1 comment »




For many of my project I have written code which simply a link having some parameters as query string in url to pass information from one page to another by using GET request. However there are situations when I want to hide the query string to avoid tempering by users. Many people suggest using POST instead of GET, but remembering I am not submitting a form. Another possible solution is to use session variable and pass data from one page to another, this is fine if we have limited number of link having query string.
The best suggested way to hide the query string from a site user is to use rewrite rule in your htaccess file.
But I decided to encrypt the query string and then decrypt it back on landing page. I does some googling and integrate the suggestion from there with some of the built in php function to get a working code.
Here is the code

<?php

	//function to encrypt the query string

function encryptLink($val1, $val2){

	$keySalt = "aghtUJ6y";  // change it

	$qryStr = "name1=".$val1."&name2=".$val2;  //making query string

	$query = base64_encode(urlencode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($keySalt), $qryStr, MCRYPT_MODE_CBC, md5(md5($keySalt)))));    //this line of code encrypt the query string

	$link = "landing_page.php?".$query;

	return $link;

}

$v1 = "foo";  // you can generate this value dynamically 

$v2 = "bar";

$pagelink = encryptLink($v1, $v2);

?>

<a href="<?php echo $pagelink ?>">link</a>

The function encryptLink take parameters which are the values to be passed on landing_page.php (in this example). Off course you can pass the parameters in different style and manipulate according to your need. The mcrypt_encrypt() function take different parameters details of which you can find on its documentation on php.net.

Now on landing_page.php you must have decrypt script to get the query string back in original from so that you can process any further logic
It’s very simple

<?php

$keySalt = "aghtUJ6y";     // same as used in encryptLink function

$queryString = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($keySalt), urldecode(base64_decode($_SERVER['QUERY_STRING'])), MCRYPT_MODE_CBC, md5(md5($keySalt))), "\0");   //this line of code decrypt the query string

parse_str($queryString);   //parse query string

if(!empty($name1) && !empty($name2)){

       echo $name1;  //  will print "foo"

      echo $name2;  // will print "bar"

}
else{

	exit("Invalid parameters passed");

}

?>

That’s all you need to mask you link so the user cannot temper with your url query string. Hope this small tutorial helps you. Feel free to comment or leave a suggestion.

Dynamically resizing IFRAME as per the inner content

August 22nd, 2012 by amit No comments »





An IFRAME is the best way to embed a page into another page . The main problem sometimes faced is if the content within the iframe is going to change in length dynamically then at some point you will see scrollbars showing up. How to avoid this?

This can be avoided by  using javascript in the main external page in which the iframe is embedded. Every time a link is clicked within the iframe or a form submission occurs, the onload event of the iframe tag is fired. So what we do is hook that event in our external page and then calcuate what the height of the iframe should be after getting the height of the inner content .

Please note this approach only works if the iframe accesses pages within the same domain as the external page, otherwise it will throw a Security error due to cross-site scripting restrictions in javascript.

The sample html code is given below:

<div id="forum_Container">
	
	<iframe src="/Forum/index.php" id="powerhouse" frameborder="0" style="overflow:hidden;" scrolling="no" width="975px" onload = "setIframeHeight( this.id )"></iframe>
</div>

The sample javascript is given below.

function setIframeHeight( iframeId ) /** IMPORTANT: All framed documents *must* have a DOCTYPE applied **/
{
 var ifDoc, ifRef = document.getElementById( iframeId );

 try
 {   
  ifDoc = ifRef.contentWindow.document.documentElement;  
 }
 catch( e )
 { 
  try
  { 
   ifDoc = ifRef.contentDocument.documentElement;  
  }
  catch(ee)
  {   
  }  
 }
 
 if( ifDoc )
 {
  ifRef.height = 1;  
  ifRef.height = ifDoc.scrollHeight;
  divh = document.getElementById("forum_Container");
  pk = Number(ifRef.height) + Number(10);
  divh.style.height = pk+"px";
  
  /* For width resize, enable below.  */
  
  // ifRef.width = 1;
  // ifRef.width = ifDoc.scrollWidth; 
 }
}

The iframe is surrounded with a DIV tag and we manipulate the height of the DIV as well as the height of the iframe.

IE ISSUES HANDLED

A lot of people are able to do iframe resizing but get the problem of scrollbars coming up in IE both horizontally and vertically. That can be taken care of by some or all the fixes given below:

1.In the pages within the iframe put the overflow and scroll atrributes in the body tag:

<body style=”overflow:hidden;” scroll=”no“>

2.Sometimes the reason why IE still shows scrollbars is more basic i.e the height and width set in CSS for the inner pages may be more than the width and height of the iframe.

We have been able to fix the scrollbar issue in IE 7,8, and 9 using the above points.

Any suggestions or queries are welcome.

PHPFileMon – A simple PHP based file monitoring system

August 13th, 2012 by amit No comments »




WHAT IS IT?

File monitor

File monitor

What led us to the creation of PHPFileMon was finding a simple way of monitoring if any file on a website had been altered or changed. This can be mainly used as a  notification tool for security purposes in case someone hacks into the website and plants extra code or malicious code in one of the files/pages physically. This can also be used as a notification system, every time someone uploads or does authorized changes to the files on the website. There could be other uses for it also. What PHPfilemon does is detect changes and send notifications so it can be applied to any number of things.

There are lot of defacement and change scanners available both commercial and open source like TripWire etc. but we wanted something simple which could be used on any shared hosts without having to install something on the server.

HOW IT WORKS

The working is quite simple. These are the following files involved:

  1. files.dat – this is the source file respository which defines which paths/files need to be monitored.
  2. phpfilemon.dat – this is an xml file which is created once the updater is run.
  3. semaphore.dat – this acts like a simplistic semaphore to prevent the scanner and updater from running at the same time
  4. updater.php – runs through all the files in files.dat and creates phpfilemon.dat
  5. scanner.php – processes phpfilemon.dat to detect changes and sends notification mails

The updater stores the latest file dates and times of each file in files.dat , in phpfilemon.dat . The scanner compares the current file date/time for each file in files.dat and compares it to the stored file date/time for the file in phpfilemon.dat . If there is a difference then it flags it as a changed file.

THE SOURCE FILE REPOSITORY

The source file repository files.dat takes in a single path per row:

../*.php
../classes/*.php

/var/websites/thissite/thisfile.txt

../includes/*.*

As shown above you can put relative paths or absolute paths, with or without wildcard characters. This file has to be created by hand as every website would have different files to be monitored. Paths specified here are NOT recursive. So if a folder has subfolders, then each of those subfolders also need to be entered separately.

THE XML REPOSITORY

PHPmonlist.dat is an xml file created by the updater when it runs. It has a very simple structure. It stores the full filepath and the datetime it was created. :

<?xml version=”1.0″ encoding=”UTF-8″?>
<files>
<file name=”../ExpenseBook/add_entry.php” date=”1344685028″/>
<file name=”../ExpenseBook/contactUs.php” date=”1343068892″/>
</files>

THE UPDATER

updater.php will run through files.dat and get the file datetime of each file mentioned If it finds a wildcard entry it will expand it first to get all the files matching that wildcard. The PHPmonlist.dat thus created will contain the entries of individual files.

THE SCANNER

scanner.php will run and compare the datetimes of each file in phpmonlist.dat and compare it with its current datetime. If there is a difference it stores in a notification list and then sends the list as a mail.

THE SEMAPHORE FILE

The semaphore.dat can have three states. The three states are stored as a simple string:

  1. Idle  – neither scanner not updater is running
  2. Updating – updater is running
  3. Scanning – scanner is running

The updater and scanner both check the status of the semaphore before running. If the status is not idle they terminate without doing any work. Once work is completed,. the status is set back to Idle

HOW TO MAKE ALL THIS WORK

First put all the files in a folder in your website. Make sure the folder has write permissions since phpmonlist.dat will need to be updated periodically by updater.php

In scanner.php you need to add in your own code to send the actual notification mail . There is space and comments left for it at the end of scanner.php. You can still run scanner.php without the mail part – it will display the files which have changed.

Once you have specified the files and paths in files.dat, run updater.php. Then you can run scanner.php any number of times to detect changes. Ideally you should put scanner.php in a cron job to run periodically and send auto-notification for changes.

FALSE ALARMS

The current code will send a false alarm if you make changes or upload changes to files on your webserver and the scanner runs automatically after that. Preferably you should run updater once after you make any changes to the file, so that scanner does not trigger a false alarm.

This part has been kept open-ended as there are multiple ways of handling false alarms. One way would be to have a fourth status for the semaphore called DISABLE which would prevent the scanner from running. This status would get reset again by running updater the next time.

DOWNLOAD

The complete source can be downloaded here and you are free to change or use it any application. We just request you to keep the copyright message in the files.

There is a lot of scope for improvement and added features, and we welcome any suggestions, bugs or feedback on this.

Integrate the Constant Contact API with your PHP application

August 8th, 2012 by amit No comments »




Constant Contact

WHAT IS CONSTANT CONTACT?

Constant Contact is an online service for creating social and email campaigns which is widely used by a lot of websites. It provides a developer API which handles subscription lists and create/send newsletters.

In this article we will focus on the basics of creating contact lists and creating email campaigns. We will not go into Event Marketing and Bulk Imports/Exports. This article serves as a quick and simple way of integrating the Constant Contact AppConnect API with your PHP application.

FIRST STEP

The first step requires you to have an API Key.  The API key is a unique identifier for a Developer, not a customer, and they recommend having one API Key for each integration that you build.  So if you have two software platforms and you integrate both, you should create two API keys.

To create an API Key,  you need  a Constant Contact account and fill out their API Key request form.

The Constant Contact APIs allow you to manage contacts in your account in various ways.  By utilizing contacts collection and resource, you can add, edit, unsubscribe contacts as well as modify their contact list memberships. The Constant Contact REST API contains several useful methods to enable developers to easily manage contacts.

To see their complete API docs, click here.

Before you can start writing code, you need the PHP wrapper classes for this API available on Sourceforge: http://sourceforge.net/projects/ctctphplib/

Include the classes from this in all your code.

ADDING/UPDATING A CONTACT

$apikey = 'YOUR CONSTANT CONTACT APIKEY';
$cc_username =  'YOUR CONSTANT CONTACT USERNAME;
$cc_password =   'YOUR CONSTANT CONTACT PASSWORD;

//constructor for constant contact class
$Datastore = new CTCTDataStore();

$ConstantContact = new ConstantContact('basic', $apikey, $cc_username,$cc_password);
$lists = $ConstantContact->getLists();

$nextLists = "";
do {
if ($nextLists != "") 
{
		$Lists = $nextLists;
		$nextLists = $ConstantContact->getLists( $Lists ['nextLink'] );
	} 
	else if ($nextLists == "") 
	{
		$nextLists = $ConstantContact->getLists ();	
		$Lists = $nextLists;
	}
		
	foreach ( $Lists ['lists'] as $list ) 
	{
		if($list->name == 'YOUR LIST NAME')
		$cc_daily_deals_list_id = $list->id;						}
} while ( $Lists ['nextLink'] != false );

$search = $ConstantContact->searchContactsByEmail($emailId);
if($search == false)
{
// adding contact in constant contact in both the list
	$Contact = new Contact();
	$Contact->emailAddress ='EMAIL ID TO SUBSCRIBE';
	$Contact->City = 'CITY';
	$Contact->lists = array($cc_daily_deals_list_id);				
$Contact->CountryCode = 'COUNTRY_CODE';
$NewContact = $ConstantContact->addContact($Contact);			
}
else
{	
$Contact = $ConstantContact->getContactDetails($search[0]);
if($Contact->status != "Do Not Mail")	
{
$Contact->City = 'CITY';
$Contact->CountryCode = 'COUNTRY_CODE';
		if(!in_array($cc_daily_deals_list_id,$Contact->lists))
			array_push($Contact->lists,$cc_daily_deals_list_id);										
		if($Contact->status == "Removed")								if(!in_array($cc_daily_deals_list_id,$Contact->lists))
			$Contact->lists = array($cc_daily_deals_list_id);			
if(!in_array($cc_daily_deals_list_id,$Contact->lists))
			array_push($Contact->lists, $cc_daily_deals_list_id);
										
		// Update the contact and DONE
		$NewContact = $ConstantContact->updateContact($Contact);		  }
  }			

CREATE AN EMAIL  CAMPAIGN

1)	Create Campaign
$Datastore = new CTCTDataStore ();
$ConstantContact = new ConstantContact('basic', $apikey, $cc_username,$cc_password);

$lists = $ConstantContact->getLists();	
			
// Get all verified email addresses
$VerifiedEmailAddresses = $ConstantContact->getVerifiedAddresses ();

// getting the list id to send campaign to
$nextLists = "";
do 	{
	if ($nextLists != "") 
	{
		$Lists = $nextLists;									$nextLists = $ConstantContact->getLists( $Lists ['nextLink'] );
	} 
	else if ($nextLists == "") 
	{
		$nextLists = $ConstantContact->getLists ();						$Lists = $nextLists;
	}
	foreach ( $Lists ['lists'] as $list ) 
{
		//exit(var_dump($list));
		if($list->name == $cc_list_name)
			$cc_daily_deals_list_id = $list->id;				
		}
				
 } while ( $Lists ['nextLink'] != false );

// email content
$ch = curl_init();
$timeout = 400000;
curl_setopt($ch, CURLOPT_TIMEOUT, 200000);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);

 // Getting binary data
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt ($ch, CURLOPT_URL, âYOUR PAGE URâ);
 $codeString = curl_exec($ch);
 curl_close($ch);
						
$codeString = str_replace("£","&pound;",$codeString);
$codeString = str_replace("â¬","&euro;",$codeString);
$codeString = str_replace('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 strict//EN">',"",$codeString);
//$codeString = substr($codeString,0,75732);

// creating campaign starts here	
try {
	$campaign_name = "Campaign For ".ucfirst($city_name)." on ".date('Y-m-d h:i:s');	//Build Campaign Object
	$myCampaign = new Campaign();							$myCampaign->name = $campaign_name;
	$myCampaign->subject = ucfirst($city_name).": ".$mail_subject;
	$myCampaign->fromName = $from_name;	
							
	if ($from_email != "") 
	{
		// Do while is necessary to grab the next 50 results and build
		// the next request so it keeps iterating until completion
	do {
		// Gets the next link if there are more than 50 results to
		// be returned
$moreVerifiedEmailAddresses = $ConstantContact->getVerifiedAddresses ( $VerifiedEmailAddresses ['nextLink'] );
	foreach ( $VerifiedEmailAddresses ['addresses'] as $VerifiedEmailAddress ) {
		if ($VerifiedEmailAddress->email == $from_email) {
			$myCampaign->fromAddress = $VerifiedEmailAddress;					$myCampaign->replyAddress = $VerifiedEmailAddress;					$fromAddress = $VerifiedEmailAddress;
			break; // We found our email address so we can exit our
	  // foreach
	}	
	}
	// Sets original Verified emaillAddresses to the next 50 so
	// it will replace its values
	$VerifiedEmailAddresses = $moreVerifiedEmailAddresses;
	} while ( $VerifiedEmailAddresses ['nextLink'] != false );
}

	$myCampaign->greetingName = "NONE";
	$myCampaign->greetingSalutation = "DEAR";
	$myCampaign->greetingString = "";
	$myCampaign->orgName = $orgn_name;
	$myCampaign->orgAddr1 = $address;
	$myCampaign->orgAddr2 = "";
	$myCampaign->orgAddr3 = "";
$myCampaign->orgCity = $cc_city;
	$myCampaign->orgState = $cc_state;
	
// Validates that if the user selects United states, the stateOther
// is empty
if ($cc_country == "United States") {
		$stateOther == NULL;
}
	$myCampaign->orgInternationalState = NULL;
$myCampaign->orgPostalCode = $cc_zipCode;
	$myCampaign->orgCountry = $cc_country;						$myCampaign->incForwardEmail = "NO";
	$myCampaign->incSubscribeLink = "NO";
	$myCampaign->forwardEmailLinkText = "";
	$myCampaign->subscribeLinkText = "";
	$myCampaign->emailContentFormat = "HTML";
$myCampaign->emailContent = htmlspecialchars($codeString);
	$myCampaign->textVersionContent = "<text>This is the text version</text>";
	$myCampaign->lists = array($cc_daily_deals_list_id);
	//print_r($myCampaign->emailContent);
	//exit;
$CampaignResult = $ConstantContact->addCampaign($myCampaign, $fromAddress);
	//print_r($CampaignResult);
	//exit;
	$schedule_time = date("Y-m-d")." ".$local_time;				
							$obj=new convert_timezone;
							$str=$obj->conver_to_time($country_timezone,$schedule_time,0);
							
							echo "New Campaign created for " .$city_name ." ID:".$CampaignResult->id."\n<br />";
							
							$lastCampaigns = $ConstantContact->getCampaigns();
							
							if (isset ( $CampaignResult->status )) {
								foreach ( $lastCampaigns ['campaigns'] as $recent ) {
									if ($recent->name == $CampaignResult->name) {
										$CampaignResult = $ConstantContact->getCampaignDetails ( $recent );
										
									}
								}
							}
							
							// schedule campaign here
							$schedule_flag = $ConstantContact->scheduleCampaign($CampaignResult,date('c',strtotime($str)));
							if(isset($schedule_flag) && $schedule_flag != NULL && $schedule_flag != ""){
								echo "Campaign is scheduled at ".$str."<br /><br />";

UNSUBSCRIBE CONTACT FROM A LIST

$search = $ConstantContact->searchContactsByEmail('OUR EMAIL TO UNSUBSCRIBE';
$Contact = $ConstantContact->getContactDetails($search[0]);
if(in_array($list_id, $Contact->lists))
$Contact->lists = array_values(array_diff($Contact->lists, array($list_id)));
$NewContact = $ConstantContact->updateContact($Contact);

The above are the basic operations which will be used. For more details, its best to consult the API documentation from the link given above.

Socrates – the only online MySQL schema diagram generator

August 2nd, 2012 by amit 8 comments »




WHAT IS SOCRATES

Socrates is a web based application which generates a database schema diagram. It is written in PHP and uses JQuery in the front end.

All it requires as an input is your Mysql dump sql script. Just paste the sql and it generates a schema diagram. Simple and fast. Please note that this is a work in progress and I am in the process of adding in more features.

WHY SOCRATES WAS MADE

As part of our documentation of projects, we are often needed to create database schema diagrams of our existing MySQL databases. Now the only way to do that is :

  • Use a commercial application/tool which automatically creates a schema diagram
  • Create a schema diagram by hand by entering everything manually

I did a lot of searching over the web to look for a free online tool which could immediately show me a schema diagram of my MySQL database, but there is nothing like that available. As a result I decided I would make an online application for this purpose.

FEATURES IN VERSION 0.5 (Aug 2nd, 2012)

  1. Takes in a maximum of 750k characters for the sql dump input.
  2. Parses the sql file to generate information about tables,fields and primary keys
  3. Ability to extract table name, field info, default values and primary key info.
  4. Shows the parsed information as a diagram.
  5. The boxes in the diagram are draggable so that you can achieve your desired layout

A sample screenshot of a Joomla CMS database schema is shown below:

EXPLANATION OF THE SCHEMA DIAGRAM

Each table is shown as a block. Within each table it shows

  • The table name
  • The field list. For each field it shows the name, field type and default value if any
  • If a field is part of a primary key it is marked as P
  • If a field is allowed to have a null value is marked with an N

SAVE THE DIAGRAM AS A SCREENSHOT

Since its not possible for any server-side language to save a web page as an image, without using external server hosted applications, I would recommend that you install any of the several free browser-addons which lets you save the current page in the browser as an image. Any of these add-ons would save the entire diagram as an image.

WHAT NEXT

As mentioned before, this is a work in progress, so I will be continually adding more features. I am particularly aiming to automatically show foreign key linkages using connectors between the blocks as thats something which is very useful.

I would be very interested in your feedback and comments on any bugs or ideas that you want to convey.

Click Here To Use Socrates

How to survive in the IT field in 2012 – various inputs

August 1st, 2012 by amit 1 comment »





2012 has turned out to be a stern year for a lot of people in the IT field, both companies and individuals. And it looks like its going to be like this for a while. I wanted to take this opportunity to put across my views on what can help us survive and also took expert views and opinions from a cross-section of people who are in this field.

I will use the terms ‘IT’ and ‘software’ interchangeably. This article might be leaning towards developers and designers, but its still applicable to anyone working in the IT field.

WHY SOFTWARE IS A LITTLE DIFFERENT FROM OTHER FIELDS

By its very nature, the software field exists only because it provides value to other fields. On its own software is of no value. This concept stems from the fact that software is used to make a device/computer useful to its user. A mobile phone/tablet/server/laptop is basically a tool which helps someone become more productive or do things better. So a piece of hardware needs software to make it useful, else its just a piece of useless metal.

THE YEAR 2012

Its more or less the same story everywhere – economic crisis, downsizing, shrinking markets, less demand, less money. Every industry is affected, every country is affected. Since the software field is dependent on other fields (as mentioned above), it also faces the same grim scenario.

SO HOW DO WE GET THROUGH THIS YEAR

Dont just be an expert in a language/platform. Try to get domain expertise.

That is the first and foremost way you can retain value across the years. It is easier for a cardiologist to learn to program than for a programmer to learn cardiology. So if a clinic/hospital wanted to develop some cardiology software, it would prefer to go for the former rather than the latter.

So become knowledgeable about some domain outside software. There are thousands to choose from and all of them require software. Become an expert in logistics solutions, aircraft scheduling, pharmaceutical inventory management, travel portals, school systems, telecom billing, the apparel industry. The list could go on and on.

Your value will skyrocket if you are known as a ‘medical billing solutions specialist’ or a ‘media planning solutions specialist’ than just a ‘.NET developer’ or a ‘Java enterprise architect’ or a ‘PHP programmer’.

Get cross-platform exposure

Mobile applications, web applications, cloud computing, data mining/warehousing. There are lot of upcoming technology fields which will be around for a while. It is important to have a foot in at least one of them. That makes you future-proof and you dont get obsolete. In software the trick is to be a Jack of all trades and master of one.  I dont recommend trying to become a master of too many fields – its not possible anyway and you never know if by the time you master the field, it might have become obsolete. So just enough knowledge of multiple skillsets so that you quickly get into it deeper if the situation requires it.

Keep your eyes and ears open

Dont box yourself in and be content to just work on your current job or project. It is easy for developers to get caught up in code or designers to get cught up in their designs and forget everything else. Look around you, see whats happening, check the news for whats happening in your country, your region, your industry. Talk to people from other industries/fields. You will be surprised at how much insight you gain by just doing this. You will find your attitude towards your work changing once you see it from a different point of view.

Take it a little easy

If you are facing slow times in work, or some downtime, dont panic. Use this as an oppportunity to learn something new, or simply improve your existing skills.

OTHER INPUTS

Stefan Didak, an experienced graphics developer and entrepreneur has a very interesting take on this whole situation. In his own words:

“There are no real secrets to business or success really. Some people say it’s all about hard work but I know people who have worked really hard and got nowhere. Other people say you have to “want” something bad enough to get it and go after it. But I know people who have done that and also didn’t get what they were going after. In the end the real factors in any success are; 1) whether other people will allow you to be successful (but most of the time you don’t know which people those might be), 2) being at the right places at the right times (but you never know what the right place or time is), which leads to 3) luck. That’s probably what it all comes down to, a lot of luck which makes that the things in 1 and 2 work.”

I do think I have a basic rule that I’ve stuck to for the past 20+ years. “Don’t let anyone tell you what to do”. :-)

Some inputs from UX designers working in Fortune 100 companies – Arun Sengupta, Uday Nandan and some others:

  • Don’t be close minded – Always be open to ideas ,new or old, be it from an experienced person or a fresher who has no experience
  •  Always keep improving your communication skills and people skills to get the best requirements / suggestions for the design from the user / clients.
  • Never limit your creativity / designs due to technological constraints
  • Experimentation with new concepts or technologies
  • Be proficient in Latest Web Technologies
  • Always keep yourself updated with the changes in the industry , technology , hardware & demand.
  • There is always an added advantage for web designers who have development knowledge or skills
  • Experience & knowledge of mobile technology

John Sansom, a DBA mentions a couple of survival rules for DBAs which are applicable anytime , not just 2012:

  • Things can and will go wrong. So dont get complacent.
  • Adopting a defensive mindset. This is related to the above point.  Plan for scenarios where things can go wrong
  • Preparation is the key. Always be prepared to face the unexpected

CONCLUSION

The way I see the big picture is that, 2012 is the year when you will be tested to see if you can survive hard times and obstacles. Its like an examination. If you get through fine, then you will get rewarded, else you will be out on the wayside.

Mumbai Monsoon Magic

July 30th, 2012 by vartika 9 comments »





I have been in Mumbai since 3 years now.  And from that day, I am noticing how happening this city is.  The more you look, the more you will be surprised.  I will cover this topic later sometime, for now lets focus on the rains.

A glimpse:

Monsoons are most happening season out here. It starts Mid June – September End. Almost  4 big fat Months.

The Rains

Very interesting thing about Mumbai’s Monsoons is that the rains will take not more than 2 secs to reach  its highest, i.e 2 secs back it could have been bright and sunny and a moment later heavy rainfalls – unlike any city in India.

The LifeStyle:

The lifestyle here completely changes in this part of the year.  Mumbaikars start their monsoon shopping from beginning of May itself. They shop mainly for Umbrellas , Raincoats and Shoes.  And these don’t last long enough for next monsoons.

But the best part is no one waits for the rainfall to get over here.  In short this city “Never Sleeps and never takes pause”. They follow their normal routine as it was without the rains.

Non Stop Masti at Gateway of India

No bunking of Schools, Colleges and Offices. Yes but people do enjoy the rains by moving out of their places. They roam around with or without Umbrella. Enjoy Tea and cigarettes at the gate of their Colleges and Offices. Housewives enjoy Tea and Pakoras at home.

The hot Spots :

For best view of rains, people visit : Gateway of India, Marine Drive, Chowpatti, Juhu Beach, Bandstand. Best part of these places is that you sit, relax and enjoy with your family and friends,the beauty of the falls.

Yet other best places can be your terrace and balconies with a mug full of Hot Coffee.

The Machines :

Local Trains, Buses, auto rickshaws and Pedestrians will almost swim but wont stop.  Its rare in the history that local trains dont work since they are lifeline of Mumbai. So at most they might come to a halt for an hour or two  so that the tracks can be cleared, after that its business as usual.

Mumbai’s Life Line

At the End:

This romantic weather is my favorite. Whether you are just walking, sitting, traveling by car or bike, the environment is worth watching. Whether it is small children playing cricket at the street from the slums, or bikers passing at high speed or high class people pulling up their car windows, all is really worth capturing.

The awesome computer setup of Stefan Didak

July 28th, 2012 by amit No comments »





Stefan who?? Thats a good question. I being a programmer myself for the last 100 years , have this fascination with complex and amazing computer gear setups which other developers/designers use. I used to run a website which showcased computer setups, but due to lack of time the website died a natural death. I myself use 4 desktops with one laptop for my daily use as a software architect.

So you have a lot of geeks who love working on multiple computers at the same time. A lot of people have two systems to play with- usually a desktop and a laptop, but some go further and have three or four.

I have seen some amazing setups over the years, but Stefan Didak is the ultimate. He is the KING for the last few years. He works with , (DRUM ROLL) 7 computers. And thats not all. The hardware he has is top of the line.  Take a look below and drool:

Home office

Home office

Home office

Home office

So what does Stefan do for a living? He is a CGI, computer animation, computer generated art, computer music/audio developer and consultant. He works as a consultant to other companies advising them on these areas. He divides his time between USA and Holland.

I have been following his office setup over the years and have had the good fortune of communicating with him on and off a few times over his amazing setup.

For more information about his setup, what he does etc. you can check out his website.