Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

ProfileSubscriptions_Controller::action_index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
nc 1
nop 0
ccs 0
cts 1
cp 0
crap 2
1
<?php
2
3
/**
4
 * Handles paid subscriptions on a user's profile.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
/**
19
 * ProfileSubscriptions_Controller Class
20
 * This class handles the paid subscriptions on a user's profile.
21
 */
22
class ProfileSubscriptions_Controller extends Action_Controller
23
{
24
	/**
25
	 * Holds the the details of the subscription order
26
	 * @var
27
	 */
28
	private $_order;
29
30
	/**
31
	 * Holds all the available gateways so they can be initialized
32
	 * @var array
33
	 */
34
	private $_gateways;
35
36
	/**
37
	 * The id of the subscription
38
	 * @var int
39
	 */
40
	private $_id_sub;
41
42
	/**
43
	 * Default action for the controller
44
	 *
45
	 * - This is just a stub as action_subscriptions is called from a menu pick
46
	 * and not routed through this method.
47
	 *
48
	 * @see Action_Controller::action_index()
49
	 */
50
	public function action_index()
51
	{
52
		// $this->action_subscriptions();
53
	}
54
55
	/**
56
	 * Method for doing all the paid subscription stuff - kinda.
57
	 */
58
	public function action_subscriptions()
59
	{
60
		global $context, $txt;
61
62
		// Load the paid template anyway.
63
		loadTemplate('ManagePaid');
64
		loadLanguage('ManagePaid');
65
66
		$memID = currentMemberID();
67
		$context['member']['id'] = $memID;
68
69
		// Load all of the subscriptions in the system (loads in to $context)
70
		require_once(SUBSDIR . '/PaidSubscriptions.subs.php');
71
		loadSubscriptions();
72
73
		// Remove any invalid ones, ones not properly set up
74
		$this->_remove_invalid();
75
76
		// Work out what payment gateways are enabled.
77
		$this->_gateways = loadPaymentGateways();
78
		foreach ($this->_gateways as $id => $gateway)
79
		{
80
			$this->_gateways[$id] = new $gateway['display_class']();
81
82
			if (!$this->_gateways[$id]->gatewayEnabled())
83
				unset($this->_gateways[$id]);
84
		}
85
86
		// No gateways yet, no way to pay then, blame the admin !
87
		if (empty($this->_gateways))
88
			throw new Elk_Exception($txt['paid_admin_not_setup_gateway']);
89
90
		// Get the members current subscriptions.
91
		$context['current'] = loadMemberSubscriptions($memID, $context['subscriptions']);
92
93
		// Find the active subscribed ones
94
		foreach ($context['current'] as $id => $current)
95
		{
96
			if ($current['status'] == 1)
97
				$context['subscriptions'][$id]['subscribed'] = true;
98
		}
99
100
		// Simple "done"?
101
		if (isset($this->_req->query->done))
102
			$this->_orderDone($memID);
103
		// They have selected a subscription to order.
104
		elseif (isset($this->_req->query->confirm) && isset($this->_req->post->sub_id) && is_array($this->_req->post->sub_id))
105
			$this->_confirmOrder($memID);
106
		// Show the users whats available and what they have
107
		else
108
			$context['sub_template'] = 'user_subscription';
109
	}
110
111
	/**
112
	 * Removes any subscriptions that are found to be invalid
113
	 *
114
	 * - Invalid defined by missing cost or missing period
115
	 */
116
	private function _remove_invalid()
117
	{
118
		global $context;
119
120
		foreach ($context['subscriptions'] as $id => $sub)
121
		{
122
			// Work out the costs.
123
			$costs = Util::unserialize($sub['real_cost']);
124
125
			$cost_array = array();
126
127
			// Flexible cost to time?
128
			if ($sub['real_length'] === 'F')
129
			{
130
				foreach ($costs as $duration => $cost)
131
				{
132
					if ($cost != 0)
133
					{
134
						$cost_array[$duration] = $cost;
135
					}
136
				}
137
			}
138
			else
139
			{
140
				$cost_array['fixed'] = $costs['fixed'];
141
			}
142
143
			// No cost associated with it, then drop it
144
			if (empty($cost_array))
145
			{
146
				unset($context['subscriptions'][$id]);
147
			}
148
			else
149
			{
150
				$context['subscriptions'][$id]['member'] = 0;
151
				$context['subscriptions'][$id]['subscribed'] = false;
152
				$context['subscriptions'][$id]['costs'] = $cost_array;
153
			}
154
		}
155
	}
156
157
	/**
158
	 * When the chosen payment gateway is done and it supports a receipt link url
159
	 * it will be set to come here.
160
	 *
161
	 * - This is NOT the same as the notify processing url which will point to subscriptions.php
162
	 * - Accessed by ?action=profile;u=123;area=subscriptions;sub_id=?;done
163
	 *
164
	 * @param int $memID
165
	 */
166
	private function _orderDone($memID)
167
	{
168
		global $context;
169
170
		$sub_id = (int) $this->_req->query->sub_id;
171
172
		// Must exist but let's be sure...
173
		if (isset($context['current'][$sub_id]))
174
		{
175
			// What are the pending details?
176
			$current_pending = Util::unserialize($context['current'][$sub_id]['pending_details']);
177
178
			// Nothing pending, nothing to do
179
			if (!empty($current_pending))
180
			{
181
				$current_pending = array_reverse($current_pending);
182
				foreach ($current_pending as $id => $sub)
183
				{
184
					// Just find one and change it to payback
185
					if ($sub[0] == $sub_id && trim($sub[3]) === 'prepay')
186
					{
187
						$current_pending[$id][3] = 'payback';
188
						break;
189
					}
190
				}
191
192
				// Save the details back.
193
				$pending_details = serialize($current_pending);
194
				updatePendingStatus($context['current'][$sub_id]['id'], $memID, $pending_details);
195
			}
196
		}
197
198
		// A simple thank you
199
		$context['sub_template'] = 'paid_done';
200
	}
201
202
	/**
203
	 * Called when the user selects "Order" from the subscription page
204
	 *
205
	 * - Accessed with ?action=profile;u=123;area=subscriptions;confirm
206
	 *
207
	 * @param int $memID The id of the member who is ordering
208
	 *
209
	 * @throws Elk_Exception paid_sub_not_active
210
	 */
211
	private function _confirmOrder($memID)
212
	{
213
		global $context, $modSettings, $txt;
214
215
		// Hopefully just one, if not we use the last one.
216
		foreach ($this->_req->post->sub_id as $k => $v)
217
			$this->_id_sub = (int) $k;
218
219
		// Selecting a subscription that does not exist or is not active?
220
		if (!isset($this->_id_sub, $context['subscriptions'][$this->_id_sub]) || $context['subscriptions'][$this->_id_sub]['active'] == 0)
221
		{
222
			throw new Elk_Exception('paid_sub_not_active');
223
		}
224
225
		// Simplify...
226
		$this->_order = $context['subscriptions'][$this->_id_sub];
227
228
		// Set the period in case this is a flex time frame.
229
		$period = 'xx';
230
		if ($this->_order['flexible'])
231
		{
232
			$period = isset($this->_req->post->cur[$this->_id_sub]) && isset($this->_order['costs'][$this->_req->post->cur[$this->_id_sub]]) ? $this->_req->post->cur[$this->_id_sub] : 'xx';
233
		}
234
235
		// Check we have a valid cost.
236
		if ($this->_order['flexible'] && $period === 'xx')
237
		{
238
			throw new Elk_Exception('paid_sub_not_active');
239
		}
240
241
		// Sort out the cost/currency.
242
		$context['currency'] = $modSettings['paid_currency_code'];
243
		$context['recur'] = $this->_order['repeatable'];
244
245
		// Payment details based on one time or flex
246
		$this->_set_value_cost_context();
247
248
		// Setup the all the payment gateway context.
249
		$this->_set_payment_gatway_context($memID, $period);
250
251
		// No active payment gateways, then no way to pay, time to bail out, blame the admin
252
		if (empty($context['gateways']))
253
			throw new Elk_Exception($txt['paid_admin_not_setup_gateway']);
254
255
		// Now we are going to assume they want to take this out ;)
256
		$new_data = array($this->_order['id'], $context['value'], $period, 'prepay');
257
258
		// They have one of these already?
259
		if (isset($context['current'][$this->_order['id']]))
260
		{
261
			// What are the details like?
262
			$current_pending = array();
263
			if ($context['current'][$this->_order['id']]['pending_details'] != '')
264
				$current_pending = Util::unserialize($context['current'][$this->_order['id']]['pending_details']);
265
266
			// Don't get silly.
267
			if (count($current_pending) > 9)
268
				$current_pending = array();
269
270
			// Only record real pending payments as will otherwise confuse the admin!
271
			$pending_count = 0;
272
			foreach ($current_pending as $pending)
273
			{
274
				if (trim($pending[3]) === 'payback')
275
					$pending_count++;
276
			}
277
278
			// If its already pending, don't increase the pending count
279
			if (!in_array($new_data, $current_pending))
280
			{
281
				$current_pending[] = $new_data;
282
				$pending_details = serialize($current_pending);
283
				updatePendingSubscriptionCount($pending_count, $context['current'][$this->_order['id']]['id'], $memID, $pending_details);
284
			}
285
		}
286
		// Never had this before, lovely.
287
		else
288
		{
289
			$pending_details = serialize(array($new_data));
290
			logNewSubscription($this->_order['id'], $memID, $pending_details);
291
		}
292
293
		// Change the template.
294
		$context['sub'] = $this->_order;
295
		$context['sub_template'] = 'choose_payment';
296
	}
297
298
	/**
299
	 * Sets the value/cost/period/unit of the chosen order for use in templates
300
	 */
301
	private function _set_value_cost_context()
302
	{
303
		global $context, $modSettings, $txt;
304
305
		if ($this->_order['flexible'])
306
		{
307
			// Real cost...
308
			$context['value'] = $this->_order['costs'][$this->_req->post->cur[$this->_id_sub]];
309
			$context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']) . '/' . $txt[$this->_req->post->cur[$this->_id_sub]];
310
311
			// The period value for paypal.
312
			$context['paypal_period'] = strtoupper(substr($this->_req->post->cur[$this->_id_sub], 0, 1));
313
		}
314
		else
315
		{
316
			// Real cost...
317
			$context['value'] = $this->_order['costs']['fixed'];
318
			$context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']);
319
320
			// Recur?
321
			preg_match('~(\d*)(\w)~', $this->_order['real_length'], $match);
322
			$context['paypal_unit'] = $match[1];
323
			$context['paypal_period'] = $match[2];
324
		}
325
	}
326
327
	/**
328
	 * Sets the required payment form fields for the various payment gateways
329
	 *
330
	 * @param int $memID The id of the member who is ordering
331
	 * @param string period xx for none or a value of time
0 ignored issues
show
Bug introduced by
The type period was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
332
	 */
333
	private function _set_payment_gatway_context($memID, $period)
334
	{
335
		global $context, $scripturl;
336
337
		$context['gateways'] = array();
338
		foreach ($this->_gateways as $id => $gateway)
339
		{
340
			$fields = $this->_gateways[$id]->fetchGatewayFields($this->_order['id'] . '+' . $memID, $this->_order, $context['value'], $period, $scripturl . '?action=profile&u=' . $memID . '&area=subscriptions&sub_id=' . $this->_order['id'] . '&done');
341
342
			if (!empty($fields['form']))
343
			{
344
				$context['gateways'][] = $fields;
345
346
				// Does this gateway have any javascript?
347
				if (!empty($fields['javascript']))
348
				{
349
					addInlineJavascript($fields['javascript'], true);
350
				}
351
			}
352
		}
353
	}
354
}