Subscriptions-PayPal.php 9.1 KB

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