Test Setup Failed
Push — master ( 9fa851...19bcef )
by
unknown
15:59 queued 13:07
created

Endpoint::validateRequired()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 12
rs 9.6111
cc 5
nc 6
nop 2
1
<?php
2
3
namespace Iamolayemi\Paystack\Endpoints;
4
5
use Iamolayemi\Paystack\Exceptions\PaystackApiException;
6
use Iamolayemi\Paystack\Exceptions\PaystackConnectionException;
7
use Iamolayemi\Paystack\Exceptions\PaystackValidationException;
8
use Iamolayemi\Paystack\Paystack;
9
use Illuminate\Http\Client\Response;
10
11
abstract class Endpoint
12
{
13
    protected const BASE_URL = 'https://api.paystack.co/';
14
15
    public Paystack $paystack;
16
17
    protected ?Response $response = null;
18
19
    /**
20
     * @throws PaystackConnectionException
21
     */
22
    public function __construct(Paystack $paystack)
23
    {
24
        $this->paystack = $paystack;
25
26
        if ($this->paystack->getConnection() === null) {
27
            throw PaystackConnectionException::connectionFailed(self::BASE_URL);
28
        }
29
    }
30
31
    /**
32
     * Return the data from http request.
33
     *
34
     * @return array<string, mixed>|null The data from the response, or null if no request has been made.
35
     */
36
    public function data(): ?array
37
    {
38
        return $this->response ? $this->response('data') : null;
39
    }
40
41
    /**
42
     * Return the response from http request.
43
     *
44
     * @param  string  $key  Optional key to extract a specific value from the JSON response.
45
     * @return array<string, mixed>|mixed The full response or a specific value from the response.
46
     */
47
    public function response(string $key = ''): mixed
48
    {
49
        if (empty($key)) {
50
            return $this->response?->json();
51
        }
52
53
        return $this->response?->json($key);
54
    }
55
56
    /**
57
     * Check if the response was successful.
58
     */
59
    public function isSuccessful(): bool
60
    {
61
        return $this->response?->successful() ?? false;
62
    }
63
64
    /**
65
     * Get the HTTP status code of the response.
66
     */
67
    public function getStatusCode(): ?int
68
    {
69
        return $this->response?->status();
70
    }
71
72
    protected function url(string $endpoint): string
73
    {
74
        return self::BASE_URL.ltrim($endpoint, '/');
75
    }
76
77
    /**
78
     * Make a http get request
79
     *
80
     * @param  string  $url  The URL to send the request to.
81
     * @param  array<string, mixed>|string|null  $query  Optional query string parameters.
82
     * @return self This instance for method chaining.
83
     *
84
     * @throws PaystackApiException
85
     */
86
    protected function get(string $url, array|string|null $query = []): self
87
    {
88
        try {
89
            $connection = $this->paystack->getConnection();
90
            if ($connection === null) {
91
                throw PaystackConnectionException::connectionFailed($url);
92
            }
93
94
            $response = $connection->get($url, $query);
95
            $this->response = $response;
96
97
            if (! $response->successful()) {
98
                $this->handleErrorResponse($response, $url);
99
            }
100
101
            return $this;
102
        } catch (\Exception $e) {
103
            throw PaystackConnectionException::connectionFailed($url, $e);
104
        }
105
    }
106
107
    /**
108
     * Make a http post request
109
     *
110
     * @param  string  $url  The URL to send the request to.
111
     * @param  array<string, mixed>  $payload  The data to be sent.
112
     * @return self This instance for method chaining.
113
     *
114
     * @throws PaystackApiException
115
     */
116
    protected function post(string $url, array $payload = []): self
117
    {
118
        try {
119
            $connection = $this->paystack->getConnection();
120
            if ($connection === null) {
121
                throw PaystackConnectionException::connectionFailed($url);
122
            }
123
124
            $response = $connection->post($url, $payload);
125
            $this->response = $response;
126
127
            if (! $response->successful()) {
128
                $this->handleErrorResponse($response, $url);
129
            }
130
131
            return $this;
132
        } catch (\Exception $e) {
133
            throw PaystackConnectionException::connectionFailed($url, $e);
134
        }
135
    }
136
137
    /**
138
     * Make a http put request
139
     *
140
     * @param  string  $url  The URL to send the request to.
141
     * @param  array<string, mixed>  $payload  The data to be sent.
142
     * @return self This instance for method chaining.
143
     *
144
     * @throws PaystackApiException
145
     */
146
    protected function put(string $url, array $payload = []): self
147
    {
148
        try {
149
            $connection = $this->paystack->getConnection();
150
            if ($connection === null) {
151
                throw PaystackConnectionException::connectionFailed($url);
152
            }
153
154
            $response = $connection->put($url, $payload);
155
            $this->response = $response;
156
157
            if (! $response->successful()) {
158
                $this->handleErrorResponse($response, $url);
159
            }
160
161
            return $this;
162
        } catch (\Exception $e) {
163
            throw PaystackConnectionException::connectionFailed($url, $e);
164
        }
165
    }
166
167
    /**
168
     * Make a http delete request
169
     *
170
     * @param  string  $url  The URL to send the request to.
171
     * @param  array<string, mixed>  $payload  The data to be sent.
172
     * @return self This instance for method chaining.
173
     *
174
     * @throws PaystackApiException
175
     */
176
    protected function delete(string $url, array $payload = []): self
177
    {
178
        try {
179
            $connection = $this->paystack->getConnection();
180
            if ($connection === null) {
181
                throw PaystackConnectionException::connectionFailed($url);
182
            }
183
184
            $response = $connection->delete($url, $payload);
185
            $this->response = $response;
186
187
            if (! $response->successful()) {
188
                $this->handleErrorResponse($response, $url);
189
            }
190
191
            return $this;
192
        } catch (\Exception $e) {
193
            throw PaystackConnectionException::connectionFailed($url, $e);
194
        }
195
    }
196
197
    /**
198
     * Handle error responses from the API.
199
     *
200
     * @throws PaystackApiException
201
     */
202
    protected function handleErrorResponse(Response $response, string $url): void
203
    {
204
        $responseData = $response->json();
205
206
        if (is_array($responseData)) {
0 ignored issues
show
introduced by
The condition is_array($responseData) is always true.
Loading history...
207
            throw PaystackApiException::fromResponse($responseData, $url);
208
        }
209
210
        throw new PaystackApiException(
211
            'Unknown API error occurred',
212
            $response->status(),
213
            $url
214
        );
215
    }
216
217
    /**
218
     * Validate required parameters.
219
     *
220
     * @param  array<string, mixed>  $data
221
     * @param  array<int, string>  $required
222
     *
223
     * @throws PaystackValidationException
224
     */
225
    protected function validateRequired(array $data, array $required): void
226
    {
227
        $errors = [];
228
229
        foreach ($required as $field) {
230
            if (! isset($data[$field]) || empty($data[$field])) {
231
                $errors[$field] = "The {$field} field is required.";
232
            }
233
        }
234
235
        if (! empty($errors)) {
236
            throw new PaystackValidationException('Validation failed', $errors);
237
        }
238
    }
239
}
240