subscriptions.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * This file is the file which all subscription gateways should call
  4. * when a payment has been received - it sorts out the user status.
  5. *
  6. * Simple Machines Forum (SMF)
  7. *
  8. * @package SMF
  9. * @author Simple Machines http://www.simplemachines.org
  10. * @copyright 2012 Simple Machines
  11. * @license http://www.simplemachines.org/about/smf/license.php BSD
  12. *
  13. * @version 2.1 Alpha 1
  14. */
  15. // Start things rolling by getting SMF alive...
  16. $ssi_guest_access = true;
  17. if (!file_exists(dirname(__FILE__) . '/SSI.php'))
  18. die('Cannot find SSI.php');
  19. require_once(dirname(__FILE__) . '/SSI.php');
  20. require_once($sourcedir . '/ManagePaid.php');
  21. // For any admin emailing.
  22. require_once($sourcedir . '/Subs-Admin.php');
  23. loadLanguage('ManagePaid');
  24. // If there's literally nothing coming in, let's take flight!
  25. if (empty($_POST))
  26. {
  27. header('Content-Type: text/html; charset=' . (empty($modSettings['global_character_set']) ? (empty($txt['lang_character_set']) ? 'ISO-8859-1' : $txt['lang_character_set']) : $modSettings['global_character_set']));
  28. die($txt['paid_no_data']);
  29. }
  30. // I assume we're even active?
  31. if (empty($modSettings['paid_enabled']))
  32. exit;
  33. // If we have some custom people who find out about problems load them here.
  34. $notify_users = array();
  35. if (!empty($modSettings['paid_email_to']))
  36. foreach (explode(',', $modSettings['paid_email_to']) as $email)
  37. $notify_users[] = array(
  38. 'email' => $email,
  39. 'name' => $txt['who_member'],
  40. 'id' => 0,
  41. );
  42. // We need to see whether we can find the correct payment gateway,
  43. // we'll going to go through all our gateway scripts and find out
  44. // if they are happy with what we have.
  45. $txnType = '';
  46. $gatewayHandles = loadPaymentGateways();
  47. foreach ($gatewayHandles as $gateway)
  48. {
  49. $gatewayClass = new $gateway['payment_class']();
  50. if ($gatewayClass->isValid())
  51. {
  52. $txnType = $gateway['code'];
  53. break;
  54. }
  55. }
  56. if (empty($txnType))
  57. generateSubscriptionError($txt['paid_unknown_transaction_type']);
  58. // Get the subscription and member ID amoungst others...
  59. @list ($subscription_id, $member_id) = $gatewayClass->precheck();
  60. // Integer these just in case.
  61. $subscription_id = (int) $subscription_id;
  62. $member_id = (int) $member_id;
  63. // This would be bad...
  64. if (empty($member_id))
  65. generateSubscriptionError($txt['paid_empty_member']);
  66. // Verify the member.
  67. $request = $smcFunc['db_query']('', '
  68. SELECT id_member, member_name, real_name, email_address
  69. FROM {db_prefix}members
  70. WHERE id_member = {int:current_member}',
  71. array(
  72. 'current_member' => $member_id,
  73. )
  74. );
  75. // Didn't find them?
  76. if ($smcFunc['db_num_rows']($request) == 0)
  77. generateSubscriptionError(sprintf($txt['paid_could_not_find_member'], $member_id));
  78. $member_info = $smcFunc['db_fetch_assoc']($request);
  79. $smcFunc['db_free_result']($request);
  80. // Get the subscription details.
  81. $request = $smcFunc['db_query']('', '
  82. SELECT cost, length, name
  83. FROM {db_prefix}subscriptions
  84. WHERE id_subscribe = {int:current_subscription}',
  85. array(
  86. 'current_subscription' => $subscription_id,
  87. )
  88. );
  89. // Didn't find it?
  90. if ($smcFunc['db_num_rows']($request) == 0)
  91. generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription'], $member_id, $subscription_id));
  92. $subscription_info = $smcFunc['db_fetch_assoc']($request);
  93. $smcFunc['db_free_result']($request);
  94. // We wish to check the pending payments to make sure we are expecting this.
  95. $request = $smcFunc['db_query']('', '
  96. SELECT id_sublog, payments_pending, pending_details, end_time
  97. FROM {db_prefix}log_subscribed
  98. WHERE id_subscribe = {int:current_subscription}
  99. AND id_member = {int:current_member}
  100. LIMIT 1',
  101. array(
  102. 'current_subscription' => $subscription_id,
  103. 'current_member' => $member_id,
  104. )
  105. );
  106. if ($smcFunc['db_num_rows']($request) == 0)
  107. generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription_log'], $member_id, $subscription_id));
  108. $subscription_info += $smcFunc['db_fetch_assoc']($request);
  109. $smcFunc['db_free_result']($request);
  110. // Is this a refund etc?
  111. if ($gatewayClass->isRefund())
  112. {
  113. // If the end time subtracted by current time, is not greater
  114. // than the duration (ie length of subscription), then we close it.
  115. if ($subscription_info['end_time'] - time() < $subscription_info['length'])
  116. {
  117. // Delete user subscription.
  118. removeSubscription($subscription_id, $member_id);
  119. $subscription_act = time();
  120. $status = 0;
  121. }
  122. else
  123. {
  124. loadSubscriptions();
  125. $subscription_act = $subscription_info['end_time'] - $context['subscriptions'][$subscription_id]['num_length'];
  126. $status = 1;
  127. }
  128. // Mark it as complete so we have a record.
  129. $smcFunc['db_query']('', '
  130. UPDATE {db_prefix}log_subscribed
  131. SET end_time = {int:current_time}
  132. WHERE id_subscribe = {int:current_subscription}
  133. AND id_member = {int:current_member}
  134. AND status = {int:status}',
  135. array(
  136. 'current_time' => $subscription_act,
  137. 'current_subscription' => $subscription_id,
  138. 'current_member' => $member_id,
  139. 'status' => $status,
  140. )
  141. );
  142. // Receipt?
  143. if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2)
  144. {
  145. $replacements = array(
  146. 'NAME' => $subscription_info['name'],
  147. 'REFUNDNAME' => $member_info['member_name'],
  148. 'REFUNDUSER' => $member_info['real_name'],
  149. 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id,
  150. 'DATE' => timeformat(time(), false),
  151. );
  152. emailAdmins('paid_subscription_refund', $replacements, $notify_users);
  153. }
  154. }
  155. // Otherwise is it what we want, a purchase?
  156. elseif ($gatewayClass->isPayment() || $gatewayClass->isSubscription())
  157. {
  158. $cost = unserialize($subscription_info['cost']);
  159. $total_cost = $gatewayClass->getCost();
  160. $notify = false;
  161. // For one off's we want to only capture them once!
  162. if (!$gatewayClass->isSubscription())
  163. {
  164. $real_details = @unserialize($subscription_info['pending_details']);
  165. if (empty($real_details))
  166. generateSubscriptionError(sprintf($txt['paid_count_not_find_outstanding_payment'], $member_id, $subscription_id));
  167. // Now we just try to find anything pending.
  168. // We don't really care which it is as security happens later.
  169. foreach ($real_details as $id => $detail)
  170. {
  171. unset($real_details[$id]);
  172. if ($detail[3] == 'payback' && $subscription_info['payments_pending'])
  173. $subscription_info['payments_pending']--;
  174. break;
  175. }
  176. $subscription_info['pending_details'] = empty($real_details) ? '' : serialize($real_details);
  177. $smcFunc['db_query']('', '
  178. UPDATE {db_prefix}log_subscribed
  179. SET payments_pending = {int:payments_pending}, pending_details = {string:pending_details}
  180. WHERE id_sublog = {int:current_subscription_item}',
  181. array(
  182. 'payments_pending' => $subscription_info['payments_pending'],
  183. 'current_subscription_item' => $subscription_info['id_sublog'],
  184. 'pending_details' => $subscription_info['pending_details'],
  185. )
  186. );
  187. }
  188. // Is this flexible?
  189. if ($subscription_info['length'] == 'F')
  190. {
  191. $found_duration = 0;
  192. // This is a little harder, can we find the right duration?
  193. foreach ($cost as $duration => $value)
  194. {
  195. if ($duration == 'fixed')
  196. continue;
  197. elseif ((float) $value == (float) $total_cost)
  198. $found_duration = strtoupper(substr($duration, 0, 1));
  199. }
  200. // If we have the duration then we're done.
  201. if ($found_duration!== 0)
  202. {
  203. $notify = true;
  204. addSubscription($subscription_id, $member_id, $found_duration);
  205. }
  206. }
  207. else
  208. {
  209. $actual_cost = $cost['fixed'];
  210. // It must be at least the right amount.
  211. if ($total_cost != 0 && $total_cost >= $actual_cost)
  212. {
  213. // Add the subscription.
  214. $notify = true;
  215. addSubscription($subscription_id, $member_id);
  216. }
  217. }
  218. // Send a receipt?
  219. if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2 && $notify)
  220. {
  221. $replacements = array(
  222. 'NAME' => $subscription_info['name'],
  223. 'SUBNAME' => $member_info['member_name'],
  224. 'SUBUSER' => $member_info['real_name'],
  225. 'SUBEMAIL' => $member_info['email_address'],
  226. 'PRICE' => sprintf($modSettings['paid_currency_symbol'], $total_cost),
  227. 'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id,
  228. 'DATE' => timeformat(time(), false),
  229. );
  230. emailAdmins('paid_subscription_new', $replacements, $notify_users);
  231. }
  232. }
  233. // In case we have anything specific to do.
  234. $gatewayClass->close();
  235. // Log an error then die.
  236. function generateSubscriptionError($text)
  237. {
  238. global $modSettings, $notify_users, $smcFunc;
  239. // Send an email?
  240. if (!empty($modSettings['paid_email']))
  241. {
  242. $replacements = array(
  243. 'ERROR' => $text,
  244. );
  245. emailAdmins('paid_subscription_error', $replacements, $notify_users);
  246. }
  247. // Maybe we can try to give them the post data?
  248. if (!empty($_POST))
  249. foreach ($_POST as $key => $val)
  250. $text .= '<br />' . $smcFunc['htmlspecialchars']($key) . ': ' . $smcFunc['htmlspecialchars']($val);
  251. // Then just log and die.
  252. log_error($text);
  253. exit;
  254. }