Order::new()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
c 0
b 0
f 0
dl 0
loc 37
rs 9.568
cc 4
nc 5
nop 2
1
<?php
2
3
namespace Rogierw\RwAcme\Endpoints;
4
5
use Rogierw\RwAcme\DTO\AccountData;
6
use Rogierw\RwAcme\DTO\OrderData;
7
use Rogierw\RwAcme\Exceptions\LetsEncryptClientException;
8
use Rogierw\RwAcme\Exceptions\OrderNotFoundException;
9
use Rogierw\RwAcme\Exceptions\RateLimitException;
10
use Rogierw\RwAcme\Support\Base64;
11
12
class Order extends Endpoint
13
{
14
    public function new(AccountData $accountData, array $domains): OrderData
15
    {
16
        $identifiers = [];
17
        foreach ($domains as $domain) {
18
            if (preg_match_all('~(\*\.)~', $domain) > 1) {
19
                throw new LetsEncryptClientException('Cannot create orders with multiple wildcards in one domain.');
20
            }
21
22
            $identifiers[] = [
23
                'type' => 'dns',
24
                'value' => $domain,
25
            ];
26
        }
27
28
        $payload = [
29
            'identifiers' => $identifiers,
30
            'notBefore' => '',
31
            'notAfter' => '',
32
        ];
33
34
        $newOrderUrl = $this->client->directory()->newOrder();
35
36
        $keyId = $this->createKeyId(
37
            $accountData->url,
38
            $this->client->directory()->newOrder(),
39
            $payload
40
        );
41
42
        $response = $this->client->getHttpClient()->post($newOrderUrl, $keyId);
43
44
        if ($response->getHttpResponseCode() === 201) {
45
            return OrderData::fromResponse($response, $accountData->url);
46
        }
47
48
        $this->logResponse('error', 'Creating new order failed; bad response code.', $response, ['payload' => $payload]);
49
50
        throw new LetsEncryptClientException('Creating new order failed; bad response code.');
51
    }
52
53
    public function get(string $id): OrderData
54
    {
55
        $account = $this->client->account()->get();
56
57
        $orderUrl = sprintf(
58
            '%s%s/%s',
59
            $this->client->directory()->getOrder(),
60
            $account->id,
61
            $id,
62
        );
63
64
        $response = $this->client->getHttpClient()->get($orderUrl);
65
66
        // Everything below 400 is a success.
67
        if ($response->getHttpResponseCode() < 400) {
68
            return OrderData::fromResponse($response, $account->url);
69
        }
70
71
        // Always log the error.
72
        $this->logResponse('error', 'Getting order failed; bad response code.', $response);
73
74
        match ($response->getHttpResponseCode()) {
75
            404 => throw new OrderNotFoundException($response->getBody()['detail'] ?? 'Order cannot be found.'),
76
            429 => throw new RateLimitException($response->getBody()['detail'] ?? 'Too many requests.'),
77
            default => throw new LetsEncryptClientException($response->getBody()['detail'] ?? 'Unknown error.'),
78
        };
79
    }
80
81
    public function finalize(OrderData $orderData, string $csr): bool
82
    {
83
        if (!$orderData->isReady()) {
84
            $this->client->logger(
85
                'error',
86
                "Order status for {$orderData->id} is {$orderData->status}. Cannot finalize order."
87
            );
88
89
            return false;
90
        }
91
92
        if (preg_match('~-----BEGIN\sCERTIFICATE\sREQUEST-----(.*)-----END\sCERTIFICATE\sREQUEST-----~s', $csr, $matches)) {
93
            $csr = $matches[1];
94
        }
95
96
        $csr = trim(Base64::urlSafeEncode(base64_decode($csr)));
97
98
        $signedPayload = $this->createKeyId(
99
            $orderData->accountUrl,
100
            $orderData->finalizeUrl,
101
            compact('csr')
102
        );
103
104
        $response = $this->client->getHttpClient()->post($orderData->finalizeUrl, $signedPayload);
105
106
        if ($response->getHttpResponseCode() === 200) {
107
            $body = $response->getBody();
108
109
            if (isset($body['certificate'])) {
110
                $orderData->setCertificateUrl($body['certificate']);
111
            }
112
113
            return true;
114
        }
115
116
        $this->logResponse('error', 'Cannot finalize order '.$orderData->id, $response, ['orderData' => $orderData]);
117
118
        return false;
119
    }
120
}
121