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

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

Initial import.

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.