annotate forum/Sources/Subscriptions-PayPal.php @ 85:6d7b61434be7 website

Add a copy of this here, just in case!
author Chris Cannam
date Mon, 20 Jan 2014 11:02:36 +0000
parents e3e11437ecea
children
rev   line source
Chris@76 1 <?php
Chris@76 2
Chris@76 3 /**
Chris@76 4 * Simple Machines Forum (SMF)
Chris@76 5 *
Chris@76 6 * @package SMF
Chris@76 7 * @author Simple Machines http://www.simplemachines.org
Chris@76 8 * @copyright 2011 Simple Machines
Chris@76 9 * @license http://www.simplemachines.org/about/smf/license.php BSD
Chris@76 10 *
Chris@76 11 * @version 2.0.3
Chris@76 12 */
Chris@76 13
Chris@76 14 // This won't be dedicated without this - this must exist in each gateway!
Chris@76 15 // SMF Payment Gateway: paypal
Chris@76 16
Chris@76 17 if (!defined('SMF'))
Chris@76 18 die('Hacking attempt...');
Chris@76 19
Chris@76 20 class paypal_display
Chris@76 21 {
Chris@76 22 public $title = 'PayPal';
Chris@76 23
Chris@76 24 public function getGatewaySettings()
Chris@76 25 {
Chris@76 26 global $txt;
Chris@76 27
Chris@76 28 $setting_data = array(
Chris@76 29 array('text', 'paypal_email', 'subtext' => $txt['paypal_email_desc']),
Chris@76 30 );
Chris@76 31
Chris@76 32 return $setting_data;
Chris@76 33 }
Chris@76 34
Chris@76 35 // Is this enabled for new payments?
Chris@76 36 public function gatewayEnabled()
Chris@76 37 {
Chris@76 38 global $modSettings;
Chris@76 39
Chris@76 40 return !empty($modSettings['paypal_email']);
Chris@76 41 }
Chris@76 42
Chris@76 43 // What do we want?
Chris@76 44 public function fetchGatewayFields($unique_id, $sub_data, $value, $period, $return_url)
Chris@76 45 {
Chris@76 46 global $modSettings, $txt, $boardurl;
Chris@76 47
Chris@76 48 $return_data = array(
Chris@76 49 'form' => 'https://www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com/cgi-bin/webscr',
Chris@76 50 'id' => 'paypal',
Chris@76 51 'hidden' => array(),
Chris@76 52 'title' => $txt['paypal'],
Chris@76 53 'desc' => $txt['paid_confirm_paypal'],
Chris@76 54 'submit' => $txt['paid_paypal_order'],
Chris@76 55 'javascript' => '',
Chris@76 56 );
Chris@76 57
Chris@76 58 // All the standard bits.
Chris@76 59 $return_data['hidden']['business'] = $modSettings['paypal_email'];
Chris@76 60 $return_data['hidden']['item_name'] = $sub_data['name'] . ' ' . $txt['subscription'];
Chris@76 61 $return_data['hidden']['item_number'] = $unique_id;
Chris@76 62 $return_data['hidden']['currency_code'] = strtoupper($modSettings['paid_currency_code']);
Chris@76 63 $return_data['hidden']['no_shipping'] = 1;
Chris@76 64 $return_data['hidden']['no_note'] = 1;
Chris@76 65 $return_data['hidden']['amount'] = $value;
Chris@76 66 $return_data['hidden']['cmd'] = !$sub_data['repeatable'] ? '_xclick' : '_xclick-subscriptions';
Chris@76 67 $return_data['hidden']['return'] = $return_url;
Chris@76 68 $return_data['hidden']['a3'] = $value;
Chris@76 69 $return_data['hidden']['src'] = 1;
Chris@76 70 $return_data['hidden']['notify_url'] = $boardurl . '/subscriptions.php';
Chris@76 71
Chris@76 72 // Now stuff dependant on what we're doing.
Chris@76 73 if ($sub_data['flexible'])
Chris@76 74 {
Chris@76 75 $return_data['hidden']['p3'] = 1;
Chris@76 76 $return_data['hidden']['t3'] = strtoupper(substr($period, 0, 1));
Chris@76 77 }
Chris@76 78 else
Chris@76 79 {
Chris@76 80 preg_match('~(\d*)(\w)~', $sub_data['real_length'], $match);
Chris@76 81 $unit = $match[1];
Chris@76 82 $period = $match[2];
Chris@76 83
Chris@76 84 $return_data['hidden']['p3'] = $unit;
Chris@76 85 $return_data['hidden']['t3'] = $period;
Chris@76 86 }
Chris@76 87
Chris@76 88 // If it's repeatable do soem javascript to respect this idea.
Chris@76 89 if (!empty($sub_data['repeatable']))
Chris@76 90 $return_data['javascript'] = '
Chris@76 91 document.write(\'<label for="do_paypal_recur"><input type="checkbox" name="do_paypal_recur" id="do_paypal_recur" checked="checked" onclick="switchPaypalRecur();" class="input_check" />' . $txt['paid_make_recurring'] . '</label><br />\');
Chris@76 92
Chris@76 93 function switchPaypalRecur()
Chris@76 94 {
Chris@76 95 document.getElementById("paypal_cmd").value = document.getElementById("do_paypal_recur").checked ? "_xclick-subscriptions" : "_xclick";
Chris@76 96 }';
Chris@76 97
Chris@76 98 return $return_data;
Chris@76 99 }
Chris@76 100 }
Chris@76 101
Chris@76 102 class paypal_payment
Chris@76 103 {
Chris@76 104 private $return_data;
Chris@76 105
Chris@76 106 // This function returns true/false for whether this gateway thinks the data is intended for it.
Chris@76 107 public function isValid()
Chris@76 108 {
Chris@76 109 global $modSettings;
Chris@76 110
Chris@76 111 // Has the user set up an email address?
Chris@76 112 if (empty($modSettings['paypal_email']))
Chris@76 113 return false;
Chris@76 114 // Check the correct transaction types are even here.
Chris@76 115 if ((!isset($_POST['txn_type']) && !isset($_POST['payment_status'])) || (!isset($_POST['business']) && !isset($_POST['receiver_email'])))
Chris@76 116 return false;
Chris@76 117 // Correct email address?
Chris@76 118 if (!isset($_POST['business']))
Chris@76 119 $_POST['business'] = $_POST['receiver_email'];
Chris@76 120 if ($modSettings['paypal_email'] != $_POST['business'] && (empty($modSettings['paypal_additional_emails']) || !in_array($_POST['business'], explode(',', $modSettings['paypal_additional_emails']))))
Chris@76 121 return false;
Chris@76 122 return true;
Chris@76 123 }
Chris@76 124
Chris@76 125 // Validate all the data was valid.
Chris@76 126 public function precheck()
Chris@76 127 {
Chris@76 128 global $modSettings, $txt;
Chris@76 129
Chris@76 130 // Put this to some default value.
Chris@76 131 if (!isset($_POST['txn_type']))
Chris@76 132 $_POST['txn_type'] = '';
Chris@76 133
Chris@76 134 // Build the request string - starting with the minimum requirement.
Chris@76 135 $requestString = 'cmd=_notify-validate';
Chris@76 136
Chris@76 137 // Now my dear, add all the posted bits.
Chris@76 138 foreach ($_POST as $k => $v)
Chris@76 139 $requestString .= '&' . $k . '=' . urlencode($v);
Chris@76 140
Chris@76 141 // Can we use curl?
Chris@76 142 if (function_exists('curl_init') && $curl = curl_init((!empty($modSettings['paidsubs_test']) ? 'https://www.sandbox.' : 'http://www.') . 'paypal.com/cgi-bin/webscr'))
Chris@76 143 {
Chris@76 144 // Set the post data.
Chris@76 145 curl_setopt($curl, CURLOPT_POST, true);
Chris@76 146 curl_setopt($curl, CURLOPT_POSTFIELDSIZE, 0);
Chris@76 147 curl_setopt($curl, CURLOPT_POSTFIELDS, $requestString);
Chris@76 148
Chris@76 149 curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
Chris@76 150 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
Chris@76 151 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
Chris@76 152 curl_setopt($curl, CURLOPT_FORBID_REUSE, 1);
Chris@76 153 curl_setopt($curl, CURLOPT_HTTPHEADER, array(
Chris@76 154 'Host: www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com',
Chris@76 155 'Connection: close'
Chris@76 156 ));
Chris@76 157
Chris@76 158 // Fetch the data returned as a string.
Chris@76 159 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
Chris@76 160
Chris@76 161 // Fetch the data.
Chris@76 162 $this->return_data = curl_exec($curl);
Chris@76 163
Chris@76 164 // Close the session.
Chris@76 165 curl_close($curl);
Chris@76 166 }
Chris@76 167 // Otherwise good old HTTP.
Chris@76 168 else
Chris@76 169 {
Chris@76 170 // Setup the headers.
Chris@76 171 $header = 'POST /cgi-bin/webscr HTTP/1.1' . "\r\n";
Chris@76 172 $header .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
Chris@76 173 $header .= 'Host: www.' . (!empty($modSettings['paidsubs_test']) ? 'sandbox.' : '') . 'paypal.com' . "\r\n";
Chris@76 174 $header .= 'Content-Length: ' . strlen ($requestString) . "\r\n";
Chris@76 175 $header .= 'Connection: close' . "\r\n\r\n";
Chris@76 176
Chris@76 177 // Open the connection.
Chris@76 178 if (!empty($modSettings['paidsubs_test']))
Chris@76 179 $fp = fsockopen('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
Chris@76 180 else
Chris@76 181 $fp = fsockopen('www.paypal.com', 80, $errno, $errstr, 30);
Chris@76 182
Chris@76 183 // Did it work?
Chris@76 184 if (!$fp)
Chris@76 185 generateSubscriptionError($txt['paypal_could_not_connect']);
Chris@76 186
Chris@76 187 // Put the data to the port.
Chris@76 188 fputs($fp, $header . $requestString);
Chris@76 189
Chris@76 190 // Get the data back...
Chris@76 191 while (!feof($fp))
Chris@76 192 {
Chris@76 193 $this->return_data = fgets($fp, 1024);
Chris@76 194 if (strcmp(trim($this->return_data), 'VERIFIED') == 0)
Chris@76 195 break;
Chris@76 196 }
Chris@76 197
Chris@76 198 // Clean up.
Chris@76 199 fclose($fp);
Chris@76 200 }
Chris@76 201
Chris@76 202 // If this isn't verified then give up...
Chris@76 203 // !! This contained a comment "send an email", but we don't appear to send any?
Chris@76 204 if (strcmp(trim($this->return_data), 'VERIFIED') != 0)
Chris@76 205 exit;
Chris@76 206
Chris@76 207 // Check that this is intended for us.
Chris@76 208 if ($modSettings['paypal_email'] != $_POST['business'] && (empty($modSettings['paypal_additional_emails']) || !in_array($_POST['business'], explode(',', $modSettings['paypal_additional_emails']))))
Chris@76 209 exit;
Chris@76 210
Chris@76 211 // Is this a subscription - and if so it's it a secondary payment that we need to process?
Chris@76 212 if ($this->isSubscription() && (empty($_POST['item_number']) || strpos($_POST['item_number'], '+') === false))
Chris@76 213 // Calculate the subscription it relates to!
Chris@76 214 $this->_findSubscription();
Chris@76 215
Chris@76 216 // Verify the currency!
Chris@76 217 if (strtolower($_POST['mc_currency']) != $modSettings['paid_currency_code'])
Chris@76 218 exit;
Chris@76 219
Chris@76 220 // Can't exist if it doesn't contain anything.
Chris@76 221 if (empty($_POST['item_number']))
Chris@76 222 exit;
Chris@76 223
Chris@76 224 // Return the id_sub and id_member
Chris@76 225 return explode('+', $_POST['item_number']);
Chris@76 226 }
Chris@76 227
Chris@76 228 // Is this a refund?
Chris@76 229 public function isRefund()
Chris@76 230 {
Chris@76 231 if ($_POST['payment_status'] == 'Refunded' || $_POST['payment_status'] == 'Reversed' || $_POST['txn_type'] == 'Refunded' || ($_POST['txn_type'] == 'reversal' && $_POST['payment_status'] == 'Completed'))
Chris@76 232 return true;
Chris@76 233 else
Chris@76 234 return false;
Chris@76 235 }
Chris@76 236
Chris@76 237 // Is this a subscription?
Chris@76 238 public function isSubscription()
Chris@76 239 {
Chris@76 240 if (substr($_POST['txn_type'], 0, 14) == 'subscr_payment' && $_POST['payment_status'] == 'Completed')
Chris@76 241 return true;
Chris@76 242 else
Chris@76 243 return false;
Chris@76 244 }
Chris@76 245
Chris@76 246 // Is this a normal payment?
Chris@76 247 public function isPayment()
Chris@76 248 {
Chris@76 249 if ($_POST['payment_status'] == 'Completed' && $_POST['txn_type'] == 'web_accept')
Chris@76 250 return true;
Chris@76 251 else
Chris@76 252 return false;
Chris@76 253 }
Chris@76 254
Chris@76 255 // How much was paid?
Chris@76 256 public function getCost()
Chris@76 257 {
Chris@76 258 return (isset($_POST['tax']) ? $_POST['tax'] : 0) + $_POST['mc_gross'];
Chris@76 259 }
Chris@76 260
Chris@76 261 // exit.
Chris@76 262 public function close()
Chris@76 263 {
Chris@76 264 global $smcFunc, $subscription_id;
Chris@76 265
Chris@76 266 // If it's a subscription record the reference.
Chris@76 267 if ($_POST['txn_type'] == 'subscr_payment' && !empty($_POST['subscr_id']))
Chris@76 268 {
Chris@76 269 $_POST['subscr_id'] = $_POST['subscr_id'];
Chris@76 270 $smcFunc['db_query']('', '
Chris@76 271 UPDATE {db_prefix}log_subscribed
Chris@76 272 SET vendor_ref = {string:vendor_ref}
Chris@76 273 WHERE id_sublog = {int:current_subscription}',
Chris@76 274 array(
Chris@76 275 'current_subscription' => $subscription_id,
Chris@76 276 'vendor_ref' => $_POST['subscr_id'],
Chris@76 277 )
Chris@76 278 );
Chris@76 279 }
Chris@76 280
Chris@76 281 exit();
Chris@76 282 }
Chris@76 283
Chris@76 284 // A private function to find out the subscription details.
Chris@76 285 private function _findSubscription()
Chris@76 286 {
Chris@76 287 global $smcFunc;
Chris@76 288
Chris@76 289 // Assume we have this?
Chris@76 290 if (empty($_POST['subscr_id']))
Chris@76 291 return false;
Chris@76 292
Chris@76 293 // Do we have this in the database?
Chris@76 294 $request = $smcFunc['db_query']('', '
Chris@76 295 SELECT id_member, id_subscribe
Chris@76 296 FROM {db_prefix}log_subscribed
Chris@76 297 WHERE vendor_ref = {string:vendor_ref}
Chris@76 298 LIMIT 1',
Chris@76 299 array(
Chris@76 300 'vendor_ref' => $_POST['subscr_id'],
Chris@76 301 )
Chris@76 302 );
Chris@76 303 // No joy?
Chris@76 304 if ($smcFunc['db_num_rows']($request) == 0)
Chris@76 305 {
Chris@76 306 // Can we identify them by email?
Chris@76 307 if (!empty($_POST['payer_email']))
Chris@76 308 {
Chris@76 309 $smcFunc['db_free_result']($request);
Chris@76 310 $request = $smcFunc['db_query']('', '
Chris@76 311 SELECT ls.id_member, ls.id_subscribe
Chris@76 312 FROM {db_prefix}log_subscribed AS ls
Chris@76 313 INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ls.id_member)
Chris@76 314 WHERE mem.email_address = {string:payer_email}
Chris@76 315 LIMIT 1',
Chris@76 316 array(
Chris@76 317 'payer_email' => $_POST['payer_email'],
Chris@76 318 )
Chris@76 319 );
Chris@76 320 if ($smcFunc['db_num_rows']($request) == 0)
Chris@76 321 return false;
Chris@76 322 }
Chris@76 323 else
Chris@76 324 return false;
Chris@76 325 }
Chris@76 326 list ($member_id, $subscription_id) = $smcFunc['db_fetch_row']($request);
Chris@76 327 $_POST['item_number'] = $member_id . '+' . $subscription_id;
Chris@76 328 $smcFunc['db_free_result']($request);
Chris@76 329 }
Chris@76 330 }
Chris@76 331
Chris@76 332 ?>