Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional appInfo to StripeClient config #1700

Merged
merged 5 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 37 additions & 24 deletions lib/ApiRequestor.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ class ApiRequestor
*/
private $_apiBase;

/**
* @var null|array
*/
private $_appInfo;

/**
* @var HttpClient\ClientInterface
*/
Expand All @@ -38,14 +43,16 @@ class ApiRequestor
*
* @param null|string $apiKey
* @param null|string $apiBase
* @param null|array $appInfo
*/
public function __construct($apiKey = null, $apiBase = null)
public function __construct($apiKey = null, $apiBase = null, $appInfo = null)
{
$this->_apiKey = $apiKey;
if (!$apiBase) {
$apiBase = Stripe::$apiBase;
}
$this->_apiBase = $apiBase;
$this->_appInfo = $appInfo;
}

/**
Expand Down Expand Up @@ -124,7 +131,7 @@ public function request($method, $url, $params = null, $headers = null, $usage =
$params = $params ?: [];
$headers = $headers ?: [];
list($rbody, $rcode, $rheaders, $myApiKey) =
$this->_requestRaw($method, $url, $params, $headers, $usage);
$this->_requestRaw($method, $url, $params, $headers, $usage);
helenye-stripe marked this conversation as resolved.
Show resolved Hide resolved
$json = $this->_interpretResponse($rbody, $rcode, $rheaders);
$resp = new ApiResponse($rbody, $rcode, $rheaders, $json);

Expand All @@ -146,7 +153,7 @@ public function requestStream($method, $url, $readBodyChunkCallable, $params = n
$params = $params ?: [];
$headers = $headers ?: [];
list($rbody, $rcode, $rheaders, $myApiKey) =
$this->_requestRawStreaming($method, $url, $params, $headers, $usage, $readBodyChunkCallable);
$this->_requestRawStreaming($method, $url, $params, $headers, $usage, $readBodyChunkCallable);
if ($rcode >= 300) {
$this->_interpretResponse($rbody, $rcode, $rheaders);
}
Expand All @@ -165,7 +172,7 @@ public function handleErrorResponse($rbody, $rcode, $rheaders, $resp)
{
if (!\is_array($resp) || !isset($resp['error'])) {
$msg = "Invalid response object from API: {$rbody} "
. "(HTTP response code was {$rcode})";
. "(HTTP response code was {$rcode})";

throw new Exception\UnexpectedValueException($msg);
}
Expand Down Expand Up @@ -213,7 +220,7 @@ private static function _specificAPIError($rbody, $rcode, $rheaders, $resp, $err
return Exception\IdempotencyException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code);
}

// no break
// no break
case 404:
return Exception\InvalidRequestException::factory($msg, $rcode, $rbody, $resp, $rheaders, $code, $param);

Expand Down Expand Up @@ -284,10 +291,10 @@ private static function _formatAppInfo($appInfo)
{
if (null !== $appInfo) {
$string = $appInfo['name'];
if (null !== $appInfo['version']) {
if (\array_key_exists('version', $appInfo) && null !== $appInfo['version']) {
$string .= '/' . $appInfo['version'];
}
if (null !== $appInfo['url']) {
if (\array_key_exists('url', $appInfo) && null !== $appInfo['url']) {
$string .= ' (' . $appInfo['url'] . ')';
}

Expand Down Expand Up @@ -320,20 +327,22 @@ private static function _isDisabled($disableFunctionsOutput, $functionName)
/**
* @static
*
* @param string $apiKey
* @param null $clientInfo
* @param string $apiKey the Stripe API key, to be used in regular API requests
* @param null $clientInfo client user agent information
* @param null $appInfo information to identify a plugin that integrates Stripe using this library
*
* @return array
*/
private static function _defaultHeaders($apiKey, $clientInfo = null)
private static function _defaultHeaders($apiKey, $clientInfo = null, $appInfo = null)
{
$uaString = 'Stripe/v1 PhpBindings/' . Stripe::VERSION;

$langVersion = \PHP_VERSION;
$uname_disabled = self::_isDisabled(\ini_get('disable_functions'), 'php_uname');
$uname = $uname_disabled ? '(disabled)' : \php_uname();

$appInfo = Stripe::getAppInfo();
// Fallback to global configuration to maintain backwards compatibility.
$appInfo = $appInfo ?: Stripe::getAppInfo();
$ua = [
'bindings_version' => Stripe::VERSION,
'lang' => 'php',
Expand Down Expand Up @@ -366,9 +375,9 @@ private function _prepareRequest($method, $url, $params, $headers)

if (!$myApiKey) {
$msg = 'No API key provided. (HINT: set your API key using '
. '"Stripe::setApiKey(<API-KEY>)". You can generate API keys from '
. 'the Stripe web interface. See https://stripe.com/api for '
. 'details, or email [email protected] if you have any questions.';
. '"Stripe::setApiKey(<API-KEY>)". You can generate API keys from '
. 'the Stripe web interface. See https://stripe.com/api for '
. 'details, or email [email protected] if you have any questions.';

throw new Exception\AuthenticationException($msg);
}
Expand All @@ -390,15 +399,15 @@ function ($key) use ($params) {
);
if (\count($optionKeysInParams) > 0) {
$message = \sprintf('Options found in $params: %s. Options should '
. 'be passed in their own array after $params. (HINT: pass an '
. 'empty array to $params if you do not have any.)', \implode(', ', $optionKeysInParams));
. 'be passed in their own array after $params. (HINT: pass an '
. 'empty array to $params if you do not have any.)', \implode(', ', $optionKeysInParams));
\trigger_error($message, \E_USER_WARNING);
}
}

