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

stripe / stripe-php / 7122068993

07 Dec 2023 12:30AM UTC coverage: 63.454% (-0.07%) from 63.52%
7122068993

push

github

web-flow
Merge pull request #1616 from stripe/richardm-merge-master-into-beta

Merge master into beta

9 of 22 new or added lines in 5 files covered. (40.91%)

2429 of 3828 relevant lines covered (63.45%)

3.29 hits per line

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

73.87
/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
        'client_id' => null,
20
        'stripe_account' => null,
21
        'stripe_version' => \Stripe\Util\ApiVersion::CURRENT,
22
        'api_base' => self::DEFAULT_API_BASE,
23
        'connect_base' => self::DEFAULT_CONNECT_BASE,
24
        'files_base' => self::DEFAULT_FILES_BASE,
25
    ];
26

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

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

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

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

72
        $config = \array_merge(self::DEFAULT_CONFIG, $config);
24✔
73
        $this->validateConfig($config);
24✔
74

75
        $this->config = $config;
20✔
76

77
        $this->defaultOpts = \Stripe\Util\RequestOptions::parse([
20✔
78
            'stripe_account' => $config['stripe_account'],
20✔
79
            'stripe_version' => $config['stripe_version'],
20✔
80
        ]);
20✔
81

82
        $this->preview = new Preview($this);
20✔
83
    }
84

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

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

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

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

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

135
    /**
136
     * Sends a request to Stripe's API.
137
     *
138
     * @param 'delete'|'get'|'post' $method the HTTP method
139
     * @param string $path the path of the request
140
     * @param array $params the parameters of the request
141
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
142
     *
143
     * @return \Stripe\StripeObject the object returned by Stripe's API
144
     */
145
    public function request($method, $path, $params, $opts)
13✔
146
    {
147
        $opts = $this->defaultOpts->merge($opts, true);
13✔
148
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
11✔
149
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
11✔
150
        list($response, $opts->apiKey) = $requestor->request($method, $path, $params, $opts->headers, 'standard', ['stripe_client']);
10✔
151
        $opts->discardNonPersistentHeaders();
9✔
152
        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
9✔
153
        $obj->setLastResponse($response);
9✔
154

155
        return $obj;
9✔
156
    }
157

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

190
        $defaultRawRequestOpts = $this->defaultOpts;
5✔
191
        if ('preview' === $apiMode) {
5✔
192
            $defaultRawRequestOpts = $defaultRawRequestOpts->merge(['stripe_version' => \Stripe\Util\ApiVersion::PREVIEW], true);
3✔
193
        }
194

195
        $opts = $defaultRawRequestOpts->merge($opts, true);
5✔
196

197
        // Concatenate $headers to $opts->headers, removing duplicates.
198
        $opts->headers = \array_merge($opts->headers, $headers);
5✔
199
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
5✔
200
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
5✔
201
        list($response) = $requestor->request($method, $path, $params, $opts->headers, $apiMode);
5✔
202

203
        return $response;
5✔
204
    }
205

206
    /**
207
     * Sends a request to Stripe's API, passing chunks of the streamed response
208
     * into a user-provided $readBodyChunkCallable callback.
209
     *
210
     * @param 'delete'|'get'|'post' $method the HTTP method
211
     * @param string $path the path of the request
212
     * @param callable $readBodyChunkCallable a function that will be called
213
     * @param array $params the parameters of the request
214
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
215
     * with chunks of bytes from the body if the request is successful
216
     */
217
    public function requestStream($method, $path, $readBodyChunkCallable, $params, $opts)
×
218
    {
219
        $opts = $this->defaultOpts->merge($opts, true);
×
220
        $baseUrl = $opts->apiBase ?: $this->getApiBase();
×
221
        $requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
×
NEW
222
        list($response, $opts->apiKey) = $requestor->requestStream($method, $path, $readBodyChunkCallable, $params, $opts->headers, 'standard', ['stripe_client']);
×
223
    }
224

225
    /**
226
     * Sends a request to Stripe's API.
227
     *
228
     * @param 'delete'|'get'|'post' $method the HTTP method
229
     * @param string $path the path of the request
230
     * @param array $params the parameters of the request
231
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
232
     *
233
     * @return \Stripe\Collection of ApiResources
234
     */
235
    public function requestCollection($method, $path, $params, $opts)
2✔
236
    {
237
        $obj = $this->request($method, $path, $params, $opts);
2✔
238
        if (!($obj instanceof \Stripe\Collection)) {
2✔
239
            $received_class = \get_class($obj);
1✔
240
            $msg = "Expected to receive `Stripe\\Collection` object from Stripe API. Instead received `{$received_class}`.";
1✔
241

242
            throw new \Stripe\Exception\UnexpectedValueException($msg);
1✔
243
        }
244
        $obj->setFilters($params);
1✔
245

246
        return $obj;
1✔
247
    }
248

