source: branches/2.0singleton/lib/PayPal.inc.php @ 128

Last change on this file since 128 was 128, checked in by scdev, 18 years ago

finished /lib folder

File size: 12.0 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        $app =& App::getInstance();
77
78        if (!is_array($options) || empty($options)) {
79            $app->logMsg(sprintf('Invalid options: %s', getDump($options)), LOG_WARNING, __FILE__, __LINE__);
80            return false;
81        }
82
83        if (is_null($type) || '_global' == $type) {
84            $this->_default_button_options['_global'] = array_merge($this->_default_button_options['_global'], $options);
85        } else if (!isset($this->_default_button_options[$type])) {
86            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
87            return false;
88        }
89
90        $this->_default_button_options[$type] = array_merge($this->_default_button_options[$type], $options);
91        return true;
92    }
93
94    /**
95     * Creates a new element in the _buttons array. Uses _default_button_options
96     * merged with provided options.
97     *
98     * @access  public
99     *
100     * @param   string  $type       Type of button to create.
101     * @param   string  $name       Name of button to create.
102     * @param   array   $options    Options of button. Overwrites _default_button_options.
103     *
104     * @return  bool    True on success, false on failure.
105     */
106    function newButton($type, $name, $options=null)
107    {
108        $app =& App::getInstance();
109
110        if (!isset($this->_default_button_options[$type])) {
111            $app->logMsg(sprintf('Invalid button type: %s', $type), LOG_WARNING, __FILE__, __LINE__);
112            return false;
113        }
114
115        if (!is_array($options) || empty($options)) {
116            $app->logMsg(sprintf('Invalid options: %s', getDump($options)), LOG_WARNING, __FILE__, __LINE__);
117            return false;
118        }
119
120        if (isset($this->_buttons[$name])) {
121            $app->logMsg(sprintf('Overwriting existing button name: %s', getDump($this->_buttons[$name])), LOG_DEBUG, __FILE__, __LINE__);
122        }
123
124        $this->_buttons[$name] = array(
125            'type' => $type,
126            'options' => array_merge($this->_default_button_options['_global'], $this->_default_button_options[$type], $options)
127        );
128
129        return true;
130    }
131
132    /**
133     * Returns the URL link for specified button.
134     *
135     * @access  public
136     *
137     * @param   string  $name   Name of button for which to generate link.
138     *
139     * @return  mixed   Link of button, or false on failure.
140     */
141    function getLink($name)
142    {
143        $app =& App::getInstance();
144
145        if (!isset($this->_buttons[$name])) {
146            $app->logMsg(sprintf('Button does not exist: %s', $name), LOG_WARNING, __FILE__, __LINE__);
147            return false;
148        }
149
150        $query_string = '';
151        $delim = '';
152        if (is_array($this->_buttons[$name]['options']) && !empty($this->_buttons[$name]['options'])) {
153            foreach ($this->_buttons[$name]['options'] as $key=>$val) {
154                if (!in_array($key, array('button_url', 'link_url', 'cmd', 'submit_img', 'submit_text'))) {
155                    $query_string .= $delim . $key . '=' . urlencode($val);
156                    $delim = '&';
157                }
158            }
159        }
160
161        // PayPal links do not like urlencoded slashes for some stupid reason.
162        $search = array('/%2F/');
163        $replace = array('/');
164
165        return $this->_buttons[$name]['options']['link_url'] . preg_replace($search, $replace, $query_string);
166    }
167
168    /**
169     * Prints the link returned by getLink().
170     *
171     * @access  public
172     *
173     * @param   string  $name   Name of button for which to generate link.
174     */
175    function printLink($name)
176    {
177        echo $this->getLink($name);
178    }
179
180    /**
181     * Prints button with specified name.
182     *
183     * @access  public
184     *
185     * @param   string  $name   Name of button to print.
186     */
187    function printButton($name)
188    {
189        ?>
190        <form action="<?php echo $this->_buttons[$name]['options']['button_url']; ?>" method="post">
191        <?php
192        if (is_array($this->_buttons[$name]['options']) && !empty($this->_buttons[$name]['options'])) {
193            foreach ($this->_buttons[$name]['options'] as $key=>$val) {
194                if (!in_array($key, array('button_url', 'link_url', 'submit_img', 'submit_text'))) {
195                    ?>
196                    <input type="hidden" name="<?php echo $key; ?>" value="<?php echo $val; ?>" />
197                    <?php
198                }
199            }
200        }
201        ?>
202        <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']; ?>" />
203        </form>
204        <?php
205    }
206
207    /**
208     * Set (or overwrite existing) parameters by passing an array of new parameters.
209     *
210     * @access public
211     * @param  array    $params     Array of parameters (key => val pairs).
212     */
213    function setParam($params)
214    {
215        $app =& App::getInstance();
216
217        if (isset($params) && is_array($params)) {
218            // Merge new parameters with old overriding only those passed.
219            $this->_params = array_merge($this->_params, $params);
220        } else {
221            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
222        }
223    }
224
225    /**
226     * Return the value of a parameter, if it exists.
227     *
228     * @access public
229     * @param string $param        Which parameter to return.
230     * @return mixed               Configured parameter value.
231     */
232    function getParam($param)
233    {
234        $app =& App::getInstance();
235
236        if (isset($this->_params[$param])) {
237            return $this->_params[$param];
238        } else {
239            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
240            return null;
241        }
242    }
243
244    /**
245     * Tests if the HTTP request is a valid IPN request from PayPal.
246     *
247     * @access  public
248     *
249     * @return  bool    True if valid, false if invalid.
250     */
251    function incomingIPNRequest()
252    {
253        if ($_SERVER['REQUEST_METHOD'] == 'POST'
254        && $_SERVER['CONTENT_TYPE'] == 'application/x-www-form-urlencoded'
255        && !empty($_POST)) {
256            return true;
257        } else {
258            return false;
259        }
260    }
261
262    /**
263     * Process incoming IPN.
264     *
265     * @access  public
266     *
267     * @return  bool    True on success, false on failure.
268     */
269    function processIPN()
270    {
271        $app =& App::getInstance();
272
273        if (getPost('test_ipn') == '1' || $this->getParam('test_mode')) {
274            $app->logMsg(sprintf('Processing PayPal IPN in test mode: %s', getDump(getFormData())), LOG_DEBUG, __FILE__, __LINE__);
275            $url = parse_url('https://www.sandbox.paypal.com/cgi-bin/webscr');
276        } else {
277            $app->logMsg(sprintf('Processing PayPal IPN: %s', getDump(getFormData())), LOG_DEBUG, __FILE__, __LINE__);
278            $url = parse_url($this->getParam('paypal_url'));
279        }
280
281        // Read POST request and add 'cmd'.
282        $received_data = getPost();
283        $return_data = 'cmd=_notify-validate';
284        foreach ($received_data as $post_key => $post_val) {
285            $return_data .= '&' . $post_key . '=' . urlencode($post_val);
286        }
287
288        // Set the port number based on the scheme.
289        if ($url['scheme'] == "https") {
290            $url['port'] = 443;
291            $ssl = 'ssl://';
292        } else {
293            $url['port'] = 80;
294            $ssl = '';
295        }
296
297        // Open connection to PayPal server.
298        $fp = fsockopen($ssl . $url['host'], $url['port'], $errnum, $errstr, 30);
299
300        if (!$fp) {
301            $app->logMsg(sprintf('Connection to PayPal URL %s failed with error: %s (%s)', $ssl . $url['host'], $errstr, $errnum), LOG_WARNING, __FILE__, __LINE__);
302            return false;
303        } else {
304            fputs($fp, "POST {$url['path']} HTTP/1.1\r\n");
305            fputs($fp, "Host: {$url['host']}\r\n");
306            fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
307            fputs($fp, "Content-length: " . strlen($return_data) . "\r\n");
308            fputs($fp, "Connection: close\r\n\r\n");
309            fputs($fp, $return_data . "\r\n\r\n");
310
311            // Loop through the response lines from the server.
312            $this->_ipn_response = '';
313            while (!feof($fp)) {
314                $this->_ipn_response .= fgets($fp, 1024);
315            }
316            fclose($fp);
317
318            $app->logMsg(sprintf('IPN response received: %s', $this->_ipn_response), LOG_NOTICE, __FILE__, __LINE__);
319            return true;
320        }
321    }
322
323    /**
324     * Checks the response received from PayPal's IPN upon calling processIPN().
325     *
326     * @access  public
327     *
328     * @return  bool    True if response contains VERIFIED, false otherwise.
329     */
330    function verifiedIPN()
331    {
332        $app =& App::getInstance();
333
334        if (!isset($this->_ipn_response)) {
335            $app->logMsg(sprintf('Cannot verify IPN, response not received.', null), LOG_WARNING, __FILE__, __LINE__);
336            return false;
337        }
338
339        if (empty($this->_ipn_response)) {
340            $app->logMsg(sprintf('Cannot verify IPN, response empty.', null), LOG_WARNING, __FILE__, __LINE__);
341            return false;
342        }
343
344        if (preg_match('/VERIFIED/', $this->_ipn_response)) {
345            $app->logMsg(sprintf('IPN verified!', null), LOG_DEBUG, __FILE__, __LINE__);
346            return true;
347        } else if (preg_match('/INVALID/', $this->_ipn_response)) {
348            $app->logMsg(sprintf('IPN invalid.', null), LOG_DEBUG, __FILE__, __LINE__);
349            return false;
350        } else {
351            $app->logMsg(sprintf('IPN unknown.', null), LOG_WARNING, __FILE__, __LINE__);
352            return false;
353        }
354    }
355
356
357} // End of class.
358
359?>
Note: See TracBrowser for help on using the repository browser.