Completed
Push — master ( 085f89...a587cf )
by Aimeos
11:11
created

Base::updatePush()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2017
7
 * @package MShop
8
 * @subpackage Service
9
 */
10
11
12
namespace Aimeos\MShop\Service\Provider;
13
14
15
/**
16
 * Abstract class for all service provider implementations with some default methods.
17
 *
18
 * @package MShop
19
 * @subpackage Service
20
 */
21
abstract class Base
22
{
23
	private $context;
24
	private $serviceItem;
25
	private $communication;
26
	private $beGlobalConfig;
27
28
29
	/**
30
	 * Initializes the service provider object.
31
	 *
32
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object with required objects
33
	 * @param \Aimeos\MShop\Service\Item\Iface $serviceItem Service item with configuration for the provider
34
	 */
35
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, \Aimeos\MShop\Service\Item\Iface $serviceItem )
36
	{
37
		$this->context = $context;
38
		$this->serviceItem = $serviceItem;
39
	}
40
41
42
	/**
43
	 * Catch unknown methods
44
	 *
45
	 * @param string $name Name of the method
46
	 * @param array $param List of method parameter
47
	 * @throws \Aimeos\MShop\Common\Manager\Exception If method call failed
48
	 */
49
	public function __call( $name, array $param )
50
	{
51
		throw new \Aimeos\MShop\Service\Exception( sprintf( 'Unable to call method "%1$s"', $name ) );
52
	}
53
54
55
	/**
56
	 * Returns the price when using the provider.
57
	 * Usually, this is the lowest price that is available in the service item but can also be a calculated based on
58
	 * the basket content, e.g. 2% of the value as transaction cost.
59
	 *
60
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
61
	 * @return \Aimeos\MShop\Price\Item\Iface Price item containing the price, shipping, rebate
62
	 */
63
	public function calcPrice( \Aimeos\MShop\Order\Item\Base\Iface $basket )
64
	{
65
		$priceManager = \Aimeos\MShop\Factory::createManager( $this->context, 'price' );
66
		$prices = $this->serviceItem->getRefItems( 'price', 'default', 'default' );
67
68
		if( count( $prices ) > 0 ) {
69
			return $priceManager->getLowestPrice( $prices, 1 );
70
		}
71
72
		return $priceManager->createItem();
73
	}
74
75
76
	/**
77
	 * Checks the backend configuration attributes for validity.
78
	 *
79
	 * @param array $attributes Attributes added by the shop owner in the administraton interface
80
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
81
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
82
	 */
83
	public function checkConfigBE( array $attributes )
84
	{
85
		return [];
86
	}
87
88
89
	/**
90
	 * Checks the frontend configuration attributes for validity.
91
	 *
92
	 * @param array $attributes Attributes entered by the customer during the checkout process
93
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
94
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
95
	 */
96
	public function checkConfigFE( array $attributes )
97
	{
98
		return [];
99
	}
100
101
102
	/**
103
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
104
	 * rules for the value of each field in the administration interface.
105
	 *
106
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
107
	 */
108
	public function getConfigBE()
109
	{
110
		return [];
111
	}
112
113
114
	/**
115
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
116
	 * rules for the value of each field in the frontend.
117
	 *
118
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
119
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
120
	 */
121
	public function getConfigFE( \Aimeos\MShop\Order\Item\Base\Iface $basket )
122
	{
123
		return [];
124
	}
125
126
127
	/**
128
	 * Returns the service item which also includes the configuration for the service provider.
129
	 *
130
	 * @return \Aimeos\MShop\Service\Item\Iface Service item
131
	 */
132
	public function getServiceItem()
133
	{
134
		return $this->serviceItem;
135
	}
136
137
138
	/**
139
	 * Injects additional global configuration for the backend.
140
	 *
141
	 * It's used for adding additional backend configuration from the application
142
	 * like the URLs to redirect to.
143
	 *
144
	 * Supported redirect URLs are:
145
	 * - payment.url-success
146
	 * - payment.url-failure
147
	 * - payment.url-cancel
148
	 * - payment.url-update
149
	 *
150
	 * @param array $config Associative list of config keys and their value
151
	 */
