source: trunk/lib/PayPal.inc.php @ 20

Last change on this file since 20 was 20, checked in by scdev, 19 years ago

Tons of little updates and bugfixes. CSS updates to templates and core css files. File upload ability to module_maker. Remade Upload interface to use setParam/getParam.

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