MailChimp is a marketing automation platform, which provides a good REST API (MailChimp API 3.0) to communicate with other systems. MailChimp provides ‘MailChimp for Salesforce’ Appexchange app to integrate Salesforce to Mailchimp. Through this app, we can sync the Contact, Lead, Campaign from Salesforce to MailChimp and vise versa.
In this blog, I am going to demonstrate how can we listen to the change made on the MailChimp side and update the record in the Salesforce based on the change event.
MailChimp webhook allow us to collect the changed information based on the events (like Unsubscribe, Subscribe, Email Change, etc). To get the changed data we have to provide the URL for webhook, that URL going to be our Salesforce public site URL.
Below are the steps we need to follow to implement the functionality.
- Create a Visulforce page that will listen to the changes from MailChimp.
<apex:page controller="MCUnsubscribeCntrl" sidebar="false" showHeader="false" action="{! UpdateUnsubscribeMembers}" > | |
MailChimp Webhooks... | |
</apex:page> |
- Create an Apex controller for the Visualforce page, this Apex class will get the changed data from MailChimp and update the record in the Salesforce.
public without sharing class MCWebhookCntrl { | |
public PageReference UpdateUnsubscribeMembers() { | |
String sCode = fetchSecrateCode(); | |
System.debug('***Secrate Code: ### ' + sCode ); | |
String MCrequestCode = ApexPages.currentPage().getParameters().get('code'); | |
if (MCrequestCode == null || !sCode.equals(MCrequestCode )) { | |
System.debug('Code is not valid: Please use valid code: ' + MCrequestCode ); | |
return null; | |
} | |
String eventType= ApexPages.currentPage().getParameters().get('type'); | |
system.debug('Type of event: '+ eventType); | |
if(eventType == 'upemail'){ //Email Changed event | |
// Getting New Email | |
String newEmail = ApexPages.currentPage().getParameters().get('data[new_email]'); | |
system.debug('Changed Email: ' +newEmail); | |
// Getting Old Email | |
String oldEmail = ApexPages.currentPage().getParameters().get('data[old_email]'); | |
system.debug('Old Email: ' +oldEmail ); | |
if (oldEmail == null || oldEmail.length() <= 0) { | |
System.debug('Email Id is null or blank'); | |
return null; | |
} | |
else { | |
System.debug('Email for Email Opt Out: ' + oldEmail ); | |
} | |
String safeEmail = String.escapeSingleQuotes(oldEmail); | |
safeEmail.replace('*', '\\*'); | |
safeEmail.replace('?', '\\?'); | |
// SOSL search for records (Contact) | |
List<List<SObject>> searchList = [FIND :oldEmail IN EMAIL FIELDS RETURNING Contact(id) limit 1]; | |
//Contact List | |
List<Contact> contacts = ((List<Contact>)searchList[0]); | |
// Getting first record. you can iterate the list to get all records. | |
List<Contact> conListForUpdate = [select id,Email,MailChimpActivity__c from Contact where id =: contacts[0].id ]; | |
for (Contact c : conListForUpdate ) { | |
if( c.MailChimpActivity__c ==null) | |
c.MailChimpActivity__c =''; | |
c.MailChimpActivity__c += 'Email chnaged from '+c.Email+ ' to '+newEmail+' \r\n' ; | |
c.Email = newEmail; | |
} | |
// Update Lists | |
if (conListForUpdate.size() > 0) { | |
update conListForUpdate; | |
} | |
} | |
// Unsubscribe/ Subscribe | |
else if(eventType != 'profile'){ | |
String emailAddr = ApexPages.currentPage().getParameters().get('data[email]'); | |
if (emailAddr == null || emailAddr .length() <= 0) { | |
System.debug('Email Id is null or blank'); | |
return null; | |
} | |
else { | |
System.debug('Email for Email Opt Out: ' + emailAddr ); | |
} | |
String safeEmail = String.escapeSingleQuotes(emailAddr); | |
safeEmail.replace('*', '\\*'); | |
safeEmail.replace('?', '\\?'); | |
// SOSL search for records (Contact/Lead) | |
List<List<SObject>> searchList = [FIND :emailAddr IN EMAIL FIELDS RETURNING Contact(id), Lead(id) ]; | |
//Contact List | |
List<Contact> contacts = ((List<Contact>)searchList[0]); | |
//Lead List | |
List<Lead> leads = ((List<Lead>)searchList[1]); | |
for (Contact c : contacts) { | |
if(eventType =='subscribe'){ | |
c.HasOptedOutOfEmail = false; | |
}else if(eventType =='unsubscribe'){ | |
c.HasOptedOutOfEmail = true; | |
} | |
} | |
for (Lead l : leads) { | |
if(eventType =='subscribe'){ | |
l.HasOptedOutOfEmail = false; | |
}else if(eventType =='unsubscribe'){ | |
l.HasOptedOutOfEmail = true; | |
} | |
} | |
// Update Lists | |
if (contacts.size() > 0) { | |
update contacts; | |
} | |
if (leads.size() > 0) { | |
update leads; | |
} | |
} | |
return null; | |
} | |
public String fetchSecrateCode() { | |
try { | |
StaticResource sr = [ SELECT Body, Name FROM StaticResource WHERE Name = 'WebHooksConfig' LIMIT 1 ]; | |
String xml = sr.Body.toString(); | |
System.debug('XML data: ' + xml); | |
//XML parsing | |
Dom.Document doc = new Dom.Document(); | |
doc.load(xml); | |
Dom.XMLNode webhooks = doc.getRootElement(); | |
for (Dom.XMLNode node : webhooks.getChildElements()) { | |
String id = node.getAttribute('id', ''); | |
if (id.equalsIgnoreCase('unsubscribe')) { | |
return node.getAttribute('code', ''); | |
} | |
} | |
} | |
catch (Exception e) { | |
System.debug('*** Something went wrong: Error Occured! : ' + e.getMessage()); | |
} | |
return null; | |
} | |
} |
- Create an XML file. Upload this file in the static resource, static resource Name will be ‘WebHooksConfig’.
<webhooks> | |
<webhook id="unsubscribe" code="YOUR_SECRETE_CODE"/> | |
</webhooks> |
- Create the public site (force.com site) in Salesforce.
- Go to quick search and type – Site -> click Sites -> Create a domain if it is not created. Otherwise, click on the ‘New’ button to create a new site.
- Click on ‘Public Access Settings’ and give the view/edit access Contact/Lead for fields. e.g- (HasOptedOutOfEmail, MailChimpActivity__c )
Also, give access for Apex class and Visualforce page.
- Create Webhook for MailChimp List. Login into your MailChimp account and click on ‘List’ tab.
- Enter Webhook URL:( https://YourOrgDomain-developer- edition.ap5.force.com/webhooks/MCUnsubscribe?code=YOUR_SECRETE_CODE). Select the types of updates to be sent (Subscribe, Unsubscribe, email changed) and click on Save.