$absUrl = $this->_apiBase . $url;
$params = self::_encodeObjects($params);
$defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo);
$defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo, $this->_appInfo);

if (Stripe::$accountId) {
$defaultHeaders['Stripe-Account'] = Stripe::$accountId;
Expand Down Expand Up @@ -460,9 +469,11 @@ private function _requestRaw($method, $url, $params, $headers, $usage)
$hasFile
);

if (isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']) {
if (
isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']
) {
self::$requestTelemetry = new RequestTelemetry(
$rheaders['request-id'],
Util\Util::currentTimeMillis() - $requestStartMs,
Expand Down Expand Up @@ -501,9 +512,11 @@ private function _requestRawStreaming($method, $url, $params, $headers, $usage,
$readBodyChunkCallable
);

if (isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']) {
if (
isset($rheaders['request-id'])
&& \is_string($rheaders['request-id'])
&& '' !== $rheaders['request-id']
) {
self::$requestTelemetry = new RequestTelemetry(
$rheaders['request-id'],
Util\Util::currentTimeMillis() - $requestStartMs
Expand Down Expand Up @@ -555,7 +568,7 @@ private function _interpretResponse($rbody, $rcode, $rheaders)
$jsonError = \json_last_error();
if (null === $resp && \JSON_ERROR_NONE !== $jsonError) {
$msg = "Invalid response body from API: {$rbody} "
. "(HTTP response code was {$rcode}, json_last_error() was {$jsonError})";
. "(HTTP response code was {$rcode}, json_last_error() was {$jsonError})";

throw new Exception\UnexpectedValueException($msg, $rcode);
}
Expand Down
31 changes: 28 additions & 3 deletions lib/BaseStripeClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class BaseStripeClient implements StripeClientInterface, StripeStreamingClientIn
/** @var array<string, null|string> */
const DEFAULT_CONFIG = [
'api_key' => null,
'app_info' => null,
'client_id' => null,
'stripe_account' => null,
'stripe_version' => \Stripe\Util\ApiVersion::CURRENT,
Expand All @@ -39,10 +40,12 @@ class BaseStripeClient implements StripeClientInterface, StripeStreamingClientIn
* Configuration settings include the following options:
*
* - api_key (null|string): the Stripe API key, to be used in regular API requests.
* - app_info (null|array): information to identify a plugin that integrates Stripe using this library.
* Expects: array{name: string, version?: string, url?: string, appPartnerId?: string}
* - client_id (null|string): the Stripe client ID, to be used in OAuth requests.
* - stripe_account (null|string): a Stripe account ID. If set, all requests sent by the client
* will automatically use the {@code Stripe-Account} header with that account ID.
* - stripe_version (null|string): a Stripe API verion. If set, all requests sent by the client
* - stripe_version (null|string): a Stripe API version. If set, all requests sent by the client
* will include the {@code Stripe-Version} header with that API version.
*
* The following configuration settings are also available, though setting these should rarely be necessary
Expand Down Expand Up @@ -127,6 +130,16 @@ public function getFilesBase()
return $this->config['files_base'];
}

/**
* Gets the app info for this client
*
* @return array|null information to identify a plugin that integrates Stripe using this library
*/
public function getAppInfo()
{
return $this->config['app_info'];
}

/**
* Sends a request to Stripe's API.
*
Expand All @@ -141,7 +154,7 @@ public function request($method, $path, $params, $opts)
{
$opts = $this->defaultOpts->merge($opts, true);
$baseUrl = $opts->apiBase ?: $this->getApiBase();
$requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
$requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl, $this->getAppInfo());
list($response, $opts->apiKey) = $requestor->request($method, $path, $params, $opts->headers, ['stripe_client']);
$opts->discardNonPersistentHeaders();
$obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
Expand All @@ -165,7 +178,7 @@ public function requestStream($method, $path, $readBodyChunkCallable, $params, $
{
$opts = $this->defaultOpts->merge($opts, true);
$baseUrl = $opts->apiBase ?: $this->getApiBase();
$requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
$requestor = new \Stripe\ApiRequestor($this->apiKeyForRequest($opts), $baseUrl, $this->getAppInfo());
list($response, $opts->apiKey) = $requestor->requestStream($method, $path, $readBodyChunkCallable, $params, $opts->headers, ['stripe_client']);
}

Expand Down Expand Up @@ -293,6 +306,18 @@ private function validateConfig($config)
throw new \Stripe\Exception\InvalidArgumentException('files_base must be a string');
}

// app info
if (null !== $config['app_info'] && !\is_array($config['app_info'])) {
throw new \Stripe\Exception\InvalidArgumentException('app_info must be an array');
}

$appInfoKeys = ['name', 'version', 'url', 'appPartnerId'];
if (null !== $config['app_info'] && array_diff_key($config['app_info'], array_flip($appInfoKeys))) {
$msg = 'app_info must be of type array{name: string, version?: string, url?: string, appPartnerId?: string}';

throw new \Stripe\Exception\InvalidArgumentException($msg);
}

// check absence of extra keys
$extraConfigKeys = \array_diff(\array_keys($config), \array_keys(self::DEFAULT_CONFIG));
if (!empty($extraConfigKeys)) {
Expand Down
Loading
Loading