• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stripe / stripe-php / 9294143216

29 May 2024 10:49PM UTC coverage: 61.377% (+0.06%) from 61.314%
9294143216

Pull #1704

github

web-flow
Merge 97d97cea2 into 8661523be
Pull Request #1704: Merge changes from stripe-php master

22 of 32 new or added lines in 2 files covered. (68.75%)

1 existing line in 1 file now uncovered.

2425 of 3951 relevant lines covered (61.38%)

3.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.79
/lib/BaseStripeClient.php
1
<?php
2

3
namespace Stripe;
4

5
class BaseStripeClient implements StripeClientInterface, StripeStreamingClientInterface
6
{
7
    /** @var string default base URL for Stripe's API */
8
    const DEFAULT_API_BASE = 'https://api.stripe.com';
9

10
    /** @var string default base URL for Stripe's OAuth API */
11
    const DEFAULT_CONNECT_BASE = 'https://connect.stripe.com';
12

13
    /** @var string default base URL for Stripe's Files API */
14
    const DEFAULT_FILES_BASE = 'https://files.stripe.com';
15

16
    /** @var array<string, null|string> */
17
    const DEFAULT_CONFIG = [
18
        'api_key' => null,
19
        'app_info' => null,
20
        'client_id' => null,
21
        'stripe_account' => null,
22
        'stripe_version' => \Stripe\Util\ApiVersion::CURRENT,
23
        'api_base' => self::DEFAULT_API_BASE,
24
        'connect_base' => self::DEFAULT_CONNECT_BASE,
25
        'files_base' => self::DEFAULT_FILES_BASE,
26
    ];
27

28
    /** @var array<string, mixed> */
29
    private $config;
30

31
    /** @var \Stripe\Util\RequestOptions */
32
    private $defaultOpts;
33

34
    /** @var \Stripe\Preview */
35
    public $preview;
36

37
    /**
38
     * Initializes a new instance of the {@link BaseStripeClient} class.
39
     *
40
     * The constructor takes a single argument. The argument can be a string, in which case it
41
     * should be the API key. It can also be an array with various configuration settings.
42
     *
43
     * Configuration settings include the following options:
44
     *
45
     * - api_key (null|string): the Stripe API key, to be used in regular API requests.
46
     * - app_info (null|array): information to identify a plugin that integrates Stripe using this library.
47
     *                          Expects: array{name: string, version?: string, url?: string, partner_id?: string}
48
     * - client_id (null|string): the Stripe client ID, to be used in OAuth requests.
49
     * - stripe_account (null|string): a Stripe account ID. If set, all requests sent by the client
50
     *   will automatically use the {@code Stripe-Account} header with that account ID.
51
     * - stripe_version (null|string): a Stripe API version. If set, all requests sent by the client
52
     *   will include the {@code Stripe-Version} header with that API version.
53
     *
54
     * The following configuration settings are also available, though setting these should rarely be necessary
55
     * (only useful if you want to send requests to a mock server like stripe-mock):
56
     *
57
     * - api_base (string): the base URL for regular API requests. Defaults to
58
     *   {@link DEFAULT_API_BASE}.
59
     * - connect_base (string): the base URL for OAuth requests. Defaults to
60
     *   {@link DEFAULT_CONNECT_BASE}.
61
     * - files_base (string): the base URL for file creation requests. Defaults to
62
     *   {@link DEFAULT_FILES_BASE}.
63
     *
64
     * @param array<string, mixed>|string $config the API key as a string, or an array containing
65
     *   the client configuration settings
66
     */
67
    public function __construct($config = [])
31✔
68
    {
69
        if (\is_string($config)) {
31✔
70
            $config = ['api_key' => $config];
2✔
71
        } elseif (!\is_array($config)) {
29✔
72
            throw new \Stripe\Exception\InvalidArgumentException('$config must be a string or an array');
1✔
73
        }
74

75
        $config = \array_merge(self::DEFAULT_CONFIG, $config);
30✔
76
        $this->validateConfig($config);
30✔
77

78
        $this->config = $config;
25✔
79

80
        $this->defaultOpts = \Stripe\Util\RequestOptions::parse([
25✔
81
            'stripe_account' => $config['stripe_account'],
25✔
82
            'stripe_version' => $config['stripe_version'],
25✔
83
        ]);
25✔
84

85
        $this->preview = new Preview($this);
25✔
86
    }
87

88
    /**
89
     * Gets the API key used by the client to send requests.
90
     *
91
     * @return null|string the API key used by the client to send requests
92
     */
93
    public function getApiKey()
21✔
94
    {
95
        return $this->config['api_key'];
21✔
96
    }
97

98
    /**
99
     * Gets the client ID used by the client in OAuth requests.
100
     *
101
     * @return null|string the client ID used by the client in OAuth requests
102
     */
103
    public function getClientId()
×
104
    {
105
        return $this->config['client_id'];
×
106
    }
107

108
    /**
109
     * Gets the base URL for Stripe's API.
110
     *
111
     * @return string the base URL for Stripe's API
112
     */
113
    public function getApiBase()
21✔
114
    {
115
        return $this->config['api_base'];
21✔
116
    }
117

118
    /**
119
     * Gets the base URL for Stripe's OAuth API.
120
     *
121
     * @return string the base URL for Stripe's OAuth API
122
     */
123
    public function getConnectBase()
×
124
    {
125
        return $this->config['connect_base'];
×
126
    }
127

128
    /**
129
     * Gets the base URL for Stripe's Files API.
130
     *
131
     * @return string the base URL for Stripe's Files API
132
     */
133
    public function getFilesBase()
×
134
    {
135
        return $this->config['files_base'];
×
136
    }
137

138
    /**
139
     * Gets the app info for this client.
140
     *
141
     * @return null|array information to identify a plugin that integrates Stripe using this library
142
     */
143
    public function getAppInfo()
14✔
144
    {
145
        return $this->config['app_info'];
14✔
146
    }
147

148
    /**
149
     * Sends a request to Stripe's API.
150
     *
151
     * @param 'delete'|'get'|'post' $method the HTTP method
152
     * @param string $path the path of the request
153
     * @param array $params the parameters of the request
154
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
155
     *
156
     * @return \Stripe\StripeObject the object returned by Stripe's API
157
     */
158
    public function request($method, $path, $params, $opts)
17✔
159
    {
160
        $opts = $this->defaultOpts->merge($opts, true);
17✔
161
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
15✔
162
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl, $this->getAppInfo());
15✔
163
        list($response, $opts->apiKey) = $requestor->request($method, $path, $params, $opts->headers, 'standard', ['stripe_client']);
14✔
164
        $opts->discardNonPersistentHeaders();
13✔
165
        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
13✔
166
        $obj->setLastResponse($response);
13✔
167

168
        return $obj;
13✔
169
    }
