1 <?php
2 /**
3 * Copyright 2019 Klarna AB
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * File containing the Connector class.
18 */
19
20 namespace Klarna\Rest\Transport;
21
22 use GuzzleHttp\Client;
23 use GuzzleHttp\ClientInterface;
24 use GuzzleHttp\Exception\RequestException;
25 use GuzzleHttp\Exception\ClientException;
26 use GuzzleHttp\Exception\TransportException;
27 use GuzzleHttp\Psr7\Request;
28 use Psr\Http\Message\RequestInterface;
29 use Psr\Http\Message\ResponseInterface;
30
31 /**
32 * Transport connector used to authenticate and make HTTP requests against the
33 * Klarna APIs. Transport uses Guzzle HTTP client to perform HTTP(s) calls.
34 */
35 class GuzzleConnector implements ConnectorInterface
36 {
37 /**
38 * HTTP transport client.
39 *
40 * @var ClientInterface
41 */
42 protected $client;
43
44 /**
45 * Merchant ID.
46 *
47 * @var string
48 */
49 protected $merchantId;
50
51 /**
52 * Shared secret.
53 *
54 * @var string
55 */
56 protected $sharedSecret;
57
58 /**
59 * HTTP user agent.
60 *
61 * @var UserAgent
62 */
63 protected $userAgent;
64
65 /**
66 * Constructs a connector instance.
67 *
68 * Example usage:
69 *
70 * $client = new \GuzzleHttp\Client(['base_url' => 'https://api.klarna.com']);
71 * $connector = new \Klarna\Transport\Connector($client, '0', 'sharedSecret');
72 *
73 *
74 * @param ClientInterface $client HTTP transport client
75 * @param string $merchantId Merchant ID
76 * @param string $sharedSecret Shared secret
77 * @param UserAgentInterface $userAgent HTTP user agent to identify the client
78 */
79 public function __construct(
80 ClientInterface $client,
81 $merchantId,
82 $sharedSecret,
83 UserAgentInterface $userAgent = null
84 ) {
85 $this->client = $client;
86 $this->merchantId = $merchantId;
87 $this->sharedSecret = $sharedSecret;
88
89 if ($userAgent === null) {
90 $userAgent = UserAgent::createDefault(['Guzzle/' . ClientInterface::VERSION]);
91 }
92 $this->userAgent = $userAgent;
93 }
94
95 /**
96 * Sends HTTP GET request to specified path.
97 *
98 * @param string $path URL path.
99 * @param array $headers HTTP request headers
100 * @return ApiResponse Processed response
101 *
102 * @throws RuntimeException if HTTP transport failed to execute a call
103 */
104 public function get($path, $headers = [])
105 {
106 $request = $this->createRequest($path, Method::GET, $headers);
107 $response = $this->send($request);
108 return $this->getApiResponse($response);
109 }
110
111 /**
112 * Sends HTTP POST request to specified path.
113 *
114 * @param string $path URL path.
115 * @param string $data Data to be sent to API server in a payload. Example: json-encoded string
116 * @param array $headers HTTP request headers
117 * @return ApiResponse Processed response
118 *
119 * @throws RuntimeException if HTTP transport failed to execute a call
120 */
121 public function post($path, $data = null, $headers = [])
122 {
123 $request = $this->createRequest($path, Method::POST, $headers, $data);
124 $response = $this->send($request);
125 return $this->getApiResponse($response);
126 }
127
128 /**
129 * Sends HTTP PUT request to specified path.
130 *
131 * @param string $path URL path.
132 * @param string $data Data to be sent to API server in a payload. Example: json-encoded string
133 * @param array $headers HTTP request headers
134 * @return ApiResponse Processed response
135 *
136 * @throws RuntimeException if HTTP transport failed to execute a call
137 */
138 public function put($path, $data = null, $headers = [])
139 {
140 $request = $this->createRequest($path, Method::PUT, $headers, $data);
141 $response = $this->send($request);
142 return $this->getApiResponse($response);
143 }
144
145 /**
146 * Sends HTTP PATCH request to specified path.
147 *
148 * @param string $path URL path.
149 * @param string $data Data to be sent to API server in a payload. Example: json-encoded string
150 * @param array $headers HTTP request headers
151 * @return ApiResponse Processed response
152 *
153 * @throws RuntimeException if HTTP transport failed to execute a call
154 */
155 public function patch($path, $data = null, $headers = [])
156 {
157 $request = $this->createRequest($path, Method::PATCH, $headers, $data);
158 $response = $this->send($request);
159 return $this->getApiResponse($response);
160 }
161
162 /**
163 * Sends HTTP DELETE request to specified path.
164 *
165 * @param string $path URL path.
166 * @param string $data Data to be sent to API server in a payload. Example: json-encoded string
167 * @param array $headers HTTP request headers
168 * @return ApiResponse Processed response
169 *
170 * @throws RuntimeException if HTTP transport failed to execute a call
171 */
172 public function delete($path, $data = null, $headers = [])
173 {
174 $request = $this->createRequest($path, Method::DELETE, $headers, $data);
175 $response = $this->send($request);
176 return $this->getApiResponse($response);
177 }
178
179 /**
180 * Converts ResponseInterface to ApiResponse.
181 *
182 * @param response ResponseInterface intance
183 * @return ApiResponse
184 */
185 protected function getApiResponse(ResponseInterface $response)
186 {
187 return new ApiResponse(
188 $response->getStatusCode(),
189 $response->getBody()->getContents(),
190 $response->getHeaders()
191 );
192 }
193
194 /**
195 * @deprecated No longer used and not recommended. Use direct get, post, put, delete and patch methods instead.
196 * Creates a request object.
197 *
198 * @param string $url URL
199 * @param string $method HTTP method
200 *
201 * @return RequestInterface
202 */
203 public function createRequest($url, $method = 'GET', array $headers = [], $body = null)
204 {
205 $headers = array_merge($headers, ['User-Agent' => strval($this->userAgent)]);
206 return new Request($method, $url, $headers, $body);
207 }
208
209 /**
210 * @deprecated No longer used and not recommended. Use direct get, post, put, delete and patch methods instead.
211 * Sends the request.
212 *
213 * @param RequestInterface $request Request to send
214 * @param string[] $options Request options
215 *
216 * @throws RequestException When an error is encountered
217 * @throws \RuntimeException When the adapter does not populate a response
218 *
219 * @return ResponseInterface
220 */
221 public function send(RequestInterface $request, array $options = [])
222 {
223 $requestOptions = $this->client->getConfig('request');
224 if (is_array($requestOptions)) {
225 $options = array_merge($requestOptions, $options);
226 }
227 $options['auth'] = [$this->merchantId, $this->sharedSecret, 'basic'];
228 $options['http_errors'] = false;
229
230 try {
231 $response = $this->client->send($request, $options);
232 return $response;
233 } catch (RequestException $e) {
234 throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
235 } catch (\Throwable $e) {
236 throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
237 }
238 }
239
240 /**
241 * Gets the HTTP transport client.
242 *
243 * @return ClientInterface
244 */
245 public function getClient()
246 {
247 return $this->client;
248 }
249
250 /**
251 * Gets the user agent.
252 *
253 * @return UserAgentInterface
254 */
255 public function getUserAgent()
256 {
257 return $this->userAgent;
258 }
259
260 /**
261 * Factory method to create a connector instance.
262 *
263 * @param string $merchantId Merchant ID
264 * @param string $sharedSecret Shared secret
265 * @param string $baseUrl Base URL for HTTP requests
266 * @param UserAgentInterface $userAgent HTTP user agent to identify the client
267 *
268 * @return self
269 */
270 public static function create(
271 $merchantId,
272 $sharedSecret,
273 $baseUrl = self::EU_BASE_URL,
274 UserAgentInterface $userAgent = null
275 ) {
276 $client = new Client(['base_uri' => $baseUrl]);
277
278 return new static($client, $merchantId, $sharedSecret, $userAgent);
279 }
280 }
281