Make Salesforce and PayPal Work Together

How can I make my organization's PayPal account and Salesforce account interact more closely?

By default, only credit card transactions which take place immediately after the form is submitted will show in the FormAssembly response. A PayPal transaction can take anywhere from moments (with a credit card) to days (with an eCheck).

Once the payment clears on PayPal, the IPN should go out within minutes.

With a little bit of Salesforce VisualForce code, you can make your PayPal Instant Payment Notifications (IPNs) update your Salesforce records, closing the loop from your forms to PayPal to Salesforce.

Note: The following instructions are geared towards advanced Salesforce users who are comfortable with Apex code and site setup.

In the steps below, we'll use the example of a form set up to create a Salesforce Opportunity and receive a payment for participation in that Opportunity by the respondent. The same procedure would work for any other Salesforce object type, but if you have any questions, email us.


Configure Your Salesforce Object

In Salesforce, go to Setup ➜ App Setup ➜Customize and select the object you wish to contain PayPal information. Here, we'll go with Opportunity. Add the following custom fields:

FormAssemblyID
This is a unique key that FormAssembly adds to your object in order to reference it later.
Properties: Text(255) (External ID) (Unique Case Insensitive)
API Name: FormAssemblyID__c
paid
A checkbox for easy querying in Salesforce over your objects to see if the object's PayPal payment has completed.
Properties: Checkbox
API Name: paid__c
PayPalInfo
A large field to hold all data sent by PayPal about the transaction once complete.
Properties: Long Text Area(32768)
API Name: PayPalInfo__c

Create Your Apex PayPal Notification Class

In Salesforce, go to Setup ➜ App Setup ➜ Develop ➜ Apex Classes and select New. Give the class the name IPNHandlerController, and paste the following code into the interface:

public class IPNHandlerController {

    public PageReference myIPNupdate() {
     try{
        PageReference pageRef = ApexPages.currentPage();
        //Get the value of the 'custom' parameter from current page
        String paramCustom = pageRef.getParameters().get('custom');
        opportunity = [select Id,paid__c from Opportunity where FormAssemblyID__c = :paramCustom ];

        String content = '';
        for(String key : pageRef.getParameters().keySet()){
            //Note that there is no guarantee of order in the parameter key map.
            content += key + ' : ' + pageRef.getParameters().get(key) + '\n';
        }
        opportunity.PayPalInfo__c = content;
        opportunity.paid__c = True;
        update opportunity;

        PageReference newPage = new ApexPages.StandardController(opportunity).view();
        newPage.setRedirect(true);        

        return newPage;
     } catch (System.Exception e){
         //A failure occurred
         system.debug(e);
         return null;
     }
    }

    public Opportunity opportunity {get; set;}

    public IPNHandlerController() {
    }
}

Next, if testing / code coverage is a concern, create a new Apex Class called IPNHandlerTestClass by pasting this code into a new Apex Class window:

@istest
private class IPNHandlerTestClass {
    public static testMethod void testMyIPNupdateSuccess(){
        IPNHandlerController ipn = new IPNHandlerController();

        Opportunity c = new Opportunity(Name='Test01',CloseDate=date.parse('1/1/2010'),StageName='Qualification',
FormAssemblyId__c='111111101111110z');
        insert c;
        ApexPages.currentPage().getParameters().put('custom', '111111101111110z');

        PageReference p = ipn.myIPNupdate();
        System.assertNotEquals(null,p);
    }

    public static testMethod void testMyIPNupdateFailure(){
        IPNHandlerController ipn = new IPNHandlerController();
        ApexPages.currentPage().getParameters().put('custom', '111111101111111z');
        PageReference p = ipn.myIPNupdate();
        System.assertEquals(null,p);
    }

    public static testMethod void testIPNHandlerController(){
        //You should customize this to fit your needs.
        System.assertEquals(true,true);
    }

}

Note that in the code above, we're using the Opportunity type object in Salesforce as our targeted object type to update when PayPal information comes in.  You could just as easily use a Contact object or a Custom object, by substituting the object type name for Opportunity above.


Create Your Apex PayPal Notification Endpoint:

In Salesforce, go to Setup ➜ App Setup ➜ Develop ➜ Pages and select New. Name the page IPNHandler, and paste the following code into the interface:

<apex:page controller="IPNHandlerController" action="{!myIPNupdate}" />

Now go to Setup ➜ App Setup ➜ Develop ➜ Sites and place the page into your Salesforce Site. For example, with a new site, you could set the Active Site Home Page to IPNHandler. However, this is not recommended for existing sites.