170

171
    /**
172
     * Sends a raw request to Stripe's API. This is the lowest level method for interacting
173
     * with the Stripe API. This method is useful for interacting with endpoints that are not
174
     * covered yet in stripe-php.
175
     *
176
     * @param 'delete'|'get'|'post' $method the HTTP method
177
     * @param string $path the path of the request
178
     * @param null|array $params the parameters of the request
179
     * @param array $opts the special modifiers of the request
180
     *
181
     * @return \Stripe\ApiResponse
182
     */
183
    public function rawRequest($method, $path, $params = null, $opts = [])
7✔
184
    {
185
        if ('post' !== $method && null !== $params) {
7✔
186
            throw new Exception\InvalidArgumentException('Error: rawRequest only supports $params on post requests. Please pass null and add your parameters to $path');
1✔
187
        }
188
        $apiMode = 'standard';
6✔
189
        $headers = [];
6✔
190
        if (\is_array($opts) && \array_key_exists('api_mode', $opts)) {
6✔
191
            $apiMode = $opts['api_mode'];
5✔
192
            unset($opts['api_mode']);
5✔
193
        }
194
        if (\is_array($opts) && \array_key_exists('headers', $opts)) {
6✔
195
            $headers = $opts['headers'] ?: [];
×
196
            unset($opts['headers']);
×
197
        }
198
        if (\is_array($opts) && \array_key_exists('stripe_context', $opts)) {
6✔
199
            $headers['Stripe-Context'] = $opts['stripe_context'];
1✔
200
            unset($opts['stripe_context']);
1✔
201
        }
202

203
        $defaultRawRequestOpts = $this->defaultOpts;
6✔
204
        if ('preview' === $apiMode) {
6✔
205
            $defaultRawRequestOpts = $defaultRawRequestOpts->merge(['stripe_version' => \Stripe\Util\ApiVersion::PREVIEW], true);
3✔
206
        }
207

208
        $opts = $defaultRawRequestOpts->merge($opts, true);
6✔
209

210
        // Concatenate $headers to $opts->headers, removing duplicates.
211
        $opts->headers = \array_merge($opts->headers, $headers);
6✔
212
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
6✔
213
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
6✔
214
        list($response) = $requestor->request($method, $path, $params, $opts->headers, $apiMode, ['raw_request']);
6✔
215

216
        return $response;
6✔
217
    }
