source: branches/eli_branch/lib/PayPal.inc.php @ 527

Last change on this file since 527 was 439, checked in by anonymous, 11 years ago

added public and private keywords to all properties and methods, changed old classname constructor function to construct, removed more ?> closing tags

File size: 13.1 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-2012 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    private $_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    private $_default_button_options = array();
42
43    // Array of buttons created by newButton().
44    private $_buttons = array();
45
46    // Store the response from the last IPN.
47    private $_ipn_response;
48
49    /**
50     * Constructor.
51     *
52     * @param   bool    $test_mode  Use PayPal sandbox for testing.
53     */
54    public function __construct($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    public 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    public 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    public 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    public 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    public 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    public 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    public 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    public 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    public 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    public 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
Note: See TracBrowser for help on using the repository browser.