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 & 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://home.plimus.com/DocumentationCenter/Static/Plimus%20Web%20Services_create_shopper.htm
 * 
 * The API manual is available under "BuyAnyware" section: 
 * http://home.plimus.com/DocumentationCenter/Static/default.htm
 * 
 * This code example is also available from: 
 * https://alfasin.com/api/create_shopper.html
 *
 * and if you want to see it "in action": 
 * https://alfasin.com/create_shopper.php
 * 
 */
    

    // 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);
    }

   // TODO: Change the following URL to our rackspace machine
   // An example of how to call this PHP code:
   // https://alfasin.com/blog/code/create_shopper.php?firstName=bob&lastName=Smith&email=bob.Smith@gmail.com&address1=123 Main Street&address2=Apt K-9&city=Parkville&state=TN&country=us&phone=411-555-1212&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']);
      
   // EXAMPLE INPUT
   // -------------
 $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';
    // for the sandbox testing account TODO: include this parameter from another file and remove explicit credentials from the code
    $credentials = 'XXX:YYY'; 
    $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)) {
	echo '<br>
              A new shopper entity was created on our servers with shopper-id: '
              . $shopper_id .
              '<br><br>';
    }
    // FAIL
    else {
        echo '<br><br>
              <font color="red"><b>Something went wrong!</b></font>
              <br>
              Server reponse:
              <br><br>
              <pre style="display: block; font-family: monospace; white-space: pre; margin: 1em 0px;">'
              . $response .
              '</pre><br>';
    }
?>

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

Improving Customer-Experience and Conversion with API

Online Payment Processing

ecom

I started writing a post that demonstrates how a vendor can use API calls in order to create a better shopping experience and help convert shoppers into buyers. But after a few lines I figured that there is a basic terminology and motivation that might be obvious to me (since I worked in the eCommerce industry for the last 6 years) but not necessarily to the readers… So I decided to write a “pre-post” that will provide an overview about online payment-processing  – and only then I’ll move on to implementing API calls and suggestions to improve your conversion rate.

There are many developers out there that build cool software, apps, plugins etc – but have no idea how to sell it. Some of them just open a Paypal/Google wallet/Authorize.NET account, implement their API/HTTP-redirect and think that they’ve done the best thing possible. Well, think again!

Let’s start with some

Definitions & Terminology

Payment Gateway” or “Payment Processor” – In order to simplify things, we’ll treat the two as the same: a company or a third party in which the merchant (developer) opens a merchant-account and which processes transactions by moving the money from the customer’s issuing bank to the merchants’ bank.

Motivation

There are two major problems with Paypal/G-Wallet/Authorize.NET:

1. You, the developer, need to take care (implement) all the possible scenarios:  success, and possible failure reasons (display the proper error), calculate the currency conversion rates, hedge the amount in case the currency-rates change dramatically and the customer asks for a refund (or alternatively, lose money…) and that’s only part of the list of issues that should be handled.

2. Localization: there are so many potential-customers in other parts of the world that won’t buy since your buy-page is not translated to their language and the payment options you offer are not popular in their countries. For example, in the Netherlands (where you process in Euros) there’s a payment method that most of US residents probably never heard of, called iDEAL.

If you’re a good programmer, you’ll probably work hard and solve most of the critical issues in #1, but the issues in #2 are not solvable unless you do one of the following:

  • use the services of a professional online payment processor (such as Bluesnap or DR)
  • implement a full eCommerce solution…

Of course that there are many other reasons to go with a professional online payment processing solution, and maybe the most important of which is having a fail-over mechanism, for example, if you tried to process a credit-card with Authorize.NET and Wells-Fargo returned a generic error, you can try processing the same transaction with a different gateway instead of loosing business.

Why shouldn’t I use Authorize.NET ?

You may decide to use Authorize.NET, but then you’ll loose market segments such as:

  • Businesses in countries which don’t favor credit-card transactions
  • B2B – which normaly use PO (purchase orders), check or wire transactions
  • Local customers that feel more secure using their Paypal accounts, checks or other payment options

