Background links
There are times when you have an existing lead collection process in-place such as a feedburner subscription opt-in or a forum login where you need the application/function to continue to operate as it does but you would also like to convert the visitors into names in your Marketo database. This is a perfect use-case for the Marketo Munchkin code and a dual submit
The complexity of this type of integration is primarily driven by whether you have access to a server-side environment where your form is hosted, mostly due to the generation of a security hash. If you have access to the server-side environment, then generating the security hash becomes almost trivial. On the other hand, if you don't have access to the environment, then you will need to setup a web service to generate the security hash. In the example below, I use the web service technique to keep the whole process in the browser/javascript realm.
There are three steps to this process:
For security reasons, Marketo requires that a SHA-1 hash be generated by combining a secret key chosen by you or your organisation and the lead's email address. This is designed to prevent unauthorized access to your database and filling it with spam. You can activate your Munchkin settings in the Marketo application => Admin => Integration => Munchkin => API Configuration => Edit. Enable Munchkin by checking the selection box and add a paraphrase similar to a complex password for the API Private Key and do not share it outside of your organisation.
Marketo Munchkin Admin settings
The code sample below assumes you have processed the email address via the server by either reloading the page or processing a server callout. To be more detailed, on submit you need to either reload the page to ensure that the PHP $_REQUEST global variable contains the email address in either a cookie or as a parameter of the post or get HTTP statements. For this reason, my preference is to use option 2 to avoid the page load problem. If you are already intercepting the submit, then this might be appropriate by it would be far easier to use the SOAP API instead.
code
<script type="text/javascript" language="Javascript" src="http://munchkin.marketo.net/munchkin.js"></script>
<script type="text/javascript" language="Javascript">
// make sure munchkin is initialized
mktoMunchkin("999-AAA-999");
// send the lead's email address to Marketo
mktoMunchkinFunction(
'associateLead', {
'Email': jQuery('#form.[name="Email"]').val(),
},
'<?php echo hash('sha1', 'my-secret-key' . $_REQUEST["email"]); ?>'
);
</script>
Marketo highly recommends generating the SHA-1 hash in a server-side environment like PHP, ASP, ASP.NET or Java. In this case, we have to create a small webservice that will accept an email address and return the SHA-1 hash.
PHP code
<?php
// define the API key
define('_myMarketoApiKey','mySecretKey');
// test string
//curl -v -F "email=test@example.com" http://example.com/ws.php
// Helper method to get a string description for an HTTP status code
// From http://www.gen-x-design.com/archives/create-a-rest-api-with-php/
function getStatusCodeMessage($status) {
// these could be stored in a .ini file and loaded via parse_ini_file()... however, this will suffice
$codes = Array(
100 => 'Continue', 101 => 'Switching Protocols',
200 => 'OK',201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content',
205 => 'Reset Content', 206 => 'Partial Content',
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified',
305 => 'Use Proxy',306 => '(Unused)', 307 => 'Temporary Redirect',
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found',
405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout',
409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed',
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable',
504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported'
);
return (isset($codes[$status])) ? $codes[$status] : '';
}
// Helper method to send a HTTP response code/message
function sendResponse($status = 200, $body = '', $content_type = 'text/html') {
$status_header = 'HTTP/1.1 ' . $status . ' ' . getStatusCodeMessage($status);
header($status_header);
header('Content-type: ' . $content_type);
echo $body;
}
class SimpleSHA1Service {
// Main method to calculate a SHA-1 hash
function get_sha1() {
if (isset($_REQUEST['email'])) {
$email = $_REQUEST['email'];
// check the email address via regex
if(eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $email)) {
$result = hash('sha1', _myMarketoApiKey . $email );
// build the JSON response
$result = '{ "hash":"' . $result . '" }';
sendResponse(200, $result);
return true;
}
sendResponse(405, 'Bad email address');
return false ;
}
sendResponse(400, 'Invalid request');
return false;
}
}
// This is the first thing that gets called when this page is loaded
// Creates a new instance of the SimpleSHA1Service class and calls the get_sha1 method
$api = new SimpleSHA1Service;
$api->get_sha1();
?>
Later on when we bind the form submit, we have to transform the form data into a JSON string in order to be able to send it to Marketo. To do that, we have to serialize the form data. In basic terms, convert it from a bunch individual form elmenents to a single string
Download the plugin
jQuery plugin code
<script type="text/javascript" language="Javascript">
/* jQuery.form.serialize-json.js
http://stackoverflow.com/questions/1184624/serialize-form-to-json-with-jquery
added code to remove the password
*/
jQuery.fn.serializeObject = function() {
var arrayData, objectData;
arrayData = this.serializeArray();
objectData = {};
$.each(arrayData, function() {
var value = (this.value != null ? this.value : '');
if (this.name.search(/password/i) == -1) {
if (objectData[this.name] != null) {
if (!objectData[this.name].push) {
objectData[this.name] = [objectData[this.name]];
}
objectData[this.name].push(value);
} else {
objectData[this.name] = value;
}
}
});
return objectData;
};
</script>
Now that we have all the parts, we will use a javascript AJAX call to fetch the data, process it into JSON and sent it to Marketo. Back to our favourite javascript library, jquery as there is a whole infrastructure support various AJAX methods.
jQuery code
<script type="text/javascript" language="Javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" language="Javascript">
function associateLead(email, form_data){
// a function to process the form submit and send all but the password data to Marketo's Munchkin AssociateLead function
var hash = new Object();
// the AJAX call to our SHA-1 webservice
jQuery.ajax({
type: 'POST',
url: 'http://test.hollebone.ca/ws.php',
data: 'email=' + email,
async: false,
cache: false,
success: (function(jsdata){
// should be a JSON string {"hash":"....."}
hash.key = eval("(" + jsdata + ")").hash;
hash.status = 200;
hash.error = false;
mktoMunchkinFunction("associateLead", form_data, hash.key);
}),
error: (function(jsObj, textStatus, errorThrown){
hash.key = '';
hash.response = jsObj.responseText;
hash.status = jsObj.status;
hash.error = true;
})
});
return false;
}
</script>
The last piece of the puzzle is to bind a jquery event to the form submit button and have the associateLead function as the callback
jQuery code
<script type="text/javascript" language="Javascript">
jQuery(document).ready($){
// bind the form submit
$('#FormId').submit(function(event) {
// get some values from elements on the page:
var $form = $( this ),
email = $form.find( 'input[name="Email"]' ).val();
if (email) {
// call our AJAX function and serialize the data before send (and remove any password field)
associateLead(email, JSON.stringify($form.serializeObject()));
}
});
});
</script>
Connect with me on: