Completed
Push — release-2.1 ( 093156...093156 )
by Michael
23s queued 14s
created

subscriptions.php (1 issue)

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

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
319
}
320
321
?>