Make a note of where IPNHandler page is located. It should be something that looks like:

http://xxxxx.na3.force.com/yyyy/IPNHandler

where xxxxx.na3.force.com is your Salesforce sites domain, yyyy is the path to where you are hosting the IPNHandler page, and IPNHandler is the name of the page we've created above.


Configure Your FormAssembly Form

In FormAssembly, open your form's Salesforce Connector configuration page.

  1. Map your usual Opportunity fields.
  2. Map the Salesforce field FormAssemblyID to a formula.
  3. Set that formula value to: http://www.tfaforms.com/responses/view/%%RESPONSE_ID%%

Now, open your form's PayPal Connector configuration page:

  1. Map your PayPal connector as usual.
  2. Change your IPN notification field to the location of your IPNHandler page. For example: http://xxxxx.na3.force.com/IPNHandler

Test Your Setup

When properly configured, the process will work like so:
  1. A respondent completes the form.
  2. A new Opportunity record is created in Salesforce, with a value like http://www.tfaforms.com/responses/view/1222222 in the FormAssemblyID Salesforce field.
  3. Respondent completes PayPal section, starting the PayPal authorization and verification process.
  4. When PayPal completes that transaction, PayPal will trigger an IPN to the location: http://xxxxx.na3.force.com/IPNHandler
  5. Once there, the Apex code we've created will lookup the Opportunity based on the custom parameter included in the PayPal IPN notification which will match the FormAssemblyID field, populate the field paid to checked, and populate the field PayPalInfo to a newline delimited list of the PayPal parameters passed from the IPN.

Troubleshooting

A couple things to re-confirm, if you continue to have issues:

  • Did you "activate" the Force.com site?
  • Is the IPNHandler URL in FormAssembly correct?

There is an IPN log on the PayPal side which should be looked at first.

On the Salesforce side, you can check the Apex debug logs.

Note: PayPal's payment system is asynchronous, meaning that a person who pays by PayPal may have the transaction complete in seconds (if by credit card) or days (if by eCheck).

As a result, the data from the transaction is not available at the time the email notification is fired off and the Salesforce Connector is run. This is what setting up the IPNHandler in Salesforce is to accomplish: provide a "receiver" that will receive PayPal's transaction info seconds or days later, when the transaction is complete and the money is in your PayPal account.

Salesforce Connector

  1. Salesforce Form Import Tool
  2. New Custom Salesforce Fields (Refresh Schema)
  3. Salesforce Connector Troubleshooting
  4. Salesforce Tips & Tricks
  5. Connecting to Salesforce
  6. Create a New Salesforce Record
  7. Update an Existing Salesforce Record
  8. Field Mapping & Required Fields
  9. Field Mapping: Multiple Choice Fields, Send-As Values, Checkboxes
  10. Skip-if Formula: Conditionally Create or Update Records in Salesforce
  11. Connector Options & Triggers
  12. Populate a Form with Salesforce Data
  13. Create Dependent Objects
  14. Use Case: Create Lead or Update Contact
  15. Use Case: Campaign Members
  16. File Uploads and Salesforce Attachments
  17. Repeatable Fields & Sections with Salesforce
  18. Object Alias
  19. Households in Salesforce
  20. Record Type IDs
  21. Install FormAssembly for AppExchange
  22. Create a Secure Salesforce API User
  23. Make Salesforce and PayPal Work Together
  24. Using FormAssembly's Publishing API with Salesforce
  25. State and County Picklists
  26. Set Up Your Salesforce Connector (v1 - Obsolete)
  27. Update a Record in Salesforce with Your Form (v1 - Obsolete)
  28. Salesforce Error: INVALID_QUERY_FILTER_OPERATOR
  29. Salesforce Error: Required Fields are Missing
  30. Salesforce Error: Is not a valid value for the type xsd:date
  31. Salesforce Error: INVALID_OPERATION_WITH_EXPIRED_PASSWORD
  32. Salesforce Error: INVALID_LOGIN: Invalid username, password, security token, user not active, or user locked out
  33. Salesforce Error: Create OBJECT - The content of elements must consist of well-formed character data or markup
  34. Salesforce Error: INVALID_FIELD: No such column
  35. Salesforce Error: Create Case - No selected contact: Contact ID
  36. Salesforce Error: REQUEST_LIMIT_EXCEEDED

Feedback and Knowledge Base