1: <?php
2: namespace Hyperwallet\Util;
3: use GuzzleHttp\Client;
4: use GuzzleHttp\Exception\BadResponseException;
5: use GuzzleHttp\Exception\ConnectException;
6: use Hyperwallet\Exception\HyperwalletApiException;
7: use Hyperwallet\Exception\HyperwalletException;
8: use Hyperwallet\Model\BaseModel;
9: use Hyperwallet\Response\ErrorResponse;
10: use Psr\Http\Message\ResponseInterface;
11:
12: /**
13: * The internal API client
14: *
15: * @package Hyperwallet\Util
16: */
17: class ApiClient {
18:
19: /**
20: * The SDK version number
21: *
22: * @var string
23: */
24: const VERSION = '2.2.3';
25:
26: /**
27: * The Guzzle http client
28: *
29: * @var Client
30: */
31: private $client;
32:
33: /**
34: * The Encryption service for http client requests/responses
35: *
36: * @var HyperwalletEncryption
37: */
38: private $encryption;
39:
40: /**
41: * The UUID generator for http request/response
42: *
43: * @var HyperwalletUUID
44: */
45: private $uuid;
46:
47: /**
48: * Boolean flag that checks if ApiClient is constructed with encryption enabled or not
49: *
50: * @var boolean
51: */
52: private $isEncrypted = false;
53:
54: /**
55: * Creates a instance of the API client
56: *
57: * @param string $username The API username
58: * @param string $password The API password
59: * @param string $server The API server to connect to
60: * @param array $clientOptions Guzzle Client Options
61: * @param array $encryptionData Encryption data to initialize ApiClient with encryption enabled
62: */
63: public function __construct($username, $password, $server, $clientOptions = array(), $encryptionData = array()) {
64: $this->uuid = HyperwalletUUID::v4();
65: // Setup http client if not specified
66: $this->client = new Client(array_merge_recursive(array(
67: 'base_uri' => $server,
68: 'auth' => array($username, $password),
69: 'headers' => array(
70: 'User-Agent' => 'Hyperwallet PHP SDK v' . self::VERSION,
71: 'Accept' => 'application/json',
72: 'x-sdk-version' => self::VERSION,
73: 'x-sdk-type' => 'PHP',
74: 'x-sdk-contextId' => $this->uuid)
75: ), $clientOptions));
76: if (!empty($encryptionData) && isset($encryptionData['clientPrivateKeySetLocation']) &&
77: isset($encryptionData['hyperwalletKeySetLocation'])) {
78: $this->isEncrypted = true;
79: $this->encryption = new HyperwalletEncryption($encryptionData['clientPrivateKeySetLocation'], $encryptionData['hyperwalletKeySetLocation']);
80: }
81: }
82:
83: /**
84: * Do a POST call to the Hyperwallet API server
85: *
86: * @param string $partialUrl The url template
87: * @param array $uriParams The url template parameters
88: * @param BaseModel $data The data to submit
89: * @param array $query Query parameters
90: * @param array $headers Additional headers
91: * @return array
92: *
93: * @throws HyperwalletApiException
94: */
95: public function doPost($partialUrl, array $uriParams, BaseModel $data = null, array $query = array(), array $headers = array()) {
96: return $this->doRequest('POST', $partialUrl, $uriParams, array(
97: 'query' => $query,
98: 'body' => $data ? \GuzzleHttp\json_encode($data->getPropertiesForCreate(), JSON_FORCE_OBJECT) : '{}',
99: 'headers' => array_merge($headers, array(
100: 'Content-Type' => 'application/json'
101: ))
102: ));
103: }
104:
105: /**
106: * Do a PUT call to the Hyperwallet API server
107: *
108: * @param string $partialUrl The url template
109: * @param array $uriParams The url template parameters
110: * @param BaseModel $data The data to update
111: * @param array $query Query parameters
112: * @return array
113: *
114: * @throws HyperwalletApiException
115: */
116: public function doPut($partialUrl, array $uriParams, BaseModel $data, array $query) {
117: return $this->doRequest('PUT', $partialUrl, $uriParams, array(
118: 'query' => $query,
119: 'body' => \GuzzleHttp\json_encode($data->getPropertiesForUpdate(), JSON_FORCE_OBJECT),
120: 'headers' => array(
121: 'Content-Type' => 'application/json'
122: )
123: ));
124: }
125:
126: /**
127: * Do a GET call to the Hyperwallet API server
128: *
129: * @param string $partialUrl The url template
130: * @param array $uriParams The url template parameters
131: * @param array $query Query parameters
132: * @return array
133: *
134: * @throws HyperwalletApiException
135: */
136: public function doGet($partialUrl, array $uriParams, array $query) {
137: return $this->doRequest('GET', $partialUrl, $uriParams, array(
138: 'query' => $query
139: ));
140: }
141:
142: /**
143: * Execute API call and map error messages
144: *
145: * @param string $method The http method
146: * @param string $url The url template
147: * @param array $urlParams The url template parameters
148: * @param array $options The request options
149: * @return array
150: *
151: * @throws HyperwalletApiException
152: */
153: private function doRequest($method, $url, array $urlParams, array $options) {
154: try {
155: $uri = new HyperwalletUriTemplate();
156: if (!isset($options['headers'])) {
157: $options[] = array('headers' => array());
158: }
159: $options['headers']['Accept'] = 'application/json';
160: if ($this->isEncrypted) {
161: $options['headers']['Accept'] = 'application/jose+json';
162: $options['headers']['Content-Type'] = 'application/jose+json';
163: if (isset($options['body'])) {
164: $options['body'] = $this->encryption->encrypt(json_decode($options['body'], true));
165: }
166: }
167: $response = $this->client->request($method, $uri->expand($url, $urlParams), $options);
168: if ($response->getStatusCode() === 204) {
169: return array();
170: }
171: $this->checkResponseHeaderContentType($response);
172: $body = $this->isEncrypted ? \GuzzleHttp\json_decode(\GuzzleHttp\json_encode($this->encryption->decrypt($response->getBody())), true) :
173: \GuzzleHttp\json_decode($response->getBody(), true);
174:
175: return $body;
176: } catch (ConnectException $e) {
177: $errorResponse = new ErrorResponse(0, array('errors' => array(
178: array(
179: 'message' => 'Could not communicate with ' . $this->client->getConfig('base_uri'),
180: 'code' => 'COMMUNICATION_ERROR'
181: )
182: )));
183: throw new HyperwalletApiException($errorResponse, $e);
184: } catch (BadResponseException $e) {
185: $body = \GuzzleHttp\json_decode($e->getResponse()->getBody(), true);
186: if (is_null($body) || !isset($body['errors']) || empty($body['errors'])) {
187: $body = array('errors' => array(
188: array(
189: 'message' => 'Failed to get any error message from response',
190: 'code' => 'BAD_REQUEST'
191: )
192: ));
193: }
194: $errorResponse = new ErrorResponse($e->getResponse()->getStatusCode(), $body);
195: throw new HyperwalletApiException($errorResponse, $e);
196: }
197: }
198:
199: /**
200: * Checks whether Content-Type header is valid in response
201: *
202: * @param ResponseInterface $response Response to be checked
203: *
204: * @throws HyperwalletException
205: */
206: private function checkResponseHeaderContentType($response) {
207: $contentType = implode('', $response->getHeader('Content-Type'));
208: $expectedContentType = $this->isEncrypted ? 'application/jose+json' : 'application/json';
209: $invalidContentType = $response->getStatusCode() !== 204 && !empty($contentType) && strpos($contentType, $expectedContentType) === false;
210: if ($invalidContentType) {
211: throw new HyperwalletException('Invalid Content-Type specified in Response Header');
212: }
213: }
214:
215: /**
216: * Do a PUT call to the Hyperwallet API server
217: *
218: * @param string $partialUrl The url template
219: * @param array $uriParams The url template parameters
220: * @param array $options The request options
221: * @return array
222: *
223: * @throws HyperwalletApiException
224: */
225: public function putMultipartData($partialUrl, array $uriParams, array $options) {
226: return $this->doRequest('PUT', $partialUrl, $uriParams, $options);
227: }
228: }
229: