[
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012, Micah Carrick\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, \nare permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright notice, \n      this list of conditions and the following disclaimer.\n      \n    * Redistributions in binary form must reproduce the above copyright notice, \n    this list of conditions and the following disclaimer in the documentation \n    and/or other materials provided with the distribution.\n    \n    * Neither the name of Quixotix, LLC nor the names of its \n    contributors may be used to endorse or promote products derived from this \n    software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND \nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR \nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "PHP-PayPal-IPN\n==============\n\n__This code is over a decade old and no longer relevant. Please consult Paypal developer documentation instead.__\n\nA PayPal Instant Payment Notification (IPN) class for PHP 5. \n\nUse the `IpnListener` class in your PHP IPN script to handle the encoding \nof POST data, post back to PayPal, and parsing of the response from PayPal.\n\n\nFeatures\n--------\n\n* Switch between live and sandbox by setting the `use_sandbox` property.\n* Supports both secure SSL and plain HTTP transactions by setting the `use_ssl`\n  property (SSL is recommended).\n* Supports both cURL and fsockopen network libraries by setting the `use_curl`\n  property (cURL is recommended).\n* Verifies an HTTP &quot;200&quot; response status code from the PayPal server.\n* Get detailed plain text reports of the entire IPN using the `getTextReport()` \n  method for use in emails and logs to administrators.\n* Throws various exceptions to differentiate between common errors in code or\n  server configuration versus invalid IPN responses.\n\n\nGetting Started\n---------------\n\nThis code is intended for web developers. You should understand how the IPN\nprocess works conceptually and you should understand when and why you would be\nusing IPN. Reading the [PayPal Instant Payment Notification Guide][1] is a good\nplace to start.\n\nYou should also have a [PayPal Sandbox Account][2] with a test buyer account and\na test seller account. When logged into your sandbox account there is an IPN\nsimulator under the 'Test Tools' menu which you can used to test your IPN \nlistener.\n\n[1]: https://cms.paypal.com/cms_content/US/en_US/files/developer/IPNGuide.pdf\n[2]: https://developer.paypal.com\n\nOnce you have your sandbox account setup, you simply create a PHP script that\nwill be your IPN listener. In that script, use the `IpnListener()` class as shown\nbelow. For a more thoroughly documented example, take a look at the \n`example/ipn.php` script in the source code.\n\n    <?php\n\n    include('ipnlistener.php');\n\n    $listener = new IpnListener();\n    $listener->use_sandbox = true;\n\n    try {\n        $verified = $listener->processIpn();\n    } catch (Exception $e) {\n        // fatal error trying to process IPN.\n        exit(0);\n    }\n\n    if ($verified) {\n        // IPN response was \"VERIFIED\"\n    } else {\n        // IPN response was \"INVALID\"\n    }\n\n    ?>\n\n\nDocumentation\n-------------\n\nDocumentation has not been generated yet, but, there are phpDocumentor style\ndocstrings (comments) throughout `ipnlistener.php` which explain the important\npublic properties and methods.\n\nI have also written a more in-depth IPN tutorial on my blog: [PayPal IPN with PHP][3]\n\n[3]: http://www.micahcarrick.com/paypal-ipn-with-php.html\n\n\nKnown Issues\n------------\n\n__Problem__\n\nThe `processIpn()` method throws the following exception:\n\n    cURL error: [52] GnuTLS recv error (-9): A TLS packet with unexpected length was received.\n\n__Solution__\n\nWhen cURL is compiled with GnuTLS the call to PayPal will fail if the SSL version\nis not explicitly set as a cURL option. Set the `force_ssl_v3` property to force \nSSL 3:\n\n    $listener = new IpnListener();\n    $listener->force_ssl_v3 = true;\n\n_Note: force_ssl_v3 is now true by default_\n\n\n\n__Problem__\n\n     PHP Warning: curl_setopt() [function.curl-setopt]: CURLOPT_FOLLOWLOCATION \n     cannot be activated when in safe_mode or an open_basedir is set in ...\n\n__Solution__\n\nIf you need PHP safe mode, you can disable CURLOPT_FOLLOWLOCATION using the\n`follow_location` property.\n\n    $listener = new IpnListener();\n    $listener->follow_location = false;\n\n_Note: follow_location is now false enabled by default_\n\n\nExample Report\n--------------\n\nHere is an example of a report returned by the `getTextReport()` method. Create\nyour own reports by extending the `IpnListener()` class or by accessing the data\ndirectly in your ipn script.\n\n    --------------------------------------------------------------------------------\n    [09/09/2011 8:35 AM] - https://www.sandbox.paypal.com/cgi-bin/webscr (curl)\n    --------------------------------------------------------------------------------\n    HTTP/1.1 200 OK\n    Date: Fri, 09 Sep 2011 13:35:39 GMT\n    Server: Apache\n    X-Frame-Options: SAMEORIGIN\n    Set-Cookie: c9MWDuvPtT9GIMyPc3jwol1VSlO=Ch-NORlHUjlmbEm__KG9LupR4mfMfQTkx1QQ6hHDyc0RImWr88NY_ILeICENiwtVX3iw4jEnT1-1gccYjQafWrQCkDmiykNT8TeDUg7R7L0D9bQm47PTG8MafmrpyrUAxQfst0%7c_jG1ZL6CffJgwrC-stQeqni04tKaYSIZqyqhFU7tKnV520wiYOw0hwk5Ehrh3hLDvBxkpm%7cYTFdl0w0YpEqxu0D1jDTVTlEGXlmLs4wob2Glu9htpZkFV9O2aCyfQ4CvA2kLJmlI6YiXm%7c1315575340; domain=.paypal.com; path=/; Secure; HttpOnly\n    Set-Cookie: cookie_check=yes; expires=Mon, 06-Sep-2021 13:35:40 GMT; domain=.paypal.com; path=/; Secure; HttpOnly\n    Set-Cookie: navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly\n    Set-Cookie: navlns=0.0; expires=Thu, 04-Sep-2031 13:35:40 GMT; domain=.paypal.com; path=/; Secure; HttpOnly\n    Set-Cookie: Apache=10.72.109.11.1315575339707456; path=/; expires=Sun, 01-Sep-41 13:35:39 GMT\n    X-Cnection: close\n    Transfer-Encoding: chunked\n    Content-Type: text/html; charset=UTF-8\n\n    VERIFIED\n    --------------------------------------------------------------------------------\n    test_ipn                 1\n    payment_type             instant\n    payment_date             06:34:51 Sep 09, 2011 PDT\n    payment_status           Completed\n    address_status           confirmed\n    payer_status             verified\n    first_name               John\n    last_name                Smith\n    payer_email              buyer@paypalsandbox.com\n    payer_id                 TESTBUYERID01\n    address_name             John Smith\n    address_country          United States\n    address_country_code     US\n    address_zip              95131\n    address_state            CA\n    address_city             San Jose\n    address_street           123, any street\n    business                 seller@paypalsandbox.com\n    receiver_email           seller@paypalsandbox.com\n    receiver_id              TESTSELLERID1\n    residence_country        US\n    item_name                something\n    item_number              AK-1234\n    quantity                 1\n    shipping                 3.04\n    tax                      2.02\n    mc_currency              USD\n    mc_fee                   0.44\n    mc_gross                 12.34\n    mc_gross_1               9.34\n    txn_type                 web_accept\n    txn_id                   51991334\n    notify_version           2.1\n    custom                   xyz123\n    charset                  windows-1252\n    verify_sign              Ah5rOpfPGo5g6FNg95DMPybP51J5AUEdXS1hqyRAP6WYYwaixKNDgQRR\n"
  },
  {
    "path": "cert/api_cert_chain.crt",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIDgzCCAuygAwIBAgIQJUuKhThCzONY+MXdriJupDANBgkqhkiG9w0BAQUFADBf\r\nMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT\r\nLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw\r\nHhcNOTcwNDE3MDAwMDAwWhcNMTExMDI0MjM1OTU5WjCBujEfMB0GA1UEChMWVmVy\r\naVNpZ24gVHJ1c3QgTmV0d29yazEXMBUGA1UECxMOVmVyaVNpZ24sIEluYy4xMzAx\r\nBgNVBAsTKlZlcmlTaWduIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gQ2xhc3Mg\r\nMzFJMEcGA1UECxNAd3d3LnZlcmlzaWduLmNvbS9DUFMgSW5jb3JwLmJ5IFJlZi4g\r\nTElBQklMSVRZIExURC4oYyk5NyBWZXJpU2lnbjCBnzANBgkqhkiG9w0BAQEFAAOB\r\njQAwgYkCgYEA2IKA6NYZAn0fhRg5JaJlK+G/1AXTvOY2O6rwTGxbtueqPHNFVbLx\r\nveqXQu2aNAoV1Klc9UAl3dkHwTKydWzEyruj/lYncUOqY/UwPpMo5frxCTvzt01O\r\nOfdcSVq4wR3Tsor+cDCVQsv+K1GLWjw6+SJPkLICp1OcTzTnqwSye28CAwEAAaOB\r\n4zCB4DAPBgNVHRMECDAGAQH/AgEAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw\r\nKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL0NQUzA0BgNV\r\nHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIGCWCGSAGG+EIEAQYKYIZIAYb4RQEI\r\nATALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMDEGA1UdHwQqMCgwJqAk\r\noCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA0GCSqGSIb3DQEB\r\nBQUAA4GBAAgB7ORolANC8XPxI6I63unx2sZUxCM+hurPajozq+qcBBQHNgYL+Yhv\r\n1RPuKSvD5HKNRO3RrCAJLeH24RkFOLA9D59/+J4C3IYChmFOJl9en5IeDCSk9dBw\r\nE88mw0M9SR2egi5SX7w+xmYpAY5Okiy8RnUDgqxz6dl+C2fvVFIa\r\n-----END CERTIFICATE-----\r\n-----BEGIN CERTIFICATE-----\r\nMIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG\r\nA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz\r\ncyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2\r\nMDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV\r\nBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt\r\nYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN\r\nADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE\r\nBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is\r\nI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G\r\nCSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do\r\nlbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc\r\nAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k\r\n-----END CERTIFICATE-----\r\n-----BEGIN CERTIFICATE-----\r\nMIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ\r\nBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh\r\nc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy\r\nMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp\r\nemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X\r\nDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw\r\nFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg\r\nUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo\r\nYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5\r\nMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB\r\nAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4\r\npO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0\r\n13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID\r\nAQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk\r\nU01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i\r\nF6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY\r\noJ2daZH9\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "example/ipn.php",
    "content": "<?php\n/**\n *  PHP-PayPal-IPN Example\n *\n *  This shows a basic example of how to use the IpnListener() PHP class to \n *  implement a PayPal Instant Payment Notification (IPN) listener script.\n *\n *  For a more in depth tutorial, see my blog post:\n *  http://www.micahcarrick.com/paypal-ipn-with-php.html\n *\n *  This code is available at github:\n *  https://github.com/Quixotix/PHP-PayPal-IPN\n *\n *  @package    PHP-PayPal-IPN\n *  @author     Micah Carrick\n *  @copyright  (c) 2011 - Micah Carrick\n *  @license    http://opensource.org/licenses/gpl-3.0.html\n */\n \n \n/*\nSince this script is executed on the back end between the PayPal server and this\nscript, you will want to log errors to a file or email. Do not try to use echo\nor print--it will not work! \n\nHere I am turning on PHP error logging to a file called \"ipn_errors.log\". Make\nsure your web server has permissions to write to that file. In a production \nenvironment it is better to have that log file outside of the web root.\n*/\nini_set('log_errors', true);\nini_set('error_log', dirname(__FILE__).'/ipn_errors.log');\n\n\n// instantiate the IpnListener class\ninclude('../ipnlistener.php');\n$listener = new IpnListener();\n\n\n/*\nWhen you are testing your IPN script you should be using a PayPal \"Sandbox\"\naccount: https://developer.paypal.com\nWhen you are ready to go live change use_sandbox to false.\n*/\n$listener->use_sandbox = true;\n\n/*\nBy default the IpnListener object is going  going to post the data back to PayPal\nusing cURL over a secure SSL connection. This is the recommended way to post\nthe data back, however, some people may have connections problems using this\nmethod. \n\nTo post over standard HTTP connection, use:\n$listener->use_ssl = false;\n\nTo post using the fsockopen() function rather than cURL, use:\n$listener->use_curl = false;\n*/\n\n/*\nThe processIpn() method will encode the POST variables sent by PayPal and then\nPOST them back to the PayPal server. An exception will be thrown if there is \na fatal error (cannot connect, your server is not configured properly, etc.).\nUse a try/catch block to catch these fatal errors and log to the ipn_errors.log\nfile we setup at the top of this file.\n\nThe processIpn() method will send the raw data on 'php://input' to PayPal. You\ncan optionally pass the data to processIpn() yourself:\n$verified = $listener->processIpn($my_post_data);\n*/\ntry {\n    $listener->requirePostMethod();\n    $verified = $listener->processIpn();\n} catch (Exception $e) {\n    error_log($e->getMessage());\n    exit(0);\n}\n\n\n/*\nThe processIpn() method returned true if the IPN was \"VERIFIED\" and false if it\nwas \"INVALID\".\n*/\nif ($verified) {\n    /*\n    Once you have a verified IPN you need to do a few more checks on the POST\n    fields--typically against data you stored in your database during when the\n    end user made a purchase (such as in the \"success\" page on a web payments\n    standard button). The fields PayPal recommends checking are:\n    \n        1. Check the $_POST['payment_status'] is \"Completed\"\n\t    2. Check that $_POST['txn_id'] has not been previously processed \n\t    3. Check that $_POST['receiver_email'] is your Primary PayPal email \n\t    4. Check that $_POST['payment_amount'] and $_POST['payment_currency'] \n\t       are correct\n    \n    Since implementations on this varies, I will leave these checks out of this\n    example and just send an email using the getTextReport() method to get all\n    of the details about the IPN.  \n    */\n    mail('YOUR EMAIL ADDRESS', 'Verified IPN', $listener->getTextReport());\n\n} else {\n    /*\n    An Invalid IPN *may* be caused by a fraudulent transaction attempt. It's\n    a good idea to have a developer or sys admin manually investigate any \n    invalid IPN.\n    */\n    mail('YOUR EMAIL ADDRESS', 'Invalid IPN', $listener->getTextReport());\n}\n\n?>\n"
  },
  {
    "path": "example/ipn_errors.log",
    "content": ""
  },
  {
    "path": "ipnlistener.php",
    "content": "<?php\n/**\n *  PayPal IPN Listener\n *\n *  A class to listen for and handle Instant Payment Notifications (IPN) from \n *  the PayPal server.\n *\n *  https://github.com/Quixotix/PHP-PayPal-IPN\n *\n *  @package    PHP-PayPal-IPN\n *  @author     Micah Carrick\n *  @copyright  (c) 2012 - Micah Carrick\n *  @version    2.1.0\n */\nclass IpnListener {\n    \n    /**\n     *  If true, the recommended cURL PHP library is used to send the post back \n     *  to PayPal. If flase then fsockopen() is used. Default true.\n     *\n     *  @var boolean\n     */\n    public $use_curl = true;     \n    \n    /**\n     *  If true, explicitly sets cURL to use SSL version 3. Use this if cURL\n     *  is compiled with GnuTLS SSL.\n     *\n     *  @var boolean\n     */\n    public $force_ssl_v3 = true;     \n   \n    /**\n     *  If true, cURL will use the CURLOPT_FOLLOWLOCATION to follow any \n     *  \"Location: ...\" headers in the response.\n     *\n     *  @var boolean\n     */\n    public $follow_location = false;     \n    \n    /**\n     *  If true, an SSL secure connection (port 443) is used for the post back \n     *  as recommended by PayPal. If false, a standard HTTP (port 80) connection\n     *  is used. Default true.\n     *\n     *  @var boolean\n     */\n    public $use_ssl = true;      \n    \n    /**\n     *  If true, the paypal sandbox URI www.sandbox.paypal.com is used for the\n     *  post back. If false, the live URI www.paypal.com is used. Default false.\n     *\n     *  @var boolean\n     */\n    public $use_sandbox = false; \n    \n    /**\n     *  The amount of time, in seconds, to wait for the PayPal server to respond\n     *  before timing out. Default 30 seconds.\n     *\n     *  @var int\n     */\n    public $timeout = 30;       \n    \n    private $post_data = array();\n    private $post_uri = '';     \n    private $response_status = '';\n    private $response = '';\n\n    const PAYPAL_HOST = 'www.paypal.com';\n    const SANDBOX_HOST = 'www.sandbox.paypal.com';\n    \n    /**\n     *  Post Back Using cURL\n     *\n     *  Sends the post back to PayPal using the cURL library. Called by\n     *  the processIpn() method if the use_curl property is true. Throws an\n     *  exception if the post fails. Populates the response, response_status,\n     *  and post_uri properties on success.\n     *\n     *  @param  string  The post data as a URL encoded string\n     */\n    protected function curlPost($encoded_data) {\n\n        if ($this->use_ssl) {\n            $uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';\n            $this->post_uri = $uri;\n        } else {\n            $uri = 'http://'.$this->getPaypalHost().'/cgi-bin/webscr';\n            $this->post_uri = $uri;\n        }\n        \n        $ch = curl_init();\n\n\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);\n\t\tcurl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);\n\t\tcurl_setopt($ch, CURLOPT_CAINFO, \n\t\t            dirname(__FILE__).\"/cert/api_cert_chain.crt\");\n        curl_setopt($ch, CURLOPT_URL, $uri);\n        curl_setopt($ch, CURLOPT_POST, true);\n        curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);\n        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->follow_location);\n        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n        curl_setopt($ch, CURLOPT_HEADER, true);\n        \n        if ($this->force_ssl_v3) {\n            curl_setopt($ch, CURLOPT_SSLVERSION, 3);\n        }\n        \n        $this->response = curl_exec($ch);\n        $this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));\n        \n        if ($this->response === false || $this->response_status == '0') {\n            $errno = curl_errno($ch);\n            $errstr = curl_error($ch);\n            throw new Exception(\"cURL error: [$errno] $errstr\");\n        }\n    }\n    \n    /**\n     *  Post Back Using fsockopen()\n     *\n     *  Sends the post back to PayPal using the fsockopen() function. Called by\n     *  the processIpn() method if the use_curl property is false. Throws an\n     *  exception if the post fails. Populates the response, response_status,\n     *  and post_uri properties on success.\n     *\n     *  @param  string  The post data as a URL encoded string\n     */\n    protected function fsockPost($encoded_data) {\n    \n        if ($this->use_ssl) {\n            $uri = 'ssl://'.$this->getPaypalHost();\n            $port = '443';\n            $this->post_uri = $uri.'/cgi-bin/webscr';\n        } else {\n            $uri = $this->getPaypalHost(); // no \"http://\" in call to fsockopen()\n            $port = '80';\n            $this->post_uri = 'http://'.$uri.'/cgi-bin/webscr';\n        }\n\n        $fp = fsockopen($uri, $port, $errno, $errstr, $this->timeout);\n        \n        if (!$fp) { \n            // fsockopen error\n            throw new Exception(\"fsockopen error: [$errno] $errstr\");\n        } \n\n        $header = \"POST /cgi-bin/webscr HTTP/1.1\\r\\n\";\n        $header .= \"Host: \".$this->getPaypalHost().\"\\r\\n\";\n        $header .= \"Content-Type: application/x-www-form-urlencoded\\r\\n\";\n        $header .= \"Content-Length: \".strlen($encoded_data).\"\\r\\n\";\n        $header .= \"Connection: Close\\r\\n\\r\\n\";\n        \n        fputs($fp, $header.$encoded_data.\"\\r\\n\\r\\n\");\n        \n        while(!feof($fp)) { \n            if (empty($this->response)) {\n                // extract HTTP status from first line\n                $this->response .= $status = fgets($fp, 1024); \n                $this->response_status = trim(substr($status, 9, 4));\n            } else {\n                $this->response .= fgets($fp, 1024); \n            }\n        } \n        \n        fclose($fp);\n    }\n    \n    private function getPaypalHost() {\n        if ($this->use_sandbox) return self::SANDBOX_HOST;\n        else return self::PAYPAL_HOST;\n    }\n    \n    /**\n     *  Get POST URI\n     *\n     *  Returns the URI that was used to send the post back to PayPal. This can\n     *  be useful for troubleshooting connection problems. The default URI\n     *  would be \"ssl://www.sandbox.paypal.com:443/cgi-bin/webscr\"\n     *\n     *  @return string\n     */\n    public function getPostUri() {\n        return $this->post_uri;\n    }\n    \n    /**\n     *  Get Response\n     *\n     *  Returns the entire response from PayPal as a string including all the\n     *  HTTP headers.\n     *\n     *  @return string\n     */\n    public function getResponse() {\n        return $this->response;\n    }\n    \n    /**\n     *  Get Response Status\n     *\n     *  Returns the HTTP response status code from PayPal. This should be \"200\"\n     *  if the post back was successful. \n     *\n     *  @return string\n     */\n    public function getResponseStatus() {\n        return $this->response_status;\n    }\n    \n    /**\n     *  Get Text Report\n     *\n     *  Returns a report of the IPN transaction in plain text format. This is\n     *  useful in emails to order processors and system administrators. Override\n     *  this method in your own class to customize the report.\n     *\n     *  @return string\n     */\n    public function getTextReport() {\n        \n        $r = '';\n        \n        // date and POST url\n        for ($i=0; $i<80; $i++) { $r .= '-'; }\n        $r .= \"\\n[\".date('m/d/Y g:i A').'] - '.$this->getPostUri();\n        if ($this->use_curl) $r .= \" (curl)\\n\";\n        else $r .= \" (fsockopen)\\n\";\n        \n        // HTTP Response\n        for ($i=0; $i<80; $i++) { $r .= '-'; }\n        $r .= \"\\n{$this->getResponse()}\\n\";\n        \n        // POST vars\n        for ($i=0; $i<80; $i++) { $r .= '-'; }\n        $r .= \"\\n\";\n        \n        foreach ($this->post_data as $key => $value) {\n            $r .= str_pad($key, 25).\"$value\\n\";\n        }\n        $r .= \"\\n\\n\";\n        \n        return $r;\n    }\n    \n    /**\n     *  Process IPN\n     *\n     *  Handles the IPN post back to PayPal and parsing the response. Call this\n     *  method from your IPN listener script. Returns true if the response came\n     *  back as \"VERIFIED\", false if the response came back \"INVALID\", and \n     *  throws an exception if there is an error.\n     *\n     *  @param array\n     *\n     *  @return boolean\n     */    \n    public function processIpn($post_data=null) {\n\n        $encoded_data = 'cmd=_notify-validate';\n        \n        if ($post_data === null) { \n            // use raw POST data \n            if (!empty($_POST)) {\n                $this->post_data = $_POST;\n                $encoded_data .= '&'.file_get_contents('php://input');\n            } else {\n                throw new Exception(\"No POST data found.\");\n            }\n        } else { \n            // use provided data array\n            $this->post_data = $post_data;\n            \n            foreach ($this->post_data as $key => $value) {\n                $encoded_data .= \"&$key=\".urlencode($value);\n            }\n        }\n\n        if ($this->use_curl) $this->curlPost($encoded_data); \n        else $this->fsockPost($encoded_data);\n        \n        if (strpos($this->response_status, '200') === false) {\n            throw new Exception(\"Invalid response status: \".$this->response_status);\n        }\n        \n        if (strpos($this->response, \"VERIFIED\") !== false) {\n            return true;\n        } elseif (strpos($this->response, \"INVALID\") !== false) {\n            return false;\n        } else {\n            throw new Exception(\"Unexpected response from PayPal.\");\n        }\n    }\n    \n    /**\n     *  Require Post Method\n     *\n     *  Throws an exception and sets a HTTP 405 response header if the request\n     *  method was not POST. \n     */    \n    public function requirePostMethod() {\n        // require POST requests\n        if ($_SERVER['REQUEST_METHOD'] && $_SERVER['REQUEST_METHOD'] != 'POST') {\n            header('Allow: POST', true, 405);\n            throw new Exception(\"Invalid HTTP request method.\");\n        }\n    }\n}\n\n"
  }
]