How to process Instant Payment Notification (IPN) messages

APILegacyLast updated: December 11th 2023, @ 12:40:16 pm

Important: NVP/SOAP is a legacy integration method. We accept new integrations and support existing integrations, but there are newer solutions. If you're starting an integration, we recommend our latest solutions.

IPN is a message service that sends you a notification when any type of transaction occurs in your account. The messages are received and processed by your server(s).

Process overview

To receive and process Instant Payment Notification (IPN) messages:

  1. Create an IPN listener to receive and process the IPN messages sent by PayPal. For complete details on creating an IPN listener, see Implementing an IPN Listener.
  2. In your PayPal merchant account settings, enable the IPN Message Service and specify the URL of your listener.
  3. Continuously run the IPN listener on the website specified by your listener URL.

Note: For instructions on using the PayPal APIs, how to use the sandbox for testing, and how to move your app into production, see Apps 101.

Do it

To receive and process IPN messages:

  1. Create a listener that:

    • Receives an IPN message sent from PayPal
    • Confirms to PayPal that the message was received
    • Verifies that it is a valid IPN message using the request-response handshaking required by PayPal
    • Processes the IPN messages according to your needs

    To create a basic listener that echoes IPN messages to a browser window:

    • Capture the IPN messages that PayPal sends to your listener

      After receiving an IPN message from PayPal, you must respond to PayPal with a POST message that is an exact copy of the received message but with "cmd=_notify-validate" added to the end of the message. Append to your message a duplicate of the notification received (the same IPN fields and values in the exact order you received them):

      // STEP 1: read POST data
      // Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
      // Instead, read raw POST data from the input stream.
      $raw_post_data = file_get_contents('php://input');
      $raw_post_array = explode('&', $raw_post_data);
      $myPost = array();
      foreach ($raw_post_array as $keyval) {
        $keyval = explode ('=', $keyval);
        if (count($keyval) == 2)
          $myPost[$keyval[0]] = urldecode($keyval[1]);
      // read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
      $req = 'cmd=_notify-validate';
      if (function_exists('get_magic_quotes_gpc')) {
        $get_magic_quotes_exists = true;
      foreach ($myPost as $key => $value) {
        if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
          $value = urlencode(stripslashes($value));
        } else {
          $value = urlencode($value);
        $req .= "&$key=$value";
      // Step 2: POST IPN data back to PayPal to validate
      $ch = curl_init('');
      curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
      curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
      curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
      // In wamp-like environments that do not come bundled with root authority certificates,
      // please download 'cacert.pem' from "" and set
      // the directory path of the certificate as shown below:
      // curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
      if ( !($res = curl_exec($ch)) ) {
        // error_log("Got " . curl_error($ch) . " when processing IPN data");

      When PayPal receives your POST message, it sends a message back to your listener to indicate the validity of the initial notification. PayPal's message has an HTTP status code of 200 and a body that contains either VERIFIED or INVALID.

    • Before processing the IPN message, ensure that the message is valid as follows:

      // inspect IPN validation result and act accordingly
      if (strcmp ($res, "VERIFIED") == 0) {
        // The IPN is verified, process it
      } else if (strcmp ($res, "INVALID") == 0) {
        // IPN invalid, log for manual investigation
    • Parse the message for information on the transaction referenced by the IPN, the type of notification sent, and the associated transaction status. Take the appropriate action(s) based on the notification received:

      // inspect IPN validation result and act accordingly
      if (strcmp ($res, "VERIFIED") == 0) {
        // The IPN is verified, process it:
        // check whether the payment_status is Completed
        // check that txn_id has not been previously processed
        // check that receiver_email is your Primary PayPal email
        // check that payment_amount/payment_currency are correct
        // process the notification
        // assign posted variables to local variables
        $item_name = $_POST['item_name'];
        $item_number = $_POST['item_number'];
        $payment_status = $_POST['payment_status'];
        $payment_amount = $_POST['mc_gross'];
        $payment_currency = $_POST['mc_currency'];
        $txn_id = $_POST['txn_id'];
        $receiver_email = $_POST['receiver_email'];
        $payer_email = $_POST['payer_email'];
        // IPN message values depend upon the type of notification sent.
        // To loop through the &_POST array and print the NV pairs to the screen:
        foreach($_POST as $key => $value) {
          echo $key . " = " . $value . "<br />";
      } else if (strcmp ($res, "INVALID") == 0) {
        // IPN invalid, log for manual investigation
        echo "The response from IPN was: <b>" .$res ."</b>";
  2. Log in to your PayPal business account and specify the Notification URL to your IPN listener. For detailed instructions about how to specify the Notification URL, see Identifying your IPN listener to PayPal. As an example, the web server where you host a PHP listener might resemble the following URL:

    In addition to enabling the IPN service and setting the Notification URL location through your PayPal account, you can also set the location using the NOTIFYURL parameter in an API call. By dynamically setting the Notification URL, you can set up different listeners for different needs (such as if you are supporting different merchant sites with a single PayPal account).

  3. Run your listener on a server that hosts the URL you specified as your Notification URL in Step 1.

    Your listener must always be accessible at the specified website—PayPal can send notifications at any time of day.

Tip: View IPN Code Samples for several different programming languages.

Learn more

PayPal sends IPN messages for every type of transaction or transaction status update (including payment and subscription notifications), and each notification type contains a unique set of fields. You need to configure your listener to handle the fields for every type of IPN message you might receive, depending on the types of PayPal transactions you support. For a complete guide on the different types of IPN messages and the data fields associated with each type, see the IPN Integration Guide.

Consider the following processes in your notification handler to properly direct the notifications and to guard against fraud:

  • Use the Transaction ID value to ensure you haven't already processed the notification.
  • Confirm the status of the transaction, and take proper action depending on value. For example, payment response options include Completed, Pending, and Denied. Don't send inventory unless the transaction has completed!
  • Validate the e-mail address of the receiver.
  • Verify the item description and transaction costs with those listed on your website and catalog.
  • Use the values of txn_type or reason_code of a VERIFIED notification to determine your processing actions.