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
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2010 Strangecode, LLC
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/**
24 * PayPal.inc.php
25 *
26 * The PayPal class provides functions for creating PayPal buttons and for
27 * receiving PayPal's Instant Payment Notification (IPN) service.
28 *
29 * @author  Quinn Comendant <quinn@strangecode.com>
30 * @version 1.0
31 */
32class PayPal {
33
34    // General object parameters.
35    var $_params = array(
36        'paypal_url' => 'https://www.paypal.com/cgi-bin/webscr',
37        'test_mode' => false,
38    );
39
40    // Options used for specific buttons and links.
41    var $_default_button_options = array();
42
43    // Array of buttons created by newButton().
44    var $_buttons = array();
45
46    // Store the response from the last IPN.
47    var $_ipn_response;
48
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        }
63
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    }
84
85    /**
86     * Updates the _default_button_options array with options used for
87     * specific buttons, or all buttons if $type is null.
88     *
89     * @access  public
90     *
91     * @param   mixed   $type       The type of button to set defaults. If null,
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    {
99        $app =& App::getInstance();
100
101        if (!is_array($options) || empty($options)) {
102            $app->logMsg(sprintf('Invalid options: %s', truncate(getDump($options, true), 128, 'end')), LOG_WARNING, __FILE__, __LINE__);
103            return false;
104        }
105
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])) {
109            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
110            return false;
111        }
112
113        $this->_default_button_options[$type] = array_merge($this->_default_button_options[$type], $options);
114        return true;
115    }
116
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    {
131        $app =& App::getInstance();
132
133        if (!isset($this->_default_button_options[$type])) {
134            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
135            return false;
136        }
137
138        if (!is_array($options) || empty($options)) {
139            $app->logMsg(sprintf('Invalid options: %s', truncate(getDump($options, true), 128, 'end')), LOG_WARNING, __FILE__, __LINE__);
140            return false;
141        }
142
143        if (isset($this->_buttons[$name])) {
144            $app->logMsg(sprintf('Overwriting existing button name: %s', truncate(getDump($this->_buttons[$name], true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
145        }
146
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    {
166        $app =& App::getInstance();
167
168        if (!isset($this->_buttons[$name])) {
169            $app->logMsg(sprintf('Button does not exist: %s', $name), LOG_WARNING, __FILE__, __LINE__);
170            return false;
171        }
172
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('/');
187
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">
214        <?php
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                    ?>
219                    <input type="hidden" name="<?php echo $key; ?>" value="<?php echo $val; ?>" />
220                    <?php
221                }
222            }
223        }
224        ?>
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']; ?>" />
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    {
238        $app =& App::getInstance();
239
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 {
244            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
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    {
257        $app =& App::getInstance();
258   
259        if (isset($this->_params[$param])) {
260            return $this->_params[$param];
261        } else {
262            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
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    {
276        if ($_SERVER['REQUEST_METHOD'] == 'POST'
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()
293    {
294        $app =& App::getInstance();
295
296        if (getPost('test_ipn') == '1' || $this->getParam('test_mode')) {
297            $app->logMsg(sprintf('Processing PayPal IPN in test mode: %s', truncate(getDump(getFormData(), true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
298            $url = parse_url('https://www.sandbox.paypal.com/cgi-bin/webscr');
299        } else {
300            $app->logMsg(sprintf('Processing PayPal IPN: %s', truncate(getDump(getFormData(), true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
301            $url = parse_url($this->getParam('paypal_url'));
302        }
303
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        }
310
311        // Set the port number based on the scheme.
312        if ($url['scheme'] == "https") {
313            $url['port'] = 443;
314            $ssl = 'ssl://';
315        } else {
316            $url['port'] = 80;
317            $ssl = '';
318        }
319
320        // Open connection to PayPal server.
321        $fp = fsockopen($ssl . $url['host'], $url['port'], $errnum, $errstr, 30);
322
323        if (!$fp) {
324            $app->logMsg(sprintf('Connection to PayPal URL %s failed with error: %s (%s)', $ssl . $url['host'], $errstr, $errnum), LOG_WARNING, __FILE__, __LINE__);
325            return false;
326        } else {
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");
330            fputs($fp, "Content-length: " . mb_strlen($return_data) . "\r\n");
331            fputs($fp, "Connection: close\r\n\r\n");
332            fputs($fp, $return_data . "\r\n\r\n");
333
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);
340
341            $app->logMsg(sprintf('IPN response received: %s', $this->_ipn_response), LOG_NOTICE, __FILE__, __LINE__);
342            return true;
343        }
344    }
345
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    {
355        $app =& App::getInstance();
356
357        if (!isset($this->_ipn_response)) {
358            $app->logMsg(sprintf('Cannot verify IPN, response not received.', null), LOG_WARNING, __FILE__, __LINE__);
359            return false;
360        }
361
362        if (empty($this->_ipn_response)) {
363            $app->logMsg(sprintf('Cannot verify IPN, response empty.', null), LOG_WARNING, __FILE__, __LINE__);
364            return false;
365        }
366
367        if (preg_match('/VERIFIED/', $this->_ipn_response)) {
368            $app->logMsg(sprintf('IPN verified!', null), LOG_DEBUG, __FILE__, __LINE__);
369            return true;
370        } else if (preg_match('/INVALID/', $this->_ipn_response)) {
371            $app->logMsg(sprintf('IPN invalid.', null), LOG_DEBUG, __FILE__, __LINE__);
372            return false;
373        } else {
374            $app->logMsg(sprintf('IPN unknown.', null), LOG_WARNING, __FILE__, __LINE__);
375            return false;
376        }
377    }
378
379
380} // End of class.
381
382?>
Note: See TracBrowser for help on using the repository browser.