152
	public function injectGlobalConfigBE( array $config )
153
	{
154
		$this->beGlobalConfig = $config;
155
	}
156
157
158
	/**
159
	 * Checks if payment provider can be used based on the basket content.
160
	 * Checks for country, currency, address, RMS, etc. -> in separate decorators
161
	 *
162
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
163
	 * @return boolean True if payment provider can be used, false if not
164
	 */
165
	public function isAvailable( \Aimeos\MShop\Order\Item\Base\Iface $basket )
166
	{
167
		return true;
168
	}
169
170
171
	/**
172
	 * Checks what features the payment provider implements.
173
	 *
174
	 * @param integer $what Constant from abstract class
175
	 * @return boolean True if feature is available in the payment provider, false if not
176
	 */
177
	public function isImplemented( $what )
178
	{
179
		return false;
180
	}
181
182
183
	/**
184
	 * Queries for status updates for the given order if supported.
185
	 *
186
	 * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object
187
	 */
188
	public function query( \Aimeos\MShop\Order\Item\Iface $order )
189
	{
190
		throw new \Aimeos\MShop\Service\Exception( sprintf( 'Method "%1$s" for provider not available', 'query' ) );
191
	}
192
193
194
	/**
195
	 * Looks for new update files and updates the orders for which status updates were received.
196
	 * If batch processing of files isn't supported, this method can be empty.
197
	 *
198
	 * @return boolean True if the update was successful, false if async updates are not supported
199
	 * @throws \Aimeos\MShop\Service\Exception If updating one of the orders failed
200
	 */
201
	public function updateAsync()
202
	{
203
		return false;
204
	}
205
206
207
	/**
208
	 * Updates the order status sent by payment gateway notifications
209
	 *
210
	 * @param \Psr\Http\Message\ServerRequestInterface Request object
211
	 * @return \Psr\Http\Message\ResponseInterface Response object
212
	 */
213
	public function updatePush( \Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response )
214
	{
215
		return $response->withStatus( 501, 'Not implemented' );
216
	}
217
218
219
	/**
220
	 * Updates the orders for which status updates were received via direct requests (like HTTP).
221
	 *
222
	 * @param array $params Associative list of request parameters
223
	 * @param string|null $body Information sent within the body of the request
224
	 * @param string|null &$response Response body for notification requests
225
	 * @param array &$header Response headers for notification requests
226
	 * @return \Aimeos\MShop\Order\Item\Iface|null Order item if update was successful, null if the given parameters are not valid for this provider
227
	 * @throws \Aimeos\MShop\Service\Exception If updating one of the orders failed
228
	 */
229
	public function updateSync( array $params = [], $body = null, &$response = null, array &$header = [] )
230
	{
231
		if( isset( $params['orderid'] ) ) {
232
			return $this->getOrder( $params['orderid'] );
233
		}
234
235
		return null;
236
	}
237
238
239
	/**
240
	 * Sets the communication object for a service provider.
241
	 *
242
	 * @param \Aimeos\MW\Communication\Iface $communication Object of communication
243
	 */
244
	public function setCommunication( \Aimeos\MW\Communication\Iface $communication )
245
	{
246
		$this->communication = $communication;
247
	}
248
249
250
	/**
251
	 * Returns the communication object for the service provider.
252
	 *
253
	 * @return \Aimeos\MW\Communication\Iface Object for communication
254
	 */
255
	protected function getCommunication()
256
	{
257
		if( !isset( $this->communication ) ) {
258
			$this->communication = new \Aimeos\MW\Communication\Curl();
259
		}
260
261
		return $this->communication;
262
	}
263
264
265
	/**
266
	 * Calculates the last date behind the given timestamp depending on the other paramters.
267
	 *
268
	 * This method is used to calculate the date for comparing the order date to
269
	 * if e.g. credit card payments should be captured or direct debit should be
270
	 * checked after the given amount of days from external payment providers.
271
	 * This method can calculate with business/working days only if requested
272
	 * and use the given list of public holidays to take them into account.
273
	 *
274
	 * @param integer $timestamp Timestamp to use as starting point for the backward calculation
275
	 * @param integer $skipdays Number of days to calculate backwards
276
	 * @param boolean $businessOnly True if only business days should be used for calculation, false if not
277
	 * @param string $publicHolidays Comma separated list of public holidays in YYYY-MM-DD format
278
	 * @return string Date in YYY-MM-DD format to be compared to the order date
279
	 * @throws \Aimeos\MShop\Service\Exception If the given holiday string is in the wrong format and can't be processed
280
	 */
281
	protected function calcDateLimit( $timestamp, $skipdays = 0, $businessOnly = false, $publicHolidays = '' )
282
	{
283
		$holidays = $this->getPublicHolidays( $publicHolidays );
284
285
		if( !empty( $holidays ) )
286
		{
287
			for( $i = 0; $i <= $skipdays; $i++ )
288
			{
289
				$date = date( 'Y-m-d', $timestamp - $i * 86400 );
290
291
				if( isset( $holidays[$date] ) ) {
292
					$skipdays++;
293
				}
294
			}
295
		}
296
297
		if( $businessOnly === true )
298
		{
299
			// adds days for weekends
300
			for( $i = 0; $i <= $skipdays; $i++ )
301
			{
302
				$ts = $timestamp - $i * 86400;
303
304
				if( date( 'N', $ts ) > 5 && !isset( $holidays[date( 'Y-m-d', $ts )] ) ) {
305
					$skipdays++;
306
				}
307
			}
308
		}
309
310
		return date( 'Y-m-d', $timestamp - $skipdays * 86400 );
311
	}
312
313
314
	/**
315
	 * Checks required fields and the types of the config array.
316
	 *
317
	 * @param array $config Config parameters
318
	 * @param array $attributes Attributes for the config array
319
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
320
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
321
	 */
322
	protected function checkConfig( array $config, array $attributes )
323
	{
324
		$errors = [];
325
326
		foreach( $config as $key => $def )
327
		{
328
			if( $def['required'] === true && ( !isset( $attributes[$key] ) || $attributes[$key] === '' ) )
329
			{
330
				$errors[$key] = sprintf( 'Configuration for "%1$s" is missing', $key );
331
				continue;
332
			}
333
334
			if( isset( $attributes[$key] ) )
335
			{
336
				switch( $def['type'] )
337
				{
338
					case 'boolean':
339
						if( !is_string( $attributes[$key] ) || $attributes[$key] !== '0' && $attributes[$key] !== '1' ) {
340
							$errors[$key] = sprintf( 'Not a true/false value' ); continue 2;
341
						}
342
						break;
343
					case 'string':
344
						if( is_string( $attributes[$key] ) === false ) {
345
							$errors[$key] = sprintf( 'Not a string' ); continue 2;
346
						}
347
						break;
348
					case 'integer':
349
						if( ctype_digit( $attributes[$key] ) === false ) {
350
							$errors[$key] = sprintf( 'Not an integer number' ); continue 2;
351
						}
352
						break;
353
					case 'number':
354
						if( is_numeric( $attributes[$key] ) === false ) {
355
							$errors[$key] = sprintf( 'Not a number' ); continue 2;
356
						}
357
						break;
358
					case 'date':
359
						$pattern = '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/';
360
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
361
							$errors[$key] = sprintf( 'Not a date' ); continue 2;
362
						}
363
						break;
364
					case 'datetime':
365
						$pattern = '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9](:[0-5][0-9])?$/';
366
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
367
							$errors[$key] = sprintf( 'Not a date and time' ); continue 2;
368
						}
369
						break;
370
					case 'time':
371
						$pattern = '/^([0-2])?[0-9]:[0-5][0-9](:[0-5][0-9])?$/';
372
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
373
							$errors[$key] = sprintf( 'Not a time' ); continue 2;
374
						}
375
						break;
376
					case 'list':
377
					case 'select':
378
						if( !is_array( $def['default'] ) || !isset( $def['default'][$attributes[$key]] )
379
							&& !in_array( $attributes[$key], $def['default'] )
380
						) {
381
							$errors[$key] = sprintf( 'Not a listed value' ); continue 2;
382
						}
383
						break;
384
					case 'map':
385
						if( !is_array( $attributes[$key] ) ) {
386
							$errors[$key] = sprintf( 'Not a key/value map' ); continue 2;
387
						}
388
						break;
389
					default:
390
						throw new \Aimeos\MShop\Service\Exception( sprintf( 'Invalid type "%1$s"', $def['type'] ) );
391
				}
