I'm using Gravity Forms on a WP site. My forms POST via ajax to Pardot using Pardot form handlers. I am running into an issue where Pardot processes the form 6x, with no other errors. Research indicates that this is because Pardot does not support CORS or JSONP, and thus gets stuck in a loop when using ajax to submit. It's processing the submission but never "finishing" when the form handler's Success URL is set as referring URL. It tries 6x before giving up, processing the submitted data and sending new prospect notification emails each time.
Pardot help docs suggest the following solution:
It is possible to simulate a JSONP response by setting the Success and Error URLs for the form handler to be JavaScript URLs that execute Success and Error callbacks, respectively.
I'm not totally sure what this means or how to approach it. I've done some stackoverflowing and googling, but I can't seem to wrap my head around how to do it. Can someone help clarify this concept or point me in the right direction?
Thanks!
I'm using Gravity Forms on a WP site. My forms POST via ajax to Pardot using Pardot form handlers. I am running into an issue where Pardot processes the form 6x, with no other errors. Research indicates that this is because Pardot does not support CORS or JSONP, and thus gets stuck in a loop when using ajax to submit. It's processing the submission but never "finishing" when the form handler's Success URL is set as referring URL. It tries 6x before giving up, processing the submitted data and sending new prospect notification emails each time.
Pardot help docs suggest the following solution:
It is possible to simulate a JSONP response by setting the Success and Error URLs for the form handler to be JavaScript URLs that execute Success and Error callbacks, respectively.
I'm not totally sure what this means or how to approach it. I've done some stackoverflowing and googling, but I can't seem to wrap my head around how to do it. Can someone help clarify this concept or point me in the right direction?
Thanks!
Share Improve this question edited Dec 28, 2019 at 7:16 sideshowbarker♦ 88k29 gold badges214 silver badges211 bronze badges asked Nov 1, 2017 at 2:48 alisonalison 1651 silver badge5 bronze badges5 Answers
Reset to default 7EDIT: So, after a few days battling with this I'll post my final solution that might help others trying to communicate with Pardot using JSONP. It's a three part problem:
- Send a JSONP request to a Pardot Form Handler
- Redirect Pardot Form Handler success/error to your own server
- Return JSONP from your server
Send a JSONP request to Pardot From Handler
To send form data to the Form Handler you need to URI encode the field names and values.
(Example using JQuery. The '?callback=' is added by ajax() when specifying the dataType: 'jsonp'):
var data = { 'field1' = 'value1', 'field' = 'value2' };
var uriEncodedParams = $.param(data);
var targetUrl = <Pardot Form Handler URL> + '?' + uriEncodedParams;
$.ajax({
url: targetUrl,
dataType: 'jsonp',
jsonpCallback: 'callback'
});
window.callback = function (data) {...}
Redirect Pardot From Handler success/error to your own server
See @nickjag's answer:
Set the Success Location and Error Location to endpoints on your backend.
As pardot will not forward any of the GET parameters you passed in you'll have to use some defaults on i.e. the callback function name (hence specifying jsonpCallback and not having a success in my request).
Return JSONP from your server
I was having problems with console errors (Uncaught SyntaxError: Unexpected token :
) when using:
return "{ 'result' : 'success' }"
as it is a JSON-object and JSONP expects a file with JavaScript. So the format to return should be: return "callback({ 'result' : 'success' })"
And as again Pardot don't forward the GET params, the generated callback function name from JQuery didn't propagate and I couldn't return the correct JavaScript code. Defaulted to use the function name "callback" if none was provided.
Guide for returning JSONP from .NET MVC backend
What Pardot is suggesting is to create 2 static URLs on your own server that return a simple JSON response.
So for example:
mysite.com/pardot-success
Returns: {"result":"success"}
mysite.com/pardot-error
Returns: {"result":"error"}
You'll then use those two URLs as your success and error redirect URLs for your Pardot form handler settings.
An AJAX request can then be made to your Pardot form handler using JSONP, which will wrap and return the JSON response from one of those URLs (depending on the result).
Your AJAX response data would include your JSON result (success or error).
All the answers here really helped me get going on this solution, but my requirement was to get everything working only through Salesforce. So for anybody looking for the answer using only Salesforce's backend architecture, I hope this helps. I had to structure my Ajax call a little differently to get it working:
var data = { 'username': username };
var uriEncodedParams = $.param(data);
var targetUrl = 'https://my.pardot.url' + '?' + uriEncodedParams;
$.ajax({
url: targetUrl,
type: 'GET',
dataType: 'jsonp',
crossDomain: true
});
window.callback = function(data) {
performMoreActions(data);
}
My server, which is built in Apex (Salesforce's programming language) looks like this:
@RestResource(urlMapping='/pardot/success/*')
global without sharing class Pardot_SuccessProxy {
@HttpGet
global static void Pardot_SuccessProxy() {
RestContext.response.addHeader('Content-Type', 'application/json');
RestContext.response.responseBody = (Blob.valueOf('callback({ "result" : "success" })'));
}
}
I then expose this REST webservice through a Salesforce SITE, at a URL like:
https://my-domain.server-domain.force.com/services/apexrest/pardot/success
https://my-domain.server-domain.force.com/services/apexrest/pardot/error
And in the Pardot Form Handler UI, set the Success Location and Error Location fields to these URLs respectively.
This is very similar to the other answers for this question, but taken from an entirely Salesforce approach. It might be somewhat unrelated the the OPs tech-stack, but it should be helpful for people looking for answers in the future.
Thank you to all the answers on here, after a day of head scratching I managed to work out how to get this working with vanilla js (using vue). I thought I'd post here to help avoids any poor soul going through the same frustration.
Success/Error js files to be on the same domain
success.js:
logResult({ result: 'success' });
error.js:
logResult({ result: 'error' });
Add these URLs to the "Success Location" and "Error Location" fields in the Pardot Form Handler
Jsonp function
I used vue to build this site so I added the below function to /asses/js/util.js
// Copyright (c) 2017 Ziqiang Li - copied and edited from https://github.com/liziqiang/simple-jsonp-promise/blob/master/src/index.js
// Callback index.
let count = 0;
function jsonp(url, options) {
options = options || {};
const prefix = options.prefix || 'logResult';
const callback = options.callback || 'callback';
const callbackName = options.callbackName;
const params = options.data || {};
const target = document.getElementsByTagName('script')[0] || document.head;
let script;
let timer;
// Generate a unique id for the request.
const id = callbackName || prefix + count++;
function noop() {}
function cleanup() {
// Remove the script tag.
if (script && script.parentNode) {
script.parentNode.removeChild(script);
}
window[id] = noop;
if (timer) {
clearTimeout(timer);
}
}
function serialize(params) {
let param = '';
for (const key in params) {
if (params.hasOwnProperty(key)) {
param += `&${key}=${encodeURIComponent(params[key])}`;
}
}
return param;
}
function handleUrl(url, params) {
if (!~url.indexOf('?')) { url += '?'; }
url += serialize(params);
url = url.replace('?&', '?');
return url;
}
return new Promise((resolve, reject) => {
window[id] = function(data) {
cleanup();
resolve(data);
};
if (!callbackName) {
params[callback] = id;
}
url = handleUrl(url, params);
// Create script.
script = document.createElement('script');
script.src = url;
window.logResult = function(json) {
if (json.result === 'success') {
resolve();
} else if (json.result === 'error') {
reject(new Error('Error'));
}
};
script.onerror = function() {
cleanup();
reject(new Error('Network Error'));
};
target.parentNode.insertBefore(script, target);
});
}
export default jsonp;
Note: the callback function name (logResult) needs to be the same function name that is in your success/error js files
Submit function
Script in my vue component (should be easily transferrable to vanilla js/other frameworks):
<script>
import jsonp from '@/assets/js/util';
export default {
name: 'FormTest',
data() {
return {
firstname: '',
lastname: '',
email: '',
};
},
methods: {
submit() {
const options = {
data: {
firstname: this.firstname,
lastname: this.lastname,
email: this.email,
},
};
jsonp('PARDOT_FORM_HANDLER_URL', options).then(() => {
console.log('success');
}).catch(err => {
console.error(err);
});
},
},
};
</script>
@nickjag, @anders-steinrud and others answers are partially correct but they have few problems
More comprehensive steps to Pardot JSONP call would be:
- generate JSONP request by adding script to your dom (jquery ajax is not necessary, but suggested in other answers)
- choose JSONP wrapper (callback JSON or JSONP doesn't have to be
{"result":"success"}
or{"result":"error"}
, as Pardot handler redirects to an error location with a url parametererrorMessage
in case of the error) - setup a redirect location (use of 2 separate locations for success and error is suggested, but it is possible to have only one location, if using server side script to generate JSON or JSONP.)
- differentiate between JSONP requests if you are planning to have multiple requests or there is possibility there will be simultaneous requests (can be done: by adding attribute to JSONP scripts DOM element, later referencing it with
document.currentScript
; by having separate callback functions for separate JSONP requests; by using callback parameter not as a function to call but as a variable within the padded json) - clean the DOM by removing the script that was added in the first step.
I have created a solution for html forms posting to Pardot handler which includes all the steps, and more as it doesn't only deal with separate JSONP requests but handles the form itself making JSONP calls when submitting the form: Link to demo page, Link to GitHub page. Hopefully it will be useful to some readers and save their time.