Handling PayPal IPN Messages With NodeJS

Daniel Sternlicht
5 min readJul 24, 2018

--

If you ever developed an app that had any kind of business model you probably needed to support payments from PayPal.

Using PayPal as a platform to accept payments and managing subscriptions is a great solution that as easy as opening PayPal account, creating payment buttons, and adding a few lines of code to your web app. Yup. It’s that easy. 1, 2, 3 and you’re rich!

What’s an IPN Message anyway?

As a developer, I want to get notify on events such as incoming payments, subscription signups, cancellations, etc, in order to change my users’ plan, status, account balance, and even create invoices. PayPal created a webhook solution called “Instant Payment Notification” (or IPN) which allows you to set a webhook URL for your app, so once a payment event occur — PayPal will send an IPN message to the URL you set with the relevant payload.

PayPal has created a GitHub repository with a few code samples on how to implement an IPN endpoint with C#, ASP, PHP, Python, and languages, and they even have one for AWS and Google Cloud’s serverless solutions, but I couldn’t find a dissent NodeJS and Express sample.

The result: This article 🤗

P.S - Although there are two existing npm packages for handling IPN messages with NodeJS (express-ipn and paypal-ipn), in this article I’m going for the end-to-end solution - including how to handle the different transaction types.

Assuming you already have a running ExpressJS app, let’s jump into the interesting stuff.

In your main app file, make sure you’re using the body-parser middleware so you’ll end up with an app.js file that looks like that:

Next, since PayPal IPN messages are being sent in POST method, we should add a POST route to our app that will handle these requests:

Note that I created a class called IPNController which has only one static method called index . The router will route the incoming POST requests from PayPal to this method. For now, the only thing this method does is to return a 200 status code and log a line of text, so every time our Node server will get a POST request to the /ipn route, it will return 200.

Important: PayPal require us to return an OK status even before we processed the request. This way they know that the IPN endpoint is valid.

Before continuing, let’s make a quick test to ensure everything works as we expect.

IPN Simulator

PayPal offers a simple simulator that allows you to simulate IPN messages. However, the simulator isn’t capable to send requests to localhost and non-secured URLs, so we’re going to use “ngrok” in order to reveal a public secured URL (you may read all about it here).

Once ngrok if up and running, navigate to PayPal IPN Simulator, and enter the relevant address in the “IPN handler URL” field (f.e: https://v5as6v5w.ngrok.io/ipn):

Next, select a transaction type (at the moment it’s not really matter which one), scroll to the bottom of the page, and click the “Send IPN” button.

If everything works, you should get both a success message in the simulator, and the following log in your console application:

Now that we ensured everything works it’s time to move on.

Validating IPN Messages

Before processing an IPN message, we must validate that the message did come from PayPal and not from a malicious source.

In order to validate the IPN message with PayPal, there are a few steps we need to produce.

  • Get the original request payload.
  • Transform the payload into a query string.
  • Prepend a _notify_validate flag to the payload string.
  • Send the manipulated payload back to PayPal.
  • Handle PayPal response.

Important: PayPal requires us to send the original body string, with an addition of prepending cmd=_notify-validate to the string. Not sending one of the keys / values of the original body will return an INVALID response.

We’ll create a new service called PayPalService . This service will be in charge of validating the incoming IPN messages with PayPal.

As you see, this simple service follows the steps above. Once the service receives response from PayPal it will either resolve the promise if the message is verified by PayPal, or reject it if not.

This is how our IPNController will look like after validating the message with PayPal (using async / await):

Processing a PayPal IPN Message

Now that we know for sure that the IPN message is valid and sent by PayPal, we may process the message body.

Processing an IPN message could be tricky, and there are multiple transaction types needed to be handled. I’ll try to save you the hassle by mapping the important transaction types.

  • subscr_payment / web_accept — Will be sent whenever a transaction has been made, meaning, each time you receive payment.
  • subscr_signup — Will be sent when a new subscription has been created.
  • subscr_cancel — Will be sent when a subscription has been cancelled.
  • subscr_eot — Will be sent only after a subscription has been cancelled, and the subscription ended.
  • subscr_failed — Will be sent after a subscription payment has been failed.
  • recurring_payment_suspended / recurring_payment_suspended_due_to_max_failed_payment — Will be sent if a subscription is suspended due to failed payments or any other reason.

A complete list of transaction types are available on PayPal’s documentations.

Finally, after mapping the most important transaction types, our IPNController should look like this:

Updating The IPN URL on PayPal

We have a working NodeJS service that knows how to handle PayPal IPN messages. You may test it again with PayPal’s IPN simulator just to make sure that everything plays nice, and then deploy it.

Once deployed, our final step will be to update the IPN URL in PayPal’s business settings:

  • Enter your PayPal account.
  • Click the profile button, and select the “Profile and settings” link.
  • In the profile page, go to the “My Selling Tools” page.
  • Look for the “Getting paid and managing my risk” section, find the “Instant payment notifications” row, and click the “Update” button.
  • Press the “Edit Settings” button.
  • In the “Notification URL” field, insert the URL of your new PayPal IPN endpoint.
  • Click “Save”.

And… That’s pretty much it.

Hope you liked this article and found it useful, if you did — go for the claps! 👏🏻

--

--

Daniel Sternlicht

Frontend & Web technologies addict, founder of Common Ninja and There is a bot for that. Check out my personal website at http://danielsternlicht.com :)