Completed
Push — master ( 7f6c5c...544918 )
by Aimeos
02:57
created

Standard::getPaymentForm()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.7537
c 0
b 0
f 0
cc 6
nc 4
nop 3
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017
6
 * @package Client
7
 * @subpackage JsonApi
8
 */
9
10
11
namespace Aimeos\Client\JsonApi\Order;
12
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\ServerRequestInterface;
15
16
17
/**
18
 * JSON API standard client
19
 *
20
 * @package Client
21
 * @subpackage JsonApi
22
 */
23
class Standard
24
	extends \Aimeos\Client\JsonApi\Base
25
	implements \Aimeos\Client\JsonApi\Iface
26
{
27
	/**
28
	 * Returns the resource or the resource list
29
	 *
30
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
31
	 * @param \Psr\Http\Message\ResponseInterface $response Response object
32
	 * @return \Psr\Http\Message\ResponseInterface Modified response object
33
	 */
34
	public function get( ServerRequestInterface $request, ResponseInterface $response )
35
	{
36
		$view = $this->getView();
37
38
		try
39
		{
40
			$cntl = \Aimeos\Controller\Frontend\Factory::createController( $this->getContext(), 'order' );
41
42
			if( ( $id = $view->param( 'id' ) ) != '' )
43
			{
44
				$view->items = $cntl->getItem( $id );
45
				$view->total = 1;
46
			}
47
			else
48
			{
49
				$total = 0;
50
				$filter = $cntl->createFilter();
51
				$this->initCriteria( $filter, $view->param() );
52
53
				$view->items = $cntl->searchItems( $filter, $total );
54
				$view->total = $total;
55
			}
56
57
			$status = 200;
58
		}
59
		catch( \Aimeos\Controller\Frontend\Exception $e )
60
		{
61
			$status = 403;
62
			$view->errors = $this->getErrorDetails( $e, 'controller/frontend' );
63
		}
64
		catch( \Aimeos\MShop\Exception $e )
65
		{
66
			$status = 404;
67
			$view->errors = $this->getErrorDetails( $e, 'mshop' );
68
		}
69
		catch( \Exception $e )
70
		{
71
			$status = 500;
72
			$view->errors = $this->getErrorDetails( $e );
73
		}
74
75
		return $this->render( $response, $view, $status );
76
	}
77
78
79
	/**
80
	 * Creates or updates the resource or the resource list
81
	 *
82
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
83
	 * @param \Psr\Http\Message\ResponseInterface $response Response object
84
	 * @return \Psr\Http\Message\ResponseInterface Modified response object
85
	 */
86
	public function post( ServerRequestInterface $request, ResponseInterface $response )
87
	{
88
		$view = $this->getView();
89
90
		try
91
		{
92
			$body = (string) $request->getBody();
93
94
			if( ( $payload = json_decode( $body ) ) === null || !isset( $payload->data->attributes ) ) {
95
				throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Invalid JSON in body' ), 400 );
96
			}
97
98
			if( !isset( $payload->data->attributes->{'order.baseid'} ) ) {
99
				throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Required attribute "order.baseid" is missing' ), 400 );
100
			}
101
102
			$basket = $this->getBasket( $payload->data->attributes->{'order.baseid'} );
103
			$item = $this->createOrder( $payload->data->attributes->{'order.baseid'} );
104
105
			$view->form = $this->getPaymentForm( $basket, $item, (array) $payload->data->attributes );
106
			$view->items = $item;
107
			$view->total = 1;
108
109
			$status = 201;
110
		}
111
		catch( \Aimeos\Client\JsonApi\Exception $e )
112
		{
113
			$status = $e->getCode();
114
			$view->errors = $this->getErrorDetails( $e, 'client/jsonapi' );
115
		}
116
		catch( \Aimeos\Controller\Frontend\Exception $e )
117
		{
118
			$status = 403;
119
			$view->errors = $this->getErrorDetails( $e, 'controller/frontend' );
120
		}
121
		catch( \Aimeos\MShop\Exception $e )
122
		{
123
			$status = 404;
124
			$view->errors = $this->getErrorDetails( $e, 'mshop' );
125
		}
126
		catch( \Exception $e )
127
		{
128
			$status = 500;
129
			$view->errors = $this->getErrorDetails( $e );
130
		}
131
132
		return $this->render( $response, $view, $status );
133
	}
134
135
136
	/**
137
	 * Returns the available REST verbs and the available parameters
138
	 *
139
	 * @param \Psr\Http\Message\ServerRequestInterface $request Request object
140
	 * @param \Psr\Http\Message\ResponseInterface $response Response object
141
	 * @return \Psr\Http\Message\ResponseInterface Modified response object
142
	 */
143
	public function options( ServerRequestInterface $request, ResponseInterface $response )
144
	{
145
		$view = $this->getView();
146
147
		$view->attributes = [
148
			'order.baseid' => [
149
				'label' => 'ID of the stored basket (POST only)',
150
				'type' => 'string', 'default' => '', 'required' => true,
151
			],
152
		];
153
154
		$tplconf = 'client/jsonapi/standard/template-options';
155
		$default = 'options-standard.php';
156
157
		$body = $view->render( $view->config( $tplconf, $default ) );
158
159
		return $response->withHeader( 'Allow', 'GET,OPTIONS,POST' )
160
			->withHeader( 'Cache-Control', 'max-age=300' )
161
			->withHeader( 'Content-Type', 'application/vnd.api+json' )
162
			->withBody( $view->response()->createStreamFromString( $body ) )
163
			->withStatus( 200 );
164
	}
165
166
167
	/**
168
	 * Adds and returns a new order item for the given order base ID
169
	 *
170
	 * @param string $baseId Unique order base ID
171
	 * @return \Aimeos\MShop\Order\Item\Iface New order item
172
	 */
173
	protected function createOrder( $baseId )
174
	{
175
		$context = $this->getContext();
176
		$cntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'order' );
177
178
		$item = $cntl->addItem( $baseId, 'jsonapi' );
179
		$cntl->block( $item );
180
181
		$context->getSession()->set( 'aimeos/orderid', $item->getId() );
182
183
		return $item;
184
	}
