Improving Customer-Experience and Conversion with API

Important: all the links and the code-examples that are embedded in this post, link toPlimus service in  Bluesnap’s sandbox – so feel free to play with it – all the credit-card charges are not real!

On the previous post , we talked a bit about the different options of online payment-processing and now it’s time to talk about the “how”.

One of the nightmares of every marketer is abandoned carts, in this post we’ll see how you can use an eCommerce API in order to create a better shopper experience and as a result to improve the conversion rate.

There are so many theories that deal with customization of a Buy-Page , lists of Do’s andDon’ts  and so on and so forth . People like to think that there’s some secret formula that once applied – conversion will rocket high.

The truth is that there are many different truths that apply to different types of businesses, for example, if your online store sells SSL-certificates or Anti-virus software (or any other protection-software like firewalls) you really should have a ’Hacker Safe’ logo or something alike – your buyers WILL look for it on their checkout page and if it won’t be there – it will be a little suspicious…

There are products for which you’d better customize a single-checkout page process (letting the customer enter his personal details and credit-card information on the same page), such a single checkout page usually works fine for low-price items, but if your product includes shipping, for example, then a two-pages checkout will work better in most of the cases.

Like I said – there’s no “secret trick” and if you want to confirm that you did a good job, A/B testing should be applied (if you can’t measure it – you can’t prove that it works!).

Still, there are a few “universal truths” that can be applied to your store, and this is what we’re going to deal with in this post, so roll your sleeves up and let’s get to work!

If your website support user-registration, making the shopper enter ALL his personal information AGAIN in the checkout page is not a very nice user experience to say the least. That’s exactly why we should use the create-shopper API call . An example of how it works can be viewed here . As you can see, the PHP code is pretty simple and short (if you remove the comments you’ll see that it’s about 60 lines of code):

<?php
 
