From dacbcab7ef3fff11a16215eca2d06afba66422c5 Mon Sep 17 00:00:00 2001 From: Christian Dawson Date: Thu, 4 Aug 2016 11:12:49 -0500 Subject: [PATCH 1/5] Initial Stripe integration --- libs/StripePHP/Account.php | 131 ++++++++ libs/StripePHP/AlipayAccount.php | 13 + libs/StripePHP/ApiRequestor.php | 244 ++++++++++++++ libs/StripePHP/ApiResource.php | 205 ++++++++++++ libs/StripePHP/ApiResponse.php | 32 ++ libs/StripePHP/ApplicationFee.php | 69 ++++ libs/StripePHP/ApplicationFeeRefund.php | 44 +++ libs/StripePHP/AttachedObject.php | 31 ++ libs/StripePHP/Balance.php | 26 ++ libs/StripePHP/BalanceTransaction.php | 57 ++++ libs/StripePHP/BankAccount.php | 25 ++ libs/StripePHP/BitcoinReceiver.php | 85 +++++ libs/StripePHP/BitcoinTransaction.php | 13 + libs/StripePHP/Card.php | 13 + libs/StripePHP/Charge.php | 184 +++++++++++ libs/StripePHP/Collection.php | 87 +++++ libs/StripePHP/CountrySpec.php | 44 +++ libs/StripePHP/Coupon.php | 77 +++++ libs/StripePHP/Customer.php | 190 +++++++++++ libs/StripePHP/Dispute.php | 83 +++++ libs/StripePHP/Error/Api.php | 7 + libs/StripePHP/Error/ApiConnection.php | 7 + libs/StripePHP/Error/Authentication.php | 7 + libs/StripePHP/Error/Base.php | 60 ++++ libs/StripePHP/Error/Card.php | 30 ++ libs/StripePHP/Error/InvalidRequest.php | 23 ++ libs/StripePHP/Error/RateLimit.php | 7 + libs/StripePHP/Event.php | 43 +++ libs/StripePHP/ExternalAccount.php | 89 ++++++ libs/StripePHP/FileUpload.php | 61 ++++ libs/StripePHP/HttpClient/ClientInterface.php | 18 ++ libs/StripePHP/HttpClient/CurlClient.php | 298 ++++++++++++++++++ libs/StripePHP/Invoice.php | 92 ++++++ libs/StripePHP/InvoiceItem.php | 77 +++++ libs/StripePHP/JsonSerializable.php | 18 ++ libs/StripePHP/Order.php | 87 +++++ libs/StripePHP/OrderReturn.php | 33 ++ libs/StripePHP/Plan.php | 77 +++++ libs/StripePHP/Product.php | 77 +++++ libs/StripePHP/Recipient.php | 92 ++++++ libs/StripePHP/Refund.php | 79 +++++ libs/StripePHP/SKU.php | 77 +++++ libs/StripePHP/SingletonApiResource.php | 36 +++ libs/StripePHP/Stripe.php | 100 ++++++ libs/StripePHP/StripeObject.php | 293 +++++++++++++++++ libs/StripePHP/Subscription.php | 97 ++++++ libs/StripePHP/ThreeDSecure.php | 25 ++ libs/StripePHP/Token.php | 43 +++ libs/StripePHP/Transfer.php | 115 +++++++ libs/StripePHP/TransferReversal.php | 53 ++++ libs/StripePHP/Util/AutoPagingIterator.php | 61 ++++ libs/StripePHP/Util/RequestOptions.php | 79 +++++ libs/StripePHP/Util/Set.php | 44 +++ libs/StripePHP/Util/Util.php | 140 ++++++++ libs/verysimple/Payment/Stripe.php | 131 ++++++++ libs/verysimple/Phreeze/Criteria.php | 2 +- 56 files changed, 4230 insertions(+), 1 deletion(-) create mode 100644 libs/StripePHP/Account.php create mode 100644 libs/StripePHP/AlipayAccount.php create mode 100644 libs/StripePHP/ApiRequestor.php create mode 100644 libs/StripePHP/ApiResource.php create mode 100644 libs/StripePHP/ApiResponse.php create mode 100644 libs/StripePHP/ApplicationFee.php create mode 100644 libs/StripePHP/ApplicationFeeRefund.php create mode 100644 libs/StripePHP/AttachedObject.php create mode 100644 libs/StripePHP/Balance.php create mode 100644 libs/StripePHP/BalanceTransaction.php create mode 100644 libs/StripePHP/BankAccount.php create mode 100644 libs/StripePHP/BitcoinReceiver.php create mode 100644 libs/StripePHP/BitcoinTransaction.php create mode 100644 libs/StripePHP/Card.php create mode 100644 libs/StripePHP/Charge.php create mode 100644 libs/StripePHP/Collection.php create mode 100644 libs/StripePHP/CountrySpec.php create mode 100644 libs/StripePHP/Coupon.php create mode 100644 libs/StripePHP/Customer.php create mode 100644 libs/StripePHP/Dispute.php create mode 100644 libs/StripePHP/Error/Api.php create mode 100644 libs/StripePHP/Error/ApiConnection.php create mode 100644 libs/StripePHP/Error/Authentication.php create mode 100644 libs/StripePHP/Error/Base.php create mode 100644 libs/StripePHP/Error/Card.php create mode 100644 libs/StripePHP/Error/InvalidRequest.php create mode 100644 libs/StripePHP/Error/RateLimit.php create mode 100644 libs/StripePHP/Event.php create mode 100644 libs/StripePHP/ExternalAccount.php create mode 100644 libs/StripePHP/FileUpload.php create mode 100644 libs/StripePHP/HttpClient/ClientInterface.php create mode 100644 libs/StripePHP/HttpClient/CurlClient.php create mode 100644 libs/StripePHP/Invoice.php create mode 100644 libs/StripePHP/InvoiceItem.php create mode 100644 libs/StripePHP/JsonSerializable.php create mode 100644 libs/StripePHP/Order.php create mode 100644 libs/StripePHP/OrderReturn.php create mode 100644 libs/StripePHP/Plan.php create mode 100644 libs/StripePHP/Product.php create mode 100644 libs/StripePHP/Recipient.php create mode 100644 libs/StripePHP/Refund.php create mode 100644 libs/StripePHP/SKU.php create mode 100644 libs/StripePHP/SingletonApiResource.php create mode 100644 libs/StripePHP/Stripe.php create mode 100644 libs/StripePHP/StripeObject.php create mode 100644 libs/StripePHP/Subscription.php create mode 100644 libs/StripePHP/ThreeDSecure.php create mode 100644 libs/StripePHP/Token.php create mode 100644 libs/StripePHP/Transfer.php create mode 100644 libs/StripePHP/TransferReversal.php create mode 100644 libs/StripePHP/Util/AutoPagingIterator.php create mode 100644 libs/StripePHP/Util/RequestOptions.php create mode 100644 libs/StripePHP/Util/Set.php create mode 100644 libs/StripePHP/Util/Util.php create mode 100644 libs/verysimple/Payment/Stripe.php diff --git a/libs/StripePHP/Account.php b/libs/StripePHP/Account.php new file mode 100644 index 00000000..e46f42d6 --- /dev/null +++ b/libs/StripePHP/Account.php @@ -0,0 +1,131 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Account The deleted account. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Account The rejected account. + */ + public function reject($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/reject'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Accounts + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/StripePHP/AlipayAccount.php b/libs/StripePHP/AlipayAccount.php new file mode 100644 index 00000000..1ba34bfa --- /dev/null +++ b/libs/StripePHP/AlipayAccount.php @@ -0,0 +1,13 @@ +_apiKey = $apiKey; + if (!$apiBase) { + $apiBase = Stripe::$apiBase; + } + $this->_apiBase = $apiBase; + } + + private static function _encodeObjects($d) + { + if ($d instanceof ApiResource) { + return Util\Util::utf8($d->id); + } elseif ($d === true) { + return 'true'; + } elseif ($d === false) { + return 'false'; + } elseif (is_array($d)) { + $res = array(); + foreach ($d as $k => $v) { + $res[$k] = self::_encodeObjects($v); + } + return $res; + } else { + return Util\Util::utf8($d); + } + } + + /** + * @param string $method + * @param string $url + * @param array|null $params + * @param array|null $headers + * + * @return array An array whose first element is an API response and second + * element is the API key used to make the request. + */ + public function request($method, $url, $params = null, $headers = null) + { + if (!$params) { + $params = array(); + } + if (!$headers) { + $headers = array(); + } + list($rbody, $rcode, $rheaders, $myApiKey) = + $this->_requestRaw($method, $url, $params, $headers); + $json = $this->_interpretResponse($rbody, $rcode, $rheaders); + $resp = new ApiResponse($rbody, $rcode, $rheaders, $json); + return array($resp, $myApiKey); + } + + /** + * @param string $rbody A JSON string. + * @param int $rcode + * @param array $rheaders + * @param array $resp + * + * @throws Error\InvalidRequest if the error is caused by the user. + * @throws Error\Authentication if the error is caused by a lack of + * permissions. + * @throws Error\Card if the error is the error code is 402 (payment + * required) + * @throws Error\RateLimit if the error is caused by too many requests + * hitting the API. + * @throws Error\Api otherwise. + */ + public function handleApiError($rbody, $rcode, $rheaders, $resp) + { + if (!is_array($resp) || !isset($resp['error'])) { + $msg = "Invalid response object from API: $rbody " + . "(HTTP response code was $rcode)"; + throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); + } + + $error = $resp['error']; + $msg = isset($error['message']) ? $error['message'] : null; + $param = isset($error['param']) ? $error['param'] : null; + $code = isset($error['code']) ? $error['code'] : null; + + switch ($rcode) { + case 400: + // 'rate_limit' code is deprecated, but left here for backwards compatibility + // for API versions earlier than 2015-09-08 + if ($code == 'rate_limit') { + throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + } + + // intentional fall-through + case 404: + throw new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders); + case 401: + throw new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders); + case 402: + throw new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders); + case 429: + throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + default: + throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); + } + } + + private function _requestRaw($method, $url, $params, $headers) + { + $myApiKey = $this->_apiKey; + if (!$myApiKey) { + $myApiKey = Stripe::$apiKey; + } + + if (!$myApiKey) { + $msg = 'No API key provided. (HINT: set your API key using ' + . '"Stripe::setApiKey()". You can generate API keys from ' + . 'the Stripe web interface. See https://stripe.com/api for ' + . 'details, or email support@stripe.com if you have any questions.'; + throw new Error\Authentication($msg); + } + + $absUrl = $this->_apiBase.$url; + $params = self::_encodeObjects($params); + $langVersion = phpversion(); + $uname = php_uname(); + $ua = array( + 'bindings_version' => Stripe::VERSION, + 'lang' => 'php', + 'lang_version' => $langVersion, + 'publisher' => 'stripe', + 'uname' => $uname, + ); + $defaultHeaders = array( + 'X-Stripe-Client-User-Agent' => json_encode($ua), + 'User-Agent' => 'Stripe/v1 PhpBindings/' . Stripe::VERSION, + 'Authorization' => 'Bearer ' . $myApiKey, + ); + if (Stripe::$apiVersion) { + $defaultHeaders['Stripe-Version'] = Stripe::$apiVersion; + } + + if (Stripe::$accountId) { + $defaultHeaders['Stripe-Account'] = Stripe::$accountId; + } + + $hasFile = false; + $hasCurlFile = class_exists('\CURLFile', false); + foreach ($params as $k => $v) { + if (is_resource($v)) { + $hasFile = true; + $params[$k] = self::_processResourceParam($v, $hasCurlFile); + } elseif ($hasCurlFile && $v instanceof \CURLFile) { + $hasFile = true; + } + } + + if ($hasFile) { + $defaultHeaders['Content-Type'] = 'multipart/form-data'; + } else { + $defaultHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + $combinedHeaders = array_merge($defaultHeaders, $headers); + $rawHeaders = array(); + + foreach ($combinedHeaders as $header => $value) { + $rawHeaders[] = $header . ': ' . $value; + } + + list($rbody, $rcode, $rheaders) = $this->httpClient()->request( + $method, + $absUrl, + $rawHeaders, + $params, + $hasFile + ); + return array($rbody, $rcode, $rheaders, $myApiKey); + } + + private function _processResourceParam($resource, $hasCurlFile) + { + if (get_resource_type($resource) !== 'stream') { + throw new Error\Api( + 'Attempted to upload a resource that is not a stream' + ); + } + + $metaData = stream_get_meta_data($resource); + if ($metaData['wrapper_type'] !== 'plainfile') { + throw new Error\Api( + 'Only plainfile resource streams are supported' + ); + } + + if ($hasCurlFile) { + // We don't have the filename or mimetype, but the API doesn't care + return new \CURLFile($metaData['uri']); + } else { + return '@'.$metaData['uri']; + } + } + + private function _interpretResponse($rbody, $rcode, $rheaders) + { + try { + $resp = json_decode($rbody, true); + } catch (Exception $e) { + $msg = "Invalid response body from API: $rbody " + . "(HTTP response code was $rcode)"; + throw new Error\Api($msg, $rcode, $rbody); + } + + if ($rcode < 200 || $rcode >= 300) { + $this->handleApiError($rbody, $rcode, $rheaders, $resp); + } + return $resp; + } + + public static function setHttpClient($client) + { + self::$_httpClient = $client; + } + + private function httpClient() + { + if (!self::$_httpClient) { + self::$_httpClient = HttpClient\CurlClient::instance(); + } + return self::$_httpClient; + } +} diff --git a/libs/StripePHP/ApiResource.php b/libs/StripePHP/ApiResource.php new file mode 100644 index 00000000..f10b948f --- /dev/null +++ b/libs/StripePHP/ApiResource.php @@ -0,0 +1,205 @@ + true, 'Stripe-Version' => true); + + public static function baseUrl() + { + return Stripe::$apiBase; + } + + /** + * @return ApiResource The refreshed resource. + */ + public function refresh() + { + $requestor = new ApiRequestor($this->_opts->apiKey, static::baseUrl()); + $url = $this->instanceUrl(); + + list($response, $this->_opts->apiKey) = $requestor->request( + 'get', + $url, + $this->_retrieveOptions, + $this->_opts->headers + ); + $this->setLastResponse($response); + $this->refreshFrom($response->json, $this->_opts); + return $this; + } + + /** + * @return string The name of the class, with namespacing and underscores + * stripped. + */ + public static function className() + { + $class = get_called_class(); + // Useful for namespaces: Foo\Charge + if ($postfixNamespaces = strrchr($class, '\\')) { + $class = substr($postfixNamespaces, 1); + } + // Useful for underscored 'namespaces': Foo_Charge + if ($postfixFakeNamespaces = strrchr($class, '')) { + $class = $postfixFakeNamespaces; + } + if (substr($class, 0, strlen('Stripe')) == 'Stripe') { + $class = substr($class, strlen('Stripe')); + } + $class = str_replace('_', '', $class); + $name = urlencode($class); + $name = strtolower($name); + return $name; + } + + /** + * @return string The endpoint URL for the given class. + */ + public static function classUrl() + { + $base = static::className(); + return "/v1/${base}s"; + } + + /** + * @return string The instance endpoint URL for the given class. + */ + public static function resourceUrl($id) + { + if ($id === null) { + $class = get_called_class(); + $message = "Could not determine which URL to request: " + . "$class instance has invalid ID: $id"; + throw new Error\InvalidRequest($message, null); + } + $id = Util\Util::utf8($id); + $base = static::classUrl(); + $extn = urlencode($id); + return "$base/$extn"; + } + + /** + * @return string The full API URL for this API resource. + */ + public function instanceUrl() + { + return static::resourceUrl($this['id']); + } + + private static function _validateParams($params = null) + { + if ($params && !is_array($params)) { + $message = "You must pass an array as the first argument to Stripe API " + . "method calls. (HINT: an example call to create a charge " + . "would be: \"Stripe\\Charge::create(array('amount' => 100, " + . "'currency' => 'usd', 'card' => array('number' => " + . "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")"; + throw new Error\Api($message); + } + } + + protected function _request($method, $url, $params = array(), $options = null) + { + $opts = $this->_opts->merge($options); + list($resp, $options) = static::_staticRequest($method, $url, $params, $opts); + $this->setLastResponse($resp); + return array($resp->json, $options); + } + + protected static function _staticRequest($method, $url, $params, $options) + { + $opts = Util\RequestOptions::parse($options); + $requestor = new ApiRequestor($opts->apiKey, static::baseUrl()); + list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers); + foreach ($opts->headers as $k => $v) { + if (!array_key_exists($k, self::$HEADERS_TO_PERSIST)) { + unset($opts->headers[$k]); + } + } + return array($response, $opts); + } + + protected static function _retrieve($id, $options = null) + { + $opts = Util\RequestOptions::parse($options); + $instance = new static($id, $opts); + $instance->refresh(); + return $instance; + } + + protected static function _all($params = null, $options = null) + { + self::_validateParams($params); + $url = static::classUrl(); + + list($response, $opts) = static::_staticRequest('get', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + if (!is_a($obj, 'Stripe\\Collection')) { + $class = get_class($obj); + $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead"; + throw new Error\Api($message); + } + $obj->setLastResponse($response); + $obj->setRequestParams($params); + return $obj; + } + + protected static function _create($params = null, $options = null) + { + self::_validateParams($params); + $base = static::baseUrl(); + $url = static::classUrl(); + + list($response, $opts) = static::_staticRequest('post', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param string $id The ID of the API resource to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource the updated API resource + */ + protected static function _update($id, $params = null, $options = null) + { + self::_validateParams($params); + $base = static::baseUrl(); + $url = static::resourceUrl($id); + + list($response, $opts) = static::_staticRequest('post', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + protected function _save($options = null) + { + $params = $this->serializeParameters(); + if (count($params) > 0) { + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + } + return $this; + } + + protected function _delete($params = null, $options = null) + { + self::_validateParams($params); + + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('delete', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/ApiResponse.php b/libs/StripePHP/ApiResponse.php new file mode 100644 index 00000000..31f54a50 --- /dev/null +++ b/libs/StripePHP/ApiResponse.php @@ -0,0 +1,32 @@ +body = $body; + $this->code = $code; + $this->headers = $headers; + $this->json = $json; + } +} diff --git a/libs/StripePHP/ApplicationFee.php b/libs/StripePHP/ApplicationFee.php new file mode 100644 index 00000000..8145541c --- /dev/null +++ b/libs/StripePHP/ApplicationFee.php @@ -0,0 +1,69 @@ +refunds->create($params, $opts); + $this->refresh(); + return $this; + } +} diff --git a/libs/StripePHP/ApplicationFeeRefund.php b/libs/StripePHP/ApplicationFeeRefund.php new file mode 100644 index 00000000..44695ef8 --- /dev/null +++ b/libs/StripePHP/ApplicationFeeRefund.php @@ -0,0 +1,44 @@ +_save($opts); + } +} diff --git a/libs/StripePHP/AttachedObject.php b/libs/StripePHP/AttachedObject.php new file mode 100644 index 00000000..489517d6 --- /dev/null +++ b/libs/StripePHP/AttachedObject.php @@ -0,0 +1,31 @@ +_values), array_keys($properties)); + // Don't unset, but rather set to null so we send up '' for deletion. + foreach ($removed as $k) { + $this->$k = null; + } + + foreach ($properties as $k => $v) { + $this->$k = $v; + } + } +} diff --git a/libs/StripePHP/Balance.php b/libs/StripePHP/Balance.php new file mode 100644 index 00000000..56c26818 --- /dev/null +++ b/libs/StripePHP/Balance.php @@ -0,0 +1,26 @@ +instanceUrl() . '/verify'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/BitcoinReceiver.php b/libs/StripePHP/BitcoinReceiver.php new file mode 100644 index 00000000..3f2c835b --- /dev/null +++ b/libs/StripePHP/BitcoinReceiver.php @@ -0,0 +1,85 @@ +instanceUrl() . '/refund'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/BitcoinTransaction.php b/libs/StripePHP/BitcoinTransaction.php new file mode 100644 index 00000000..6b8e5421 --- /dev/null +++ b/libs/StripePHP/BitcoinTransaction.php @@ -0,0 +1,13 @@ +_save($options); + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Charge The refunded charge. + */ + public function refund($params = null, $options = null) + { + $url = $this->instanceUrl() . '/refund'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Charge The captured charge. + */ + public function capture($params = null, $options = null) + { + $url = $this->instanceUrl() . '/capture'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @deprecated Use the `save` method on the Dispute object + * + * @return array The updated dispute. + */ + public function updateDispute($params = null, $options = null) + { + $url = $this->instanceUrl() . '/dispute'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom(array('dispute' => $response), $opts, true); + return $this->dispute; + } + + /** + * @param array|string|null $options + * + * @deprecated Use the `close` method on the Dispute object + * + * @return Charge The updated charge. + */ + public function closeDispute($options = null) + { + $url = $this->instanceUrl() . '/dispute/close'; + list($response, $opts) = $this->_request('post', $url, null, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Charge The updated charge. + */ + public function markAsFraudulent($opts = null) + { + $params = array('fraud_details' => array('user_report' => 'fraudulent')); + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Charge The updated charge. + */ + public function markAsSafe($opts = null) + { + $params = array('fraud_details' => array('user_report' => 'safe')); + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/Collection.php b/libs/StripePHP/Collection.php new file mode 100644 index 00000000..61b01e0a --- /dev/null +++ b/libs/StripePHP/Collection.php @@ -0,0 +1,87 @@ +_requestParams = $params; + } + + public function all($params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + list($response, $opts) = $this->_request('get', $url, $params, $opts); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + public function create($params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + public function retrieve($id, $params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + $id = Util\Util::utf8($id); + $extn = urlencode($id); + list($response, $opts) = $this->_request( + 'get', + "$url/$extn", + $params, + $opts + ); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + /** + * @return AutoPagingIterator An iterator that can be used to iterate + * across all objects across all pages. As page boundaries are + * encountered, the next page will be fetched automatically for + * continued iteration. + */ + public function autoPagingIterator() + { + return new Util\AutoPagingIterator($this, $this->_requestParams); + } + + private function extractPathAndUpdateParams($params) + { + $url = parse_url($this->url); + if (!isset($url['path'])) { + throw new Error\Api("Could not parse list url into parts: $url"); + } + + if (isset($url['query'])) { + // If the URL contains a query param, parse it out into $params so they + // don't interact weirdly with each other. + $query = array(); + parse_str($url['query'], $query); + // PHP 5.2 doesn't support the ?: operator :( + $params = array_merge($params ? $params : array(), $query); + } + + return array($url['path'], $params); + } +} diff --git a/libs/StripePHP/CountrySpec.php b/libs/StripePHP/CountrySpec.php new file mode 100644 index 00000000..dabb88b4 --- /dev/null +++ b/libs/StripePHP/CountrySpec.php @@ -0,0 +1,44 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Coupon The saved coupon. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Coupons + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/StripePHP/Customer.php b/libs/StripePHP/Customer.php new file mode 100644 index 00000000..1dfbfc6a --- /dev/null +++ b/libs/StripePHP/Customer.php @@ -0,0 +1,190 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Customer The deleted customer. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + /** + * @param array|null $params + * + * @return InvoiceItem The resulting invoice item. + */ + public function addInvoiceItem($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $ii = InvoiceItem::create($params, $this->_opts); + return $ii; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's Invoices. + */ + public function invoices($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $invoices = Invoice::all($params, $this->_opts); + return $invoices; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's InvoiceItems. + */ + public function invoiceItems($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $iis = InvoiceItem::all($params, $this->_opts); + return $iis; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's Charges. + */ + public function charges($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $charges = Charge::all($params, $this->_opts); + return $charges; + } + + /** + * @param array|null $params + * + * @return Subscription The updated subscription. + */ + public function updateSubscription($params = null) + { + $url = $this->instanceUrl() . '/subscription'; + list($response, $opts) = $this->_request('post', $url, $params); + $this->refreshFrom(array('subscription' => $response), $opts, true); + return $this->subscription; + } + + /** + * @param array|null $params + * + * @return Subscription The cancelled subscription. + */ + public function cancelSubscription($params = null) + { + $url = $this->instanceUrl() . '/subscription'; + list($response, $opts) = $this->_request('delete', $url, $params); + $this->refreshFrom(array('subscription' => $response), $opts, true); + return $this->subscription; + } + + /** + * @return Customer The updated customer. + */ + public function deleteDiscount() + { + $url = $this->instanceUrl() . '/discount'; + list($response, $opts) = $this->_request('delete', $url); + $this->refreshFrom(array('discount' => null), $opts, true); + } +} diff --git a/libs/StripePHP/Dispute.php b/libs/StripePHP/Dispute.php new file mode 100644 index 00000000..ce70d499 --- /dev/null +++ b/libs/StripePHP/Dispute.php @@ -0,0 +1,83 @@ +_save($options); + } + + /** + * @param array|string|null $options + * + * @return Dispute The closed dispute. + */ + public function close($options = null) + { + $url = $this->instanceUrl() . '/close'; + list($response, $opts) = $this->_request('post', $url, null, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/Error/Api.php b/libs/StripePHP/Error/Api.php new file mode 100644 index 00000000..dc7d069e --- /dev/null +++ b/libs/StripePHP/Error/Api.php @@ -0,0 +1,7 @@ +httpStatus = $httpStatus; + $this->httpBody = $httpBody; + $this->jsonBody = $jsonBody; + $this->httpHeaders = $httpHeaders; + $this->requestId = null; + + if ($httpHeaders && isset($httpHeaders['Request-Id'])) { + $this->requestId = $httpHeaders['Request-Id']; + } + } + + public function getHttpStatus() + { + return $this->httpStatus; + } + + public function getHttpBody() + { + return $this->httpBody; + } + + public function getJsonBody() + { + return $this->jsonBody; + } + + public function getHttpHeaders() + { + return $this->httpHeaders; + } + + public function getRequestId() + { + return $this->requestId; + } + + public function __toString() + { + $id = $this->requestId ? " from API request '{$this->requestId}'": ""; + $message = explode("\n", parent::__toString()); + $message[0] .= $id; + return implode("\n", $message); + } +} diff --git a/libs/StripePHP/Error/Card.php b/libs/StripePHP/Error/Card.php new file mode 100644 index 00000000..15551551 --- /dev/null +++ b/libs/StripePHP/Error/Card.php @@ -0,0 +1,30 @@ +stripeParam = $stripeParam; + $this->stripeCode = $stripeCode; + } + + public function getStripeCode() + { + return $this->stripeCode; + } + + public function getStripeParam() + { + return $this->stripeParam; + } +} diff --git a/libs/StripePHP/Error/InvalidRequest.php b/libs/StripePHP/Error/InvalidRequest.php new file mode 100644 index 00000000..493bc184 --- /dev/null +++ b/libs/StripePHP/Error/InvalidRequest.php @@ -0,0 +1,23 @@ +stripeParam = $stripeParam; + } + + public function getStripeParam() + { + return $this->stripeParam; + } +} diff --git a/libs/StripePHP/Error/RateLimit.php b/libs/StripePHP/Error/RateLimit.php new file mode 100644 index 00000000..2fa3be4e --- /dev/null +++ b/libs/StripePHP/Error/RateLimit.php @@ -0,0 +1,7 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return ExternalAccount The saved external account. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return ExternalAccount The verified (or not) external account. + */ + public function verify($params = null, $opts = null) + { + if ($this['customer']) { + $url = $this->instanceUrl() . '/verify'; + list($response, $options) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $options); + return $this; + } else { + $message = 'Only customer external accounts can be verified in this manner.'; + throw new Error\Api($message); + } + } +} diff --git a/libs/StripePHP/FileUpload.php b/libs/StripePHP/FileUpload.php new file mode 100644 index 00000000..7dc98c78 --- /dev/null +++ b/libs/StripePHP/FileUpload.php @@ -0,0 +1,61 @@ +defaultOptions = $defaultOptions; + } + + public function getDefaultOptions() + { + return $this->defaultOptions; + } + + // USER DEFINED TIMEOUTS + + const DEFAULT_TIMEOUT = 80; + const DEFAULT_CONNECT_TIMEOUT = 30; + + private $timeout = self::DEFAULT_TIMEOUT; + private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT; + + public function setTimeout($seconds) + { + $this->timeout = (int) max($seconds, 0); + return $this; + } + + public function setConnectTimeout($seconds) + { + $this->connectTimeout = (int) max($seconds, 0); + return $this; + } + + public function getTimeout() + { + return $this->timeout; + } + + public function getConnectTimeout() + { + return $this->connectTimeout; + } + + // END OF USER DEFINED TIMEOUTS + + public function request($method, $absUrl, $headers, $params, $hasFile) + { + $curl = curl_init(); + $method = strtolower($method); + + $opts = array(); + if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value + $opts = call_user_func_array($this->defaultOptions, func_get_args()); + if (!is_array($opts)) { + throw new Error\Api("Non-array value returned by defaultOptions CurlClient callback"); + } + } elseif (is_array($this->defaultOptions)) { // set default curlopts from array + $opts = $this->defaultOptions; + } + + if ($method == 'get') { + if ($hasFile) { + throw new Error\Api( + "Issuing a GET request with a file parameter" + ); + } + $opts[CURLOPT_HTTPGET] = 1; + if (count($params) > 0) { + $encoded = self::encode($params); + $absUrl = "$absUrl?$encoded"; + } + } elseif ($method == 'post') { + $opts[CURLOPT_POST] = 1; + $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params); + } elseif ($method == 'delete') { + $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; + if (count($params) > 0) { + $encoded = self::encode($params); + $absUrl = "$absUrl?$encoded"; + } + } else { + throw new Error\Api("Unrecognized method $method"); + } + + // Create a callback to capture HTTP headers for the response + $rheaders = array(); + $headerCallback = function ($curl, $header_line) use (&$rheaders) { + // Ignore the HTTP request line (HTTP/1.1 200 OK) + if (strpos($header_line, ":") === false) { + return strlen($header_line); + } + list($key, $value) = explode(":", trim($header_line), 2); + $rheaders[trim($key)] = trim($value); + return strlen($header_line); + }; + + // By default for large request body sizes (> 1024 bytes), cURL will + // send a request without a body and with a `Expect: 100-continue` + // header, which gives the server a chance to respond with an error + // status code in cases where one can be determined right away (say + // on an authentication problem for example), and saves the "large" + // request body from being ever sent. + // + // Unfortunately, the bindings don't currently correctly handle the + // success case (in which the server sends back a 100 CONTINUE), so + // we'll error under that condition. To compensate for that problem + // for the time being, override cURL's behavior by simply always + // sending an empty `Expect:` header. + array_push($headers, 'Expect: '); + + $absUrl = Util\Util::utf8($absUrl); + $opts[CURLOPT_URL] = $absUrl; + $opts[CURLOPT_RETURNTRANSFER] = true; + $opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout; + $opts[CURLOPT_TIMEOUT] = $this->timeout; + $opts[CURLOPT_HEADERFUNCTION] = $headerCallback; + $opts[CURLOPT_HTTPHEADER] = $headers; + if (!Stripe::$verifySslCerts) { + $opts[CURLOPT_SSL_VERIFYPEER] = false; + } + + // @codingStandardsIgnoreStart + // PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION + // constants to not abide by those rules. + // + // Explicitly set a TLS version for cURL to use now that we're starting + // to block 1.0 and 1.1 requests. + // + // If users are on OpenSSL >= 1.0.1, we know that they support TLS 1.2, + // so set that explicitly because on some older Linux distros, clients may + // default to TLS 1.0 even when they have TLS 1.2 available. + // + // For users on much older versions of OpenSSL, set a valid range of + // TLS 1.0 to 1.2 (CURL_SSLVERSION_TLSv1). Note that this may result in + // their requests being blocked unless they're specially flagged into + // being able to use an old TLS version. + // + // Note: The int on the right is pulled from the source of OpenSSL 1.0.1a. + if (OPENSSL_VERSION_NUMBER >= 0x1000100f) { + if (!defined('CURL_SSLVERSION_TLSv1_2')) { + // Note the value 6 comes from its position in the enum that + // defines it in cURL's source code. + define('CURL_SSLVERSION_TLSv1_2', 6); // constant not defined in PHP < 5.5 + } + $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2; + } else { + if (!defined('CURL_SSLVERSION_TLSv1')) { + define('CURL_SSLVERSION_TLSv1', 1); // constant not defined in PHP < 5.5 + } + $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1; + } + // @codingStandardsIgnoreEnd + + curl_setopt_array($curl, $opts); + $rbody = curl_exec($curl); + + if (!defined('CURLE_SSL_CACERT_BADFILE')) { + define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP + } + + $errno = curl_errno($curl); + if ($errno == CURLE_SSL_CACERT || + $errno == CURLE_SSL_PEER_CERTIFICATE || + $errno == CURLE_SSL_CACERT_BADFILE + ) { + array_push( + $headers, + 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}' + ); + $cert = self::caBundle(); + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_CAINFO, $cert); + $rbody = curl_exec($curl); + } + + if ($rbody === false) { + $errno = curl_errno($curl); + $message = curl_error($curl); + curl_close($curl); + $this->handleCurlError($absUrl, $errno, $message); + } + + $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + curl_close($curl); + return array($rbody, $rcode, $rheaders); + } + + /** + * @param number $errno + * @param string $message + * @throws Error\ApiConnection + */ + private function handleCurlError($url, $errno, $message) + { + switch ($errno) { + case CURLE_COULDNT_CONNECT: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_OPERATION_TIMEOUTED: + $msg = "Could not connect to Stripe ($url). Please check your " + . "internet connection and try again. If this problem persists, " + . "you should check Stripe's service status at " + . "https://twitter.com/stripestatus, or"; + break; + case CURLE_SSL_CACERT: + case CURLE_SSL_PEER_CERTIFICATE: + $msg = "Could not verify Stripe's SSL certificate. Please make sure " + . "that your network is not intercepting certificates. " + . "(Try going to $url in your browser.) " + . "If this problem persists,"; + break; + default: + $msg = "Unexpected error communicating with Stripe. " + . "If this problem persists,"; + } + $msg .= " let us know at support@stripe.com."; + + $msg .= "\n\n(Network error [errno $errno]: $message)"; + throw new Error\ApiConnection($msg); + } + + private static function caBundle() + { + return dirname(__FILE__) . '/../../data/ca-certificates.crt'; + } + + /** + * @param array $arr An map of param keys to values. + * @param string|null $prefix + * + * Only public for testability, should not be called outside of CurlClient + * + * @return string A querystring, essentially. + */ + public static function encode($arr, $prefix = null) + { + if (!is_array($arr)) { + return $arr; + } + + $r = array(); + foreach ($arr as $k => $v) { + if (is_null($v)) { + continue; + } + + if ($prefix) { + if ($k !== null && (!is_int($k) || is_array($v))) { + $k = $prefix."[".$k."]"; + } else { + $k = $prefix."[]"; + } + } + + if (is_array($v)) { + $enc = self::encode($v, $k); + if ($enc) { + $r[] = $enc; + } + } else { + $r[] = urlencode($k)."=".urlencode($v); + } + } + + return implode("&", $r); + } +} diff --git a/libs/StripePHP/Invoice.php b/libs/StripePHP/Invoice.php new file mode 100644 index 00000000..e994566c --- /dev/null +++ b/libs/StripePHP/Invoice.php @@ -0,0 +1,92 @@ +json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param array|string|null $opts + * + * @return Invoice The saved invoice. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @return Invoice The paid invoice. + */ + public function pay($opts = null) + { + $url = $this->instanceUrl() . '/pay'; + list($response, $opts) = $this->_request('post', $url, null, $opts); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/StripePHP/InvoiceItem.php b/libs/StripePHP/InvoiceItem.php new file mode 100644 index 00000000..ab9c4ba3 --- /dev/null +++ b/libs/StripePHP/InvoiceItem.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return InvoiceItem The deleted invoice item. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/StripePHP/JsonSerializable.php b/libs/StripePHP/JsonSerializable.php new file mode 100644 index 00000000..2fdf8526 --- /dev/null +++ b/libs/StripePHP/JsonSerializable.php @@ -0,0 +1,18 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Orders + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @return Order The paid order. + */ + public function pay($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/pay'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @return OrderReturn The newly created return. + */ + public function returnOrder($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/returns'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + return Util\Util::convertToStripeObject($response, $opts); + } +} diff --git a/libs/StripePHP/OrderReturn.php b/libs/StripePHP/OrderReturn.php new file mode 100644 index 00000000..aa7fd4e9 --- /dev/null +++ b/libs/StripePHP/OrderReturn.php @@ -0,0 +1,33 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Plan The saved plan. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Plans + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/StripePHP/Product.php b/libs/StripePHP/Product.php new file mode 100644 index 00000000..2fdd22a0 --- /dev/null +++ b/libs/StripePHP/Product.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Products + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Product The deleted product. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/StripePHP/Recipient.php b/libs/StripePHP/Recipient.php new file mode 100644 index 00000000..04bcb7bb --- /dev/null +++ b/libs/StripePHP/Recipient.php @@ -0,0 +1,92 @@ +_save($opts); + } + + /** + * @param array|null $params + * + * @return Recipient The deleted recipient. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + + /** + * @param array|null $params + * + * @return Collection of the Recipient's Transfers + */ + public function transfers($params = null) + { + if ($params === null) { + $params = array(); + } + $params['recipient'] = $this->id; + $transfers = Transfer::all($params, $this->_opts); + return $transfers; + } +} diff --git a/libs/StripePHP/Refund.php b/libs/StripePHP/Refund.php new file mode 100644 index 00000000..b4e0e76a --- /dev/null +++ b/libs/StripePHP/Refund.php @@ -0,0 +1,79 @@ +_save($opts); + } +} diff --git a/libs/StripePHP/SKU.php b/libs/StripePHP/SKU.php new file mode 100644 index 00000000..7089604e --- /dev/null +++ b/libs/StripePHP/SKU.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of SKUs + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return SKU The deleted sku. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/StripePHP/SingletonApiResource.php b/libs/StripePHP/SingletonApiResource.php new file mode 100644 index 00000000..c6de4448 --- /dev/null +++ b/libs/StripePHP/SingletonApiResource.php @@ -0,0 +1,36 @@ +refresh(); + return $instance; + } + + /** + * @return string The endpoint associated with this singleton class. + */ + public static function classUrl() + { + $base = static::className(); + return "/v1/${base}"; + } + + /** + * @return string The endpoint associated with this singleton API resource. + */ + public function instanceUrl() + { + return static::classUrl(); + } +} diff --git a/libs/StripePHP/Stripe.php b/libs/StripePHP/Stripe.php new file mode 100644 index 00000000..f6345adb --- /dev/null +++ b/libs/StripePHP/Stripe.php @@ -0,0 +1,100 @@ +_lastResponse; + } + + /** + * @param ApiResponse + * + * @return void Set the last response from the Stripe API + */ + public function setLastResponse($resp) + { + $this->_lastResponse = $resp; + } + + protected $_opts; + protected $_values; + protected $_unsavedValues; + protected $_transientValues; + protected $_retrieveOptions; + protected $_lastResponse; + + public function __construct($id = null, $opts = null) + { + $this->_opts = $opts ? $opts : new Util\RequestOptions(); + $this->_values = array(); + $this->_unsavedValues = new Util\Set(); + $this->_transientValues = new Util\Set(); + + $this->_retrieveOptions = array(); + if (is_array($id)) { + foreach ($id as $key => $value) { + if ($key != 'id') { + $this->_retrieveOptions[$key] = $value; + } + } + $id = $id['id']; + } + + if ($id !== null) { + $this->id = $id; + } + } + + // Standard accessor magic methods + public function __set($k, $v) + { + if ($v === "") { + throw new InvalidArgumentException( + 'You cannot set \''.$k.'\'to an empty string. ' + .'We interpret empty strings as NULL in requests. ' + .'You may set obj->'.$k.' = NULL to delete the property' + ); + } + + if (self::$nestedUpdatableAttributes->includes($k) + && isset($this->$k) && $this->$k instanceof AttachedObject && is_array($v)) { + $this->$k->replaceWith($v); + } else { + // TODO: may want to clear from $_transientValues (Won't be user-visible). + $this->_values[$k] = $v; + } + if (!self::$permanentAttributes->includes($k)) { + $this->_unsavedValues->add($k); + } + } + + public function __isset($k) + { + return isset($this->_values[$k]); + } + public function __unset($k) + { + unset($this->_values[$k]); + $this->_transientValues->add($k); + $this->_unsavedValues->discard($k); + } + public function &__get($k) + { + // function should return a reference, using $nullval to return a reference to null + $nullval = null; + if (!empty($this->_values) && array_key_exists($k, $this->_values)) { + return $this->_values[$k]; + } else if (!empty($this->_transientValues) && $this->_transientValues->includes($k)) { + $class = get_class($this); + $attrs = join(', ', array_keys($this->_values)); + $message = "Stripe Notice: Undefined property of $class instance: $k. " + . "HINT: The $k attribute was set in the past, however. " + . "It was then wiped when refreshing the object " + . "with the result returned by Stripe's API, " + . "probably as a result of a save(). The attributes currently " + . "available on this object are: $attrs"; + error_log($message); + return $nullval; + } else { + $class = get_class($this); + error_log("Stripe Notice: Undefined property of $class instance: $k"); + return $nullval; + } + } + + // ArrayAccess methods + public function offsetSet($k, $v) + { + $this->$k = $v; + } + + public function offsetExists($k) + { + return array_key_exists($k, $this->_values); + } + + public function offsetUnset($k) + { + unset($this->$k); + } + public function offsetGet($k) + { + return array_key_exists($k, $this->_values) ? $this->_values[$k] : null; + } + + public function keys() + { + return array_keys($this->_values); + } + + /** + * This unfortunately needs to be public to be used in Util\Util + * + * @param array $values + * @param array $opts + * + * @return StripeObject The object constructed from the given values. + */ + public static function constructFrom($values, $opts) + { + $obj = new static(isset($values['id']) ? $values['id'] : null); + $obj->refreshFrom($values, $opts); + return $obj; + } + + /** + * Refreshes this object using the provided values. + * + * @param array $values + * @param array|Util\RequestOptions $opts + * @param boolean $partial Defaults to false. + */ + public function refreshFrom($values, $opts, $partial = false) + { + if (is_array($opts)) { + $opts = Util\RequestOptions::parse($opts); + } + + $this->_opts = $opts; + + // Wipe old state before setting new. This is useful for e.g. updating a + // customer, where there is no persistent card parameter. Mark those values + // which don't persist as transient + if ($partial) { + $removed = new Util\Set(); + } else { + $removed = array_diff(array_keys($this->_values), array_keys($values)); + } + + foreach ($removed as $k) { + if (self::$permanentAttributes->includes($k)) { + continue; + } + + unset($this->$k); + } + + foreach ($values as $k => $v) { + if (self::$permanentAttributes->includes($k) && isset($this[$k])) { + continue; + } + + if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) { + $this->_values[$k] = AttachedObject::constructFrom($v, $opts); + } else { + $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts); + } + + $this->_transientValues->discard($k); + $this->_unsavedValues->discard($k); + } + } + + /** + * @return array A recursive mapping of attributes to values for this object, + * including the proper value for deleted attributes. + */ + public function serializeParameters() + { + $params = array(); + if ($this->_unsavedValues) { + foreach ($this->_unsavedValues->toArray() as $k) { + $v = $this->$k; + if ($v === null) { + $v = ''; + } + + $params[$k] = $v; + } + } + + // Get nested updates. + foreach (self::$nestedUpdatableAttributes->toArray() as $property) { + if (isset($this->$property)) { + if ($this->$property instanceof StripeObject) { + $serialized = $this->$property->serializeParameters(); + if ($serialized) { + $params[$property] = $serialized; + } + } + } + } + + return $params; + } + + public function jsonSerialize() + { + return $this->__toArray(true); + } + + public function __toJSON() + { + if (defined('JSON_PRETTY_PRINT')) { + return json_encode($this->__toArray(true), JSON_PRETTY_PRINT); + } else { + return json_encode($this->__toArray(true)); + } + } + + public function __toString() + { + $class = get_class($this); + return $class . ' JSON: ' . $this->__toJSON(); + } + + public function __toArray($recursive = false) + { + if ($recursive) { + return Util\Util::convertStripeObjectToArray($this->_values); + } else { + return $this->_values; + } + } +} + +StripeObject::init(); diff --git a/libs/StripePHP/Subscription.php b/libs/StripePHP/Subscription.php new file mode 100644 index 00000000..a95ab091 --- /dev/null +++ b/libs/StripePHP/Subscription.php @@ -0,0 +1,97 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Subscription The saved subscription. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @return Subscription The updated subscription. + */ + public function deleteDiscount() + { + $url = $this->instanceUrl() . '/discount'; + list($response, $opts) = $this->_request('delete', $url); + $this->refreshFrom(array('discount' => null), $opts, true); + } +} diff --git a/libs/StripePHP/ThreeDSecure.php b/libs/StripePHP/ThreeDSecure.php new file mode 100644 index 00000000..a7dcc5ad --- /dev/null +++ b/libs/StripePHP/ThreeDSecure.php @@ -0,0 +1,25 @@ +instanceUrl() . '/reversals'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @return Transfer The canceled transfer. + */ + public function cancel() + { + $url = $this->instanceUrl() . '/cancel'; + list($response, $opts) = $this->_request('post', $url); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Transfer The saved transfer. + */ + public function save($opts = null) + { + return $this->_save($opts); + } +} diff --git a/libs/StripePHP/TransferReversal.php b/libs/StripePHP/TransferReversal.php new file mode 100644 index 00000000..33948c93 --- /dev/null +++ b/libs/StripePHP/TransferReversal.php @@ -0,0 +1,53 @@ +_save($opts); + } +} diff --git a/libs/StripePHP/Util/AutoPagingIterator.php b/libs/StripePHP/Util/AutoPagingIterator.php new file mode 100644 index 00000000..80877e67 --- /dev/null +++ b/libs/StripePHP/Util/AutoPagingIterator.php @@ -0,0 +1,61 @@ +page = $collection; + $this->params = $params; + } + + public function rewind() + { + // Actually rewinding would require making a copy of the original page. + } + + public function current() + { + $item = current($this->page->data); + $this->lastId = $item !== false ? $item['id'] : null; + + return $item; + } + + public function key() + { + return key($this->page->data) + $this->pageOffset; + } + + public function next() + { + $item = next($this->page->data); + if ($item === false) { + // If we've run out of data on the current page, try to fetch another one + // and increase the offset the new page would start at + $this->pageOffset += count($this->page->data); + if ($this->page['has_more']) { + $this->params = array_merge( + $this->params ? $this->params : array(), + array('starting_after' => $this->lastId) + ); + $this->page = $this->page->all($this->params); + } else { + return false; + } + } + } + + public function valid() + { + $key = key($this->page->data); + $valid = ($key !== null && $key !== false); + return $valid; + } +} diff --git a/libs/StripePHP/Util/RequestOptions.php b/libs/StripePHP/Util/RequestOptions.php new file mode 100644 index 00000000..14af2b8a --- /dev/null +++ b/libs/StripePHP/Util/RequestOptions.php @@ -0,0 +1,79 @@ +apiKey = $key; + $this->headers = $headers; + } + + /** + * Unpacks an options array and merges it into the existing RequestOptions + * object. + * @param array|string|null $options a key => value array + * + * @return RequestOptions + */ + public function merge($options) + { + $other_options = self::parse($options); + if ($other_options->apiKey === null) { + $other_options->apiKey = $this->apiKey; + } + $other_options->headers = array_merge($this->headers, $other_options->headers); + return $other_options; + } + + /** + * Unpacks an options array into an RequestOptions object + * @param array|string|null $options a key => value array + * + * @return RequestOptions + */ + public static function parse($options) + { + if ($options instanceof self) { + return $options; + } + + if (is_null($options)) { + return new RequestOptions(null, array()); + } + + if (is_string($options)) { + return new RequestOptions($options, array()); + } + + if (is_array($options)) { + $headers = array(); + $key = null; + if (array_key_exists('api_key', $options)) { + $key = $options['api_key']; + } + if (array_key_exists('idempotency_key', $options)) { + $headers['Idempotency-Key'] = $options['idempotency_key']; + } + if (array_key_exists('stripe_account', $options)) { + $headers['Stripe-Account'] = $options['stripe_account']; + } + if (array_key_exists('stripe_version', $options)) { + $headers['Stripe-Version'] = $options['stripe_version']; + } + return new RequestOptions($key, $headers); + } + + $message = 'The second argument to Stripe API method calls is an ' + . 'optional per-request apiKey, which must be a string, or ' + . 'per-request options, which must be an array. (HINT: you can set ' + . 'a global apiKey by "Stripe::setApiKey()")'; + throw new Error\Api($message); + } +} diff --git a/libs/StripePHP/Util/Set.php b/libs/StripePHP/Util/Set.php new file mode 100644 index 00000000..2a500cd6 --- /dev/null +++ b/libs/StripePHP/Util/Set.php @@ -0,0 +1,44 @@ +_elts = array(); + foreach ($members as $item) { + $this->_elts[$item] = true; + } + } + + public function includes($elt) + { + return isset($this->_elts[$elt]); + } + + public function add($elt) + { + $this->_elts[$elt] = true; + } + + public function discard($elt) + { + unset($this->_elts[$elt]); + } + + public function toArray() + { + return array_keys($this->_elts); + } + + public function getIterator() + { + return new ArrayIterator($this->toArray()); + } +} diff --git a/libs/StripePHP/Util/Util.php b/libs/StripePHP/Util/Util.php new file mode 100644 index 00000000..2f6894e2 --- /dev/null +++ b/libs/StripePHP/Util/Util.php @@ -0,0 +1,140 @@ + $v) { + // FIXME: this is an encapsulation violation + if ($k[0] == '_') { + continue; + } + if ($v instanceof StripeObject) { + $results[$k] = $v->__toArray(true); + } elseif (is_array($v)) { + $results[$k] = self::convertStripeObjectToArray($v); + } else { + $results[$k] = $v; + } + } + return $results; + } + + /** + * Converts a response from the Stripe API to the corresponding PHP object. + * + * @param array $resp The response from the Stripe API. + * @param array $opts + * @return StripeObject|array + */ + public static function convertToStripeObject($resp, $opts) + { + $types = array( + 'account' => 'Stripe\\Account', + 'alipay_account' => 'Stripe\\AlipayAccount', + 'bank_account' => 'Stripe\\BankAccount', + 'balance_transaction' => 'Stripe\\BalanceTransaction', + 'card' => 'Stripe\\Card', + 'charge' => 'Stripe\\Charge', + 'country_spec' => 'Stripe\\CountrySpec', + 'coupon' => 'Stripe\\Coupon', + 'customer' => 'Stripe\\Customer', + 'dispute' => 'Stripe\\Dispute', + 'list' => 'Stripe\\Collection', + 'invoice' => 'Stripe\\Invoice', + 'invoiceitem' => 'Stripe\\InvoiceItem', + 'event' => 'Stripe\\Event', + 'file' => 'Stripe\\FileUpload', + 'token' => 'Stripe\\Token', + 'transfer' => 'Stripe\\Transfer', + 'order' => 'Stripe\\Order', + 'order_return' => 'Stripe\\OrderReturn', + 'plan' => 'Stripe\\Plan', + 'product' => 'Stripe\\Product', + 'recipient' => 'Stripe\\Recipient', + 'refund' => 'Stripe\\Refund', + 'sku' => 'Stripe\\SKU', + 'subscription' => 'Stripe\\Subscription', + 'three_d_secure' => 'Stripe\\ThreeDSecure', + 'fee_refund' => 'Stripe\\ApplicationFeeRefund', + 'bitcoin_receiver' => 'Stripe\\BitcoinReceiver', + 'bitcoin_transaction' => 'Stripe\\BitcoinTransaction', + ); + if (self::isList($resp)) { + $mapped = array(); + foreach ($resp as $i) { + array_push($mapped, self::convertToStripeObject($i, $opts)); + } + return $mapped; + } elseif (is_array($resp)) { + if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']])) { + $class = $types[$resp['object']]; + } else { + $class = 'Stripe\\StripeObject'; + } + return $class::constructFrom($resp, $opts); + } else { + return $resp; + } + } + + /** + * @param string|mixed $value A string to UTF8-encode. + * + * @return string|mixed The UTF8-encoded string, or the object passed in if + * it wasn't a string. + */ + public static function utf8($value) + { + if (self::$isMbstringAvailable === null) { + self::$isMbstringAvailable = function_exists('mb_detect_encoding'); + + if (!self::$isMbstringAvailable) { + trigger_error("It looks like the mbstring extension is not enabled. " . + "UTF-8 strings will not properly be encoded. Ask your system " . + "administrator to enable the mbstring extension, or write to " . + "support@stripe.com if you have any questions.", E_USER_WARNING); + } + } + + if (is_string($value) && self::$isMbstringAvailable && mb_detect_encoding($value, "UTF-8", true) != "UTF-8") { + return utf8_encode($value); + } else { + return $value; + } + } +} diff --git a/libs/verysimple/Payment/Stripe.php b/libs/verysimple/Payment/Stripe.php new file mode 100644 index 00000000..b9bdbf18 --- /dev/null +++ b/libs/verysimple/Payment/Stripe.php @@ -0,0 +1,131 @@ + 1000, // amount in cents, again + "currency" => "usd", + "source" => $token, + "description" => "Example charge" + )); + } catch(\Stripe\Error\Card $e) { + // The card has been declined + die(print_r($charge,1)); + } + + die(print_r($charge,1)); + } +} +?> \ No newline at end of file diff --git a/libs/verysimple/Phreeze/Criteria.php b/libs/verysimple/Phreeze/Criteria.php index 56ea6218..5d7af509 100644 --- a/libs/verysimple/Phreeze/Criteria.php +++ b/libs/verysimple/Phreeze/Criteria.php @@ -290,7 +290,7 @@ private final function Prepare() elseif (substr($prop,-10) == "_BitwiseOr" && strlen($this->$prop)) { $dbfield = $this->GetFieldFromProp(str_replace("_BitwiseOr","",$prop)); - $this->_where .= $this->_where_delim . " (" . $dbfield ." | '". $this->Escape($val) . ")"; + $this->_where .= $this->_where_delim . " (" . $dbfield ." | ". $this->Escape($val) . ")"; $this->_where_delim = " and"; } elseif (substr($prop,-11) == "_BitwiseAnd" && strlen($this->$prop)) From 1144704d039224bfa35c8667a45eda49c92b63c3 Mon Sep 17 00:00:00 2001 From: Christian Dawson Date: Thu, 4 Aug 2016 11:56:48 -0500 Subject: [PATCH 2/5] Added abstract methods to Stripe custom class --- libs/verysimple/Payment/Stripe.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libs/verysimple/Payment/Stripe.php b/libs/verysimple/Payment/Stripe.php index b9bdbf18..3daafe67 100644 --- a/libs/verysimple/Payment/Stripe.php +++ b/libs/verysimple/Payment/Stripe.php @@ -127,5 +127,22 @@ function Process(PaymentRequest $req) die(print_r($charge,1)); } + + /** + * Called on contruction + * @param bool $test set to true to enable test mode. default = false + */ + function Init($testmode) + { + // TODO + } + + /** + * @see PaymentProcessor::Refund() + */ + function Refund(RefundRequest $req) + { + throw new Exception("not implemented, use Stripe"); + } } ?> \ No newline at end of file From 410ad310bc6cc0856c29230614c6eaa9a527531b Mon Sep 17 00:00:00 2001 From: Christian Dawson Date: Mon, 8 Aug 2016 10:55:04 -0500 Subject: [PATCH 3/5] Added Stripe integration --- libs/{StripePHP => Stripe}/Account.php | 0 libs/{StripePHP => Stripe}/AlipayAccount.php | 0 libs/{StripePHP => Stripe}/ApiRequestor.php | 0 libs/{StripePHP => Stripe}/ApiResource.php | 0 libs/{StripePHP => Stripe}/ApiResponse.php | 0 libs/{StripePHP => Stripe}/ApplicationFee.php | 0 .../ApplicationFeeRefund.php | 0 libs/{StripePHP => Stripe}/AttachedObject.php | 0 libs/{StripePHP => Stripe}/Balance.php | 0 .../BalanceTransaction.php | 0 libs/{StripePHP => Stripe}/BankAccount.php | 0 .../{StripePHP => Stripe}/BitcoinReceiver.php | 0 .../BitcoinTransaction.php | 0 libs/{StripePHP => Stripe}/Card.php | 0 libs/{StripePHP => Stripe}/Charge.php | 0 libs/{StripePHP => Stripe}/Collection.php | 0 libs/{StripePHP => Stripe}/CountrySpec.php | 0 libs/{StripePHP => Stripe}/Coupon.php | 0 libs/{StripePHP => Stripe}/Customer.php | 0 libs/{StripePHP => Stripe}/Dispute.php | 0 libs/{StripePHP => Stripe}/Error/Api.php | 0 .../Error/ApiConnection.php | 0 .../Error/Authentication.php | 0 libs/{StripePHP => Stripe}/Error/Base.php | 0 libs/{StripePHP => Stripe}/Error/Card.php | 0 .../Error/InvalidRequest.php | 0 .../{StripePHP => Stripe}/Error/RateLimit.php | 0 libs/{StripePHP => Stripe}/Event.php | 0 .../{StripePHP => Stripe}/ExternalAccount.php | 0 libs/{StripePHP => Stripe}/FileUpload.php | 0 .../HttpClient/ClientInterface.php | 0 .../HttpClient/CurlClient.php | 0 libs/{StripePHP => Stripe}/Invoice.php | 0 libs/{StripePHP => Stripe}/InvoiceItem.php | 0 .../JsonSerializable.php | 0 libs/{StripePHP => Stripe}/Order.php | 0 libs/{StripePHP => Stripe}/OrderReturn.php | 0 libs/{StripePHP => Stripe}/Plan.php | 0 libs/{StripePHP => Stripe}/Product.php | 0 libs/Stripe/README.txt | 1 + libs/{StripePHP => Stripe}/Recipient.php | 0 libs/{StripePHP => Stripe}/Refund.php | 0 libs/{StripePHP => Stripe}/SKU.php | 0 .../SingletonApiResource.php | 0 libs/{StripePHP => Stripe}/Stripe.php | 0 libs/{StripePHP => Stripe}/StripeObject.php | 0 libs/{StripePHP => Stripe}/Subscription.php | 0 libs/{StripePHP => Stripe}/ThreeDSecure.php | 0 libs/{StripePHP => Stripe}/Token.php | 0 libs/{StripePHP => Stripe}/Transfer.php | 0 .../TransferReversal.php | 0 .../Util/AutoPagingIterator.php | 0 .../Util/RequestOptions.php | 0 libs/{StripePHP => Stripe}/Util/Set.php | 0 libs/{StripePHP => Stripe}/Util/Util.php | 0 libs/Stripe/init.php | 65 +++++++++++++++++++ libs/verysimple/Payment/PaymentRequest.php | 3 +- libs/verysimple/Payment/Stripe.php | 28 +++++--- 58 files changed, 87 insertions(+), 10 deletions(-) rename libs/{StripePHP => Stripe}/Account.php (100%) rename libs/{StripePHP => Stripe}/AlipayAccount.php (100%) rename libs/{StripePHP => Stripe}/ApiRequestor.php (100%) rename libs/{StripePHP => Stripe}/ApiResource.php (100%) rename libs/{StripePHP => Stripe}/ApiResponse.php (100%) rename libs/{StripePHP => Stripe}/ApplicationFee.php (100%) rename libs/{StripePHP => Stripe}/ApplicationFeeRefund.php (100%) rename libs/{StripePHP => Stripe}/AttachedObject.php (100%) rename libs/{StripePHP => Stripe}/Balance.php (100%) rename libs/{StripePHP => Stripe}/BalanceTransaction.php (100%) rename libs/{StripePHP => Stripe}/BankAccount.php (100%) rename libs/{StripePHP => Stripe}/BitcoinReceiver.php (100%) rename libs/{StripePHP => Stripe}/BitcoinTransaction.php (100%) rename libs/{StripePHP => Stripe}/Card.php (100%) rename libs/{StripePHP => Stripe}/Charge.php (100%) rename libs/{StripePHP => Stripe}/Collection.php (100%) rename libs/{StripePHP => Stripe}/CountrySpec.php (100%) rename libs/{StripePHP => Stripe}/Coupon.php (100%) rename libs/{StripePHP => Stripe}/Customer.php (100%) rename libs/{StripePHP => Stripe}/Dispute.php (100%) rename libs/{StripePHP => Stripe}/Error/Api.php (100%) rename libs/{StripePHP => Stripe}/Error/ApiConnection.php (100%) rename libs/{StripePHP => Stripe}/Error/Authentication.php (100%) rename libs/{StripePHP => Stripe}/Error/Base.php (100%) rename libs/{StripePHP => Stripe}/Error/Card.php (100%) rename libs/{StripePHP => Stripe}/Error/InvalidRequest.php (100%) rename libs/{StripePHP => Stripe}/Error/RateLimit.php (100%) rename libs/{StripePHP => Stripe}/Event.php (100%) rename libs/{StripePHP => Stripe}/ExternalAccount.php (100%) rename libs/{StripePHP => Stripe}/FileUpload.php (100%) rename libs/{StripePHP => Stripe}/HttpClient/ClientInterface.php (100%) rename libs/{StripePHP => Stripe}/HttpClient/CurlClient.php (100%) rename libs/{StripePHP => Stripe}/Invoice.php (100%) rename libs/{StripePHP => Stripe}/InvoiceItem.php (100%) rename libs/{StripePHP => Stripe}/JsonSerializable.php (100%) rename libs/{StripePHP => Stripe}/Order.php (100%) rename libs/{StripePHP => Stripe}/OrderReturn.php (100%) rename libs/{StripePHP => Stripe}/Plan.php (100%) rename libs/{StripePHP => Stripe}/Product.php (100%) create mode 100644 libs/Stripe/README.txt rename libs/{StripePHP => Stripe}/Recipient.php (100%) rename libs/{StripePHP => Stripe}/Refund.php (100%) rename libs/{StripePHP => Stripe}/SKU.php (100%) rename libs/{StripePHP => Stripe}/SingletonApiResource.php (100%) rename libs/{StripePHP => Stripe}/Stripe.php (100%) rename libs/{StripePHP => Stripe}/StripeObject.php (100%) rename libs/{StripePHP => Stripe}/Subscription.php (100%) rename libs/{StripePHP => Stripe}/ThreeDSecure.php (100%) rename libs/{StripePHP => Stripe}/Token.php (100%) rename libs/{StripePHP => Stripe}/Transfer.php (100%) rename libs/{StripePHP => Stripe}/TransferReversal.php (100%) rename libs/{StripePHP => Stripe}/Util/AutoPagingIterator.php (100%) rename libs/{StripePHP => Stripe}/Util/RequestOptions.php (100%) rename libs/{StripePHP => Stripe}/Util/Set.php (100%) rename libs/{StripePHP => Stripe}/Util/Util.php (100%) create mode 100644 libs/Stripe/init.php diff --git a/libs/StripePHP/Account.php b/libs/Stripe/Account.php similarity index 100% rename from libs/StripePHP/Account.php rename to libs/Stripe/Account.php diff --git a/libs/StripePHP/AlipayAccount.php b/libs/Stripe/AlipayAccount.php similarity index 100% rename from libs/StripePHP/AlipayAccount.php rename to libs/Stripe/AlipayAccount.php diff --git a/libs/StripePHP/ApiRequestor.php b/libs/Stripe/ApiRequestor.php similarity index 100% rename from libs/StripePHP/ApiRequestor.php rename to libs/Stripe/ApiRequestor.php diff --git a/libs/StripePHP/ApiResource.php b/libs/Stripe/ApiResource.php similarity index 100% rename from libs/StripePHP/ApiResource.php rename to libs/Stripe/ApiResource.php diff --git a/libs/StripePHP/ApiResponse.php b/libs/Stripe/ApiResponse.php similarity index 100% rename from libs/StripePHP/ApiResponse.php rename to libs/Stripe/ApiResponse.php diff --git a/libs/StripePHP/ApplicationFee.php b/libs/Stripe/ApplicationFee.php similarity index 100% rename from libs/StripePHP/ApplicationFee.php rename to libs/Stripe/ApplicationFee.php diff --git a/libs/StripePHP/ApplicationFeeRefund.php b/libs/Stripe/ApplicationFeeRefund.php similarity index 100% rename from libs/StripePHP/ApplicationFeeRefund.php rename to libs/Stripe/ApplicationFeeRefund.php diff --git a/libs/StripePHP/AttachedObject.php b/libs/Stripe/AttachedObject.php similarity index 100% rename from libs/StripePHP/AttachedObject.php rename to libs/Stripe/AttachedObject.php diff --git a/libs/StripePHP/Balance.php b/libs/Stripe/Balance.php similarity index 100% rename from libs/StripePHP/Balance.php rename to libs/Stripe/Balance.php diff --git a/libs/StripePHP/BalanceTransaction.php b/libs/Stripe/BalanceTransaction.php similarity index 100% rename from libs/StripePHP/BalanceTransaction.php rename to libs/Stripe/BalanceTransaction.php diff --git a/libs/StripePHP/BankAccount.php b/libs/Stripe/BankAccount.php similarity index 100% rename from libs/StripePHP/BankAccount.php rename to libs/Stripe/BankAccount.php diff --git a/libs/StripePHP/BitcoinReceiver.php b/libs/Stripe/BitcoinReceiver.php similarity index 100% rename from libs/StripePHP/BitcoinReceiver.php rename to libs/Stripe/BitcoinReceiver.php diff --git a/libs/StripePHP/BitcoinTransaction.php b/libs/Stripe/BitcoinTransaction.php similarity index 100% rename from libs/StripePHP/BitcoinTransaction.php rename to libs/Stripe/BitcoinTransaction.php diff --git a/libs/StripePHP/Card.php b/libs/Stripe/Card.php similarity index 100% rename from libs/StripePHP/Card.php rename to libs/Stripe/Card.php diff --git a/libs/StripePHP/Charge.php b/libs/Stripe/Charge.php similarity index 100% rename from libs/StripePHP/Charge.php rename to libs/Stripe/Charge.php diff --git a/libs/StripePHP/Collection.php b/libs/Stripe/Collection.php similarity index 100% rename from libs/StripePHP/Collection.php rename to libs/Stripe/Collection.php diff --git a/libs/StripePHP/CountrySpec.php b/libs/Stripe/CountrySpec.php similarity index 100% rename from libs/StripePHP/CountrySpec.php rename to libs/Stripe/CountrySpec.php diff --git a/libs/StripePHP/Coupon.php b/libs/Stripe/Coupon.php similarity index 100% rename from libs/StripePHP/Coupon.php rename to libs/Stripe/Coupon.php diff --git a/libs/StripePHP/Customer.php b/libs/Stripe/Customer.php similarity index 100% rename from libs/StripePHP/Customer.php rename to libs/Stripe/Customer.php diff --git a/libs/StripePHP/Dispute.php b/libs/Stripe/Dispute.php similarity index 100% rename from libs/StripePHP/Dispute.php rename to libs/Stripe/Dispute.php diff --git a/libs/StripePHP/Error/Api.php b/libs/Stripe/Error/Api.php similarity index 100% rename from libs/StripePHP/Error/Api.php rename to libs/Stripe/Error/Api.php diff --git a/libs/StripePHP/Error/ApiConnection.php b/libs/Stripe/Error/ApiConnection.php similarity index 100% rename from libs/StripePHP/Error/ApiConnection.php rename to libs/Stripe/Error/ApiConnection.php diff --git a/libs/StripePHP/Error/Authentication.php b/libs/Stripe/Error/Authentication.php similarity index 100% rename from libs/StripePHP/Error/Authentication.php rename to libs/Stripe/Error/Authentication.php diff --git a/libs/StripePHP/Error/Base.php b/libs/Stripe/Error/Base.php similarity index 100% rename from libs/StripePHP/Error/Base.php rename to libs/Stripe/Error/Base.php diff --git a/libs/StripePHP/Error/Card.php b/libs/Stripe/Error/Card.php similarity index 100% rename from libs/StripePHP/Error/Card.php rename to libs/Stripe/Error/Card.php diff --git a/libs/StripePHP/Error/InvalidRequest.php b/libs/Stripe/Error/InvalidRequest.php similarity index 100% rename from libs/StripePHP/Error/InvalidRequest.php rename to libs/Stripe/Error/InvalidRequest.php diff --git a/libs/StripePHP/Error/RateLimit.php b/libs/Stripe/Error/RateLimit.php similarity index 100% rename from libs/StripePHP/Error/RateLimit.php rename to libs/Stripe/Error/RateLimit.php diff --git a/libs/StripePHP/Event.php b/libs/Stripe/Event.php similarity index 100% rename from libs/StripePHP/Event.php rename to libs/Stripe/Event.php diff --git a/libs/StripePHP/ExternalAccount.php b/libs/Stripe/ExternalAccount.php similarity index 100% rename from libs/StripePHP/ExternalAccount.php rename to libs/Stripe/ExternalAccount.php diff --git a/libs/StripePHP/FileUpload.php b/libs/Stripe/FileUpload.php similarity index 100% rename from libs/StripePHP/FileUpload.php rename to libs/Stripe/FileUpload.php diff --git a/libs/StripePHP/HttpClient/ClientInterface.php b/libs/Stripe/HttpClient/ClientInterface.php similarity index 100% rename from libs/StripePHP/HttpClient/ClientInterface.php rename to libs/Stripe/HttpClient/ClientInterface.php diff --git a/libs/StripePHP/HttpClient/CurlClient.php b/libs/Stripe/HttpClient/CurlClient.php similarity index 100% rename from libs/StripePHP/HttpClient/CurlClient.php rename to libs/Stripe/HttpClient/CurlClient.php diff --git a/libs/StripePHP/Invoice.php b/libs/Stripe/Invoice.php similarity index 100% rename from libs/StripePHP/Invoice.php rename to libs/Stripe/Invoice.php diff --git a/libs/StripePHP/InvoiceItem.php b/libs/Stripe/InvoiceItem.php similarity index 100% rename from libs/StripePHP/InvoiceItem.php rename to libs/Stripe/InvoiceItem.php diff --git a/libs/StripePHP/JsonSerializable.php b/libs/Stripe/JsonSerializable.php similarity index 100% rename from libs/StripePHP/JsonSerializable.php rename to libs/Stripe/JsonSerializable.php diff --git a/libs/StripePHP/Order.php b/libs/Stripe/Order.php similarity index 100% rename from libs/StripePHP/Order.php rename to libs/Stripe/Order.php diff --git a/libs/StripePHP/OrderReturn.php b/libs/Stripe/OrderReturn.php similarity index 100% rename from libs/StripePHP/OrderReturn.php rename to libs/Stripe/OrderReturn.php diff --git a/libs/StripePHP/Plan.php b/libs/Stripe/Plan.php similarity index 100% rename from libs/StripePHP/Plan.php rename to libs/Stripe/Plan.php diff --git a/libs/StripePHP/Product.php b/libs/Stripe/Product.php similarity index 100% rename from libs/StripePHP/Product.php rename to libs/Stripe/Product.php diff --git a/libs/Stripe/README.txt b/libs/Stripe/README.txt new file mode 100644 index 00000000..4de9a743 --- /dev/null +++ b/libs/Stripe/README.txt @@ -0,0 +1 @@ +To integrate this library into your code, you'll need to include the init.php file, located in the root of the Stripe directory. \ No newline at end of file diff --git a/libs/StripePHP/Recipient.php b/libs/Stripe/Recipient.php similarity index 100% rename from libs/StripePHP/Recipient.php rename to libs/Stripe/Recipient.php diff --git a/libs/StripePHP/Refund.php b/libs/Stripe/Refund.php similarity index 100% rename from libs/StripePHP/Refund.php rename to libs/Stripe/Refund.php diff --git a/libs/StripePHP/SKU.php b/libs/Stripe/SKU.php similarity index 100% rename from libs/StripePHP/SKU.php rename to libs/Stripe/SKU.php diff --git a/libs/StripePHP/SingletonApiResource.php b/libs/Stripe/SingletonApiResource.php similarity index 100% rename from libs/StripePHP/SingletonApiResource.php rename to libs/Stripe/SingletonApiResource.php diff --git a/libs/StripePHP/Stripe.php b/libs/Stripe/Stripe.php similarity index 100% rename from libs/StripePHP/Stripe.php rename to libs/Stripe/Stripe.php diff --git a/libs/StripePHP/StripeObject.php b/libs/Stripe/StripeObject.php similarity index 100% rename from libs/StripePHP/StripeObject.php rename to libs/Stripe/StripeObject.php diff --git a/libs/StripePHP/Subscription.php b/libs/Stripe/Subscription.php similarity index 100% rename from libs/StripePHP/Subscription.php rename to libs/Stripe/Subscription.php diff --git a/libs/StripePHP/ThreeDSecure.php b/libs/Stripe/ThreeDSecure.php similarity index 100% rename from libs/StripePHP/ThreeDSecure.php rename to libs/Stripe/ThreeDSecure.php diff --git a/libs/StripePHP/Token.php b/libs/Stripe/Token.php similarity index 100% rename from libs/StripePHP/Token.php rename to libs/Stripe/Token.php diff --git a/libs/StripePHP/Transfer.php b/libs/Stripe/Transfer.php similarity index 100% rename from libs/StripePHP/Transfer.php rename to libs/Stripe/Transfer.php diff --git a/libs/StripePHP/TransferReversal.php b/libs/Stripe/TransferReversal.php similarity index 100% rename from libs/StripePHP/TransferReversal.php rename to libs/Stripe/TransferReversal.php diff --git a/libs/StripePHP/Util/AutoPagingIterator.php b/libs/Stripe/Util/AutoPagingIterator.php similarity index 100% rename from libs/StripePHP/Util/AutoPagingIterator.php rename to libs/Stripe/Util/AutoPagingIterator.php diff --git a/libs/StripePHP/Util/RequestOptions.php b/libs/Stripe/Util/RequestOptions.php similarity index 100% rename from libs/StripePHP/Util/RequestOptions.php rename to libs/Stripe/Util/RequestOptions.php diff --git a/libs/StripePHP/Util/Set.php b/libs/Stripe/Util/Set.php similarity index 100% rename from libs/StripePHP/Util/Set.php rename to libs/Stripe/Util/Set.php diff --git a/libs/StripePHP/Util/Util.php b/libs/Stripe/Util/Util.php similarity index 100% rename from libs/StripePHP/Util/Util.php rename to libs/Stripe/Util/Util.php diff --git a/libs/Stripe/init.php b/libs/Stripe/init.php new file mode 100644 index 00000000..751b2f5e --- /dev/null +++ b/libs/Stripe/init.php @@ -0,0 +1,65 @@ + \ No newline at end of file diff --git a/libs/verysimple/Payment/PaymentRequest.php b/libs/verysimple/Payment/PaymentRequest.php index ac1e9ec9..c0f518e3 100644 --- a/libs/verysimple/Payment/PaymentRequest.php +++ b/libs/verysimple/Payment/PaymentRequest.php @@ -40,7 +40,8 @@ class PaymentRequest public $CCNumber = ""; public $CCExpMonth = ""; public $CCExpYear = ""; - public $CCSecurityCode = ""; + public $CCSecurityCode = ""; + public $Token = ""; public $TransactionCurrency = "USD"; diff --git a/libs/verysimple/Payment/Stripe.php b/libs/verysimple/Payment/Stripe.php index 3daafe67..a85ff122 100644 --- a/libs/verysimple/Payment/Stripe.php +++ b/libs/verysimple/Payment/Stripe.php @@ -3,6 +3,7 @@ /** import supporting libraries */ require_once("PaymentProcessor.php"); +require_once("Stripe/init.php"); /** * Stripe extends the generic PaymentProcessor object to process @@ -112,20 +113,29 @@ public static function setAccountId($accountId) */ function Process(PaymentRequest $req) { - include_once 'StripePHP/Charge.php'; + $resp = new PaymentResponse(); + try { - $charge = Charge::create(array( - "amount" => 1000, // amount in cents, again + $charge = \Stripe\Charge::create(array( + "amount" => $req->TransactionAmount * 100, // amount in cents, again "currency" => "usd", - "source" => $token, - "description" => "Example charge" + "source" => $req->CCNumber, + "description" => $req->OrderDescription )); - } catch(\Stripe\Error\Card $e) { + } catch(\Stripe\Error\Base $e) { // The card has been declined - die(print_r($charge,1)); + $resp->IsSuccess = false; + $resp->ResponseCode = $e->httpStatus; + $resp->ResponseMessage = $e->getMessage(); + return $resp; } - - die(print_r($charge,1)); + + $resp->IsSuccess = true; + $resp->TransactionId = $charge->id; + $resp->ResponseCode = $charge->id; + $resp->ResponseMessage = "Charge of $req->TransactionAmount Posted"; + + return $resp; } /** From d45b552516348ece914eec991a263fa0edea52b2 Mon Sep 17 00:00:00 2001 From: evadmin Date: Mon, 8 Aug 2016 11:02:47 -0500 Subject: [PATCH 4/5] Testing --- libs/Stripe/Account.php | 131 +++++++++ libs/Stripe/AlipayAccount.php | 13 + libs/Stripe/ApiRequestor.php | 244 +++++++++++++++++ libs/Stripe/ApiResource.php | 205 ++++++++++++++ libs/Stripe/ApiResponse.php | 32 +++ libs/Stripe/ApplicationFee.php | 69 +++++ libs/Stripe/ApplicationFeeRefund.php | 44 +++ libs/Stripe/AttachedObject.php | 31 +++ libs/Stripe/Balance.php | 26 ++ libs/Stripe/BalanceTransaction.php | 57 ++++ libs/Stripe/BankAccount.php | 25 ++ libs/Stripe/BitcoinReceiver.php | 85 ++++++ libs/Stripe/BitcoinTransaction.php | 13 + libs/Stripe/Card.php | 13 + libs/Stripe/Charge.php | 184 +++++++++++++ libs/Stripe/Collection.php | 87 ++++++ libs/Stripe/CountrySpec.php | 44 +++ libs/Stripe/Coupon.php | 77 ++++++ libs/Stripe/Customer.php | 190 +++++++++++++ libs/Stripe/Dispute.php | 83 ++++++ libs/Stripe/Error/Api.php | 7 + libs/Stripe/Error/ApiConnection.php | 7 + libs/Stripe/Error/Authentication.php | 7 + libs/Stripe/Error/Base.php | 60 +++++ libs/Stripe/Error/Card.php | 30 +++ libs/Stripe/Error/InvalidRequest.php | 23 ++ libs/Stripe/Error/RateLimit.php | 7 + libs/Stripe/Event.php | 43 +++ libs/Stripe/ExternalAccount.php | 89 ++++++ libs/Stripe/FileUpload.php | 61 +++++ libs/Stripe/HttpClient/ClientInterface.php | 18 ++ libs/Stripe/HttpClient/CurlClient.php | 298 +++++++++++++++++++++ libs/Stripe/Invoice.php | 92 +++++++ libs/Stripe/InvoiceItem.php | 77 ++++++ libs/Stripe/JsonSerializable.php | 18 ++ libs/Stripe/Order.php | 87 ++++++ libs/Stripe/OrderReturn.php | 33 +++ libs/Stripe/Plan.php | 77 ++++++ libs/Stripe/Product.php | 77 ++++++ libs/Stripe/Recipient.php | 92 +++++++ libs/Stripe/Refund.php | 79 ++++++ libs/Stripe/SKU.php | 77 ++++++ libs/Stripe/SingletonApiResource.php | 36 +++ libs/Stripe/Stripe.php | 100 +++++++ libs/Stripe/StripeObject.php | 293 ++++++++++++++++++++ libs/Stripe/Subscription.php | 97 +++++++ libs/Stripe/ThreeDSecure.php | 25 ++ libs/Stripe/Token.php | 43 +++ libs/Stripe/Transfer.php | 115 ++++++++ libs/Stripe/TransferReversal.php | 53 ++++ libs/Stripe/Util/AutoPagingIterator.php | 61 +++++ libs/Stripe/Util/RequestOptions.php | 79 ++++++ libs/Stripe/Util/Set.php | 44 +++ libs/Stripe/Util/Util.php | 140 ++++++++++ libs/Stripe/init.php | 65 +++++ libs/verysimple/Payment/Stripe.php | 42 ++- 56 files changed, 4197 insertions(+), 8 deletions(-) create mode 100644 libs/Stripe/Account.php create mode 100644 libs/Stripe/AlipayAccount.php create mode 100644 libs/Stripe/ApiRequestor.php create mode 100644 libs/Stripe/ApiResource.php create mode 100644 libs/Stripe/ApiResponse.php create mode 100644 libs/Stripe/ApplicationFee.php create mode 100644 libs/Stripe/ApplicationFeeRefund.php create mode 100644 libs/Stripe/AttachedObject.php create mode 100644 libs/Stripe/Balance.php create mode 100644 libs/Stripe/BalanceTransaction.php create mode 100644 libs/Stripe/BankAccount.php create mode 100644 libs/Stripe/BitcoinReceiver.php create mode 100644 libs/Stripe/BitcoinTransaction.php create mode 100644 libs/Stripe/Card.php create mode 100644 libs/Stripe/Charge.php create mode 100644 libs/Stripe/Collection.php create mode 100644 libs/Stripe/CountrySpec.php create mode 100644 libs/Stripe/Coupon.php create mode 100644 libs/Stripe/Customer.php create mode 100644 libs/Stripe/Dispute.php create mode 100644 libs/Stripe/Error/Api.php create mode 100644 libs/Stripe/Error/ApiConnection.php create mode 100644 libs/Stripe/Error/Authentication.php create mode 100644 libs/Stripe/Error/Base.php create mode 100644 libs/Stripe/Error/Card.php create mode 100644 libs/Stripe/Error/InvalidRequest.php create mode 100644 libs/Stripe/Error/RateLimit.php create mode 100644 libs/Stripe/Event.php create mode 100644 libs/Stripe/ExternalAccount.php create mode 100644 libs/Stripe/FileUpload.php create mode 100644 libs/Stripe/HttpClient/ClientInterface.php create mode 100644 libs/Stripe/HttpClient/CurlClient.php create mode 100644 libs/Stripe/Invoice.php create mode 100644 libs/Stripe/InvoiceItem.php create mode 100644 libs/Stripe/JsonSerializable.php create mode 100644 libs/Stripe/Order.php create mode 100644 libs/Stripe/OrderReturn.php create mode 100644 libs/Stripe/Plan.php create mode 100644 libs/Stripe/Product.php create mode 100644 libs/Stripe/Recipient.php create mode 100644 libs/Stripe/Refund.php create mode 100644 libs/Stripe/SKU.php create mode 100644 libs/Stripe/SingletonApiResource.php create mode 100644 libs/Stripe/Stripe.php create mode 100644 libs/Stripe/StripeObject.php create mode 100644 libs/Stripe/Subscription.php create mode 100644 libs/Stripe/ThreeDSecure.php create mode 100644 libs/Stripe/Token.php create mode 100644 libs/Stripe/Transfer.php create mode 100644 libs/Stripe/TransferReversal.php create mode 100644 libs/Stripe/Util/AutoPagingIterator.php create mode 100644 libs/Stripe/Util/RequestOptions.php create mode 100644 libs/Stripe/Util/Set.php create mode 100644 libs/Stripe/Util/Util.php create mode 100644 libs/Stripe/init.php diff --git a/libs/Stripe/Account.php b/libs/Stripe/Account.php new file mode 100644 index 00000000..e46f42d6 --- /dev/null +++ b/libs/Stripe/Account.php @@ -0,0 +1,131 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Account The deleted account. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Account The rejected account. + */ + public function reject($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/reject'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Accounts + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/Stripe/AlipayAccount.php b/libs/Stripe/AlipayAccount.php new file mode 100644 index 00000000..1ba34bfa --- /dev/null +++ b/libs/Stripe/AlipayAccount.php @@ -0,0 +1,13 @@ +_apiKey = $apiKey; + if (!$apiBase) { + $apiBase = Stripe::$apiBase; + } + $this->_apiBase = $apiBase; + } + + private static function _encodeObjects($d) + { + if ($d instanceof ApiResource) { + return Util\Util::utf8($d->id); + } elseif ($d === true) { + return 'true'; + } elseif ($d === false) { + return 'false'; + } elseif (is_array($d)) { + $res = array(); + foreach ($d as $k => $v) { + $res[$k] = self::_encodeObjects($v); + } + return $res; + } else { + return Util\Util::utf8($d); + } + } + + /** + * @param string $method + * @param string $url + * @param array|null $params + * @param array|null $headers + * + * @return array An array whose first element is an API response and second + * element is the API key used to make the request. + */ + public function request($method, $url, $params = null, $headers = null) + { + if (!$params) { + $params = array(); + } + if (!$headers) { + $headers = array(); + } + list($rbody, $rcode, $rheaders, $myApiKey) = + $this->_requestRaw($method, $url, $params, $headers); + $json = $this->_interpretResponse($rbody, $rcode, $rheaders); + $resp = new ApiResponse($rbody, $rcode, $rheaders, $json); + return array($resp, $myApiKey); + } + + /** + * @param string $rbody A JSON string. + * @param int $rcode + * @param array $rheaders + * @param array $resp + * + * @throws Error\InvalidRequest if the error is caused by the user. + * @throws Error\Authentication if the error is caused by a lack of + * permissions. + * @throws Error\Card if the error is the error code is 402 (payment + * required) + * @throws Error\RateLimit if the error is caused by too many requests + * hitting the API. + * @throws Error\Api otherwise. + */ + public function handleApiError($rbody, $rcode, $rheaders, $resp) + { + if (!is_array($resp) || !isset($resp['error'])) { + $msg = "Invalid response object from API: $rbody " + . "(HTTP response code was $rcode)"; + throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); + } + + $error = $resp['error']; + $msg = isset($error['message']) ? $error['message'] : null; + $param = isset($error['param']) ? $error['param'] : null; + $code = isset($error['code']) ? $error['code'] : null; + + switch ($rcode) { + case 400: + // 'rate_limit' code is deprecated, but left here for backwards compatibility + // for API versions earlier than 2015-09-08 + if ($code == 'rate_limit') { + throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + } + + // intentional fall-through + case 404: + throw new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders); + case 401: + throw new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders); + case 402: + throw new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders); + case 429: + throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders); + default: + throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders); + } + } + + private function _requestRaw($method, $url, $params, $headers) + { + $myApiKey = $this->_apiKey; + if (!$myApiKey) { + $myApiKey = Stripe::$apiKey; + } + + if (!$myApiKey) { + $msg = 'No API key provided. (HINT: set your API key using ' + . '"Stripe::setApiKey()". You can generate API keys from ' + . 'the Stripe web interface. See https://stripe.com/api for ' + . 'details, or email support@stripe.com if you have any questions.'; + throw new Error\Authentication($msg); + } + + $absUrl = $this->_apiBase.$url; + $params = self::_encodeObjects($params); + $langVersion = phpversion(); + $uname = php_uname(); + $ua = array( + 'bindings_version' => Stripe::VERSION, + 'lang' => 'php', + 'lang_version' => $langVersion, + 'publisher' => 'stripe', + 'uname' => $uname, + ); + $defaultHeaders = array( + 'X-Stripe-Client-User-Agent' => json_encode($ua), + 'User-Agent' => 'Stripe/v1 PhpBindings/' . Stripe::VERSION, + 'Authorization' => 'Bearer ' . $myApiKey, + ); + if (Stripe::$apiVersion) { + $defaultHeaders['Stripe-Version'] = Stripe::$apiVersion; + } + + if (Stripe::$accountId) { + $defaultHeaders['Stripe-Account'] = Stripe::$accountId; + } + + $hasFile = false; + $hasCurlFile = class_exists('\CURLFile', false); + foreach ($params as $k => $v) { + if (is_resource($v)) { + $hasFile = true; + $params[$k] = self::_processResourceParam($v, $hasCurlFile); + } elseif ($hasCurlFile && $v instanceof \CURLFile) { + $hasFile = true; + } + } + + if ($hasFile) { + $defaultHeaders['Content-Type'] = 'multipart/form-data'; + } else { + $defaultHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + $combinedHeaders = array_merge($defaultHeaders, $headers); + $rawHeaders = array(); + + foreach ($combinedHeaders as $header => $value) { + $rawHeaders[] = $header . ': ' . $value; + } + + list($rbody, $rcode, $rheaders) = $this->httpClient()->request( + $method, + $absUrl, + $rawHeaders, + $params, + $hasFile + ); + return array($rbody, $rcode, $rheaders, $myApiKey); + } + + private function _processResourceParam($resource, $hasCurlFile) + { + if (get_resource_type($resource) !== 'stream') { + throw new Error\Api( + 'Attempted to upload a resource that is not a stream' + ); + } + + $metaData = stream_get_meta_data($resource); + if ($metaData['wrapper_type'] !== 'plainfile') { + throw new Error\Api( + 'Only plainfile resource streams are supported' + ); + } + + if ($hasCurlFile) { + // We don't have the filename or mimetype, but the API doesn't care + return new \CURLFile($metaData['uri']); + } else { + return '@'.$metaData['uri']; + } + } + + private function _interpretResponse($rbody, $rcode, $rheaders) + { + try { + $resp = json_decode($rbody, true); + } catch (Exception $e) { + $msg = "Invalid response body from API: $rbody " + . "(HTTP response code was $rcode)"; + throw new Error\Api($msg, $rcode, $rbody); + } + + if ($rcode < 200 || $rcode >= 300) { + $this->handleApiError($rbody, $rcode, $rheaders, $resp); + } + return $resp; + } + + public static function setHttpClient($client) + { + self::$_httpClient = $client; + } + + private function httpClient() + { + if (!self::$_httpClient) { + self::$_httpClient = HttpClient\CurlClient::instance(); + } + return self::$_httpClient; + } +} diff --git a/libs/Stripe/ApiResource.php b/libs/Stripe/ApiResource.php new file mode 100644 index 00000000..f10b948f --- /dev/null +++ b/libs/Stripe/ApiResource.php @@ -0,0 +1,205 @@ + true, 'Stripe-Version' => true); + + public static function baseUrl() + { + return Stripe::$apiBase; + } + + /** + * @return ApiResource The refreshed resource. + */ + public function refresh() + { + $requestor = new ApiRequestor($this->_opts->apiKey, static::baseUrl()); + $url = $this->instanceUrl(); + + list($response, $this->_opts->apiKey) = $requestor->request( + 'get', + $url, + $this->_retrieveOptions, + $this->_opts->headers + ); + $this->setLastResponse($response); + $this->refreshFrom($response->json, $this->_opts); + return $this; + } + + /** + * @return string The name of the class, with namespacing and underscores + * stripped. + */ + public static function className() + { + $class = get_called_class(); + // Useful for namespaces: Foo\Charge + if ($postfixNamespaces = strrchr($class, '\\')) { + $class = substr($postfixNamespaces, 1); + } + // Useful for underscored 'namespaces': Foo_Charge + if ($postfixFakeNamespaces = strrchr($class, '')) { + $class = $postfixFakeNamespaces; + } + if (substr($class, 0, strlen('Stripe')) == 'Stripe') { + $class = substr($class, strlen('Stripe')); + } + $class = str_replace('_', '', $class); + $name = urlencode($class); + $name = strtolower($name); + return $name; + } + + /** + * @return string The endpoint URL for the given class. + */ + public static function classUrl() + { + $base = static::className(); + return "/v1/${base}s"; + } + + /** + * @return string The instance endpoint URL for the given class. + */ + public static function resourceUrl($id) + { + if ($id === null) { + $class = get_called_class(); + $message = "Could not determine which URL to request: " + . "$class instance has invalid ID: $id"; + throw new Error\InvalidRequest($message, null); + } + $id = Util\Util::utf8($id); + $base = static::classUrl(); + $extn = urlencode($id); + return "$base/$extn"; + } + + /** + * @return string The full API URL for this API resource. + */ + public function instanceUrl() + { + return static::resourceUrl($this['id']); + } + + private static function _validateParams($params = null) + { + if ($params && !is_array($params)) { + $message = "You must pass an array as the first argument to Stripe API " + . "method calls. (HINT: an example call to create a charge " + . "would be: \"Stripe\\Charge::create(array('amount' => 100, " + . "'currency' => 'usd', 'card' => array('number' => " + . "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")"; + throw new Error\Api($message); + } + } + + protected function _request($method, $url, $params = array(), $options = null) + { + $opts = $this->_opts->merge($options); + list($resp, $options) = static::_staticRequest($method, $url, $params, $opts); + $this->setLastResponse($resp); + return array($resp->json, $options); + } + + protected static function _staticRequest($method, $url, $params, $options) + { + $opts = Util\RequestOptions::parse($options); + $requestor = new ApiRequestor($opts->apiKey, static::baseUrl()); + list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers); + foreach ($opts->headers as $k => $v) { + if (!array_key_exists($k, self::$HEADERS_TO_PERSIST)) { + unset($opts->headers[$k]); + } + } + return array($response, $opts); + } + + protected static function _retrieve($id, $options = null) + { + $opts = Util\RequestOptions::parse($options); + $instance = new static($id, $opts); + $instance->refresh(); + return $instance; + } + + protected static function _all($params = null, $options = null) + { + self::_validateParams($params); + $url = static::classUrl(); + + list($response, $opts) = static::_staticRequest('get', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + if (!is_a($obj, 'Stripe\\Collection')) { + $class = get_class($obj); + $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead"; + throw new Error\Api($message); + } + $obj->setLastResponse($response); + $obj->setRequestParams($params); + return $obj; + } + + protected static function _create($params = null, $options = null) + { + self::_validateParams($params); + $base = static::baseUrl(); + $url = static::classUrl(); + + list($response, $opts) = static::_staticRequest('post', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param string $id The ID of the API resource to update. + * @param array|null $params + * @param array|string|null $opts + * + * @return ApiResource the updated API resource + */ + protected static function _update($id, $params = null, $options = null) + { + self::_validateParams($params); + $base = static::baseUrl(); + $url = static::resourceUrl($id); + + list($response, $opts) = static::_staticRequest('post', $url, $params, $options); + $obj = Util\Util::convertToStripeObject($response->json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + protected function _save($options = null) + { + $params = $this->serializeParameters(); + if (count($params) > 0) { + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + } + return $this; + } + + protected function _delete($params = null, $options = null) + { + self::_validateParams($params); + + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('delete', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/ApiResponse.php b/libs/Stripe/ApiResponse.php new file mode 100644 index 00000000..31f54a50 --- /dev/null +++ b/libs/Stripe/ApiResponse.php @@ -0,0 +1,32 @@ +body = $body; + $this->code = $code; + $this->headers = $headers; + $this->json = $json; + } +} diff --git a/libs/Stripe/ApplicationFee.php b/libs/Stripe/ApplicationFee.php new file mode 100644 index 00000000..8145541c --- /dev/null +++ b/libs/Stripe/ApplicationFee.php @@ -0,0 +1,69 @@ +refunds->create($params, $opts); + $this->refresh(); + return $this; + } +} diff --git a/libs/Stripe/ApplicationFeeRefund.php b/libs/Stripe/ApplicationFeeRefund.php new file mode 100644 index 00000000..44695ef8 --- /dev/null +++ b/libs/Stripe/ApplicationFeeRefund.php @@ -0,0 +1,44 @@ +_save($opts); + } +} diff --git a/libs/Stripe/AttachedObject.php b/libs/Stripe/AttachedObject.php new file mode 100644 index 00000000..489517d6 --- /dev/null +++ b/libs/Stripe/AttachedObject.php @@ -0,0 +1,31 @@ +_values), array_keys($properties)); + // Don't unset, but rather set to null so we send up '' for deletion. + foreach ($removed as $k) { + $this->$k = null; + } + + foreach ($properties as $k => $v) { + $this->$k = $v; + } + } +} diff --git a/libs/Stripe/Balance.php b/libs/Stripe/Balance.php new file mode 100644 index 00000000..56c26818 --- /dev/null +++ b/libs/Stripe/Balance.php @@ -0,0 +1,26 @@ +instanceUrl() . '/verify'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/BitcoinReceiver.php b/libs/Stripe/BitcoinReceiver.php new file mode 100644 index 00000000..3f2c835b --- /dev/null +++ b/libs/Stripe/BitcoinReceiver.php @@ -0,0 +1,85 @@ +instanceUrl() . '/refund'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/BitcoinTransaction.php b/libs/Stripe/BitcoinTransaction.php new file mode 100644 index 00000000..6b8e5421 --- /dev/null +++ b/libs/Stripe/BitcoinTransaction.php @@ -0,0 +1,13 @@ +_save($options); + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Charge The refunded charge. + */ + public function refund($params = null, $options = null) + { + $url = $this->instanceUrl() . '/refund'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @return Charge The captured charge. + */ + public function capture($params = null, $options = null) + { + $url = $this->instanceUrl() . '/capture'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|null $params + * @param array|string|null $options + * + * @deprecated Use the `save` method on the Dispute object + * + * @return array The updated dispute. + */ + public function updateDispute($params = null, $options = null) + { + $url = $this->instanceUrl() . '/dispute'; + list($response, $opts) = $this->_request('post', $url, $params, $options); + $this->refreshFrom(array('dispute' => $response), $opts, true); + return $this->dispute; + } + + /** + * @param array|string|null $options + * + * @deprecated Use the `close` method on the Dispute object + * + * @return Charge The updated charge. + */ + public function closeDispute($options = null) + { + $url = $this->instanceUrl() . '/dispute/close'; + list($response, $opts) = $this->_request('post', $url, null, $options); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Charge The updated charge. + */ + public function markAsFraudulent($opts = null) + { + $params = array('fraud_details' => array('user_report' => 'fraudulent')); + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Charge The updated charge. + */ + public function markAsSafe($opts = null) + { + $params = array('fraud_details' => array('user_report' => 'safe')); + $url = $this->instanceUrl(); + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/Collection.php b/libs/Stripe/Collection.php new file mode 100644 index 00000000..61b01e0a --- /dev/null +++ b/libs/Stripe/Collection.php @@ -0,0 +1,87 @@ +_requestParams = $params; + } + + public function all($params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + list($response, $opts) = $this->_request('get', $url, $params, $opts); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + public function create($params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + public function retrieve($id, $params = null, $opts = null) + { + list($url, $params) = $this->extractPathAndUpdateParams($params); + + $id = Util\Util::utf8($id); + $extn = urlencode($id); + list($response, $opts) = $this->_request( + 'get', + "$url/$extn", + $params, + $opts + ); + $this->_requestParams = $params; + return Util\Util::convertToStripeObject($response, $opts); + } + + /** + * @return AutoPagingIterator An iterator that can be used to iterate + * across all objects across all pages. As page boundaries are + * encountered, the next page will be fetched automatically for + * continued iteration. + */ + public function autoPagingIterator() + { + return new Util\AutoPagingIterator($this, $this->_requestParams); + } + + private function extractPathAndUpdateParams($params) + { + $url = parse_url($this->url); + if (!isset($url['path'])) { + throw new Error\Api("Could not parse list url into parts: $url"); + } + + if (isset($url['query'])) { + // If the URL contains a query param, parse it out into $params so they + // don't interact weirdly with each other. + $query = array(); + parse_str($url['query'], $query); + // PHP 5.2 doesn't support the ?: operator :( + $params = array_merge($params ? $params : array(), $query); + } + + return array($url['path'], $params); + } +} diff --git a/libs/Stripe/CountrySpec.php b/libs/Stripe/CountrySpec.php new file mode 100644 index 00000000..dabb88b4 --- /dev/null +++ b/libs/Stripe/CountrySpec.php @@ -0,0 +1,44 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Coupon The saved coupon. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Coupons + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/Stripe/Customer.php b/libs/Stripe/Customer.php new file mode 100644 index 00000000..1dfbfc6a --- /dev/null +++ b/libs/Stripe/Customer.php @@ -0,0 +1,190 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Customer The deleted customer. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + /** + * @param array|null $params + * + * @return InvoiceItem The resulting invoice item. + */ + public function addInvoiceItem($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $ii = InvoiceItem::create($params, $this->_opts); + return $ii; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's Invoices. + */ + public function invoices($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $invoices = Invoice::all($params, $this->_opts); + return $invoices; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's InvoiceItems. + */ + public function invoiceItems($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $iis = InvoiceItem::all($params, $this->_opts); + return $iis; + } + + /** + * @param array|null $params + * + * @return array An array of the customer's Charges. + */ + public function charges($params = null) + { + if (!$params) { + $params = array(); + } + $params['customer'] = $this->id; + $charges = Charge::all($params, $this->_opts); + return $charges; + } + + /** + * @param array|null $params + * + * @return Subscription The updated subscription. + */ + public function updateSubscription($params = null) + { + $url = $this->instanceUrl() . '/subscription'; + list($response, $opts) = $this->_request('post', $url, $params); + $this->refreshFrom(array('subscription' => $response), $opts, true); + return $this->subscription; + } + + /** + * @param array|null $params + * + * @return Subscription The cancelled subscription. + */ + public function cancelSubscription($params = null) + { + $url = $this->instanceUrl() . '/subscription'; + list($response, $opts) = $this->_request('delete', $url, $params); + $this->refreshFrom(array('subscription' => $response), $opts, true); + return $this->subscription; + } + + /** + * @return Customer The updated customer. + */ + public function deleteDiscount() + { + $url = $this->instanceUrl() . '/discount'; + list($response, $opts) = $this->_request('delete', $url); + $this->refreshFrom(array('discount' => null), $opts, true); + } +} diff --git a/libs/Stripe/Dispute.php b/libs/Stripe/Dispute.php new file mode 100644 index 00000000..ce70d499 --- /dev/null +++ b/libs/Stripe/Dispute.php @@ -0,0 +1,83 @@ +_save($options); + } + + /** + * @param array|string|null $options + * + * @return Dispute The closed dispute. + */ + public function close($options = null) + { + $url = $this->instanceUrl() . '/close'; + list($response, $opts) = $this->_request('post', $url, null, $options); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/Error/Api.php b/libs/Stripe/Error/Api.php new file mode 100644 index 00000000..dc7d069e --- /dev/null +++ b/libs/Stripe/Error/Api.php @@ -0,0 +1,7 @@ +httpStatus = $httpStatus; + $this->httpBody = $httpBody; + $this->jsonBody = $jsonBody; + $this->httpHeaders = $httpHeaders; + $this->requestId = null; + + if ($httpHeaders && isset($httpHeaders['Request-Id'])) { + $this->requestId = $httpHeaders['Request-Id']; + } + } + + public function getHttpStatus() + { + return $this->httpStatus; + } + + public function getHttpBody() + { + return $this->httpBody; + } + + public function getJsonBody() + { + return $this->jsonBody; + } + + public function getHttpHeaders() + { + return $this->httpHeaders; + } + + public function getRequestId() + { + return $this->requestId; + } + + public function __toString() + { + $id = $this->requestId ? " from API request '{$this->requestId}'": ""; + $message = explode("\n", parent::__toString()); + $message[0] .= $id; + return implode("\n", $message); + } +} diff --git a/libs/Stripe/Error/Card.php b/libs/Stripe/Error/Card.php new file mode 100644 index 00000000..15551551 --- /dev/null +++ b/libs/Stripe/Error/Card.php @@ -0,0 +1,30 @@ +stripeParam = $stripeParam; + $this->stripeCode = $stripeCode; + } + + public function getStripeCode() + { + return $this->stripeCode; + } + + public function getStripeParam() + { + return $this->stripeParam; + } +} diff --git a/libs/Stripe/Error/InvalidRequest.php b/libs/Stripe/Error/InvalidRequest.php new file mode 100644 index 00000000..493bc184 --- /dev/null +++ b/libs/Stripe/Error/InvalidRequest.php @@ -0,0 +1,23 @@ +stripeParam = $stripeParam; + } + + public function getStripeParam() + { + return $this->stripeParam; + } +} diff --git a/libs/Stripe/Error/RateLimit.php b/libs/Stripe/Error/RateLimit.php new file mode 100644 index 00000000..2fa3be4e --- /dev/null +++ b/libs/Stripe/Error/RateLimit.php @@ -0,0 +1,7 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return ExternalAccount The saved external account. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return ExternalAccount The verified (or not) external account. + */ + public function verify($params = null, $opts = null) + { + if ($this['customer']) { + $url = $this->instanceUrl() . '/verify'; + list($response, $options) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $options); + return $this; + } else { + $message = 'Only customer external accounts can be verified in this manner.'; + throw new Error\Api($message); + } + } +} diff --git a/libs/Stripe/FileUpload.php b/libs/Stripe/FileUpload.php new file mode 100644 index 00000000..7dc98c78 --- /dev/null +++ b/libs/Stripe/FileUpload.php @@ -0,0 +1,61 @@ +defaultOptions = $defaultOptions; + } + + public function getDefaultOptions() + { + return $this->defaultOptions; + } + + // USER DEFINED TIMEOUTS + + const DEFAULT_TIMEOUT = 80; + const DEFAULT_CONNECT_TIMEOUT = 30; + + private $timeout = self::DEFAULT_TIMEOUT; + private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT; + + public function setTimeout($seconds) + { + $this->timeout = (int) max($seconds, 0); + return $this; + } + + public function setConnectTimeout($seconds) + { + $this->connectTimeout = (int) max($seconds, 0); + return $this; + } + + public function getTimeout() + { + return $this->timeout; + } + + public function getConnectTimeout() + { + return $this->connectTimeout; + } + + // END OF USER DEFINED TIMEOUTS + + public function request($method, $absUrl, $headers, $params, $hasFile) + { + $curl = curl_init(); + $method = strtolower($method); + + $opts = array(); + if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value + $opts = call_user_func_array($this->defaultOptions, func_get_args()); + if (!is_array($opts)) { + throw new Error\Api("Non-array value returned by defaultOptions CurlClient callback"); + } + } elseif (is_array($this->defaultOptions)) { // set default curlopts from array + $opts = $this->defaultOptions; + } + + if ($method == 'get') { + if ($hasFile) { + throw new Error\Api( + "Issuing a GET request with a file parameter" + ); + } + $opts[CURLOPT_HTTPGET] = 1; + if (count($params) > 0) { + $encoded = self::encode($params); + $absUrl = "$absUrl?$encoded"; + } + } elseif ($method == 'post') { + $opts[CURLOPT_POST] = 1; + $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params); + } elseif ($method == 'delete') { + $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; + if (count($params) > 0) { + $encoded = self::encode($params); + $absUrl = "$absUrl?$encoded"; + } + } else { + throw new Error\Api("Unrecognized method $method"); + } + + // Create a callback to capture HTTP headers for the response + $rheaders = array(); + $headerCallback = function ($curl, $header_line) use (&$rheaders) { + // Ignore the HTTP request line (HTTP/1.1 200 OK) + if (strpos($header_line, ":") === false) { + return strlen($header_line); + } + list($key, $value) = explode(":", trim($header_line), 2); + $rheaders[trim($key)] = trim($value); + return strlen($header_line); + }; + + // By default for large request body sizes (> 1024 bytes), cURL will + // send a request without a body and with a `Expect: 100-continue` + // header, which gives the server a chance to respond with an error + // status code in cases where one can be determined right away (say + // on an authentication problem for example), and saves the "large" + // request body from being ever sent. + // + // Unfortunately, the bindings don't currently correctly handle the + // success case (in which the server sends back a 100 CONTINUE), so + // we'll error under that condition. To compensate for that problem + // for the time being, override cURL's behavior by simply always + // sending an empty `Expect:` header. + array_push($headers, 'Expect: '); + + $absUrl = Util\Util::utf8($absUrl); + $opts[CURLOPT_URL] = $absUrl; + $opts[CURLOPT_RETURNTRANSFER] = true; + $opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout; + $opts[CURLOPT_TIMEOUT] = $this->timeout; + $opts[CURLOPT_HEADERFUNCTION] = $headerCallback; + $opts[CURLOPT_HTTPHEADER] = $headers; + if (!Stripe::$verifySslCerts) { + $opts[CURLOPT_SSL_VERIFYPEER] = false; + } + + // @codingStandardsIgnoreStart + // PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION + // constants to not abide by those rules. + // + // Explicitly set a TLS version for cURL to use now that we're starting + // to block 1.0 and 1.1 requests. + // + // If users are on OpenSSL >= 1.0.1, we know that they support TLS 1.2, + // so set that explicitly because on some older Linux distros, clients may + // default to TLS 1.0 even when they have TLS 1.2 available. + // + // For users on much older versions of OpenSSL, set a valid range of + // TLS 1.0 to 1.2 (CURL_SSLVERSION_TLSv1). Note that this may result in + // their requests being blocked unless they're specially flagged into + // being able to use an old TLS version. + // + // Note: The int on the right is pulled from the source of OpenSSL 1.0.1a. + if (OPENSSL_VERSION_NUMBER >= 0x1000100f) { + if (!defined('CURL_SSLVERSION_TLSv1_2')) { + // Note the value 6 comes from its position in the enum that + // defines it in cURL's source code. + define('CURL_SSLVERSION_TLSv1_2', 6); // constant not defined in PHP < 5.5 + } + $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2; + } else { + if (!defined('CURL_SSLVERSION_TLSv1')) { + define('CURL_SSLVERSION_TLSv1', 1); // constant not defined in PHP < 5.5 + } + $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1; + } + // @codingStandardsIgnoreEnd + + curl_setopt_array($curl, $opts); + $rbody = curl_exec($curl); + + if (!defined('CURLE_SSL_CACERT_BADFILE')) { + define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP + } + + $errno = curl_errno($curl); + if ($errno == CURLE_SSL_CACERT || + $errno == CURLE_SSL_PEER_CERTIFICATE || + $errno == CURLE_SSL_CACERT_BADFILE + ) { + array_push( + $headers, + 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}' + ); + $cert = self::caBundle(); + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_CAINFO, $cert); + $rbody = curl_exec($curl); + } + + if ($rbody === false) { + $errno = curl_errno($curl); + $message = curl_error($curl); + curl_close($curl); + $this->handleCurlError($absUrl, $errno, $message); + } + + $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + curl_close($curl); + return array($rbody, $rcode, $rheaders); + } + + /** + * @param number $errno + * @param string $message + * @throws Error\ApiConnection + */ + private function handleCurlError($url, $errno, $message) + { + switch ($errno) { + case CURLE_COULDNT_CONNECT: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_OPERATION_TIMEOUTED: + $msg = "Could not connect to Stripe ($url). Please check your " + . "internet connection and try again. If this problem persists, " + . "you should check Stripe's service status at " + . "https://twitter.com/stripestatus, or"; + break; + case CURLE_SSL_CACERT: + case CURLE_SSL_PEER_CERTIFICATE: + $msg = "Could not verify Stripe's SSL certificate. Please make sure " + . "that your network is not intercepting certificates. " + . "(Try going to $url in your browser.) " + . "If this problem persists,"; + break; + default: + $msg = "Unexpected error communicating with Stripe. " + . "If this problem persists,"; + } + $msg .= " let us know at support@stripe.com."; + + $msg .= "\n\n(Network error [errno $errno]: $message)"; + throw new Error\ApiConnection($msg); + } + + private static function caBundle() + { + return dirname(__FILE__) . '/../../data/ca-certificates.crt'; + } + + /** + * @param array $arr An map of param keys to values. + * @param string|null $prefix + * + * Only public for testability, should not be called outside of CurlClient + * + * @return string A querystring, essentially. + */ + public static function encode($arr, $prefix = null) + { + if (!is_array($arr)) { + return $arr; + } + + $r = array(); + foreach ($arr as $k => $v) { + if (is_null($v)) { + continue; + } + + if ($prefix) { + if ($k !== null && (!is_int($k) || is_array($v))) { + $k = $prefix."[".$k."]"; + } else { + $k = $prefix."[]"; + } + } + + if (is_array($v)) { + $enc = self::encode($v, $k); + if ($enc) { + $r[] = $enc; + } + } else { + $r[] = urlencode($k)."=".urlencode($v); + } + } + + return implode("&", $r); + } +} diff --git a/libs/Stripe/Invoice.php b/libs/Stripe/Invoice.php new file mode 100644 index 00000000..e994566c --- /dev/null +++ b/libs/Stripe/Invoice.php @@ -0,0 +1,92 @@ +json, $opts); + $obj->setLastResponse($response); + return $obj; + } + + /** + * @param array|string|null $opts + * + * @return Invoice The saved invoice. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @return Invoice The paid invoice. + */ + public function pay($opts = null) + { + $url = $this->instanceUrl() . '/pay'; + list($response, $opts) = $this->_request('post', $url, null, $opts); + $this->refreshFrom($response, $opts); + return $this; + } +} diff --git a/libs/Stripe/InvoiceItem.php b/libs/Stripe/InvoiceItem.php new file mode 100644 index 00000000..ab9c4ba3 --- /dev/null +++ b/libs/Stripe/InvoiceItem.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return InvoiceItem The deleted invoice item. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/Stripe/JsonSerializable.php b/libs/Stripe/JsonSerializable.php new file mode 100644 index 00000000..2fdf8526 --- /dev/null +++ b/libs/Stripe/JsonSerializable.php @@ -0,0 +1,18 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Orders + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @return Order The paid order. + */ + public function pay($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/pay'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @return OrderReturn The newly created return. + */ + public function returnOrder($params = null, $opts = null) + { + $url = $this->instanceUrl() . '/returns'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + return Util\Util::convertToStripeObject($response, $opts); + } +} diff --git a/libs/Stripe/OrderReturn.php b/libs/Stripe/OrderReturn.php new file mode 100644 index 00000000..aa7fd4e9 --- /dev/null +++ b/libs/Stripe/OrderReturn.php @@ -0,0 +1,33 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Plan The saved plan. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Plans + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } +} diff --git a/libs/Stripe/Product.php b/libs/Stripe/Product.php new file mode 100644 index 00000000..2fdd22a0 --- /dev/null +++ b/libs/Stripe/Product.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of Products + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Product The deleted product. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/Stripe/Recipient.php b/libs/Stripe/Recipient.php new file mode 100644 index 00000000..04bcb7bb --- /dev/null +++ b/libs/Stripe/Recipient.php @@ -0,0 +1,92 @@ +_save($opts); + } + + /** + * @param array|null $params + * + * @return Recipient The deleted recipient. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } + + + /** + * @param array|null $params + * + * @return Collection of the Recipient's Transfers + */ + public function transfers($params = null) + { + if ($params === null) { + $params = array(); + } + $params['recipient'] = $this->id; + $transfers = Transfer::all($params, $this->_opts); + return $transfers; + } +} diff --git a/libs/Stripe/Refund.php b/libs/Stripe/Refund.php new file mode 100644 index 00000000..b4e0e76a --- /dev/null +++ b/libs/Stripe/Refund.php @@ -0,0 +1,79 @@ +_save($opts); + } +} diff --git a/libs/Stripe/SKU.php b/libs/Stripe/SKU.php new file mode 100644 index 00000000..7089604e --- /dev/null +++ b/libs/Stripe/SKU.php @@ -0,0 +1,77 @@ +_save($opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return Collection of SKUs + */ + public static function all($params = null, $opts = null) + { + return self::_all($params, $opts); + } + + /** + * @param array|null $params + * @param array|string|null $opts + * + * @return SKU The deleted sku. + */ + public function delete($params = null, $opts = null) + { + return $this->_delete($params, $opts); + } +} diff --git a/libs/Stripe/SingletonApiResource.php b/libs/Stripe/SingletonApiResource.php new file mode 100644 index 00000000..c6de4448 --- /dev/null +++ b/libs/Stripe/SingletonApiResource.php @@ -0,0 +1,36 @@ +refresh(); + return $instance; + } + + /** + * @return string The endpoint associated with this singleton class. + */ + public static function classUrl() + { + $base = static::className(); + return "/v1/${base}"; + } + + /** + * @return string The endpoint associated with this singleton API resource. + */ + public function instanceUrl() + { + return static::classUrl(); + } +} diff --git a/libs/Stripe/Stripe.php b/libs/Stripe/Stripe.php new file mode 100644 index 00000000..f6345adb --- /dev/null +++ b/libs/Stripe/Stripe.php @@ -0,0 +1,100 @@ +_lastResponse; + } + + /** + * @param ApiResponse + * + * @return void Set the last response from the Stripe API + */ + public function setLastResponse($resp) + { + $this->_lastResponse = $resp; + } + + protected $_opts; + protected $_values; + protected $_unsavedValues; + protected $_transientValues; + protected $_retrieveOptions; + protected $_lastResponse; + + public function __construct($id = null, $opts = null) + { + $this->_opts = $opts ? $opts : new Util\RequestOptions(); + $this->_values = array(); + $this->_unsavedValues = new Util\Set(); + $this->_transientValues = new Util\Set(); + + $this->_retrieveOptions = array(); + if (is_array($id)) { + foreach ($id as $key => $value) { + if ($key != 'id') { + $this->_retrieveOptions[$key] = $value; + } + } + $id = $id['id']; + } + + if ($id !== null) { + $this->id = $id; + } + } + + // Standard accessor magic methods + public function __set($k, $v) + { + if ($v === "") { + throw new InvalidArgumentException( + 'You cannot set \''.$k.'\'to an empty string. ' + .'We interpret empty strings as NULL in requests. ' + .'You may set obj->'.$k.' = NULL to delete the property' + ); + } + + if (self::$nestedUpdatableAttributes->includes($k) + && isset($this->$k) && $this->$k instanceof AttachedObject && is_array($v)) { + $this->$k->replaceWith($v); + } else { + // TODO: may want to clear from $_transientValues (Won't be user-visible). + $this->_values[$k] = $v; + } + if (!self::$permanentAttributes->includes($k)) { + $this->_unsavedValues->add($k); + } + } + + public function __isset($k) + { + return isset($this->_values[$k]); + } + public function __unset($k) + { + unset($this->_values[$k]); + $this->_transientValues->add($k); + $this->_unsavedValues->discard($k); + } + public function &__get($k) + { + // function should return a reference, using $nullval to return a reference to null + $nullval = null; + if (!empty($this->_values) && array_key_exists($k, $this->_values)) { + return $this->_values[$k]; + } else if (!empty($this->_transientValues) && $this->_transientValues->includes($k)) { + $class = get_class($this); + $attrs = join(', ', array_keys($this->_values)); + $message = "Stripe Notice: Undefined property of $class instance: $k. " + . "HINT: The $k attribute was set in the past, however. " + . "It was then wiped when refreshing the object " + . "with the result returned by Stripe's API, " + . "probably as a result of a save(). The attributes currently " + . "available on this object are: $attrs"; + error_log($message); + return $nullval; + } else { + $class = get_class($this); + error_log("Stripe Notice: Undefined property of $class instance: $k"); + return $nullval; + } + } + + // ArrayAccess methods + public function offsetSet($k, $v) + { + $this->$k = $v; + } + + public function offsetExists($k) + { + return array_key_exists($k, $this->_values); + } + + public function offsetUnset($k) + { + unset($this->$k); + } + public function offsetGet($k) + { + return array_key_exists($k, $this->_values) ? $this->_values[$k] : null; + } + + public function keys() + { + return array_keys($this->_values); + } + + /** + * This unfortunately needs to be public to be used in Util\Util + * + * @param array $values + * @param array $opts + * + * @return StripeObject The object constructed from the given values. + */ + public static function constructFrom($values, $opts) + { + $obj = new static(isset($values['id']) ? $values['id'] : null); + $obj->refreshFrom($values, $opts); + return $obj; + } + + /** + * Refreshes this object using the provided values. + * + * @param array $values + * @param array|Util\RequestOptions $opts + * @param boolean $partial Defaults to false. + */ + public function refreshFrom($values, $opts, $partial = false) + { + if (is_array($opts)) { + $opts = Util\RequestOptions::parse($opts); + } + + $this->_opts = $opts; + + // Wipe old state before setting new. This is useful for e.g. updating a + // customer, where there is no persistent card parameter. Mark those values + // which don't persist as transient + if ($partial) { + $removed = new Util\Set(); + } else { + $removed = array_diff(array_keys($this->_values), array_keys($values)); + } + + foreach ($removed as $k) { + if (self::$permanentAttributes->includes($k)) { + continue; + } + + unset($this->$k); + } + + foreach ($values as $k => $v) { + if (self::$permanentAttributes->includes($k) && isset($this[$k])) { + continue; + } + + if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) { + $this->_values[$k] = AttachedObject::constructFrom($v, $opts); + } else { + $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts); + } + + $this->_transientValues->discard($k); + $this->_unsavedValues->discard($k); + } + } + + /** + * @return array A recursive mapping of attributes to values for this object, + * including the proper value for deleted attributes. + */ + public function serializeParameters() + { + $params = array(); + if ($this->_unsavedValues) { + foreach ($this->_unsavedValues->toArray() as $k) { + $v = $this->$k; + if ($v === null) { + $v = ''; + } + + $params[$k] = $v; + } + } + + // Get nested updates. + foreach (self::$nestedUpdatableAttributes->toArray() as $property) { + if (isset($this->$property)) { + if ($this->$property instanceof StripeObject) { + $serialized = $this->$property->serializeParameters(); + if ($serialized) { + $params[$property] = $serialized; + } + } + } + } + + return $params; + } + + public function jsonSerialize() + { + return $this->__toArray(true); + } + + public function __toJSON() + { + if (defined('JSON_PRETTY_PRINT')) { + return json_encode($this->__toArray(true), JSON_PRETTY_PRINT); + } else { + return json_encode($this->__toArray(true)); + } + } + + public function __toString() + { + $class = get_class($this); + return $class . ' JSON: ' . $this->__toJSON(); + } + + public function __toArray($recursive = false) + { + if ($recursive) { + return Util\Util::convertStripeObjectToArray($this->_values); + } else { + return $this->_values; + } + } +} + +StripeObject::init(); diff --git a/libs/Stripe/Subscription.php b/libs/Stripe/Subscription.php new file mode 100644 index 00000000..a95ab091 --- /dev/null +++ b/libs/Stripe/Subscription.php @@ -0,0 +1,97 @@ +_delete($params, $opts); + } + + /** + * @param array|string|null $opts + * + * @return Subscription The saved subscription. + */ + public function save($opts = null) + { + return $this->_save($opts); + } + + /** + * @return Subscription The updated subscription. + */ + public function deleteDiscount() + { + $url = $this->instanceUrl() . '/discount'; + list($response, $opts) = $this->_request('delete', $url); + $this->refreshFrom(array('discount' => null), $opts, true); + } +} diff --git a/libs/Stripe/ThreeDSecure.php b/libs/Stripe/ThreeDSecure.php new file mode 100644 index 00000000..a7dcc5ad --- /dev/null +++ b/libs/Stripe/ThreeDSecure.php @@ -0,0 +1,25 @@ +instanceUrl() . '/reversals'; + list($response, $opts) = $this->_request('post', $url, $params, $opts); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @return Transfer The canceled transfer. + */ + public function cancel() + { + $url = $this->instanceUrl() . '/cancel'; + list($response, $opts) = $this->_request('post', $url); + $this->refreshFrom($response, $opts); + return $this; + } + + /** + * @param array|string|null $opts + * + * @return Transfer The saved transfer. + */ + public function save($opts = null) + { + return $this->_save($opts); + } +} diff --git a/libs/Stripe/TransferReversal.php b/libs/Stripe/TransferReversal.php new file mode 100644 index 00000000..33948c93 --- /dev/null +++ b/libs/Stripe/TransferReversal.php @@ -0,0 +1,53 @@ +_save($opts); + } +} diff --git a/libs/Stripe/Util/AutoPagingIterator.php b/libs/Stripe/Util/AutoPagingIterator.php new file mode 100644 index 00000000..80877e67 --- /dev/null +++ b/libs/Stripe/Util/AutoPagingIterator.php @@ -0,0 +1,61 @@ +page = $collection; + $this->params = $params; + } + + public function rewind() + { + // Actually rewinding would require making a copy of the original page. + } + + public function current() + { + $item = current($this->page->data); + $this->lastId = $item !== false ? $item['id'] : null; + + return $item; + } + + public function key() + { + return key($this->page->data) + $this->pageOffset; + } + + public function next() + { + $item = next($this->page->data); + if ($item === false) { + // If we've run out of data on the current page, try to fetch another one + // and increase the offset the new page would start at + $this->pageOffset += count($this->page->data); + if ($this->page['has_more']) { + $this->params = array_merge( + $this->params ? $this->params : array(), + array('starting_after' => $this->lastId) + ); + $this->page = $this->page->all($this->params); + } else { + return false; + } + } + } + + public function valid() + { + $key = key($this->page->data); + $valid = ($key !== null && $key !== false); + return $valid; + } +} diff --git a/libs/Stripe/Util/RequestOptions.php b/libs/Stripe/Util/RequestOptions.php new file mode 100644 index 00000000..14af2b8a --- /dev/null +++ b/libs/Stripe/Util/RequestOptions.php @@ -0,0 +1,79 @@ +apiKey = $key; + $this->headers = $headers; + } + + /** + * Unpacks an options array and merges it into the existing RequestOptions + * object. + * @param array|string|null $options a key => value array + * + * @return RequestOptions + */ + public function merge($options) + { + $other_options = self::parse($options); + if ($other_options->apiKey === null) { + $other_options->apiKey = $this->apiKey; + } + $other_options->headers = array_merge($this->headers, $other_options->headers); + return $other_options; + } + + /** + * Unpacks an options array into an RequestOptions object + * @param array|string|null $options a key => value array + * + * @return RequestOptions + */ + public static function parse($options) + { + if ($options instanceof self) { + return $options; + } + + if (is_null($options)) { + return new RequestOptions(null, array()); + } + + if (is_string($options)) { + return new RequestOptions($options, array()); + } + + if (is_array($options)) { + $headers = array(); + $key = null; + if (array_key_exists('api_key', $options)) { + $key = $options['api_key']; + } + if (array_key_exists('idempotency_key', $options)) { + $headers['Idempotency-Key'] = $options['idempotency_key']; + } + if (array_key_exists('stripe_account', $options)) { + $headers['Stripe-Account'] = $options['stripe_account']; + } + if (array_key_exists('stripe_version', $options)) { + $headers['Stripe-Version'] = $options['stripe_version']; + } + return new RequestOptions($key, $headers); + } + + $message = 'The second argument to Stripe API method calls is an ' + . 'optional per-request apiKey, which must be a string, or ' + . 'per-request options, which must be an array. (HINT: you can set ' + . 'a global apiKey by "Stripe::setApiKey()")'; + throw new Error\Api($message); + } +} diff --git a/libs/Stripe/Util/Set.php b/libs/Stripe/Util/Set.php new file mode 100644 index 00000000..2a500cd6 --- /dev/null +++ b/libs/Stripe/Util/Set.php @@ -0,0 +1,44 @@ +_elts = array(); + foreach ($members as $item) { + $this->_elts[$item] = true; + } + } + + public function includes($elt) + { + return isset($this->_elts[$elt]); + } + + public function add($elt) + { + $this->_elts[$elt] = true; + } + + public function discard($elt) + { + unset($this->_elts[$elt]); + } + + public function toArray() + { + return array_keys($this->_elts); + } + + public function getIterator() + { + return new ArrayIterator($this->toArray()); + } +} diff --git a/libs/Stripe/Util/Util.php b/libs/Stripe/Util/Util.php new file mode 100644 index 00000000..2f6894e2 --- /dev/null +++ b/libs/Stripe/Util/Util.php @@ -0,0 +1,140 @@ + $v) { + // FIXME: this is an encapsulation violation + if ($k[0] == '_') { + continue; + } + if ($v instanceof StripeObject) { + $results[$k] = $v->__toArray(true); + } elseif (is_array($v)) { + $results[$k] = self::convertStripeObjectToArray($v); + } else { + $results[$k] = $v; + } + } + return $results; + } + + /** + * Converts a response from the Stripe API to the corresponding PHP object. + * + * @param array $resp The response from the Stripe API. + * @param array $opts + * @return StripeObject|array + */ + public static function convertToStripeObject($resp, $opts) + { + $types = array( + 'account' => 'Stripe\\Account', + 'alipay_account' => 'Stripe\\AlipayAccount', + 'bank_account' => 'Stripe\\BankAccount', + 'balance_transaction' => 'Stripe\\BalanceTransaction', + 'card' => 'Stripe\\Card', + 'charge' => 'Stripe\\Charge', + 'country_spec' => 'Stripe\\CountrySpec', + 'coupon' => 'Stripe\\Coupon', + 'customer' => 'Stripe\\Customer', + 'dispute' => 'Stripe\\Dispute', + 'list' => 'Stripe\\Collection', + 'invoice' => 'Stripe\\Invoice', + 'invoiceitem' => 'Stripe\\InvoiceItem', + 'event' => 'Stripe\\Event', + 'file' => 'Stripe\\FileUpload', + 'token' => 'Stripe\\Token', + 'transfer' => 'Stripe\\Transfer', + 'order' => 'Stripe\\Order', + 'order_return' => 'Stripe\\OrderReturn', + 'plan' => 'Stripe\\Plan', + 'product' => 'Stripe\\Product', + 'recipient' => 'Stripe\\Recipient', + 'refund' => 'Stripe\\Refund', + 'sku' => 'Stripe\\SKU', + 'subscription' => 'Stripe\\Subscription', + 'three_d_secure' => 'Stripe\\ThreeDSecure', + 'fee_refund' => 'Stripe\\ApplicationFeeRefund', + 'bitcoin_receiver' => 'Stripe\\BitcoinReceiver', + 'bitcoin_transaction' => 'Stripe\\BitcoinTransaction', + ); + if (self::isList($resp)) { + $mapped = array(); + foreach ($resp as $i) { + array_push($mapped, self::convertToStripeObject($i, $opts)); + } + return $mapped; + } elseif (is_array($resp)) { + if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']])) { + $class = $types[$resp['object']]; + } else { + $class = 'Stripe\\StripeObject'; + } + return $class::constructFrom($resp, $opts); + } else { + return $resp; + } + } + + /** + * @param string|mixed $value A string to UTF8-encode. + * + * @return string|mixed The UTF8-encoded string, or the object passed in if + * it wasn't a string. + */ + public static function utf8($value) + { + if (self::$isMbstringAvailable === null) { + self::$isMbstringAvailable = function_exists('mb_detect_encoding'); + + if (!self::$isMbstringAvailable) { + trigger_error("It looks like the mbstring extension is not enabled. " . + "UTF-8 strings will not properly be encoded. Ask your system " . + "administrator to enable the mbstring extension, or write to " . + "support@stripe.com if you have any questions.", E_USER_WARNING); + } + } + + if (is_string($value) && self::$isMbstringAvailable && mb_detect_encoding($value, "UTF-8", true) != "UTF-8") { + return utf8_encode($value); + } else { + return $value; + } + } +} diff --git a/libs/Stripe/init.php b/libs/Stripe/init.php new file mode 100644 index 00000000..751b2f5e --- /dev/null +++ b/libs/Stripe/init.php @@ -0,0 +1,65 @@ + \ No newline at end of file diff --git a/libs/verysimple/Payment/Stripe.php b/libs/verysimple/Payment/Stripe.php index 3daafe67..1802270b 100644 --- a/libs/verysimple/Payment/Stripe.php +++ b/libs/verysimple/Payment/Stripe.php @@ -3,6 +3,7 @@ /** import supporting libraries */ require_once("PaymentProcessor.php"); +require_once("Stripe/init.php"); /** * Stripe extends the generic PaymentProcessor object to process @@ -112,20 +113,45 @@ public static function setAccountId($accountId) */ function Process(PaymentRequest $req) { - include_once 'StripePHP/Charge.php'; + $resp = new PaymentResponse(); + try { - $charge = Charge::create(array( - "amount" => 1000, // amount in cents, again + $charge = \Stripe\Charge::create(array( + "amount" => $req->TransactionAmount * 100, // amount in cents, again "currency" => "usd", - "source" => $token, - "description" => "Example charge" + "source" => $req->CCNumber, + "description" => $req->OrderDescription )); } catch(\Stripe\Error\Card $e) { // The card has been declined - die(print_r($charge,1)); + $resp->IsSuccess = false; + $resp->ResponseCode = $e->stripeCode; + $resp->ResponseMessage = $e->message; } - - die(print_r($charge,1)); + + $resp->IsSuccess = true; + $resp->TransactionId = $charge->id; + $resp->ResponseCode = $charge->id; + $resp->ResponseMessage = "Charge of $req->TransactionAmount Posted"; + + return $resp; + } + + /** + * Called on contruction + * @param bool $test set to true to enable test mode. default = false + */ + function Init($testmode) + { + // TODO + } + + /** + * @see PaymentProcessor::Refund() + */ + function Refund(RefundRequest $req) + { + throw new Exception("not implemented, use Stripe"); } /** From f02766b7229f354569850f9f820e80c3c3a82b1d Mon Sep 17 00:00:00 2001 From: Christian Dawson Date: Mon, 8 Aug 2016 14:05:36 -0500 Subject: [PATCH 5/5] Added Token to payment request --- libs/verysimple/Payment/Stripe.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/verysimple/Payment/Stripe.php b/libs/verysimple/Payment/Stripe.php index a85ff122..46b43d1d 100644 --- a/libs/verysimple/Payment/Stripe.php +++ b/libs/verysimple/Payment/Stripe.php @@ -119,7 +119,7 @@ function Process(PaymentRequest $req) $charge = \Stripe\Charge::create(array( "amount" => $req->TransactionAmount * 100, // amount in cents, again "currency" => "usd", - "source" => $req->CCNumber, + "source" => $req->Token, "description" => $req->OrderDescription )); } catch(\Stripe\Error\Base $e) {