249
    /**
250
     * Sends a request to Stripe's API.
251
     *
252
     * @param 'delete'|'get'|'post' $method the HTTP method
253
     * @param string $path the path of the request
254
     * @param array $params the parameters of the request
255
     * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
256
     *
257
     * @return \Stripe\SearchResult of ApiResources
258
     */
259
    public function requestSearchResult($method, $path, $params, $opts)
×
260
    {
261
        $obj = $this->request($method, $path, $params, $opts);
×
262
        if (!($obj instanceof \Stripe\SearchResult)) {
×
263
            $received_class = \get_class($obj);
×
264
            $msg = "Expected to receive `Stripe\\SearchResult` object from Stripe API. Instead received `{$received_class}`.";
×
265

266
            throw new \Stripe\Exception\UnexpectedValueException($msg);
×
267
        }
268
        $obj->setFilters($params);
×
269

270
        return $obj;
×
271
    }
272

273
    /**
274
     * @param \Stripe\Util\RequestOptions $opts
275
     *
276
     * @throws \Stripe\Exception\AuthenticationException
277
     *
278
     * @return string
279
     */
280
    private function apiKeyForRequest($opts)
16✔
281
    {
282
        $apiKey = $opts->apiKey ?: $this->getApiKey();
16✔
283

284
        if (null === $apiKey) {
16✔
285
            $msg = 'No API key provided. Set your API key when constructing the '
1✔
286
                . 'StripeClient instance, or provide it on a per-request basis '
1✔
287
                . 'using the `api_key` key in the $opts argument.';
1✔
288

289
            throw new \Stripe\Exception\AuthenticationException($msg);
1✔
290
        }
291

292
        return $apiKey;
15✔
293
    }
294

295
    /**
296
     * @param array<string, mixed> $config
297
     *
298
     * @throws \Stripe\Exception\InvalidArgumentException
299
     */
300
    private function validateConfig($config)
24✔
301
    {
302
        // api_key
303
        if (null !== $config['api_key'] && !\is_string($config['api_key'])) {
24✔
304
            throw new \Stripe\Exception\InvalidArgumentException('api_key must be null or a string');
1✔
305
        }
306

307
        if (null !== $config['api_key'] && ('' === $config['api_key'])) {
23✔
308
            $msg = 'api_key cannot be the empty string';
1✔
309

310
            throw new \Stripe\Exception\InvalidArgumentException($msg);
1✔
311
        }
312

313
        if (null !== $config['api_key'] && (\preg_match('/\s/', $config['api_key']))) {
22✔
314
            $msg = 'api_key cannot contain whitespace';
1✔
315

316
            throw new \Stripe\Exception\InvalidArgumentException($msg);
1✔
317
        }
318

319
        // client_id
320
        if (null !== $config['client_id'] && !\is_string($config['client_id'])) {
21✔
321
            throw new \Stripe\Exception\InvalidArgumentException('client_id must be null or a string');
×
322
        }
323

324
        // stripe_account
325
        if (null !== $config['stripe_account'] && !\is_string($config['stripe_account'])) {
21✔
326
            throw new \Stripe\Exception\InvalidArgumentException('stripe_account must be null or a string');
×
327
        }
328

329
        // stripe_version
330
        if (null !== $config['stripe_version'] && !\is_string($config['stripe_version'])) {
21✔
331
            throw new \Stripe\Exception\InvalidArgumentException('stripe_version must be null or a string');
×
332
        }
333

334
        // api_base
335
        if (!\is_string($config['api_base'])) {
21✔
336
            throw new \Stripe\Exception\InvalidArgumentException('api_base must be a string');
×
337
        }
338

339
        // connect_base
340
        if (!\is_string($config['connect_base'])) {
21✔
341
            throw new \Stripe\Exception\InvalidArgumentException('connect_base must be a string');
×
342
        }
343

344
        // files_base
345
        if (!\is_string($config['files_base'])) {
21✔
346
            throw new \Stripe\Exception\InvalidArgumentException('files_base must be a string');
×
347
        }
348

349
        // check absence of extra keys
350
        $extraConfigKeys = \array_diff(\array_keys($config), \array_keys(self::DEFAULT_CONFIG));
21✔
351
        if (!empty($extraConfigKeys)) {
21✔
352
            // Wrap in single quote to more easily catch trailing spaces errors
353
            $invalidKeys = "'" . \implode("', '", $extraConfigKeys) . "'";
1✔
354

355
            throw new \Stripe\Exception\InvalidArgumentException('Found unknown key(s) in configuration array: ' . $invalidKeys);
1✔
356
        }
357
    }
358

359
    /**
360
     * Deserializes the raw JSON string returned by rawRequest into a similar class.
361
     *
362
     * @param string $json
363
     *
364
     * @return \Stripe\StripeObject
365
     * */
366
    public function deserialize($json)
×
367
    {
368
        return \Stripe\Util\Util::convertToStripeObject(\json_decode($json, true), []);
×
369
    }
370
}
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

© 2026 Coveralls, Inc