Tuesday, 10 January 2012

Salesforce - Paypal Masspay Integration

It took me less than two hours to get processing test transactions with the PayPal Sandbox and Salesforce.com. This blog will cover how I setup the MassPay API, to allow my Salesforce application to send money to customer accounts, on demand.

Very quickly, first of all, MassPay is the Paypal API for transacting money from one Paypal account to many others, it is called MassPay because it can handle up to 250 accounts and transaction amounts in one go. If you want to integrate a "customers paying for stuff" shopping experience, maybe give this blog a read as well, http://techsahre.blogspot.com/2011/01/simple-paypal-integration-with.html

The basic process of integrating with the PayPal API involves dispatching an XML request to their web-service endpoint, interpreting the response, and then listening out yourself for incoming IPN messages from PayPal, which deliver status updates and information on your transactions as they are processed. This UML model roughly outlines the experience.

In this blog post, I will discuss the first half of the process. If you'd like to skip off and discover more about the IPN update messages, there is a second blog post by myself called Salesforce Paypal IPN Integration.

So first of all let's head over to the Paypal sandbox at https://developer.paypal.com/ and first register ourselves a PayPal developer account. Once you're in, create yourself two preconfigured accounts, make sure one is described a PayPal Payments Pro account - which will become the source of your money (so give it a chunky balance). The other can be a Buyer or Seller account, and this will be the test account that will receive the money. You can create as many of these as you like, depending on how thoroughly you like to test!

Now, we need to activate API access to the Pro account. So from the PayPal accounts list, select the Pro account and click "Enter Sandbox Test Site" and log in. Follow these steps:
  • Click "Profile" (the last item on the second menu under "My Account")
  • Click "Request API Credentials" underneath "Account Information"
  • In Option 1 click "Setup PayPal API credentials and permissions"
  • In Option 2 click "View API Signature"
  • Copy out your API Username, API Password and Signature.

We're now done on the PayPal side of things. So log in to your Salesforce.com account. As this blog is just about integration, I will talk you through a fairly simple demo, and you can then deploy the knowledge it gives you as you see fit.

For this demo, we are going to work with one simple custom object, called Paypal_obj. The MetaData for this is on the GitHub repository for this project, in the folder demo/pages


But for ease of click-development, here it is in UML for you to work through.

I set the label to "Paypal Payment" and the Record name to "Payment number" - as an Auto-numbering field, format:


and starting at "1".

 Once you have this custom object set up, you can go ahead and create a few records. Make sure your Paypal_status field is defaulting correctly to "Unpaid" and in the Paypal_email field, put (mostly) the e-mail addresses associated with the test buyer and seller accounts you created in PayPal above. Any e-mail addresses that arn't in that list, will be your test cases for failure, because they won't work!

With a bunch of unpaid PayPal payments in our app though, we'd best create the payment processing class to deal with them...

Click on Your Name > Setup > Develop > Apex Classes and select "New" (or, if you use an IDE, create a new class file). Now, head over the the GitHub repository for this integration and get hold of a copy of the PaypalProcessor.cls. 


Copy all this this code into the file, and give it a good read. It is explained through the comments. Most importantly, copy your API user details and signature (collected above) into the relevant fields in the generateRequest function. (Note: For production, please move these out to at least a Custom Setting Object!).

Before we can interact with the PayPal servers, you need to grant your organisation access to the Paypal web services. Click Your name -> Setup -> Security Controls -> Remote Site Settings, now add the following site to the list:

Paypal_SOAP:    https://api-3t.sandbox.paypal.com/2.0/

At this point, we basically have a functioning back end integration with PayPal. But.. it'd probably be nice to have a little UI for executing payments, not least for testing. So before we start firing SOAP requests all over the place, lets set up a page and controller to give us something to look at.

First, lets add the custom controller class to our application. Create a new Apex class and copy the contents of the approvedPaypalController.cls into it from GitHub


Once that is saved, click through Your name -> Setup -> Develop -> Pages and set up a new page called Payment_List. Copy the contents of the Payment_List.page file on GitHub into the body of the page:


 I personally then like to create a new custom Visualforce Tab to access this page, which is a lot easier than having to navigate to the URL over and over again.

With this page, controller, and tab, and a handful of test records, you should find yourself with a setup not a million miles away from this:

Now, I would imagine you are very excited right now about that custom action button "Pay out"... and so long as you have everything appropriately set up (in Sandbox) you should be able to click it. Go ahead!

The result should be a green success message at the top of the page, and all of your payment objects will have been updated to status "Sent".. much like below:

If you now log into your PayPal Payments pro account and have a look (click on "All account activity" or "Payments sent"), you should see a transaction appear for the total amount of your payments, and also the PayPal Fees amount the request would have cost.

 If you only paid out to e-mail addresses in your list of sandbox accounts, I would expect the transaction Payment Status to be "Completed" - but if (like in the screenshots above) you had other accounts involved in the mix, it will be "Processed" - because technically, PayPal will still be waiting for those accounts to collect their money - which in Sandbox they can actually never do.

So that's it! We're kind of there. The PayPal Mass Payments API has been invoked, and we have sent potentially thousands of pounds (or dollars) out into the Internet.

Of course, when we send money out on to the internet, most people want to know what has happened to it, and that is the second half of the process, which I will cover in a subsequent post, called Salesforce Paypal IPN Integration.

(by the way, if you have clicked "Pay Out" reverting the processing is as easy as editing the records and setting their status back to "Unpaid" - a testing loop you're probably going to become very familiar with.)

Friday, 6 January 2012

Writing Drupal FieldMaps with Salesforce Suite

The real power of the Drupal Salesforce Suite module is in its ability to specifically connect Drupal objects and their individual fields, to their Salesforce counterparts. Out of the box, the module comes with a pre-configured "User" and "Page" FieldMap, which give you a good feel for the abilities of this functionality, but with a little creativity, all sorts of powerful data management can be achieved.

Examining the User FieldMapping

If you access the field maps list (admin/config/salesforce/fieldmap) and click to edit the "User" row, you can see that the Last name and Postcode of the Target (Salesforce) object are mapped to the Last name and Post code of the (Drupal) User object. That is how easy it is to tie standard fields together. My Drupal user object had a lot more fields to it though, and as you can see here, they have all been simply mapped over to Salesforce equivalents in the "Members" object.

Custom fields are listed at the bottom of the Salesforce fields select box, which, incidentally, is populated by the information provided by the WSDL file you uploaded.

I did note, during the process of adding multiple fields, that sometimes the Drupal overlay didn't refresh correctly. Occasionally I would have to "Save" and re-edit the mapping to get the rows to appear correctly. The easiest way I found to keep the table fresh was to re-click the "Edit" tab after each "Add" click, whether I'd seen the new row appear or not, as this refreshed the form, guaranteeing the new field would be visible.

Once you have completed this mapping, and made sure that you have ticked at least the "Create" checkbox at the top of the mapping page, if you register a new user on your Drupal website, it should appear in both your Drupal Users view AND in your Salesforce.com contacts list. Your first synchronisation is complete! If you then navigate to your Drupal user page for the user (logged in as admin) you will see a Salesforce tab, which will detail the connection between the two objects.

As you can see here, the data for my Drupal user has been mapped to the relevant fields in Salesforce, and also there is an extra section detailing the Salesforce ID associated with the account.

Synchronising a Drupal object with Salesforce

One of the key pieces of functionality I had to deal with was recording user comments on the Drupal site in Salesforce. Luckily, "Comment" is a Drupal object already tied into the Salesforce suite, so by clicking "add" and then selecting "Comment: Poll comment" from the Drupal Object list was as simple as that.

My specification for this prototype was to register Comments as an "Event" within Salesforce. So from the Salesforce object list, I chose Event, and then began making my mappings. I was then involved in using a wide plethora of techniques to squeeze the relevant info out of Drupal and into Salesforce, as you can see from this screenshot:
(click to enlarge further)

For the "Location", "Type" and "Show Time as" fields, I used a Fixed value, so these are the same for all Event Objects created. In order to tell Salesforce to set the Start and End times as that instant, I used the "Evaluate PHP" option, and told it to return a new date object, formatted as "c" - which from the PHP Date API, you can see is a Unix timestamp - exactly as Salesforce is expecting.

The next fields were the trickiest. Whenever a new object is created by the Salesforce suite, because the SOAP connection is authenticated by the user login you provided during set-up, all new objects are created BY this account. Which means no matter who is logged into Drupal, the content is being created by "the system" in Salesforce. In order to connect the new content with the current Drupal user, I had to insert the following PHP snippet into the Lead ID field:

global $user;

$account = user_load($user->uid);

return $account->salesforce->sfid;

(Obviously, because of the textfield, this was all entered on one line)

This snippet retrieves the logged in user, loads up their (expanded) user account, and then returns the SFID (Salesforce ID) from it. I was able to use a similar technique to include the Users e-mail address, using "global $user; return $user->mail;" - which is otherwise unavailable to you in the drop-down boxes.

In order to group the comments against specific polls, I passed the comment subject into the Event subject field. In a custom drupal module, I used a form_alter to ensure the subject was the title of the poll, and I used the Comment settings to then hide the title field from the user.

function mymodule_form_alter(&$form, &$form_state, $form_id)

    if($form_id == "comment_node_poll_form")
        $form['subject']['#default_value'] = $form['#node']->title;

The body of the comment, obviously, was mapped to the description of the Event, and with that, all the information I collected in Drupal, was now also being stored in Salesforce.

 Tracking custom Drupal events in Salesforce

What do you do though, if you want to track a "customised" event in Salesforce..? Believe it or not, when a Drupal user votes in a Poll, for example, it doesn't actually create a Drupal object, and thus cannot be "sent" to Salesforce. In order to do this, I had to get a little more funky.

What I ended up doing, in a number of cases, was finding a relevant hook to the event, and then using it to trigger the programmatic creation of a custom object, which in turn trigged an exchange with Salesforce.

In the case of polls, there is a poll_vote hook, which, alongside a custom content type called "cck_poll_vote" allowed me to send Drupal votes to Salesforce.

In a custom module I wrote this hooking function that generates a cck_poll_vote each time a vote is cast:

function mymodule_vote($form_id, $form_values) {

    $poll = $form_values['complete form']['#node']->title;
    $choice = $form_values['complete form']['choice']['#options'][$form_values['complete form']['choice']['#value']];

    $body_text = 'A CCK Drupal object representing a poll vote.';

    $node = new stdClass();
    $node->type = 'cck_poll_vote';

    $node->title    = 'Poll Vote';
    $node->language = LANGUAGE_NONE;

    $node->field_poll_id[$node->language][0]['value'] = $poll;
    $node->field_choice[$node->language][0]['value'] = $choice;

    $node = node_submit($node);

It was then a simple case of creating a normal fieldMapping for a Drupal object, in the Salesforce Suite. cck_poll_vote automatically appears in the list of Drupal objects, and the mapping can be set to trigger whenever one is created, just like any other object.

Wednesday, 4 January 2012

Configuring the Drupal Salesforce Suite Module

Assuming you have successfully installed the Salesforce Suite module on Drupal (as per my previous post Integrating Salesforce.com and Drupal) and you have a Salesforce account (Developer or Enterprise), then the next logical step is to connect the two together.

Luckily doing so is fairly simple, and largely done through the Drupal front end.

First, in your Drupal Admin suite, you need to navigate to the Salesforce settings page (admin/config/salesforce/). This is where you will provide an account username and password, as well as a generated API security token. Security tokens can be generated in Salesforce (and e-mailed to the registered address) through: Your name > Setup > My Personal Information > Reset My Security Token.

Next, you must (optionally) supply the module with your Salesforce WSDL definition. This contains (among other things) the object structure of your Salesforce implementation, and the connection end point URL. You can obtain your WSDL by clicking through Your Name > Setup > Develop > API and selecting the relevant link to download the Enterprise WSDL. If you don't do this, you will only have access to the standard Salesforce.com objects, and you may also experience trouble connecting to Developer or Sandbox accounts, by virtue of their different EndPoint URLs for login.

The module provides a couple of mechanisms for uploading your WSDL to Drupal, via the WSDL tab in the module configuration (admin/config/salesforce/wsdl). You can either directly upload the file, or point the module to the system folder containing the XML file. I did find, during development at this point, that if I needed to update my WSDL definition, I had to go through a fairly trial-and-error combination of clearing caches and reloading pages to get the new details properly installed.

Once the WSDL definition is set though, you are basically ready to go! All you need to do now is click through to the Object Setup tab (admin/config/salesforce/object) to choose the objects you wish to synchronise with Salesforce.com, and then define the mappings between the fields in Salesforce.com and the fields in your Drupal installation. I will cover the complexities of field mappings in a later post, but for now, you should at least have an operational connection between Salesforce.com and Drupal.

Integrating Salesforce.com with Drupal

I was recently charged with producing a rich, modern consumer website, for a client already heavily invested in the Salesforce.com CRM. My employers, Desynit, already have a strong understanding of the Salesform.com platform, and alongside my experience with rapid development CMS systems, we set ourselves the goal of having a fully functional prototype system up and running within a week.

The core site functionality was to deliver polls, surveys and discussion forums to registered users, with a mechanism for rewarding customers for their participation and interaction; whilst at the same time, integrating all captured data with the existing Salesforce implementation.

Step up Drupal 7 and the Salesforce Suite module.

Anyone familiar with the Drupal platform will be aware of it's incredibly quick set-up. With the dawn of Drupal 7, its new and improved core modules, (Field, Poll, Comments) and site theming tools, by the end of the first day I already had a visually impressive website, with full functionality for user navigation, registration and interaction through forums, polls and article comments.

I was hoping that implementing the Salesforce suite module would prove equally smooth and efficent, and I must say, I was not disappointed. There is one additional step you have to take above and beyond the simple "paste the URL" process for installing contrib modules on Drupal 7, and that is to install the Salesforce.com PHP API. This can be downloaded from GitHub (ignore the "Sorry, there aren't any downloads for this repository" - and click on "Download as Zip/gz") and then extract it to \sites\all\libraries\salesforce. You can then rename the top level folder to "toolkit" (so that instructions.html resides in \sites\all\libraries\salesforce\toolkit). 

Once this is in place, you can navigate back to your modules page and enable whichever relevant submodules you want to use. I only needed the Core API and Entity modules to achieve my goals, and you should note that at the time of writing not all the modules were production-ready for Drupal 7.

Having enabled the modules, Drupal immediately has a new set of administration pages available to you at admin/config/salesforce, and the module even comes pre-configured with a couple of default object mappings for the fundamental Drupal concepts, User and Page. The next step you must take is to connect your Drupal site to your specific Salesforce.com instance, and I will outline how I went about this, in my next post.

Please note, this is all fantastically documented in the Salesforce Suite Documentation on drupal.org, as well as here.