/*
 * Written By: Ben Hultin &amp; Nir Alfasi (alfasin)
 * Nov. 2012
 * 
 * This code is used to call an API (RESTful) service of Bluesnap that creates a shopper entity on Bluesnaps' backend.
 * The shopper-entity can be used, later-on, for one-click purchases, to automate charges, to retrieve orders history and more.
 * We chose using CURL to place the API request in this demo, but we urge anyone who implements an API client 
 * to work with a RESTful API client framework in order to have a full support of all the properties of REST, such as: 
 * set HTTP method, get return-code, read/write headers, full XML support etc.
 * 
 * Documentation on "create shopper" service can be found here: 
 * http://www.bluesnap.com/helpcenter/Static/Plimus%20Web%20Services_create_shopper.htm
 * 
 * The API manual is available under "BuyAnyware" section: 
 * http://www.bluesnap.com/helpcenter/Static/default.htm 
 * 
 */
 
    // In the response of "create-shopper" API call we'll receive the 
    // newly created shopper-id which we should extract from the Location header. 
    // An example for such header: 
    // 
    // ...
    // Location: https://sandbox.plimus.com/services/2/shoppers/19372564
    // ...
    function get_shopper_from_header($ch, $string) {
        global $shopper_id;       
        //looking for the "Location" header - but since it's case insensitive...
        if(strpos($string, "ocation") > -1){ 
            $tokens = explode("/", $string);
            //the shopper-id will always be the last token
            $shopper_id = trim($tokens[count($tokens)-1]); 
        }  
        return strlen($string);
    }
 
   // An example of how to call this PHP code:
   // http://alfasin.com/create_shopper.php?firstName=bob&amp;lastName=Smith&amp;email=bob.Smith@gmail.com&amp;address1=123 Main Street&amp;address2=Apt K-9&amp;city=Parkville&amp;state=TN&amp;country=us&amp;phone=411-555-1212&amp;zipcode=37027
 
   // read the request parameters and handle special chars 
   $firstName = htmlspecialchars($_REQUEST['firstName']);            
   $lastName  = htmlspecialchars($_REQUEST['lastName']);             
   $email     = htmlspecialchars($_REQUEST['email']);                    
   $address1  = htmlspecialchars($_REQUEST['address1']);
   $address2  = htmlspecialchars($_REQUEST['address2']);
   $city      = htmlspecialchars($_REQUEST['city']);
   $state     = htmlspecialchars($_REQUEST['state']);
   $country   = htmlspecialchars($_REQUEST['country']);
   $zipCode   = htmlspecialchars($_REQUEST['zipcode']);
   $phone     = htmlspecialchars($_REQUEST['phone']);
 
   $username   = htmlentities($_REQUEST["username"]);   // vendor API username - not to confuse with the control-panel credentials
   $password   = htmlentities($_REQUEST["password"]);   // vendor API password
   // use base64 to encode the credentials
   $credentials = $username.':'.$password;
 
   // EXAMPLE
   // -------------
    $firstName = 'Bob';          
    $lastName = 'Smith';             
    $email = "bob.smith@plimus.com";      
    $address1 = "123 Main Street";
    $address2 = "Apt K-9";
    $city = "Parkville";
    $state = "TN";
    $country = "us";
    $zipCode = "37027";
    $phone = "411-555-1212";
 
   // In the following XML (which will be embedded in the BODY of the HTTP request) 
   // the <web-info> element supposed to hold customers' information (IP, browser type etc) 
    $xmlToSend = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"? >
    <shopper xmlns=\"http://ws.plimus.com\">
          <shopper-info>
                <shopper-contact-info>
                      <first-name>". $firstName ."</first-name>
                      <last-name>". $lastName ."</last-name>
                      <email>". $email ."</email>
                      <address1>". $address1 ."</address1>
                      <city>". $city ."</city>
                      <zip>". $zipCode ."</zip>
                      <country>". $country ."</country>
                      <state>". $state ."</state>
                      <phone>". $phone ."</phone>
                </shopper-contact-info>
                <locale>en</locale>
          </shopper-info>          
          <web-info> 
                <ip>62.219.121.253</ip>
                <remote-host>bzq-219-121-253.static.bezeqint.net.reinventhosting.com</remote-host>
                <user-agent>Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; GTB6.3; .NET CLR 2.0.50727</user-agent>
                <accept-language>en-us</accept-language>
          </web-info>
    </shopper>";
 
    // Set values for the POST HEADERS:
    // The URL sets the REST resource which is being called 
    $service = 'https://sandbox.plimus.com/services/2/shoppers';
    $contentType = array('Content-type: application/xml');
 
    // Initialize handle and set options
    $ch = curl_init();
    // more info about setopt options can be found here: http://www.php.net/manual/en/function.curl-setopt.php
    curl_setopt($ch, CURLOPT_URL, $service); 
    curl_setopt($ch, CURLOPT_USERPWD, $credentials); // authentication (credentials) string encoded in base-64 
 
    curl_setopt($ch, CURLOPT_HEADER, true);          // include the headers in the output
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // don't output the response to screen (default behavior)
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlToSend);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $contentType);    
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'get_shopper_from_header');
 
    // The following switches are needed only when running in development-mode on localhost
    //    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // follow redirects recursively 
    //    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // default value is "2": "check the existence of a common name and also verify that it matches the hostname provided" - we need to turn it off
    //    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // "stop cURL from verifying the peer's certificate"
 
    // For debugging purposes - we can ask the remote server to include all the request-headers in the response
    //    curl_setopt($ch, CURLINFO_HEADER_OUT, true);
 
    // Execute the request and example code to time the transaction
    $response = curl_exec($ch);           
    // if a shopper was successfully created we should receive "201 created" response code
    // and the shopper-id will be extracted into $shopper_id by get_shopper_from_header() which will iterate the response headers
 
    // Check for errors
    if ( curl_errno($ch) ) {
        echo 'HTTP error code: ' . curl_errno($ch) . '<br>error-message: "' . curl_error($ch) . '"';
        return;
    } 
 
    // SUCCESS
    if (is_numeric($shopper_id)) {
        // In *real life* this is where we save the ID 
        // of the new shopper that was created into our DB
        echo '<br>
              A new shopper entity was created on our servers with shopper-id: '
              . $shopper_id .
              '<br><br>';               
    }
    // FAIL
    else {
        // and this is the place to log (and maybe even email ourselves) the error and see
        // what needs to be corrected (for example, the customer wrote an invalid character 
        // in one of our input-fields
        echo "<br><br>
              <font color="red"><b>Something went wrong!</b></font>
              <br>
              Server reponse:
              <br><br>
              <code style='display: block; font-family: monospace; white-space: pre; margin: 1em 0px;'>
              $response
              </code><br>";
    }
?>

So, after we used the information from the user-registration form and created a new shopper entity, we can use it when the customer clicks the checkout button. We can lead the customer to the first page (out of two) of the checkout which contains all his personal information. This is an approach that I recommend using especially when it’s the first order the customer places – in case he/she wants to change their personal details – they can do it on the first checkout page, they won’t be able to do it if we lead them directly to step two(credit-card information page). Directing the customer to the second step is great for creating single-click order experience – but better do it after at least one order was already placed. Further, you should provide a way for the shopper to change the personal information .

Those of you who checked the last three links could see that we’re using the same URL, the only difference is the value of the “target” parameter that we pass to the pages, the links are:

http://alfasin.com/get_token.php?target=step1

http://alfasin.com/get_token.php?target=step2

http://alfasin.com/get_token.php?target=cp

what’s even cooler about this PHP code is that it’s only 16 lines of code!