So, When SHOULD a developer use Paypal or Authorize.NET ?

To my opinion only if you do micro-payments, or, if you belong to a small segment of businesses that process mainly local transactions in USD and target customers that use only credit-cards.

Why do I say “almost never” ?

Business owners prefer using Paypal or Authorize.NET because of the low commission-rates (around 3%) comparing to a full payment processing solution which charges somewhere between 5-15% (depends on the transaction amount). But, if we do the math, one deal that you loose because you couldn’t support the payment-method/fail-over mechanism – could “cover” a commission rate difference of another 10-15 transactions!

What I like the most about using a professional payment processing solution is that you can setup an account and start selling in less than five minutes. Want to see for yourself ? Just open an account on Bluesnap sanbox (it’s free!) and follow the go-guide to setup a product: all you have to do is choose a name, short description and a price. After a product was created – automatically the platform creates a contract/SKU – which is the *real* thing that we’ll sell. A good example to understand the difference between a Product and a Contract is to think about a product as “Microsoft Office” and the contract as “Student version” or “Home edition”. You can see that the product is an abstract entity (like a Class) while the contract is the Object or a product Instance if you will.

product-setup-300x171

product setup 1st step

product-setup-2-300x192

product setup 2nd step

Once a contract was created – you can go to the contract’ “General” settings-page (there are many configurable options – but we’ll ignore it for now), copy the “Buy-link” and paste it into your browser

contract-setup-300x157

contract setup page

it will take you to your newly sandbox buy-page. If you want to place an order – you can use a generic “Visa” credit-card: 4111-1111-1111-1111 (you can enter any 3-digits in the “Security Code” field) and place an order. An example for such a buy-page: https://sandbox.plimus.com/jsp/buynow.jsp?contractId=1711978

After you’ve gone through that – you’re ready to take the “advanced class” and start playing with the API – on which I’ll write the next post.

Disclaimer
I work for Bluesnap – an online payment-processing company, but with that said, I am not one of the owners and currently I don’t hold any shares/options!

Online Payment Processing

Blog renovation

In this world of technology using wordpres for blogging seems to be the right choice, I mean, in the last couple of months I noticed that I kept postponing writing stuff in my blog due to the overhead of maintaining my own simple html blog. So I finally took the time to install & configure WP and boy, that wasn’t simple…

I think that for non-developers it would be a quite tedious task if not impossible. For example, in order to fix a bug (the “continue reading” feature didn’t work) I had to go to different forums until I finally found out that I need to change code in /wp-content/themes/<my template>/content.php

And that was only one of the tweaks I had to do. If I would start listing all the problems I had, then the most important ones will be:

  • folder permissions problem – prevents you from upload media and plugins to your server (to fix you have to run chown and chmod from linux prompt!)
  • fix .htaccess (because of the same permissions issue) manually in order to add WP additional lines
  • manually set favicon.ico turned out to be a real pain too (edit /wp-content/themes/<my template>/header.php)
  • manually change /wp-content/themes/<my template>/footer.php to NOT display “Proudly powered by…”

But on the bright side:

  • It’s so nice to easily install (after you got over the permissions-issue) plugins like:Sociable and All in One SEO Pack
  • The speed is very good even though I didn’t install a cache-plugin just yet (this is also the place to recommend the CDN services of cloudflare (I use their free package)
  • easy to choose, install and configure a template (but then again, if you’re very meticulous and want to move a certain line one millimeter to the right – get ready to dive into tons of php & css code…)
  • you get to have comments “out of the box”
  • and much more (tags, categories, menus, tons of free plugins etc)

So by all means, if you’re using another CMS for blogging purpose (I had my experience with Joomla, Drupal and Magento store – but I got to admit that I didn’t try blogger) you should re-consider your choice.

I’ll wrap up with the following cute joke:
A programmer tells his friend that yesterday he almost got busted when his wife asked him which one he loves more: her or his computer.
Friend: So, what did you reply ?
Programmer: I said I love her more
Friend: good choice! so, why do you say you almost got busted?
Programmer: cause luckily, she didn’t ask me about my laptop…

Blog renovation