subscriptions.php 8.3 KB

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