<?php
//
// 08-22-2012 
// written by: alfasin
// 
// This PHP script demonstrates how simple it is to use the API in order to fetch an authentication token
// and then use it to log the customer into Plimus platform. It can be used to create a one-click purchase (set: target=step2)
// to log the customer into the customer-control panel so he/she could manage their subscription etc.
//
// example for *real* usage: http://alfasin.com/get_token.php?shopperId=19363122&amp;expiration=60&amp;username=[username]&amp;password=[password]&amp;contractId=2121730&amp;target=step2
// to see how it works: http://alfasin.com/get_token.php?target=step2 (try to replace step2 with: cp,step1)
 
// -------- PART I - Get the token through API call --------
 
//receive shopper-id and expiration-in-minutes from the caller
$shopperId  = htmlentities($_REQUEST["shopperId"]);  // the customer account-id, can be found in "order-locator" page for example
$expiration = htmlentities($_REQUEST["expiration"]); // the token will remain valid for number 'expiration' number of minutes 
$username   = htmlentities($_REQUEST["username"]);   // vendor API username - not to confuse with the control-panel credentials
$password   = htmlentities($_REQUEST["password"]);   // vendor API password
$contractId = htmlentities($_REQUEST["contractId"]); // contract to buy
$target     = htmlentities($_REQUEST["target"]);     // target - the Plimus page to redirect the customer: cp,step1,step2,paypal
 
//we'll use the sandbox but in order to use the live API replace sandbox.plimus.com with ws.plimus.com
$URL = "https://sandbox.plimus.com/services/2/tools/auth-token?shopperId=$shopperId&amp;expirationInMinutes=$expiration";
 
// use base64 to encode the credentials
$authorization = base64_encode($username.':'.$password); 
 
$ch = curl_init();
// set URL
curl_setopt_array($ch, array(CURLOPT_URL => $URL));
// set headers
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Basic $authorization", "Content-type: application/xml")); // This line is mandatory for every API call!
// set  HTTP request to GET
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET"); // This service (get token) is implement via RESTful GET, other services might use POST and PUT
// stop output of curl_exec to standard output (don't send output to screen)
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
// make HTTP call and read the response into an XML object
$xml = new SimpleXMLElement(curl_exec($ch));
 
// -------- PART II - Log the customer into Plimus (according to $target) --------
 
// construct Bluesnap URL
$bluesnap = "https://sandbox.plimus.com/jsp/entrance.jsp?contractId=$contractId&amp;target=$target&amp;token={$xml->token}";
// redirect
header("Location: $bluesnap");
>

A short version of the same code in Ruby:

require 'rubygems'
require 'rest_client'
require 'net/http'
require 'rexml/document'
require "uri"
 
def get_token (api_username, api_password, shopper_id, expiration)
 
  # Example input:
  # -------------
  # shopper_id = "19371968"   - this shopper has to be a shopper you created using your vendor API - otherwise you'll get "access denied"
  # expiration = "60"         - number (in minutes) for token to expire
 
  # The URL of the Token API service
  url = "https://sandbox.plimus.com/services/2/tools/auth-token?shopperId=#{shopper_id}&amp;expirationInMinutes=#{expiration}";
 
  # use REST package to make the GET HTTP call
  resource = RestClient::Resource.new(url, :user => api_username, :password => api_password)
  res = resource.get
 
  # use rexml package to read the XML from the response
  doc = REXML::Document.new(res)
  token = ""
  doc.elements.each("web-authentication/token") do |ele|
    token = ele.text
  end
  return token
end
 
# Usage:
my_api_username = "********"
my_api_password = "********"
token = get_token(my_api_username, my_api_password, shopper_id, 15)
 
sku = "2121730"       - sku is the product-id that the shopper wants to buy, ame remark we made above for the shopper goes for the sku
target = "step1"      - target page for the redirection, possible values: "step1"/"step2" or "cp" (control panel)
 
# and now we're ready to continue with the redirection, construct the following URL and use it to redirect the shopper
bluesnap = "https://sandbox.plimus.com/jsp/entrance.jsp?contractId=#{sku}&amp;target=#{target}&amp;token=#{token}"
redirect_to(bluesnap)

This code is using what we call  authentication-token auto-login API call . If we scroll back a bit and click again on the “create-shopper” demo, we can see that in the response we receive a shopper-id. This ID can be used in order to generate an authentication token which in return can be used to auto-login the shopper into one of the buy-pages (step-1 and step-2) or into the shopper control-panel.

On the next post we’ll implement a real single-click purchase process after which I’ll explain the PCI-compliance issue involved and lay-out how can we work around it (in a legit way of course!) until then, you can enjoy the Harlem shake in the bitwise version

Be Sociable, Share!

One thought on “Improving Customer-Experience and Conversion with API

  1. Hey there, You’ve performed an excellent job. I’ll definitely digg it and personally suggest to my friends. I am sure they’ll be benefited from this web site.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>