vendor/stripe/stripe-php/lib/BaseStripeClient.php line 203

Open in your IDE?
  1. <?php
  2. namespace Stripe;
  3. use Stripe\Util\Util;
  4. class BaseStripeClient implements StripeClientInterfaceStripeStreamingClientInterface
  5. {
  6.     /** @var string default base URL for Stripe's API */
  7.     const DEFAULT_API_BASE 'https://api.stripe.com';
  8.     /** @var string default base URL for Stripe's OAuth API */
  9.     const DEFAULT_CONNECT_BASE 'https://connect.stripe.com';
  10.     /** @var string default base URL for Stripe's Files API */
  11.     const DEFAULT_FILES_BASE 'https://files.stripe.com';
  12.     /** @var string default base URL for Stripe's Meter Events API */
  13.     const DEFAULT_METER_EVENTS_BASE 'https://meter-events.stripe.com';
  14.     /** @var array<string, null|int|string> */
  15.     const DEFAULT_CONFIG = [
  16.         'api_key' => null,
  17.         'app_info' => null,
  18.         'client_id' => null,
  19.         'stripe_account' => null,
  20.         'stripe_context' => 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.         'meter_events_base' => self::DEFAULT_METER_EVENTS_BASE,
  26.         // inherit from global
  27.         'max_network_retries' => null,
  28.     ];
  29.     /** @var array<string, mixed> */
  30.     private $config;
  31.     /** @var \Stripe\Util\RequestOptions */
  32.     private $defaultOpts;
  33.     /**
  34.      * Initializes a new instance of the {@link BaseStripeClient} class.
  35.      *
  36.      * The constructor takes a single argument. The argument can be a string, in which case it
  37.      * should be the API key. It can also be an array with various configuration settings.
  38.      *
  39.      * Configuration settings include the following options:
  40.      *
  41.      * - api_key (null|string): the Stripe API key, to be used in regular API requests.
  42.      * - app_info (null|array): information to identify a plugin that integrates Stripe using this library.
  43.      *                          Expects: array{name: string, version?: string, url?: string, partner_id?: string}
  44.      * - client_id (null|string): the Stripe client ID, to be used in OAuth requests.
  45.      * - stripe_account (null|string): a Stripe account ID. If set, all requests sent by the client
  46.      *   will automatically use the {@code Stripe-Account} header with that account ID.
  47.      * - stripe_context (null|string): a Stripe account or compartment ID. If set, all requests sent by the client
  48.      *   will automatically use the {@code Stripe-Context} header with that ID.
  49.      * - stripe_version (null|string): a Stripe API version. If set, all requests sent by the client
  50.      *   will include the {@code Stripe-Version} header with that API version.
  51.      * - max_network_retries (null|int): the number of times this client should retry API failures; defaults to 0.
  52.      *
  53.      * The following configuration settings are also available, though setting these should rarely be necessary
  54.      * (only useful if you want to send requests to a mock server like stripe-mock):
  55.      *
  56.      * - api_base (string): the base URL for regular API requests. Defaults to
  57.      *   {@link DEFAULT_API_BASE}.
  58.      * - connect_base (string): the base URL for OAuth requests. Defaults to
  59.      *   {@link DEFAULT_CONNECT_BASE}.
  60.      * - files_base (string): the base URL for file creation requests. Defaults to
  61.      *   {@link DEFAULT_FILES_BASE}.
  62.      * - meter_events_base (string): the base URL for high throughput requests. Defaults to
  63.      *   {@link DEFAULT_METER_EVENTS_BASE}.
  64.      *
  65.      * @param array<string, mixed>|string $config the API key as a string, or an array containing
  66.      *   the client configuration settings
  67.      */
  68.     public function __construct($config = [])
  69.     {
  70.         if (\is_string($config)) {
  71.             $config = ['api_key' => $config];
  72.         } elseif (!\is_array($config)) {
  73.             throw new Exception\InvalidArgumentException('$config must be a string or an array');
  74.         }
  75.         if (!\array_key_exists('max_network_retries'$config)) {
  76.             // if no value is passed, inherit the global value at the time of client creation
  77.             $config['max_network_retries'] = Stripe::getMaxNetworkRetries();
  78.         }
  79.         $config \array_merge(self::DEFAULT_CONFIG$config);
  80.         $this->validateConfig($config);
  81.         $this->config $config;
  82.         $this->defaultOpts \Stripe\Util\RequestOptions::parse([
  83.             'stripe_account' => $config['stripe_account'],
  84.             'stripe_context' => $config['stripe_context'],
  85.             'stripe_version' => $config['stripe_version'],
  86.             'max_network_retries' => $config['max_network_retries'],
  87.         ]);
  88.     }
  89.     /**
  90.      * Gets the API key used by the client to send requests.
  91.      *
  92.      * @return null|string the API key used by the client to send requests
  93.      */
  94.     public function getApiKey()
  95.     {
  96.         return $this->config['api_key'];
  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.      * Gets the base URL for Stripe's API.
  109.      *
  110.      * @return string the base URL for Stripe's API
  111.      */
  112.     public function getApiBase()
  113.     {
  114.         return $this->config['api_base'];
  115.     }
  116.     /**
  117.      * Gets the base URL for Stripe's OAuth API.
  118.      *
  119.      * @return string the base URL for Stripe's OAuth API
  120.      */
  121.     public function getConnectBase()
  122.     {
  123.         return $this->config['connect_base'];
  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.      * Gets the base URL for Stripe's Meter Events API.
  136.      *
  137.      * @return string the base URL for Stripe's Meter Events API
  138.      */
  139.     public function getMeterEventsBase()
  140.     {
  141.         return $this->config['meter_events_base'];
  142.     }
  143.     /**
  144.      * Gets the configured number of retries.
  145.      *
  146.      * @return int the number of times this client will retry failed requests
  147.      */
  148.     public function getMaxNetworkRetries()
  149.     {
  150.         return $this->config['max_network_retries'];
  151.     }
  152.     /**
  153.      * Gets the app info for this client.
  154.      *
  155.      * @return null|array information to identify a plugin that integrates Stripe using this library
  156.      */
  157.     public function getAppInfo()
  158.     {
  159.         return $this->config['app_info'];
  160.     }
  161.     /**
  162.      * Sends a request to Stripe's API.
  163.      *
  164.      * @param 'delete'|'get'|'post' $method the HTTP method
  165.      * @param string $path the path of the request
  166.      * @param array $params the parameters of the request
  167.      * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
  168.      *
  169.      * @return StripeObject the object returned by Stripe's API
  170.      */
  171.     public function request($method$path$params$opts)
  172.     {
  173.         $defaultRequestOpts $this->defaultOpts;
  174.         $apiMode Util::getApiMode($path);
  175.         $opts $defaultRequestOpts->merge($optstrue);
  176.         $baseUrl $opts->apiBase ?: $this->getApiBase();
  177.         $requestor = new ApiRequestor($this->apiKeyForRequest($opts), $baseUrl$this->getAppInfo());
  178.         list($response$opts->apiKey) = $requestor->request($method$path$params$opts->headers$apiMode, ['stripe_client'], $opts->maxNetworkRetries);
  179.         $opts->discardNonPersistentHeaders();
  180.         $obj Util::convertToStripeObject($response->json$opts$apiMode);
  181.         if (\is_array($obj)) {
  182.             // Edge case for v2 endpoints that return empty/void response
  183.             // Example: client->v2->billing->meterEventStream->create
  184.             $obj = new StripeObject();
  185.         }
  186.         $obj->setLastResponse($response);
  187.         return $obj;
  188.     }
  189.     /**
  190.      * Sends a raw request to Stripe's API. This is the lowest level method for interacting
  191.      * with the Stripe API. This method is useful for interacting with endpoints that are not
  192.      * covered yet in stripe-php.
  193.      *
  194.      * @param 'delete'|'get'|'post' $method the HTTP method
  195.      * @param string $path the path of the request
  196.      * @param null|array $params the parameters of the request
  197.      * @param array $opts the special modifiers of the request
  198.      * @param null|int $maxNetworkRetries
  199.      *
  200.      * @return ApiResponse
  201.      */
  202.     public function rawRequest($method$path$params null$opts = [], $maxNetworkRetries null)
  203.     {
  204.         if ('post' !== $method && null !== $params) {
  205.             throw new Exception\InvalidArgumentException('Error: rawRequest only supports $params on post requests. Please pass null and add your parameters to $path');
  206.         }
  207.         $apiMode Util::getApiMode($path);
  208.         $headers = [];
  209.         if (\is_array($opts) && \array_key_exists('headers'$opts)) {
  210.             $headers $opts['headers'] ?: [];
  211.             unset($opts['headers']);
  212.         }
  213.         if (\is_array($opts) && \array_key_exists('stripe_context'$opts)) {
  214.             $headers['Stripe-Context'] = $opts['stripe_context'];
  215.             unset($opts['stripe_context']);
  216.         }
  217.         $defaultRawRequestOpts $this->defaultOpts;
  218.         $opts $defaultRawRequestOpts->merge($optstrue);
  219.         // Concatenate $headers to $opts->headers, removing duplicates.
  220.         $opts->headers \array_merge($opts->headers$headers);
  221.         $baseUrl $opts->apiBase ?: $this->getApiBase();
  222.         $requestor = new ApiRequestor($this->apiKeyForRequest($opts), $baseUrl);
  223.         list($response) = $requestor->request($method$path$params$opts->headers$apiMode, ['raw_request'], $maxNetworkRetries);
  224.         return $response;
  225.     }
  226.     /**
  227.      * Sends a request to Stripe's API, passing chunks of the streamed response
  228.      * into a user-provided $readBodyChunkCallable callback.
  229.      *
  230.      * @param 'delete'|'get'|'post' $method the HTTP method
  231.      * @param string $path the path of the request
  232.      * @param callable $readBodyChunkCallable a function that will be called
  233.      * @param array $params the parameters of the request
  234.      * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
  235.      *
  236.      * with chunks of bytes from the body if the request is successful
  237.      */
  238.     public function requestStream($method$path$readBodyChunkCallable$params$opts)
  239.     {
  240.         $opts $this->defaultOpts->merge($optstrue);
  241.         $baseUrl $opts->apiBase ?: $this->getApiBase();
  242.         $requestor = new ApiRequestor($this->apiKeyForRequest($opts), $baseUrl$this->getAppInfo());
  243.         $apiMode Util::getApiMode($path);
  244.         list($response$opts->apiKey) = $requestor->requestStream($method$path$readBodyChunkCallable$params$opts->headers$apiMode, ['stripe_client']);
  245.     }
  246.     /**
  247.      * Sends a request to Stripe's API.
  248.      *
  249.      * @param 'delete'|'get'|'post' $method the HTTP method
  250.      * @param string $path the path of the request
  251.      * @param array $params the parameters of the request
  252.      * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
  253.      *
  254.      * @return Collection|V2\Collection of ApiResources
  255.      */
  256.     public function requestCollection($method$path$params$opts)
  257.     {
  258.         $obj $this->request($method$path$params$opts);
  259.         $apiMode Util::getApiMode($path);
  260.         if ('v1' === $apiMode) {
  261.             if (!$obj instanceof Collection) {
  262.                 $received_class \get_class($obj);
  263.                 $msg "Expected to receive `Stripe\\Collection` object from Stripe API. Instead received `{$received_class}`.";
  264.                 throw new Exception\UnexpectedValueException($msg);
  265.             }
  266.             $obj->setFilters($params);
  267.         } else {
  268.             if (!$obj instanceof V2\Collection) {
  269.                 $received_class \get_class($obj);
  270.                 $msg "Expected to receive `Stripe\\V2\\Collection` object from Stripe API. Instead received `{$received_class}`.";
  271.                 throw new Exception\UnexpectedValueException($msg);
  272.             }
  273.         }
  274.         return $obj;
  275.     }
  276.     /**
  277.      * Sends a request to Stripe's API.
  278.      *
  279.      * @param 'delete'|'get'|'post' $method the HTTP method
  280.      * @param string $path the path of the request
  281.      * @param array $params the parameters of the request
  282.      * @param array|\Stripe\Util\RequestOptions $opts the special modifiers of the request
  283.      *
  284.      * @return SearchResult of ApiResources
  285.      */
  286.     public function requestSearchResult($method$path$params$opts)
  287.     {
  288.         $obj $this->request($method$path$params$opts);
  289.         if (!$obj instanceof SearchResult) {
  290.             $received_class \get_class($obj);
  291.             $msg "Expected to receive `Stripe\\SearchResult` object from Stripe API. Instead received `{$received_class}`.";
  292.             throw new Exception\UnexpectedValueException($msg);
  293.         }
  294.         $obj->setFilters($params);
  295.         return $obj;
  296.     }
  297.     /**
  298.      * @param \Stripe\Util\RequestOptions $opts
  299.      *
  300.      * @return string
  301.      *
  302.      * @throws Exception\AuthenticationException
  303.      */
  304.     private function apiKeyForRequest($opts)
  305.     {
  306.         $apiKey $opts->apiKey ?: $this->getApiKey();
  307.         if (null === $apiKey) {
  308.             $msg 'No API key provided. Set your API key when constructing the '
  309.                 'StripeClient instance, or provide it on a per-request basis '
  310.                 'using the `api_key` key in the $opts argument.';
  311.             throw new Exception\AuthenticationException($msg);
  312.         }
  313.         return $apiKey;
  314.     }
  315.     /**
  316.      * @param array<string, mixed> $config
  317.      *
  318.      * @throws Exception\InvalidArgumentException
  319.      */
  320.     private function validateConfig($config)
  321.     {
  322.         // api_key
  323.         if (null !== $config['api_key'] && !\is_string($config['api_key'])) {
  324.             throw new Exception\InvalidArgumentException('api_key must be null or a string');
  325.         }
  326.         if (null !== $config['api_key'] && ('' === $config['api_key'])) {
  327.             $msg 'api_key cannot be the empty string';
  328.             throw new Exception\InvalidArgumentException($msg);
  329.         }
  330.         if (null !== $config['api_key'] && \preg_match('/\s/'$config['api_key'])) {
  331.             $msg 'api_key cannot contain whitespace';
  332.             throw new Exception\InvalidArgumentException($msg);
  333.         }
  334.         // client_id
  335.         if (null !== $config['client_id'] && !\is_string($config['client_id'])) {
  336.             throw new Exception\InvalidArgumentException('client_id must be null or a string');
  337.         }
  338.         // stripe_account
  339.         if (null !== $config['stripe_account'] && !\is_string($config['stripe_account'])) {
  340.             throw new Exception\InvalidArgumentException('stripe_account must be null or a string');
  341.         }
  342.         // stripe_context
  343.         if (null !== $config['stripe_context'] && !\is_string($config['stripe_context'])) {
  344.             throw new Exception\InvalidArgumentException('stripe_context must be null or a string');
  345.         }
  346.         // stripe_version
  347.         if (null !== $config['stripe_version'] && !\is_string($config['stripe_version'])) {
  348.             throw new Exception\InvalidArgumentException('stripe_version must be null or a string');
  349.         }
  350.         // api_base
  351.         if (!\is_string($config['api_base'])) {
  352.             throw new Exception\InvalidArgumentException('api_base must be a string');
  353.         }
  354.         // connect_base
  355.         if (!\is_string($config['connect_base'])) {
  356.             throw new Exception\InvalidArgumentException('connect_base must be a string');
  357.         }
  358.         // files_base
  359.         if (!\is_string($config['files_base'])) {
  360.             throw new Exception\InvalidArgumentException('files_base must be a string');
  361.         }
  362.         // app info
  363.         if (null !== $config['app_info'] && !\is_array($config['app_info'])) {
  364.             throw new Exception\InvalidArgumentException('app_info must be an array');
  365.         }
  366.         // max_network_retries
  367.         if (!\is_int($config['max_network_retries'])) {
  368.             throw new Exception\InvalidArgumentException('max_network_retries must an int');
  369.         }
  370.         $appInfoKeys = ['name''version''url''partner_id'];
  371.         if (null !== $config['app_info'] && array_diff_key($config['app_info'], array_flip($appInfoKeys))) {
  372.             $msg 'app_info must be of type array{name: string, version?: string, url?: string, partner_id?: string}';
  373.             throw new Exception\InvalidArgumentException($msg);
  374.         }
  375.         // check absence of extra keys
  376.         $extraConfigKeys \array_diff(\array_keys($config), \array_keys(self::DEFAULT_CONFIG));
  377.         if (!empty($extraConfigKeys)) {
  378.             // Wrap in single quote to more easily catch trailing spaces errors
  379.             $invalidKeys "'" \implode("', '"$extraConfigKeys) . "'";
  380.             throw new Exception\InvalidArgumentException('Found unknown key(s) in configuration array: ' $invalidKeys);
  381.         }
  382.     }
  383.     /**
  384.      * Deserializes the raw JSON string returned by rawRequest into a similar class.
  385.      *
  386.      * @param string $json
  387.      * @param 'v1'|'v2' $apiMode
  388.      *
  389.      * @return StripeObject
  390.      * */
  391.     public function deserialize($json$apiMode 'v1')
  392.     {
  393.         return Util::convertToStripeObject(\json_decode($jsontrue), [], $apiMode);
  394.     }
  395.     /**
  396.      * Returns a V2\Events instance using the provided JSON payload. Throws an
  397.      * Exception\UnexpectedValueException if the payload is not valid JSON, and
  398.      * an Exception\SignatureVerificationException if the signature
  399.      * verification fails for any reason.
  400.      *
  401.      * @param string $payload the payload sent by Stripe
  402.      * @param string $sigHeader the contents of the signature header sent by
  403.      *  Stripe
  404.      * @param string $secret secret used to generate the signature
  405.      * @param int $tolerance maximum difference allowed between the header's
  406.      *  timestamp and the current time. Defaults to 300 seconds (5 min)
  407.      *
  408.      * @return ThinEvent
  409.      *
  410.      * @throws Exception\SignatureVerificationException if the verification fails
  411.      * @throws Exception\UnexpectedValueException if the payload is not valid JSON,
  412.      */
  413.     public function parseThinEvent($payload$sigHeader$secret$tolerance Webhook::DEFAULT_TOLERANCE)
  414.     {
  415.         $eventData Util::utf8($payload);
  416.         WebhookSignature::verifyHeader($payload$sigHeader$secret$tolerance);
  417.         try {
  418.             return Util::json_decode_thin_event_object(
  419.                 $eventData,
  420.                 '\Stripe\ThinEvent'
  421.             );
  422.         } catch (\ReflectionException $e) {
  423.             // Fail gracefully
  424.             return new ThinEvent();
  425.         }
  426.     }
  427. }