392
			}
393
394
			$errors[$key] = null;
395
		}
396
397
		return $errors;
398
	}
399
400
401
	/**
402
	 * Returns the criteria attribute items for the backend configuration
403
	 *
404
	 * @return \Aimeos\MW\Criteria\Attribute\Iface[] List of criteria attribute items
405
	 */
406
	protected function getConfigItems( array $configList )
407
	{
408
		$list = [];
409
410
		foreach( $configList as $key => $config ) {
411
			$list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $config );
412
		}
413
414
		return $list;
415
	}
416
417
418
	/**
419
	 * Returns the configuration value that matches one of the given keys.
420
	 *
421
	 * The config of the service item and (optionally) the global config
422
	 * is tested in the order of the keys. The first one that matches will
423
	 * be returned.
424
	 *
425
	 * @param array|string $keys Key name or list of key names that should be tested for in the order to test
426
	 * @param mixed $default Returned value if the key wasn't was found
427
	 * @return mixed Value of the first key that matches or null if none was found
428
	 */
429
	protected function getConfigValue( $keys, $default = null )
430
	{
431
		$srvconfig = $this->getServiceItem()->getConfig();
432
433
		foreach( (array) $keys as $key )
434
		{
435
			if( isset( $srvconfig[$key] ) ) {
436
				return $srvconfig[$key];
437
			}
438
439
			if( isset( $this->beGlobalConfig[$key] ) ) {
440
				return $this->beGlobalConfig[$key];
441
			}
442
		}
443
444
		return $default;
445
	}
446
447
448
	/**
449
	 * Returns the context item.
450
	 *
451
	 * @return \Aimeos\MShop\Context\Item\Iface Context item
452
	 */
453
	protected function getContext()
454
	{
455
		return $this->context;
456
	}
457
458
459
	/**
460
	 * Returns the calculated amount of the price item
461
	 *
462
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price item
463
	 * @param boolean $costs Include costs per item
464
	 * @param boolean $tax Include tax
465
	 * @return string Formatted money amount
466
	 */
467
	protected function getAmount( \Aimeos\MShop\Price\Item\Iface $price, $costs = true, $tax = true )
468
	{
469
		$amount = $price->getValue();
470
471
		if( $costs === true ) {
472
			$amount += $price->getCosts();
473
		}
474
475
		if( $tax === true && $price->getTaxFlag() === false )
476
		{
477
			$tmp = clone $price;
478
479
			if( $costs === false )
480
			{
481
				$tmp->clear();
482
				$tmp->setValue( $price->getValue() );
483
				$tmp->setTaxRate( $price->getTaxRate() );
484
				$tmp->setQuantity( $price->getQuantity() );
485
			}
486
487
			$amount += $tmp->getTaxValue();
488
		}
489
490
		return number_format( $amount, 2, '.', '' );
491
	}
492
493
494
	/**
495
	 * Returns the order item for the given ID.
496
	 *
497
	 * @param string $id Unique order ID
498
	 * @return \Aimeos\MShop\Order\Item\Iface $item Order object
499
	 */
500
	protected function getOrder( $id )
501
	{
502
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'order' );
503
504
		$search = $manager->createSearch( true );
505
		$expr = [
506
			$search->getConditions(),
507
			$search->compare( '==', 'order.id', $id ),
508
			$search->compare( '==', 'order.base.service.code', $this->serviceItem->getCode() ),
509
		];
510
		$search->setConditions( $search->combine( '&&', $expr ) );
511
512
		$result = $manager->searchItems( $search );
513
514
		if( ( $item = reset( $result ) ) === false ) {
515
			throw new \Aimeos\MShop\Service\Exception( sprintf( 'No order for ID "%1$s" found', $id ) );
516
		}
517
518
		return $item;
519
	}
520
521
522
	/**
523
	 * Returns the base order which is equivalent to the basket.
524
	 *
525
	 * @param string $baseId Order base ID stored in the order item
526
	 * @param integer $parts Bitmap of the basket parts that should be loaded
527
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Basket, optional with addresses, products, services and coupons
528
	 */
529
	protected function getOrderBase( $baseId, $parts = \Aimeos\MShop\Order\Item\Base\Base::PARTS_SERVICE )
530
	{
531
		return \Aimeos\MShop\Factory::createManager( $this->context, 'order/base' )->load( $baseId, $parts );
532
	}
533
534
535
	/**
536
	 * Saves the order item.
537
	 *
538
	 * @param \Aimeos\MShop\Order\Item\Iface $item Order object
539
	 * @return \Aimeos\MShop\Order\Item\Iface Order object including the generated ID
0 ignored issues
show
Documentation introduced by
Should the return type not be \Aimeos\MShop\Order\Item\Iface|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
540
	 */
541
	protected function saveOrder( \Aimeos\MShop\Order\Item\Iface $item )
542
	{
543
		return \Aimeos\MShop\Factory::createManager( $this->context, 'order' )->saveItem( $item );
544
	}
545
546
547
	/**
548
	 * Saves the base order which is equivalent to the basket and its dependent objects.
549
	 *
550
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $base Order base object with associated items
551
	 * @param integer $parts Bitmap of the basket parts that should be stored
552
	 */
553
	protected function saveOrderBase( \Aimeos\MShop\Order\Item\Base\Iface $base, $parts = \Aimeos\MShop\Order\Item\Base\Base::PARTS_SERVICE )
554
	{
555
		\Aimeos\MShop\Factory::createManager( $this->context, 'order/base' )->store( $base, $parts );
556
	}
557
558
559
	/**
560
	 * Sets the attributes in the given service item.
561
	 *
562
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $orderServiceItem Order service item that will be added to the basket
563
	 * @param array $attributes Attribute key/value pairs entered by the customer during the checkout process
564
	 * @param string $type Type of the configuration values (delivery or payment)
565
	 */
566
	protected function setAttributes( \Aimeos\MShop\Order\Item\Base\Service\Iface $orderServiceItem, array $attributes, $type )
567
	{
568
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'order/base/service/attribute' );
569
570
		foreach( $attributes as $key => $value )
571
		{
572
			$item = $manager->createItem();
573
			$item->setCode( $key );
574
			$item->setValue( $value );
575
			$item->setType( $type );
576
577
			$orderServiceItem->setAttributeItem( $item );
578
		}
579
	}
580
581
582
	/**
583
	 * Returns the public holidays in ISO format
584
	 *
585
	 * @param string $list Comma separated list of public holidays in YYYY-MM-DD format
586
	 * @return array List of dates in YYYY-MM-DD format
587
	 * @throws \Aimeos\MShop\Service\Exception If the given holiday string is in the wrong format and can't be processed
588
	 */
589
	private function getPublicHolidays( $list )
590
	{
591
		$holidays = [];
592
593
		if( is_string( $list ) && $list !== '' )
594
		{
595
			$holidays = explode( ',', str_replace( ' ', '', $list ) );
596
597
			if( sort( $holidays ) === false ) {
598
				throw new \Aimeos\MShop\Service\Exception( sprintf( 'Unable to sort public holidays: "%1$s"', $list ) );
599
			}
600
601
			$holidays = array_flip( $holidays );
602
		}
603
604
		return $holidays;
605
	}
606
}
607