Completed
Pull Request — 2.0 (#101)
by Mark
02:17
created

PaymentService::getGatewayFactory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
3
/**
4
 * Payment Service
5
 *
6
 * Provides wrapper methods for interacting with the omnipay gateways
7
 * library.
8
 *
9
 * Interfaces with the omnipay library
10
 *
11
 * @package payment
12
 */
13
14
use Omnipay\Common\AbstractGateway;
15
use Omnipay\Common\GatewayFactory;
16
use Omnipay\Common\Message\AbstractResponse;
17
use Omnipay\Common\Message\AbstractRequest;
18
19
abstract class PaymentService extends Object{
20
21
	/**
22
	 * @var Guzzle\Http\ClientInterface
23
	 */
24
	private static $httpclient;
25
26
	/**
27
	 * @var Guzzle\Http\Message\Request
28
	 */
29
	private static $httprequest;
30
31
	/**
32
	 * @var Payment
33
	 */
34
	protected $payment;
35
36
	/**
37
	 * @var String
38
	 */
39
	protected $returnurl;
40
41
	/**
42
	 * @var String
43
	 */
44
	protected $cancelurl;
45
46
	/**
47
	 * @var Guzzle\Http\Message\Response
48
	 */
49
	protected $response;
50
51
    /**
52
     * @var GatewayFactory
53
     */
54
	protected $gatewayFactory;
55
56
	private static $dependencies = array(
57
		'gatewayFactory' => '%$\Omnipay\Common\GatewayFactory',
58
	);
59
60
61
    /**
62
     * @param Payment
63
     */
64
	public function __construct(Payment $payment) {
65
		parent::__construct();
66
		$this->payment = $payment;
67
	}
68
69
	/**
70
	 * Get the url to return to, that has been previously stored.
71
	 * This is not a database field.
72
	 * @return string the url
73
	 */
74
	public function getReturnUrl() {
75
		return $this->returnurl;
76
	}
77
78
	/**
79
	 * Set the url to redirect to after payment is made/attempted.
80
	 * This function also populates the cancel url, if it is empty.
81
	 * @return PaymentService this object for chaining
82
	 */
83
	public function setReturnUrl($url) {
84
		$this->returnurl = $url;
85
		if (!$this->cancelurl) {
86
			$this->cancelurl = $url;
87
		}
88
89
		return $this;
90
	}
91
92
	/**
93
	 * @return string cancel url
94
	 */
95
	public function getCancelUrl() {
96
		return $this->cancelurl;
97
	}
98
99
	/**
100
	 * Set the url to redirect to after payment is cancelled
101
	 * @return PaymentService this object for chaining
102
	 */
103
	public function setCancelUrl($url) {
104
		$this->cancelurl = $url;
105
106
		return $this;
107
	}
108
109
	/**
110
	 * Get the appropriate redirect url
111
	 */
112
	public function getRedirectURL() {
113
		if ($this->response) {
114
			if ($this->response->isSuccessful()) {
115
				return $this->getReturnUrl();
116
			} elseif ($this->response->isRedirect()) {
117
				return $this->response->getRedirectUrl();
0 ignored issues
show
Bug introduced by
The method getRedirectUrl() does not seem to exist on object<Guzzle\Http\Message\Response>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
118
			}
119
		}
120
121
		return $this->getCancelUrl();
122
	}
123
124
	/**
125
	 * Update class properties via array.
126
	 */
127
	public function update($data) {
128
		if(isset($data['returnUrl'])){
129
			$this->setReturnUrl($data['returnUrl']);
130
		}
131
		if(isset($data['cancelUrl'])){
132
			$this->setCancelUrl($data['cancelUrl']);
133
		}
134
	}
135
136
137
	/**
138
	 * Get the omnipay gateway associated with this payment,
139
	 * with configuration applied.
140
	 *
141
	 * @throws RuntimeException - when gateway doesn't exist.
142
	 * @return AbstractGateway omnipay gateway class
143
	 */
144
	public function oGateway() {
145
		$gateway = $this->getGatewayFactory()->create(
146
			$this->payment->Gateway,
0 ignored issues
show
Documentation introduced by
The property Gateway does not exist on object<Payment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
147
			self::$httpclient,
148
			self::$httprequest
0 ignored issues
show
Documentation introduced by
self::$httprequest is of type object<Guzzle\Http\Message\Request>, but the function expects a null|object<Symfony\Comp...HttpFoundation\Request>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
149
		);
150
151
		$parameters = Config::inst()->get('Payment', 'parameters');
152
		if (isset($parameters[$this->payment->Gateway])) {
0 ignored issues
show
Documentation introduced by
The property Gateway does not exist on object<Payment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
153
			$gateway->initialize($parameters[$this->payment->Gateway]);
0 ignored issues
show
Documentation introduced by
The property Gateway does not exist on object<Payment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
154
		}
155
156
		return $gateway;
157
	}
158
159
	/**
160
	 * Generate a return/notify url for off-site gateways (completePayment).
161
	 * @return string endpoint url
162
	 */
163
	protected function getEndpointURL($action, $identifier) {
164
		return PaymentGatewayController::get_endpoint_url($action, $identifier);
165
	}
166
167
	/**
168
	 * Record a transaction on this for this payment.
169
	 * @param string $type the type of transaction to create.
170
	 *        This is any class that is (or extends) PaymentMessage.
171
	 * @param array|string|AbstractResponse|AbstractRequest|OmnipayException $data the response to record, or data to store
172
	 * @return GatewayTransaction newly created dataobject, saved to database.
173
	 */
174
	protected function createMessage($type, $data = null) {
175
		$output = array();
176
		if (is_string($data)) {
177
			$output =  array(
178
				'Message' => $data
179
			);
180
		} elseif (is_array($data)) {
181
			$output = $data;
182
		} elseif ($data instanceof Omnipay\Common\Exception\OmnipayException) {
183
			$output = array(
184
				"Message" => $data->getMessage(),
185
				"Code" => $data->getCode(),
186
				"Exception" => get_class($data),
187
				"Backtrace" => $data->getTraceAsString()
188
			);
189
		} elseif ($data instanceof AbstractResponse) {
190
			$output =  array(
191
				"Message" => $data->getMessage(),
192
				"Code" => $data->getCode(),
193
				"Reference" => $data->getTransactionReference(),
194
				"Data" => $data->getData()
195
			);
196
		} elseif ($data instanceof AbstractRequest) {
197
			$output = array(
198
				'Token' => $data->getToken(),
199
				'CardReference' => $data->getCardReference(),
200
				'Amount' => $data->getAmount(),
201
				'Currency' => $data->getCurrency(),
202
				'Description' => $data->getDescription(),
203
				'TransactionId' => $data->getTransactionId(),
204
				'TransactionReference' => $data->getTransactionReference(),
205
				'ClientIp' => $data->getClientIp(),
206
				'ReturnUrl' => $data->getReturnUrl(),
207
				'CancelUrl' => $data->getCancelUrl(),
208
				'NotifyUrl' => $data->getNotifyUrl(),
209
				'Parameters' => $data->getParameters()
210
			);
211
		}
212
		$output = array_merge($output, array(
213
			"PaymentID" => $this->payment->ID,
214
			"Gateway" => $this->payment->Gateway
0 ignored issues
show
Documentation introduced by
The property Gateway does not exist on object<Payment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
215
		));
216
		$this->logToFile($output, $type);
217
		$message = $type::create($output);
218
		$message->write();
219
		$this->payment->Messages()->add($message);
0 ignored issues
show
Documentation Bug introduced by
The method Messages does not exist on object<Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
220
221
		return $message;
222
	}
223
224
	/**
225
	 * Helper function for logging gateway requests
226
	 */
227
	protected function logToFile($data, $type = "") {
228
		if($logstyle = Payment::config()->file_logging){
229
			$title = $type." (".$this->payment->Gateway.")";
0 ignored issues
show
Documentation introduced by
The property Gateway does not exist on object<Payment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
230
			if ($logstyle === "verbose") {
231
				Debug::log(
232
					$title."\n\n".
233
					print_r($data, true)
234
				);
235
			} elseif($logstyle) {
236
				Debug::log(implode(", ", array(
237
					$title,
238
					isset($data['Message']) ? $data['Message'] : " ",
239
					isset($data['Code']) ? $data['Code'] : " ",
240
				)));
241
			}
242
		}
243
	}
244
245
	protected function createGatewayResponse() {
246
		$gatewayresponse = new GatewayResponse($this->payment);
247
		$gatewayresponse->setRedirectURL($this->getRedirectURL());
248
		return $gatewayresponse;
249
	}
250
251
	/**
252
	 * @return GatewayFactory
253
	 */
254
	public function getGatewayFactory() {
255
        if (!isset($this->gatewayFactory)) {
256
            $this->gatewayFactory = Injector::inst()->get('Omnipay\Common\GatewayFactory');
257
        }
258
259
		return $this->gatewayFactory;
260
	}
261
262
	/**
263
	 * @param GatewayFactory $gatewayFactory
264
	 *
265
	 * @return $this
266
	 */
267
	public function setGatewayFactory($gatewayFactory) {
268
		$this->gatewayFactory = $gatewayFactory;
269
		return $this;
270
	}
271
272
	//testing functions (could these instead be injected somehow?)
273
274
275
	/**
276
	 * Set the guzzle client (for testing)
277
	 * @param Guzzle\Http\ClientInterface $httpClient guzzle client for testing
278
	 */
279
	public static function set_http_client(Guzzle\Http\ClientInterface $httpClient) {
280
		self::$httpclient = $httpClient;
281
	}
282
283
	public static function get_http_client() {
284
		return self::$httpclient;
285
	}
286
287
	/**
288
	 * Set the symphony http request (for testing)
289
	 * @param Symfony\Component\HttpFoundation\Request $httpRequest symphony http request for testing
290
	 */
291
	public static function set_http_request(Symfony\Component\HttpFoundation\Request $httpRequest) {
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $httpRequest. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
292
		self::$httprequest = $httpRequest;
0 ignored issues
show
Documentation Bug introduced by
It seems like $httpRequest of type object<Symfony\Component\HttpFoundation\Request> is incompatible with the declared type object<Guzzle\Http\Message\Request> of property $httprequest.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
293
	}
294
295
	public static function get_http_request() {
296
		return self::$httprequest;
297
	}
298
299
}
300