Passed
Push — master ( 48d447...c6246e )
by smiley
01:32
created

PayPal   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 10
eloc 48
c 1
b 0
f 0
dl 0
loc 94
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
B parseTokenResponse() 0 41 8
A getAccessToken() 0 24 2
1
<?php
2
/**
3
 * Class PayPal
4
 *
5
 * @link https://developer.paypal.com/docs/connect-with-paypal/integrate/
6
 *
7
 * @filesource   PayPal.php
8
 * @created      29.07.2019
9
 * @package      chillerlan\OAuth\Providers\PayPal
10
 * @author       smiley <[email protected]>
11
 * @copyright    2019 smiley
12
 * @license      MIT
13
 */
14
15
namespace chillerlan\OAuth\Providers\PayPal;
16
17
use chillerlan\HTTP\Psr7;
18
use chillerlan\OAuth\Core\{AccessToken, ClientCredentials, CSRFToken, OAuth2Provider, ProviderException, TokenExpires, TokenRefresh};
19
use Psr\Http\Message\ResponseInterface;
20
21
/**
22
 */
23
class PayPal extends OAuth2Provider implements ClientCredentials, CSRFToken, TokenExpires, TokenRefresh{
24
25
	public const SCOPE_BASIC_AUTH = 'openid';
26
	public const SCOPE_FULL_NAME  = 'profile';
27
	public const SCOPE_EMAIL      = 'email';
28
	public const SCOPE_ADDRESS    = 'address';
29
	public const SCOPE_ACCOUNT    = 'https://uri.paypal.com/services/paypalattributes';
30
31
	protected $apiURL         = 'https://api.paypal.com';
32
	protected $accessTokenURL = 'https://api.paypal.com/v1/oauth2/token';
33
	protected $authURL        = 'https://www.paypal.com/connect';
34
	protected $applicationURL = 'https://developer.paypal.com/developer/applications/';
35
	protected $apiDocs        = 'https://developer.paypal.com/docs/connect-with-paypal/reference/';
36
	protected $endpointMap    = PayPalEndpoints::class;
37
38
	/**
39
	 * @param \Psr\Http\Message\ResponseInterface $response
40
	 *
41
	 * @return \chillerlan\OAuth\Core\AccessToken
42
	 * @throws \chillerlan\OAuth\Core\ProviderException
43
	 */
44
	protected function parseTokenResponse(ResponseInterface $response):AccessToken{
45
		$data = \json_decode(Psr7\decompress_content($response), true);
0 ignored issues
show
Bug introduced by
The function decompress_content was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
		$data = \json_decode(/** @scrutinizer ignore-call */ Psr7\decompress_content($response), true);
Loading history...
46
47
		if(!\is_array($data)){
48
			throw new ProviderException('unable to parse token response');
49
		}
50
51
		foreach(['error_description', 'error'] as $field){
52
			if(isset($data[$field])){
53
				throw new ProviderException('error retrieving access token: "'.$data[$field].'"');
54
			}
55
		}
56
57
		// @codeCoverageIgnoreStart
58
		if(isset($data['name'], $data['message'])){
59
			$msg = \sprintf('error retrieving access token: "%s" [%s]', $data['message'], $data['name']);
60
61
			if(isset($data['links']) && \is_array($data['links'])){
62
				$msg .= "\n".\implode("\n", \array_column($data['links'], 'href'));
63
			}
64
65
			throw new ProviderException($msg);
66
		}
67
		// @codeCoverageIgnoreEnd
68
69
		if(!isset($data['access_token'])){
70
			throw new ProviderException('token missing');
71
		}
72
73
		$token = new AccessToken([
74
			'provider'     => $this->serviceName,
75
			'accessToken'  => $data['access_token'],
76
			'expires'      => $data['expires_in'] ?? AccessToken::EOL_NEVER_EXPIRES,
77
			'refreshToken' => $data['refresh_token'] ?? null,
78
		]);
79
80
		unset($data['expires_in'], $data['refresh_token'], $data['access_token']);
81
82
		$token->extraParams = $data;
83
84
		return $token;
85
	}
86
87
	/**
88
	 * @param string      $code
89
	 * @param string|null $state
90
	 *
91
	 * @return \chillerlan\OAuth\Core\AccessToken
92
	 */
93
	public function getAccessToken(string $code, string $state = null):AccessToken{
94
95
		if($this instanceof CSRFToken){
0 ignored issues
show
introduced by
$this is always a sub-type of chillerlan\OAuth\Core\CSRFToken.
Loading history...
96
			$this->checkState($state);
97
		}
98
99
		$body = [
100
			'code'          => $code,
101
			'grant_type'    => 'authorization_code',
102
			'redirect_uri'  => $this->options->callbackURL,
103
		];
104
105
		$request = $this->requestFactory
106
			->createRequest('POST', $this->accessTokenURL)
107
			->withHeader('Content-Type', 'application/x-www-form-urlencoded')
108
			->withHeader('Accept-Encoding', 'identity')
109
			->withHeader('Authorization', 'Basic '.\base64_encode($this->options->key.':'.$this->options->secret))
110
			->withBody($this->streamFactory->createStream(\http_build_query($body, '', '&', \PHP_QUERY_RFC1738)));
111
112
		$token = $this->parseTokenResponse($this->http->sendRequest($request));
113
114
		$this->storage->storeAccessToken($this->serviceName, $token);
115
116
		return $token;
117
	}
118
119
120
}
121