218

219
    /**
220
     * Sends a request to Stripe's API, passing chunks of the streamed response
221
     * into a user-provided $readBodyChunkCallable callback.
222
     *
223
     * @param 'delete'|'get'|'post' $method the HTTP method
224
     * @param string $path the path of the request
225
     * @param callable $readBodyChunkCallable a function that will be called
226
     * @param array $params the parameters of the request
227
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
228
     * with chunks of bytes from the body if the request is successful
229
     */
230
    public function requestStream($method, $path, $readBodyChunkCallable, $params, $opts)
×
231
    {
232
        $opts = $this->defaultOpts->merge($opts, true);
×
233
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
×
NEW
234
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl, $this->getAppInfo());
×
235
        list($response, $opts->apiKey) = $requestor->requestStream($method, $path, $readBodyChunkCallable, $params, $opts->headers, 'standard', ['stripe_client']);
×
236
    }
237

238
    /**
239
     * Sends a request to Stripe's API.
240
     *
241
     * @param 'delete'|'get'|'post' $method the HTTP method
242
     * @param string $path the path of the request
243
     * @param array $params the parameters of the request
244
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
245
     *
246
     * @return \Stripe\Collection of ApiResources
247
     */
248
    public function requestCollection($method, $path, $params, $opts)
2✔
249
    {
250
        $obj = $this->request($method, $path, $params, $opts);
2✔
251
        if (!($obj instanceof \Stripe\Collection)) {
2✔
252
            $received_class = \get_class($obj);
1✔
253
            $msg = "Expected to receive `Stripe\\Collection` object from Stripe API. Instead received `{$received_class}`.";
1✔
254

255
            throw new \Stripe\Exception\UnexpectedValueException($msg);
1✔
256
        }
257
        $obj->setFilters($params);
1✔
258

259
        return $obj;
1✔
260
    }
261

262
    /**
263
     * Sends a request to Stripe's API.
264
     *
265
     * @param 'delete'|'get'|'post' $method the HTTP method
266
     * @param string $path the path of the request
267
     * @param array $params the parameters of the request
268
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
269
     *
270
     * @return \Stripe\SearchResult of ApiResources
271
     */
272
    public function requestSearchResult($method, $path, $params, $opts)
×
273
    {
274
        $obj = $this->request($method, $path, $params, $opts);
×
275
        if (!($obj instanceof \Stripe\SearchResult)) {
×
276
            $received_class = \get_class($obj);
×
277
            $msg = "Expected to receive `Stripe\\SearchResult` object from Stripe API. Instead received `{$received_class}`.";
×
278

279
            throw new \Stripe\Exception\UnexpectedValueException($msg);
×
280
        }
281
        $obj->setFilters($params);
×
282

283
        return $obj;
×
284
    }
285

286
    /**
287
     * @param \Stripe\Util\RequestOptions $opts
288
     *
289
     * @throws \Stripe\Exception\AuthenticationException
290
     *
291
     * @return string
292
     */
293
    private function apiKeyForRequest($opts)
21✔
294
    {
295
        $apiKey = $opts->apiKey ?: $this->getApiKey();
21✔
296

297
        if (null === $apiKey) {
21✔
298
            $msg = 'No API key provided. Set your API key when constructing the '
1✔
299
                . 'StripeClient instance, or provide it on a per-request basis '
1✔
300
                . 'using the `api_key` key in the $opts argument.';
1✔
301

302
            throw new \Stripe\Exception\AuthenticationException($msg);
1✔
303
        }
304

305
        return $apiKey;
20✔
306
    }
