source: tags/2.1.5/lib/PayPal.inc.php

Last change on this file was 377, checked in by quinn, 14 years ago

Releasing trunk as stable version 2.1.5

File size: 13.0 KB
RevLine 
[1]1<?php
2/**
[362]3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
[376]5 * Copyright 2001-2010 Strangecode, LLC
[362]6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
[136]24 * PayPal.inc.php
25 *
26 * The PayPal class provides functions for creating PayPal buttons and for
[1]27 * receiving PayPal's Instant Payment Notification (IPN) service.
28 *
29 * @author  Quinn Comendant <quinn@strangecode.com>
30 * @version 1.0
31 */
32class PayPal {
[42]33
[1]34    // General object parameters.
35    var $_params = array(
36        'paypal_url' => 'https://www.paypal.com/cgi-bin/webscr',
37        'test_mode' => false,
38    );
[42]39
40    // Options used for specific buttons and links.
[1]41    var $_default_button_options = array();
[42]42
[1]43    // Array of buttons created by newButton().
44    var $_buttons = array();
[42]45
[1]46    // Store the response from the last IPN.
47    var $_ipn_response;
[42]48
[1]49    /**
50     * Constructor.
51     *
52     * @param   bool    $test_mode  Use PayPal sandbox for testing.
53     */
54    function PayPal($test_mode=false)
55    {
56        if ($test_mode) {
57            $this->setParam(array('test_mode' => true));
58            // Use PayPal sandbox when in test mode.
59            $url = 'www.sandbox.paypal.com';
60        } else {
61            $url = 'www.paypal.com';
62        }
[42]63
[1]64        $this->_default_button_options = array(
65            '_global' => array(
66                'business' => null,
67            ),
68            'web_accept' => array(
69                'cmd' => '_xclick',
70                'button_url' => 'https://' . $url . '/cgi-bin/webscr',
71                'link_url' => 'https://' . $url . '/xclick/',
72                'submit_img' => 'https://' . $url . '/en_US/i/btn/x-click-but23.gif',
73                'submit_text' => _("Pay with PayPal"),
74            ),
75            'subscriptions' => array(
76                'cmd' => '_xclick-subscriptions',
77                'button_url' => 'https://' . $url . '/cgi-bin/webscr',
78                'link_url' => 'https://' . $url . '/subscriptions/',
79                'submit_img' => 'https://' . $url . '/en_US/i/btn/x-click-but20.gif',
80                'submit_text' => _("Subscribe with PayPal"),
81            ),
82        );
83    }
[42]84
[1]85    /**
[42]86     * Updates the _default_button_options array with options used for
[1]87     * specific buttons, or all buttons if $type is null.
88     *
89     * @access  public
90     *
[42]91     * @param   mixed   $type       The type of button to set defaults. If null,
[1]92     *                              sets the global button types.
93     * @param   array   $options    Options to set for button.
94     *
95     * @return  bool    True on success, false on failure.
96     */
97    function setButtonDefaults($type, $options)
98    {
[136]99        $app =& App::getInstance();
100
[1]101        if (!is_array($options) || empty($options)) {
[331]102            $app->logMsg(sprintf('Invalid options: %s', truncate(getDump($options, true), 128, 'end')), LOG_WARNING, __FILE__, __LINE__);
[1]103            return false;
104        }
[42]105
[1]106        if (is_null($type) || '_global' == $type) {
107            $this->_default_button_options['_global'] = array_merge($this->_default_button_options['_global'], $options);
108        } else if (!isset($this->_default_button_options[$type])) {
[136]109            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
[1]110            return false;
111        }
[42]112
[1]113        $this->_default_button_options[$type] = array_merge($this->_default_button_options[$type], $options);
114        return true;
115    }
[42]116
[1]117    /**
118     * Creates a new element in the _buttons array. Uses _default_button_options
119     * merged with provided options.
120     *
121     * @access  public
122     *
123     * @param   string  $type       Type of button to create.
124     * @param   string  $name       Name of button to create.
125     * @param   array   $options    Options of button. Overwrites _default_button_options.
126     *
127     * @return  bool    True on success, false on failure.
128     */
129    function newButton($type, $name, $options=null)
130    {
[136]131        $app =& App::getInstance();
132
[1]133        if (!isset($this->_default_button_options[$type])) {
[136]134            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
[1]135            return false;
136        }
[42]137
[1]138        if (!is_array($options) || empty($options)) {
[331]139            $app->logMsg(sprintf('Invalid options: %s', truncate(getDump($options, true), 128, 'end')), LOG_WARNING, __FILE__, __LINE__);
[1]140            return false;
141        }
[42]142
[1]143        if (isset($this->_buttons[$name])) {
[331]144            $app->logMsg(sprintf('Overwriting existing button name: %s', truncate(getDump($this->_buttons[$name], true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[1]145        }
[42]146
[1]147        $this->_buttons[$name] = array(
148            'type' => $type,
149            'options' => array_merge($this->_default_button_options['_global'], $this->_default_button_options[$type], $options)
150        );
151
152        return true;
153    }
154
155    /**
156     * Returns the URL link for specified button.
157     *
158     * @access  public
159     *
160     * @param   string  $name   Name of button for which to generate link.
161     *
162     * @return  mixed   Link of button, or false on failure.
163     */
164    function getLink($name)
165    {
[136]166        $app =& App::getInstance();
167
[1]168        if (!isset($this->_buttons[$name])) {
[136]169            $app->logMsg(sprintf('Button does not exist: %s', $name), LOG_WARNING, __FILE__, __LINE__);
[1]170            return false;
171        }
[42]172
[1]173        $query_string = '';
174        $delim = '';
175        if (is_array($this->_buttons[$name]['options']) && !empty($this->_buttons[$name]['options'])) {
176            foreach ($this->_buttons[$name]['options'] as $key=>$val) {
177                if (!in_array($key, array('button_url', 'link_url', 'cmd', 'submit_img', 'submit_text'))) {
178                    $query_string .= $delim . $key . '=' . urlencode($val);
179                    $delim = '&';
180                }
181            }
182        }
183
184        // PayPal links do not like urlencoded slashes for some stupid reason.
185        $search = array('/%2F/');
186        $replace = array('/');
[42]187
[1]188        return $this->_buttons[$name]['options']['link_url'] . preg_replace($search, $replace, $query_string);
189    }
190
191    /**
192     * Prints the link returned by getLink().
193     *
194     * @access  public
195     *
196     * @param   string  $name   Name of button for which to generate link.
197     */
198    function printLink($name)
199    {
200        echo $this->getLink($name);
201    }
202
203    /**
204     * Prints button with specified name.
205     *
206     * @access  public
207     *
208     * @param   string  $name   Name of button to print.
209     */
210    function printButton($name)
211    {
212        ?>
213        <form action="<?php echo $this->_buttons[$name]['options']['button_url']; ?>" method="post">
[42]214        <?php
[1]215        if (is_array($this->_buttons[$name]['options']) && !empty($this->_buttons[$name]['options'])) {
216            foreach ($this->_buttons[$name]['options'] as $key=>$val) {
217                if (!in_array($key, array('button_url', 'link_url', 'submit_img', 'submit_text'))) {
218                    ?>
[20]219                    <input type="hidden" name="<?php echo $key; ?>" value="<?php echo $val; ?>" />
[1]220                    <?php
221                }
222            }
[42]223        }
[1]224        ?>
[20]225        <input type="image" src="<?php echo $this->_buttons[$name]['options']['submit_img']; ?>" border="0" name="submit" alt="<?php echo $this->_buttons[$name]['options']['submit_text']; ?>" />
[1]226        </form>
227        <?php
228    }
229
230    /**
231     * Set (or overwrite existing) parameters by passing an array of new parameters.
232     *
233     * @access public
234     * @param  array    $params     Array of parameters (key => val pairs).
235     */
236    function setParam($params)
237    {
[136]238        $app =& App::getInstance();
239
[1]240        if (isset($params) && is_array($params)) {
241            // Merge new parameters with old overriding only those passed.
242            $this->_params = array_merge($this->_params, $params);
243        } else {
[136]244            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
[1]245        }
246    }
247
248    /**
249     * Return the value of a parameter, if it exists.
250     *
251     * @access public
252     * @param string $param        Which parameter to return.
253     * @return mixed               Configured parameter value.
254     */
255    function getParam($param)
256    {
[136]257        $app =& App::getInstance();
258   
[1]259        if (isset($this->_params[$param])) {
260            return $this->_params[$param];
261        } else {
[146]262            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
[1]263            return null;
264        }
265    }
266
267    /**
268     * Tests if the HTTP request is a valid IPN request from PayPal.
269     *
270     * @access  public
271     *
272     * @return  bool    True if valid, false if invalid.
273     */
274    function incomingIPNRequest()
275    {
[42]276        if ($_SERVER['REQUEST_METHOD'] == 'POST'
[1]277        && $_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded'
278        && !empty($_POST)) {
279            return true;
280        } else {
281            return false;
282        }
283    }
284
285    /**
286     * Process incoming IPN.
287     *
288     * @access  public
289     *
290     * @return  bool    True on success, false on failure.
291     */
292    function processIPN()
[42]293    {
[136]294        $app =& App::getInstance();
295
[1]296        if (getPost('test_ipn') == '1' || $this->getParam('test_mode')) {
[331]297            $app->logMsg(sprintf('Processing PayPal IPN in test mode: %s', truncate(getDump(getFormData(), true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[1]298            $url = parse_url('https://www.sandbox.paypal.com/cgi-bin/webscr');
299        } else {
[331]300            $app->logMsg(sprintf('Processing PayPal IPN: %s', truncate(getDump(getFormData(), true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[1]301            $url = parse_url($this->getParam('paypal_url'));
302        }
[42]303
[1]304        // Read POST request and add 'cmd'.
305        $received_data = getPost();
306        $return_data = 'cmd=_notify-validate';
307        foreach ($received_data as $post_key => $post_val) {
308            $return_data .= '&' . $post_key . '=' . urlencode($post_val);
309        }
[42]310
[1]311        // Set the port number based on the scheme.
[42]312        if ($url['scheme'] == "https") {
[1]313            $url['port'] = 443;
314            $ssl = 'ssl://';
315        } else {
316            $url['port'] = 80;
317            $ssl = '';
318        }
[42]319
[1]320        // Open connection to PayPal server.
[42]321        $fp = fsockopen($ssl . $url['host'], $url['port'], $errnum, $errstr, 30);
[1]322
323        if (!$fp) {
[136]324            $app->logMsg(sprintf('Connection to PayPal URL %s failed with error: %s (%s)', $ssl . $url['host'], $errstr, $errnum), LOG_WARNING, __FILE__, __LINE__);
[1]325            return false;
326        } else {
[42]327            fputs($fp, "POST {$url['path']} HTTP/1.1\r\n");
328            fputs($fp, "Host: {$url['host']}\r\n");
329            fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
[247]330            fputs($fp, "Content-length: " . mb_strlen($return_data) . "\r\n");
[42]331            fputs($fp, "Connection: close\r\n\r\n");
332            fputs($fp, $return_data . "\r\n\r\n");
333
[1]334            // Loop through the response lines from the server.
335            $this->_ipn_response = '';
336            while (!feof($fp)) {
337                $this->_ipn_response .= fgets($fp, 1024);
338            }
339            fclose($fp);
[42]340
[136]341            $app->logMsg(sprintf('IPN response received: %s', $this->_ipn_response), LOG_NOTICE, __FILE__, __LINE__);
[1]342            return true;
343        }
344    }
[42]345
[1]346    /**
347     * Checks the response received from PayPal's IPN upon calling processIPN().
348     *
349     * @access  public
350     *
351     * @return  bool    True if response contains VERIFIED, false otherwise.
352     */
353    function verifiedIPN()
354    {
[136]355        $app =& App::getInstance();
356
[1]357        if (!isset($this->_ipn_response)) {
[136]358            $app->logMsg(sprintf('Cannot verify IPN, response not received.', null), LOG_WARNING, __FILE__, __LINE__);
[1]359            return false;
360        }
[42]361
[1]362        if (empty($this->_ipn_response)) {
[136]363            $app->logMsg(sprintf('Cannot verify IPN, response empty.', null), LOG_WARNING, __FILE__, __LINE__);
[1]364            return false;
365        }
[42]366
[1]367        if (preg_match('/VERIFIED/', $this->_ipn_response)) {
[136]368            $app->logMsg(sprintf('IPN verified!', null), LOG_DEBUG, __FILE__, __LINE__);
[1]369            return true;
370        } else if (preg_match('/INVALID/', $this->_ipn_response)) {
[136]371            $app->logMsg(sprintf('IPN invalid.', null), LOG_DEBUG, __FILE__, __LINE__);
[1]372            return false;
373        } else {
[136]374            $app->logMsg(sprintf('IPN unknown.', null), LOG_WARNING, __FILE__, __LINE__);
[1]375            return false;
376        }
377    }
[42]378
379
[1]380} // End of class.
381
382?>
Note: See TracBrowser for help on using the repository browser.