Thursday 2 February 2012

Salesforce Paypal IPN integration

In my previous post Salesforce Paypal Integration I went through sending payment details to the Paypal Mass Payments API. By the end of that post, your application should be receiving "Success" messages from Paypal, and seeing payments move between two accounts. This is really only half the story though.

The "Success" message is Paypal confirming it has received the SOAP request, and it was correctly structured. You don't actually know what has happened to each payment row in your request. What, for example, happens to payments issued to e-mail addresses not registered to a Paypal account? Or if a transaction is cancelled or reversed? These are things you need to know, and that is where the Paypal IPN system comes into play.

The IPN messages are documented quite well here on the PayPal website, and indeed if you were developing in ASP or Java, that's probably all you'd need to get going with them, but this is Salesforce.com, and so things are a little different.

IPN messages are the second half of our payment process diagram:

They originate from PayPal, and are delivered to a URL in your application.

Your first action is to send the whole message back to PayPal, which will verify it originated there, and that it is intact.

You can then examine the message, extract the referenced payments, and execute update actions on their status/pay dates and so forth.

IPN messages for a transaction can appear any time up to 30 days after it was requested, and IPN messages can update a single transaction many times, depending on how complicated it proves to be.


So how do we deal with IPN messages in Salesforce? In short, we are going to do the following:
  • Establish a publically visible end point, using a visualforce page, and Salesforce sites
  • Implement a custom controller to execute the behaviour described above
We'll execute these points in reverse order though, because if we try to create the page before the controller, it will complain that it can't find the referenced files and functions.

So go to Your name -> Setup -> Develop -> Apex classes and select to make a new class. Then copy the file PaypalIPNController.cls from the GitHub repository:

 https://github.com/srlawr/PaypalSFDC/blob/master/PaypalIPNController.cls



Have a read through it so you know what's going on, and you'll probably notice that because this controller has to send the message back to PayPal we will need to add the verification URL to our Remote Site Settings so your application can communicate with the outside world.

Go to Your name -> Setup -> Security Controls -> Remote Site Settings and add a new Paypal URL for:

PayPal Sandbox IPN: https://www.sandbox.paypal.com/cgi-bin/webscr

With the Apex controller in place, we need to create the page to front it. There isn't actually anything on this page, because it's never going to be served out to a browser, it is simply acting as as URL for web-service calls. We do need to point it at this controller though,

Navigate your way to  Your name -> Setup  -> Develop -> Pages and create a new page. Call it Paypal_IPN, for clarity. This Visualforce page only has one real line to it, which you can see in paypal_ipn.page on GitHub:

https://github.com/srlawr/PaypalSFDC/blob/master/paypal_ipn.page


This is all the code you really need. When the page is loaded, the controller is constructed, and you can see the Apex page "Action" attribute to call the function "digestMessage", this is because you cannot make DML calls in a controller's constructor, or any method calls direct from it.

The final action on the Salesforce side of things is to expose this page via Salesforce sites. Click Your name -> Setup -> Develop -> Sites and click to create a new Site. I simply set the label, name and default web address all to "Paypal" and then in the Active Site Home Page selected the new page we just created.

The sites detail page should list a number of attributes of this new object, one of which is "Secure Web Address" - copy the value of this to your clipboard, because we're going to need it in a minute.


Next then, back in your Paypal sandbox, we need to tell it what URL to send the messages too, so log into your Payments Pro account and click on "Profile" -> "Instant Payment Notification preferences". On this page, you will need to enter the Secure Web Address of the Salesforce.com page, and Enable IPN messages. From the confirmation page, head over to the "IPN History Page", this page could become your best friend during development/testing.



Ready to roll!

It feels like we've done enough work now. Lets see if it's all come together.

If you return to your Payments List page, and make sure you have a handful of payment records with a status of "Unpaid", you should find yourself looking at something like this:


Just like in the last stage, if you click the "Pay out" button, the page should reload, with a success message and, having updated all the PayPal Status values to "Sent".



What has happened now is PayPal has received the request and is whipping through each of your transfers. Typically, (typically!!) this takes a few seconds in the first instance, and after each row has been considered, your first IPN message will be sent over by PayPal.

Twiddle your thumbs for just a few seconds (or if you wish to use this time constructively, check your PayPal sandbox, and make sure your transaction has been recorded on their side) and then reload the Payment List page in Salesforce.

You should see the status column has been updated, to something similar to this:


In this case, three of my transactions (those associated with my PayPal Sandbox email addresses) have completed entirely, and instantly, but one transaction is currently sitting unclaimed, because that e-mail address isn't associated with a PayPal account. You can read up more on IPN status codes over on the PayPal website.

In Paypal you can view your IPN messages, like the one below, from your IPN history page, which you'll find linked from the configuration page you put your IPN endpoint in above.



That really is it though, for a basic demonstration of Integrating the PayPal Mass Payments API with Force.com.

It should be absolutely noted that this is a purely educational blog post, and no-one should ever attempt to implement payment gateways or infrastructures without the appropriate knowledge and testing.

If you would like to talk to Desynit about applying this knowledge to your business or organisation, please get in touch via the details on our website. Otherwise, any questions or discussions are always more than welcome in the comments section below.