Skip to content

Commit ff666ce

Browse files
authored
Merge pull request #4 from lamoda/support_for_order_requests_signing
Support for sending signed order requests
2 parents 9d78ee9 + afdb119 commit ff666ce

File tree

5 files changed

+178
-6
lines changed

5 files changed

+178
-6
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ $omsApi = new OmsApi($client, $serializer);
4646
*/
4747
// $response = $omsApi->getICBufferStatus();
4848
```
49+
50+
## Signing of OMS requests
51+
52+
It is also possible to send signed OMS requests for orders.
53+
54+
To do that implement `\Lamoda\OmsClient\V2\Signer\SignerInterface`.
55+
56+
Signer must return signature for the given data (no data itself transformation is required).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\OmsClient\Exception;
6+
7+
final class OmsSignerErrorException extends \RuntimeException implements OmsClientExceptionInterface
8+
{
9+
public static function becauseOfError(\Throwable $exception): self
10+
{
11+
return new static(sprintf(
12+
'Signer request finished with an error "%s"',
13+
$exception->getMessage()
14+
), 0, $exception);
15+
}
16+
}

src/V2/OmsApi.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
use Lamoda\OmsClient\Exception\OmsClientExceptionInterface;
1111
use Lamoda\OmsClient\Exception\OmsGeneralErrorException;
1212
use Lamoda\OmsClient\Exception\OmsRequestErrorException;
13+
use Lamoda\OmsClient\Exception\OmsSignerErrorException;
1314
use Lamoda\OmsClient\Serializer\SerializerInterface;
1415
use Lamoda\OmsClient\V2\Dto\CloseICArrayResponse;
1516
use Lamoda\OmsClient\V2\Dto\CreateOrderForEmissionICRequest;
1617
use Lamoda\OmsClient\V2\Dto\CreateOrderForEmissionICResponse;
1718
use Lamoda\OmsClient\V2\Dto\GetICBufferStatusResponse;
1819
use Lamoda\OmsClient\V2\Dto\GetICsFromOrderResponse;
20+
use Lamoda\OmsClient\V2\Signer\SignerInterface;
1921

2022
final class OmsApi
2123
{
@@ -38,14 +40,18 @@ public function createOrderForEmissionIC(
3840
Extension $extension,
3941
string $token,
4042
string $omsId,
41-
CreateOrderForEmissionICRequest $request
43+
CreateOrderForEmissionICRequest $request,
44+
SignerInterface $signer = null
4245
): CreateOrderForEmissionICResponse {
4346
$url = sprintf('/api/v2/%s/orders', (string)$extension);
4447
$body = $this->serializer->serialize($request);
4548

49+
$headers = [];
50+
$headers = $this->appendSignatureHeader($headers, $body, $signer);
51+
4652
$result = $this->request($token, 'POST', $url, [
4753
'omsId' => $omsId,
48-
], $body);
54+
], $body, $headers);
4955

5056
/* @noinspection PhpIncompatibleReturnTypeInspection */
5157
return $this->serializer->deserialize(CreateOrderForEmissionICResponse::class, $result);
@@ -114,14 +120,20 @@ public function closeICArray(
114120
/**
115121
* @throws OmsRequestErrorException
116122
*/
117-
private function request(string $token, string $method, string $uri, array $query = [], $body = null): string
118-
{
123+
private function request(
124+
string $token,
125+
string $method,
126+
string $uri,
127+
array $query = [],
128+
$body = null,
129+
$headers = []
130+
): string {
119131
$options = [
120132
RequestOptions::BODY => $body,
121-
RequestOptions::HEADERS => [
133+
RequestOptions::HEADERS => array_merge($headers, [
122134
'Content-Type' => 'application/json',
123135
'clientToken' => $token,
124-
],
136+
]),
125137
RequestOptions::QUERY => $query,
126138
RequestOptions::HTTP_ERRORS => true,
127139
];
@@ -150,4 +162,21 @@ private function handleRequestException(\Throwable $exception): OmsClientExcepti
150162

151163
return OmsGeneralErrorException::becauseOfError($exception);
152164
}
165+
166+
private function appendSignatureHeader(array $headers, string $data, SignerInterface $signer = null): array
167+
{
168+
if ($signer === null) {
169+
return $headers;
170+
}
171+
172+
$base64encoded = base64_encode($data);
173+
174+
try {
175+
$headers['X-Signature'] = $signer->sign($base64encoded);
176+
} catch (\Throwable $exception) {
177+
throw OmsSignerErrorException::becauseOfError($exception);
178+
}
179+
180+
return $headers;
181+
}
153182
}

src/V2/Signer/SignerInterface.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lamoda\OmsClient\V2\Signer;
6+
7+
interface SignerInterface
8+
{
9+
public function sign(string $data): string;
10+
}

tests/V2/OmsApiTest.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use GuzzleHttp\RequestOptions;
1111
use Lamoda\OmsClient\Exception\OmsGeneralErrorException;
1212
use Lamoda\OmsClient\Exception\OmsRequestErrorException;
13+
use Lamoda\OmsClient\Exception\OmsSignerErrorException;
1314
use Lamoda\OmsClient\Serializer\SerializerInterface;
1415
use Lamoda\OmsClient\V2\Dto\CloseICArrayResponse;
1516
use Lamoda\OmsClient\V2\Dto\CreateOrderForEmissionICRequestLight;
@@ -18,6 +19,8 @@
1819
use Lamoda\OmsClient\V2\Dto\GetICsFromOrderResponse;
1920
use Lamoda\OmsClient\V2\Extension;
2021
use Lamoda\OmsClient\V2\OmsApi;
22+
use Lamoda\OmsClient\V2\Signer\SignerInterface;
23+
use PHPUnit\Framework\Assert;
2124
use PHPUnit\Framework\MockObject\MockObject;
2225
use PHPUnit\Framework\TestCase;
2326
use Psr\Http\Message\RequestInterface;
@@ -184,6 +187,112 @@ public function testCreateOrderForEmissionIC(): void
184187
$this->assertEquals($expectedResult, $result);
185188
}
186189

190+
public function testCreateOrderForEmissionICWithSignature(): void
191+
{
192+
$createOrderForEmissionICRequestLight = new CreateOrderForEmissionICRequestLight(
193+
'',
194+
'',
195+
'',
196+
'',
197+
'',
198+
new \DateTimeImmutable(),
199+
[]
200+
);
201+
202+
$serializedRequest = '{"test": "value"}';
203+
$signature = 'signature';
204+
205+
$this->serializer->expects($this->once())
206+
->method('serialize')
207+
->with(
208+
$createOrderForEmissionICRequestLight
209+
)
210+
->willReturn($serializedRequest);
211+
212+
213+
$this->client->expects($this->once())
214+
->method('request')
215+
->with(
216+
'POST',
217+
'api/v2/light/orders',
218+
[
219+
RequestOptions::BODY => $serializedRequest,
220+
RequestOptions::HEADERS => [
221+
'Content-Type' => 'application/json',
222+
'clientToken' => self::TOKEN,
223+
'X-Signature' => $signature
224+
],
225+
RequestOptions::QUERY => [
226+
'omsId' => self::OMS_ID,
227+
],
228+
RequestOptions::HTTP_ERRORS => true,
229+
]
230+
)
231+
->willReturn(
232+
(new Response())
233+
->withBody(stream_for(self::API_RESPONSE))
234+
);
235+
236+
$expectedResult = new CreateOrderForEmissionICResponse(self::OMS_ID, self::ORDER_ID, 100);
237+
$this->serializer
238+
->method('deserialize')
239+
->willReturn($expectedResult);
240+
241+
$signer = $this->createMock(SignerInterface::class);
242+
$signer->method('sign')
243+
->with(base64_encode($serializedRequest))
244+
->willReturn($signature);
245+
246+
$result = $this->api->createOrderForEmissionIC(
247+
Extension::light(),
248+
self::TOKEN,
249+
self::OMS_ID,
250+
$createOrderForEmissionICRequestLight,
251+
$signer
252+
);
253+
254+
$this->assertEquals($expectedResult, $result);
255+
}
256+
257+
public function testCreateOrderForEmissionFinishedWithSignerException(): void
258+
{
259+
$createOrderForEmissionICRequestLight = new CreateOrderForEmissionICRequestLight(
260+
'',
261+
'',
262+
'',
263+
'',
264+
'',
265+
new \DateTimeImmutable(),
266+
[]
267+
);
268+
269+
$serializedRequest = '{"test": "value"}';
270+
271+
$this->serializer->expects($this->once())
272+
->method('serialize')
273+
->with(
274+
$createOrderForEmissionICRequestLight
275+
)
276+
->willReturn($serializedRequest);
277+
278+
279+
$this->client->expects($this->never())
280+
->method('request');
281+
282+
$signer = $this->createMock(SignerInterface::class);
283+
$signer->method('sign')
284+
->willThrowException(new \Exception('Something happened in signer'));
285+
286+
$this->expectException(OmsSignerErrorException::class);
287+
$this->api->createOrderForEmissionIC(
288+
Extension::light(),
289+
self::TOKEN,
290+
self::OMS_ID,
291+
$createOrderForEmissionICRequestLight,
292+
$signer
293+
);
294+
}
295+
187296
public function testGetICBufferStatus(): void
188297
{
189298
$this->client->expects($this->once())

0 commit comments

Comments
 (0)