Completed
Pull Request — patch_1-1-4 (#3210)
by Emanuele
12:56
created

subscriptions.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * This file contains code covered by:
12
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
13
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
14
 *
15
 * @version 1.1.4
16
 *
17
 */
18
19
// Start things rolling by getting the forum alive...
20
if (!file_exists(dirname(__FILE__) . '/bootstrap.php'))
21
	die('Unable to initialize');
22
23
global $ssi_guest_access;
24
25
require_once(dirname(__FILE__) . '/bootstrap.php');
26
$ssi_guest_access = true;
27
new Bootstrap(true);
28
29
global $txt, $modSettings, $context;
30
31
// Need lots of help
32
require_once(SUBSDIR . '/PaidSubscriptions.subs.php');
33
require_once(SUBSDIR . '/Admin.subs.php');
34
require_once(SUBSDIR . '/Members.subs.php');
35
36
loadLanguage('ManagePaid');
37
38
// If there's literally nothing coming in, let's take flight!
39
if (empty($_POST))
40
{
41
	header('Content-Type: text/html; charset=UTF-8');
42
	die($txt['paid_no_data']);
43
}
44
45
// I assume we're even active?
46
if (empty($modSettings['paid_enabled']))
47
	exit;
48
49
// If we have some custom people who find out about problems load them here.
50
$notify_users = array();
51
if (!empty($modSettings['paid_email_to']))
52
{
53
	foreach (explode(',', $modSettings['paid_email_to']) as $email)
54
		$notify_users[] = array(
55
			'email' => $email,
56
			'name' => $txt['who_member'],
57
			'id' => 0,
58
		);
59
}
60
61
$db = database();
62
63
// We need to see whether we can find the correct payment gateway,
64
// Go through all our gateway scripts and find out if they are happy with what we have.
65
$txnType = '';
66
$gatewayHandles = loadPaymentGateways();
67
foreach ($gatewayHandles as $gateway)
68
{
69
	$gatewayClass = new $gateway['payment_class']();
70
	if ($gatewayClass->isValid())
71
	{
72
		$txnType = $gateway['code'];
73
		break;
74
	}
75
}
76
77
if (empty($txnType))
78
	generateSubscriptionError($txt['paid_unknown_transaction_type']);
79
80
// Get the subscription and member ID amongst others...
81
@list($subscription_id, $member_id) = $gatewayClass->precheck();
82
83
// Integer these just in case.
84
$subscription_id = (int) $subscription_id;
85
$member_id = (int) $member_id;
86
87
// This would be bad...
88
if (empty($member_id))
89
	generateSubscriptionError($txt['paid_empty_member']);
90
91
// Verify the member.
92
$member_info = getBasicMemberData($member_id);
93
94
// Didn't find them?
95
if (empty($member_info))
96
	generateSubscriptionError(sprintf($txt['paid_could_not_find_member'], $member_id));
97
98
// Get the subscription details.
99
$request = $db->query('', '
100
	SELECT cost, length, name
101
	FROM {db_prefix}subscriptions
102
	WHERE id_subscribe = {int:current_subscription}',
103
	array(
104
		'current_subscription' => $subscription_id,
105
	)
106
);
107
108
// Didn't find it?
109
if ($db->num_rows($request) === 0)
110
	generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription'], $member_id, $subscription_id));
111
112
$subscription_info = $db->fetch_assoc($request);
113
$db->free_result($request);
114
115
// We wish to check the pending payments to make sure we are expecting this.
116
$request = $db->query('', '
117
	SELECT id_sublog, id_subscribe, payments_pending, pending_details, end_time
118
	FROM {db_prefix}log_subscribed
119
	WHERE id_subscribe = {int:current_subscription}
120
		AND id_member = {int:current_member}
121
	LIMIT 1',
122
	array(
123
		'current_subscription' => $subscription_id,
124
		'current_member' => $member_id,
125
	)
126
);
127
if ($db->num_rows($request) == 0)
128
	generateSubscriptionError(sprintf($txt['paid_count_not_find_subscription_log'], $member_id, $subscription_id));
129
$subscription_info += $db->fetch_assoc($request);
130
$db->free_result($request);
131
132
// Is this a refund?
133
if ($gatewayClass->isRefund())
134
{
135
	handleRefund($subscription_info, $member_id, $context['subscriptions'][$subscription_id]['num_length']);
136
137
	// Receipt?
138
	if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2)
139
	{
140
		$replacements = array(
141
			'NAME' => $subscription_info['name'],
142
			'REFUNDNAME' => $member_info['member_name'],
143
			'REFUNDUSER' => $member_info['real_name'],
144
			'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id,
145
			'DATE' => standardTime(time(), false),
146
		);
147
148
		emailAdmins('paid_subscription_refund', $replacements, $notify_users);
149
	}
150
}
151
// Otherwise is it what we want, a purchase?
152
elseif ($gatewayClass->isPayment() || $gatewayClass->isSubscription())
153
{
154
	$cost = Util::unserialize($subscription_info['cost']);
155
	$total_cost = $gatewayClass->getCost();
156
	$notify = false;
157
158
	// For one off's we want to only capture them once!
159
	if (!$gatewayClass->isSubscription())
160
	{
161
		$real_details = Util::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
165
		// Now we just try to find anything pending.
166
		// We don't really care which it is as security happens later.
167
		foreach ($real_details as $id => $detail)
168
		{
169
			unset($real_details[$id]);
170
			if ($detail[3] == 'payback' && $subscription_info['payments_pending'])
171
				$subscription_info['payments_pending']--;
172
			break;
173
		}
174
175
		$subscription_info['pending_details'] = empty($real_details) ? '' : serialize($real_details);
176
177
		updateNonrecurrent($subscription_info);
178
	}
179
180
	// Is this flexible?
181
	if ($subscription_info['length'] == 'F')
182
	{
183
		$found_duration = 0;
184
185
		// This is a little harder, can we find the right duration?
186
		foreach ($cost as $duration => $value)
187
		{
188
			if ($duration == 'fixed')
189
				continue;
190
			elseif ((float) $value == (float) $total_cost)
191
				$found_duration = strtoupper(substr($duration, 0, 1));
192
		}
193
194
		// If we have the duration then we're done.
195
		if ($found_duration !== 0)
196
		{
197
			$notify = true;
198
			addSubscription($subscription_id, $member_id, $found_duration);
199
		}
200
	}
201
	else
202
	{
203
		$actual_cost = $cost['fixed'];
204
205
		// It must be at least the right amount.
206
		if ($total_cost != 0 && $total_cost >= $actual_cost)
207
		{
208
			// Add the subscription.
209
			$notify = true;
210
			addSubscription($subscription_id, $member_id);
211
		}
212
	}
213
214
	// Send a receipt?
215
	if (!empty($modSettings['paid_email']) && $modSettings['paid_email'] == 2 && $notify)
216
	{
217
		$replacements = array(
218
			'NAME' => $subscription_info['name'],
219
			'SUBNAME' => $member_info['member_name'],
220
			'SUBUSER' => $member_info['real_name'],
221
			'SUBEMAIL' => $member_info['email_address'],
222
			'PRICE' => sprintf($modSettings['paid_currency_symbol'], $total_cost),
223
			'PROFILELINK' => $scripturl . '?action=profile;u=' . $member_id,
224
			'DATE' => standardTime(time(), false),
225
		);
226
227
		emailAdmins('paid_subscription_new', $replacements, $notify_users);
228
	}
229
}
230
// Maybe they're cancelling. This allows payment gateways to perform processing if needed
231
elseif ($gatewayClass->isCancellation())
232
{
233
	if (method_exists($gatewayClass, 'processCancelation'))
234
		$gatewayClass->processCancelation($subscription_id, $member_id, $subscription_info);
235
}
236
else
0 ignored issues
show
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
237
{
238
	// Some other "valid" transaction such as:
239
	//
240
	// subscr_signup: This IPN response (txn_type) is sent only the first time the user signs up for a subscription.
241
	// It then does not fire in any event later. This response is received somewhere before or after the first payment of
242
	// subscription is received (txn_type=subscr_payment) which is what we do process
243
	//
244
	// Should we log any of these ...
245
}
246
247
// In case we have anything specific to do.
248
$gatewayClass->close();
249
250
/**
251
 * Log an error then exit
252
 *
253
 * @param string $text
254
 * @throws \Elk_Exception
255
 */
256
function generateSubscriptionError($text)
257
{
258
	global $modSettings, $notify_users;
259
260
	// Send an email?
261
	if (!empty($modSettings['paid_email']))
262
	{
263
		$replacements = array(
264
			'ERROR' => $text,
265
		);
266
267
		emailAdmins('paid_subscription_error', $replacements, $notify_users);
268
	}
269
270
	// Maybe we can try to give them the post data?
271
	if (!empty($_POST))
272
	{
273
		foreach ($_POST as $key => $val)
274
			$text .= '<br />' . Util::htmlspecialchars($key) . ': ' . Util::htmlspecialchars($val);
275
	}
276
277
	// Then just log and die.
278
	Errors::instance()->log_error($text);
279
280
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function generateSubscriptionError() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
281
}
282