307

308
    /**
309
     * @param array<string, mixed> $config
310
     *
311
     * @throws \Stripe\Exception\InvalidArgumentException
312
     */
313
    private function validateConfig($config)
30✔
314
    {
315
        // api_key
316
        if (null !== $config['api_key'] && !\is_string($config['api_key'])) {
30✔
317
            throw new \Stripe\Exception\InvalidArgumentException('api_key must be null or a string');
1✔
318
        }
319

320
        if (null !== $config['api_key'] && ('' === $config['api_key'])) {
29✔
321
            $msg = 'api_key cannot be the empty string';
1✔
322

323
            throw new \Stripe\Exception\InvalidArgumentException($msg);
1✔
324
        }
325

326
        if (null !== $config['api_key'] && (\preg_match('/\s/', $config['api_key']))) {
28✔
327
            $msg = 'api_key cannot contain whitespace';
1✔
328

329
            throw new \Stripe\Exception\InvalidArgumentException($msg);
1✔
330
        }
331

332
        // client_id
333
        if (null !== $config['client_id'] && !\is_string($config['client_id'])) {
27✔
334
            throw new \Stripe\Exception\InvalidArgumentException('client_id must be null or a string');
×
335
        }
336

337
        // stripe_account
338
        if (null !== $config['stripe_account'] && !\is_string($config['stripe_account'])) {
27✔
339
            throw new \Stripe\Exception\InvalidArgumentException('stripe_account must be null or a string');
×
340
        }
341

342
        // stripe_version
343
        if (null !== $config['stripe_version'] && !\is_string($config['stripe_version'])) {
27✔
344
            throw new \Stripe\Exception\InvalidArgumentException('stripe_version must be null or a string');
×
345
        }
346

347
        // api_base
348
        if (!\is_string($config['api_base'])) {
27✔
349
            throw new \Stripe\Exception\InvalidArgumentException('api_base must be a string');
×
350
        }
351

352
        // connect_base
353
        if (!\is_string($config['connect_base'])) {
27✔
354
            throw new \Stripe\Exception\InvalidArgumentException('connect_base must be a string');
×
355
        }
356

357
        // files_base
358
        if (!\is_string($config['files_base'])) {
27✔
359
            throw new \Stripe\Exception\InvalidArgumentException('files_base must be a string');
×
360
        }
361

362
        // app info
363
        if (null !== $config['app_info'] && !\is_array($config['app_info'])) {
27✔
NEW
364
            throw new \Stripe\Exception\InvalidArgumentException('app_info must be an array');
×
365
        }
366

367
        $appInfoKeys = ['name', 'version', 'url', 'partner_id'];
27✔
368
        if (null !== $config['app_info'] && array_diff_key($config['app_info'], array_flip($appInfoKeys))) {
27✔
369
            $msg = 'app_info must be of type array{name: string, version?: string, url?: string, partner_id?: string}';
1✔
370

371
            throw new \Stripe\Exception\InvalidArgumentException($msg);
1✔
372
        }
373

374
        // check absence of extra keys
375
        $extraConfigKeys = \array_diff(\array_keys($config), \array_keys(self::DEFAULT_CONFIG));
26✔
376
        if (!empty($extraConfigKeys)) {
26✔
377
            // Wrap in single quote to more easily catch trailing spaces errors
378
            $invalidKeys = "'" . \implode("', '", $extraConfigKeys) . "'";
1✔
379

380
            throw new \Stripe\Exception\InvalidArgumentException('Found unknown key(s) in configuration array: ' . $invalidKeys);
1✔
381
        }
382
    }
383

384
    /**
385
     * Deserializes the raw JSON string returned by rawRequest into a similar class.
386
     *
387
     * @param string $json
388
     *
389
     * @return \Stripe\StripeObject
390
     * */
391
    public function deserialize($json)
×
392
    {
393
        return \Stripe\Util\Util::convertToStripeObject(\json_decode($json, true), []);
×
394
    }
395
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc