Passed
Pull Request — master (#6)
by
unknown
09:53
created

AWS::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Part of the AmazonGiftCode package.
5
 * Author: Kashyap Merai <[email protected]>
6
 *
7
 */
8
9
10
namespace kamerk22\AmazonGiftCode\AWS;
11
12
13
use kamerk22\AmazonGiftCode\Client\Client;
14
use kamerk22\AmazonGiftCode\Config\Config;
15
use kamerk22\AmazonGiftCode\Exceptions\AmazonErrors;
16
use kamerk22\AmazonGiftCode\Response\CancelResponse;
17
use kamerk22\AmazonGiftCode\Response\CreateBalanceResponse;
18
use kamerk22\AmazonGiftCode\Response\CreateResponse;
19
20
class AWS
21
{
22
    public const SERVICE_NAME = 'AGCODService';
23
    public const ACCEPT_HEADER = 'accept';
24
    public const CONTENT_HEADER = 'content-type';
25
    public const HOST_HEADER = 'host';
26
    public const X_AMZ_DATE_HEADER = 'x-amz-date';
27
    public const X_AMZ_TARGET_HEADER = 'x-amz-target';
28
    public const AUTHORIZATION_HEADER = 'Authorization';
29
    public const AWS_SHA256_ALGORITHM = 'AWS4-HMAC-SHA256';
30
    public const KEY_QUALIFIER = 'AWS4';
31
    public const TERMINATION_STRING = 'aws4_request';
32
    public const CREATE_GIFT_CARD_SERVICE = 'CreateGiftCard';
33
    public const CANCEL_GIFT_CARD_SERVICE = 'CancelGiftCard';
34
    public const GET_AVAILABLE_FUNDS_SERVICE = 'GetAvailableFunds';
35
36
    private $_config;
37
38
39
    /**
40
     * AWS constructor.
41
     * @param Config $config
42
     */
43
    public function __construct(Config $config)
44
    {
45
        $this->_config = $config;
46
    }
47
48
49
    /**
50
     * @param $amount
51
     * @param $creationId
52
     * @return CreateResponse
53
     *
54
     * @throws AmazonErrors
55
     */
56
    public function getCode($amount, $creationId = null): CreateResponse
57
    {
58
        $serviceOperation = self::CREATE_GIFT_CARD_SERVICE;
59
        $payload = $this->getGiftCardPayload($amount, $creationId);
60
        $canonicalRequest = $this->getCanonicalRequest($serviceOperation, $payload);
61
        $dateTimeString = $this->getTimestamp();
62
        $result = json_decode($this->makeRequest($payload, $canonicalRequest, $serviceOperation, $dateTimeString), true);
63
        return new CreateResponse($result);
64
65
    }
66
67
    /**
68
     * @param $creationRequestId
69
     * @param $gcId
70
     * @return CancelResponse
71
     */
72
    public function cancelCode($creationRequestId, $gcId): CancelResponse
73
    {
74
        $serviceOperation = self::CANCEL_GIFT_CARD_SERVICE;
75
        $payload = $this->getCancelGiftCardPayload($creationRequestId, $gcId);
76
        $canonicalRequest = $this->getCanonicalRequest($serviceOperation, $payload);
77
        $dateTimeString = $this->getTimestamp();
78
        $result = json_decode($this->makeRequest($payload, $canonicalRequest, $serviceOperation, $dateTimeString), true);
79
        return new CancelResponse($result);
80
    }
81
82
    /**
83
     * @return CreateBalanceResponse
84
     */
85
    public function getBalance(): CreateBalanceResponse
86
    {
87
        $serviceOperation = self::GET_AVAILABLE_FUNDS_SERVICE;
88
        $payload = $this->getAvailableFundsPayload();
89
        $canonicalRequest = $this->getCanonicalRequest($serviceOperation, $payload);
90
        $dateTimeString = $this->getTimestamp();
91
        $result = json_decode($this->makeRequest($payload, $canonicalRequest, $serviceOperation, $dateTimeString), true);
92
        return new CreateBalanceResponse($result);
93
    }
94
95
    /**
96
     * @param $payload
97
     * @param $canonicalRequest
98
     * @param $serviceOperation
99
     * @param $dateTimeString
100
     * @return String
101
     */
102
    public function makeRequest($payload, $canonicalRequest, $serviceOperation, $dateTimeString): string
103
    {
104
        $KEY_QUALIFIER = self::KEY_QUALIFIER;
105
        $canonicalRequestHash = $this->buildHash($canonicalRequest);
106
        $stringToSign = $this->buildStringToSign($canonicalRequestHash);
107
        $authorizationValue = $this->buildAuthSignature($stringToSign);
108
109
        $secretKey = $this->_config->getSecret();
110
        $endpoint = $this->_config->getEndpoint();
111
        $regionName = $this->getRegion();
112
113
        $SERVICE_NAME = 'AGCODService';
114
        $serviceTarget = 'com.amazonaws.agcod.' . $SERVICE_NAME . '.' . $serviceOperation;
115
        $dateString = $this->getDateString();
116
117
        $signatureAWSKey = $KEY_QUALIFIER . $secretKey;
118
119
        $kDate = $this->hmac($dateString, $signatureAWSKey);
120
        $kDate_hexis = $this->hmac($dateString, $signatureAWSKey, false);
0 ignored issues
show
Unused Code introduced by
The assignment to $kDate_hexis is dead and can be removed.
Loading history...
121
        $kRegion = $this->hmac($regionName, $kDate);
122
        $kRegion_hexis = $this->hmac($regionName, $kDate, false);
0 ignored issues
show
Unused Code introduced by
The assignment to $kRegion_hexis is dead and can be removed.
Loading history...
123
        $kService_hexis = $this->hmac($SERVICE_NAME, $kRegion, false);
0 ignored issues
show
Unused Code introduced by
The assignment to $kService_hexis is dead and can be removed.
Loading history...
124
125
        $url = 'https://' . $endpoint . '/' . $serviceOperation;
126
        $headers = $this->buildHeaders($payload, $authorizationValue, $dateTimeString, $serviceTarget);
127
        return (new Client())->request($url, $headers, $payload);
128
    }
129
130
    /**
131
     * @param $payload
132
     * @param $authorizationValue
133
     * @param $dateTimeString
134
     * @param $serviceTarget
135
     * @return array
136
     */
137
    public function buildHeaders($payload, $authorizationValue, $dateTimeString, $serviceTarget): array
138
    {
139
        $ACCEPT_HEADER = self::ACCEPT_HEADER;
140
        $X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
141
        $X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
142
        $AUTHORIZATION_HEADER = self::AUTHORIZATION_HEADER;
143
        return [
144
            'Content-Type:' . $this->getContentType(),
145
            'Content-Length: ' . strlen($payload),
146
            $AUTHORIZATION_HEADER . ':' . $authorizationValue,
147
            $X_AMZ_DATE_HEADER . ':' . $dateTimeString,
148
            $X_AMZ_TARGET_HEADER . ':' . $serviceTarget,
149
            $ACCEPT_HEADER . ':' . $this->getContentType()
150
        ];
151
    }
152
153
    /**
154
     * @param $stringToSign
155
     * @return string
156
     */
157
    public function buildAuthSignature($stringToSign): string
158
    {
159
        $AWS_SHA256_ALGORITHM = self::AWS_SHA256_ALGORITHM;
160
        $SERVICE_NAME = self::SERVICE_NAME;
161
        $TERMINATION_STRING = self::TERMINATION_STRING;
162
        $ACCEPT_HEADER = self::ACCEPT_HEADER;
163
        $HOST_HEADER = self::HOST_HEADER;
164
        $X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
165
        $X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
166
167
        $awsKeyId = $this->_config->getAccessKey();
168
        $regionName = $this->getRegion();
169
170
        $dateString = $this->getDateString();
171
        $derivedKey = $this->buildDerivedKey();
172
        // Calculate signature per http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
173
        $finalSignature = $this->hmac($stringToSign, $derivedKey, false);
174
175
        // Assemble Authorization Header with signing information
176
        // per http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
177
        $authorizationValue =
178
            $AWS_SHA256_ALGORITHM
179
            . ' Credential=' . $awsKeyId
180
            . '/' . $dateString . '/' . $regionName . '/' . $SERVICE_NAME . '/' . $TERMINATION_STRING . ','
181
            . ' SignedHeaders='
182
            . $ACCEPT_HEADER . ';' . $HOST_HEADER . ';' . $X_AMZ_DATE_HEADER . ';' . $X_AMZ_TARGET_HEADER . ','
183
            . ' Signature='
184
            . $finalSignature;
185
186
        return $authorizationValue;
187
    }
188
189
    /**
190
     * @param $canonicalRequestHash
191
     * @return string
192
     */
193
    public function buildStringToSign($canonicalRequestHash): string
194
    {
195
        $AWS_SHA256_ALGORITHM = self::AWS_SHA256_ALGORITHM;
196
        $TERMINATION_STRING = self::TERMINATION_STRING;
197
        $SERVICE_NAME = self::SERVICE_NAME;
198
        $regionName = $this->getRegion();
199
        $dateTimeString = $this->getTimestamp();
200
        $dateString = $this->getDateString();
201
        $stringToSign = "$AWS_SHA256_ALGORITHM\n$dateTimeString\n$dateString/$regionName/$SERVICE_NAME/$TERMINATION_STRING\n$canonicalRequestHash";
202
203
        return $stringToSign;
204
    }
205
206
    /**
207
     * @param bool $rawOutput
208
     * @return string
209
     */
210
    public function buildDerivedKey($rawOutput = true): string
211
    {
212
        $KEY_QUALIFIER = self::KEY_QUALIFIER;
213
        $TERMINATION_STRING = self::TERMINATION_STRING;
214
        $SERVICE_NAME = self::SERVICE_NAME;
215
216
        $awsSecretKey = $this->_config->getSecret();
217
        // Append Key Qualifier, "AWS4", to secret key per http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
218
        $signatureAWSKey = $KEY_QUALIFIER . $awsSecretKey;
219
        $regionName = $this->getRegion();
220
        $dateString = $this->getDateString();
221
222
        $kDate = $this->hmac($dateString, $signatureAWSKey);
223
        $kRegion = $this->hmac($regionName, $kDate);
224
        $kService = $this->hmac($SERVICE_NAME, $kRegion);
225
226
        // Derived the Signing key (derivedKey aka kSigning)
227
        return $this->hmac($TERMINATION_STRING, $kService, $rawOutput);
228
    }
229
230
    /**
231
     * @return string
232
     */
233
    public function getRegion(): string
234
    {
235
236
237
        $endpoint = $this->_config->getEndpoint();
238
        $regionName = 'us-east-1';
239
240
        if ($endpoint === 'agcod-v2-eu.amazon.com' || $endpoint === 'agcod-v2-eu-gamma.amazon.com') {
241
            $regionName = 'eu-west-1';
242
        } else if ($endpoint === 'agcod-v2-fe.amazon.com' || $endpoint === 'agcod-v2-fe-gamma.amazon.com') {
243
            $regionName = 'us-west-2';
244
        }
245
        return $regionName;
246
    }
247
248
249
    /**
250
     * @param $amount
251
     * @param $creationId
252
     * @return string
253
     */
254
    public function getGiftCardPayload($amount, $creationId = null): string
0 ignored issues
show
Unused Code introduced by
The parameter $creationId is not used and could be removed. ( Ignorable by Annotation )

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

254
    public function getGiftCardPayload($amount, /** @scrutinizer ignore-unused */ $creationId = null): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
255
    {
256
        $amount = trim($amount);
257
        $payload = [
258
           "creationRequestId" => $this->_config->getPartner() . "_" . time(),
259
            'partnerId' => $this->_config->getPartner(),
260
            'value' =>
261
                [
262
                    'currencyCode' => $this->_config->getCurrency(),
263
                    'amount' => (float)$amount
264
                ]
265
        ];
266
        return json_encode($payload);
267
    }
268
269
    /**
270
     * @param $creationRequestId
271
     * @param $gcId
272
     * @return string
273
     */
274
    public function getCancelGiftCardPayload($creationRequestId, $gcId): string
275
    {
276
        $gcResponseId = trim($gcId);
277
        $payload = [
278
            'creationRequestId' => $creationRequestId,
279
            'partnerId' => $this->_config->getPartner(),
280
            'gcId' => $gcResponseId
281
        ];
282
        return json_encode($payload);
283
    }
284
285
    /**
286
     * @return string
287
     */
288
    public function getAvailableFundsPayload(): string
289
    {
290
        $payload = [
291
            'partnerId' => $this->_config->getPartner(),
292
        ];
293
        return json_encode($payload);
294
    }
295
296
    /**
297
     * @param $serviceOperation
298
     * @param $payload
299
     * @return string
300
     */
301
    public function getCanonicalRequest($serviceOperation, $payload): string
302
    {
303
        $HOST_HEADER = self::HOST_HEADER;
304
        $X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
305
        $X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
306
        $ACCEPT_HEADER = self::ACCEPT_HEADER;
307
        $payloadHash = $this->buildHash($payload);
308
        $canonicalHeaders = $this->buildCanonicalHeaders($serviceOperation);
309
        $canonicalRequest = "POST\n/$serviceOperation\n\n$canonicalHeaders\n\n$ACCEPT_HEADER;$HOST_HEADER;$X_AMZ_DATE_HEADER;$X_AMZ_TARGET_HEADER\n$payloadHash";
310
        return $canonicalRequest;
311
    }
312
313
    /**
314
     * @param $data
315
     * @return string
316
     */
317
    public function buildHash($data): string
318
    {
319
        return hash('sha256', $data);
320
    }
321
322
    /**
323
     * @return false|string
324
     */
325
    public function getTimestamp()
326
    {
327
        return gmdate('Ymd\THis\Z');
328
    }
329
330
    /**
331
     * @param $data
332
     * @param $key
333
     * @param bool $raw
334
     * @return string
335
     */
336
    public function hmac($data, $key, $raw = true): string
337
    {
338
        return hash_hmac('sha256', $data, $key, $raw);
339
    }
340
341
    /**
342
     * @return bool|string
343
     */
344
    public function getDateString()
345
    {
346
        return substr($this->getTimestamp(), 0, 8);
347
    }
348
349
    /**
350
     * @return string
351
     */
352
    public function getContentType(): string
353
    {
354
        return 'application/json';
355
    }
356
357
    /**
358
     * @param $serviceOperation
359
     * @return string
360
     */
361
    public function buildCanonicalHeaders($serviceOperation): string
362
    {
363
        $ACCEPT_HEADER = self::ACCEPT_HEADER;
364
        $HOST_HEADER = self::HOST_HEADER;
365
        $X_AMZ_DATE_HEADER = self::X_AMZ_DATE_HEADER;
366
        $X_AMZ_TARGET_HEADER = self::X_AMZ_TARGET_HEADER;
367
        $dateTimeString = $this->getTimestamp();
368
        $endpoint = $this->_config->getEndpoint();
369
        $contentType = $this->getContentType();
370
        return
371
            "$ACCEPT_HEADER:$contentType\n$HOST_HEADER:$endpoint\n$X_AMZ_DATE_HEADER:$dateTimeString\n$X_AMZ_TARGET_HEADER:com.amazonaws.agcod.AGCODService.$serviceOperation";
372
    }
373
}
374