185
186
187
	/**
188
	 * Returns the basket object for the given ID
189
	 *
190
	 * @param string $basketId Unique order base ID
191
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Basket object including only the services
192
	 * @throws \Aimeos\Client\JsonApi\Exception If basket ID is not the same as stored before in the current session
193
	 */
194
	protected function getBasket( $basketId )
195
	{
196
		$context = $this->getContext();
197
		$baseId = $context->getSession()->get( 'aimeos/order.baseid' );
198
199
		if( $baseId != $basketId )
200
		{
201
			$msg = sprintf( 'No basket for the "order.baseid" ("%1$s") found', $basketId );
202
			throw new \Aimeos\Client\JsonApi\Exception( $msg, 403 );
203
		}
204
205
		$parts = \Aimeos\MShop\Order\Item\Base\Base::PARTS_SERVICE;
206
		$cntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
207
208
		return $cntl->load( $baseId, $parts, false );
209
	}
210
211
212
	/**
213
	 * Returns the form helper object for building the payment form in the frontend
214
	 *
215
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Saved basket object including payment service object
216
	 * @param \Aimeos\MShop\Order\Item\Iface $orderItem Saved order item created for the basket object
217
	 * @param array $attributes Associative list of payment data pairs
218
	 * @return \Aimeos\MShop\Common\Item\Helper\Form\Iface|null Form object with URL, parameters, etc.
219
	 * 	or null if no form data is required
220
	 */
221
	protected function getPaymentForm( \Aimeos\MShop\Order\Item\Base\Iface $basket,
222
		\Aimeos\MShop\Order\Item\Iface $orderItem, array $attributes )
223
	{
224
		$view = $this->getView();
225
		$context = $this->getContext();
226
		$total = $basket->getPrice()->getValue() + $basket->getPrice()->getCosts();
227
		$services = $basket->getService( \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_PAYMENT );
228
229
		if( $services === [] || $total <= '0.00' && $this->isSubscription( $basket->getProducts() ) === false )
230
		{
231
			$orderCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'order' );
232
			$orderCntl->saveItem( $orderItem->setPaymentStatus( \Aimeos\MShop\Order\Item\Base::PAY_AUTHORIZED ) );
233
234
			$url = $this->getUrlConfirm( $view, [], ['absoluteUri' => true, 'namespace' => false] );
235
			return new \Aimeos\MShop\Common\Item\Helper\Form\Standard( $url, 'GET' );
236
		}
237
238
		if( ( $service = reset( $services ) ) !== false )
239
		{
240
			$args = array( 'code' => $service->getCode(), 'orderid' => $orderItem->getId() );
241
			$config = array( 'absoluteUri' => true, 'namespace' => false );
242
			$urls = array(
243
				'payment.url-success' => $this->getUrlConfirm( $view, $args, $config ),
244
				'payment.url-update' => $this->getUrlUpdate( $view, $args, $config ),
245
			);
246
247
			foreach( $service->getAttributes() as $item ) {
248
				$attributes[$item->getCode()] = $item->getValue();
249
			}
250
251
			$serviceCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'service' );
252
			return $serviceCntl->process( $orderItem, $service->getServiceId(), $urls, $attributes );
253
		}
254
	}
255
256
257
	/**
258
	 * Returns the URL to the confirm page.
259
	 *
260
	 * @param \Aimeos\MW\View\Iface $view View object
261
	 * @param array $params Parameters that should be part of the URL
262
	 * @param array $config Default URL configuration
263
	 * @return string URL string
264
	 */
265
	protected function getUrlConfirm( \Aimeos\MW\View\Iface $view, array $params, array $config )
266
	{
267
		$target = $view->config( 'client/html/checkout/confirm/url/target' );
268
		$cntl = $view->config( 'client/html/checkout/confirm/url/controller', 'checkout' );
269
		$action = $view->config( 'client/html/checkout/confirm/url/action', 'confirm' );
270
		$config = $view->config( 'client/html/checkout/confirm/url/config', $config );
271
272
		return $view->url( $target, $cntl, $action, $params, [], $config );
273
	}
274
275
276
	/**
277
	 * Returns the URL to the update page.
278
	 *
279
	 * @param \Aimeos\MW\View\Iface $view View object
280
	 * @param array $params Parameters that should be part of the URL
281
	 * @param array $config Default URL configuration
282
	 * @return string URL string
283
	 */
284
	protected function getUrlUpdate( \Aimeos\MW\View\Iface $view, array $params, array $config )
285
	{
286
		$target = $view->config( 'client/html/checkout/update/url/target' );
287
		$cntl = $view->config( 'client/html/checkout/update/url/controller', 'checkout' );
288
		$action = $view->config( 'client/html/checkout/update/url/action', 'update' );
289
		$config = $view->config( 'client/html/checkout/update/url/config', $config );
290
291
		return $view->url( $target, $cntl, $action, $params, [], $config );
292
	}
293
294
295
	/**
296
	 * Tests if one of the products is a subscription
297
	 *
298
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface[] $products Ordered products
299
	 * @return boolean True if at least one product is a subscription, false if not
300
	 */
301
	protected function isSubscription( array $products )
302
	{
303
		foreach( $products as $orderProduct )
304
		{
305
			if( $orderProduct->getAttributeItem( 'interval', 'config' ) ) {
306
				return true;
307
			}
308
		}
309
310
		return false;
311
	}
312
313
314
	/**
315
	 * Returns the response object with the rendered header and body
316
	 *
317
	 * @param \Psr\Http\Message\ResponseInterface $response Response object
318
	 * @param \Aimeos\MW\View\Iface $view View instance
319
	 * @param integer $status HTTP status code
320
	 * @return \Psr\Http\Message\ResponseInterface Modified response object
321
	 */
322
	protected function render( ResponseInterface $response, \Aimeos\MW\View\Iface $view, $status )
323
	{
324
		/** client/jsonapi/order/standard/template
325
		 * Relative path to the order JSON API template
326
		 *
327
		 * The template file contains the code and processing instructions
328
		 * to generate the result shown in the JSON API body. The
329
		 * configuration string is the path to the template file relative
330
		 * to the templates directory (usually in client/jsonapi/templates).
331
		 *
332
		 * You can overwrite the template file configuration in extensions and
333
		 * provide alternative templates. These alternative templates should be
334
		 * named like the default one but with the string "standard" replaced by
335
		 * an unique name. You may use the name of your project for this. If
336
		 * you've implemented an alternative client class as well, "standard"
337
		 * should be replaced by the name of the new class.
338
		 *
339
		 * @param string Relative path to the template creating the body of the JSON API
340
		 * @since 2017.03
341
		 * @category Developer
342
		 */
343
		$tplconf = 'client/jsonapi/order/standard/template';
344
		$default = 'order/standard.php';
345
346
		$body = $view->render( $view->config( $tplconf, $default ) );
347
348
		return $response->withHeader( 'Allow', 'GET,OPTIONS,POST' )
349
			->withHeader( 'Cache-Control', 'no-cache, private' )
350
			->withHeader( 'Content-Type', 'application/vnd.api+json' )
351
			->withBody( $view->response()->createStreamFromString( $body ) )
352
			->withStatus( $status );
353
	}
354
}
355