Update Stripe PHP API

Summary: Ref T2787. This brings us up to date.

Test Plan: `git clone`

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D9916
This commit is contained in:
epriestley
2014-07-13 09:19:07 -07:00
parent 3a59cff7e6
commit 643c1c4a52
37 changed files with 6020 additions and 3282 deletions

View File

@@ -0,0 +1,15 @@
<?php
class Stripe_Account extends Stripe_SingletonApiResource
{
/**
* @param string|null $apiKey
*
* @return Stripe_Account
*/
public static function retrieve($apiKey=null)
{
$class = get_class();
return self::_scopedSingletonRetrieve($class, $apiKey);
}
}

View File

@@ -2,31 +2,57 @@
class Stripe_ApiRequestor
{
/**
* @var string $apiKey The API key that's to be used to make requests.
*/
public $apiKey;
private static $_preFlight;
private static function blacklistedCerts()
{
return array(
'05c0b3643694470a888c6e7feb5c9e24e823dc53',
'5b7dc7fbc98d78bf76d4d4fa6f597a0c901fad5c',
);
}
public function __construct($apiKey=null)
{
$this->_apiKey = $apiKey;
}
/**
* @param string $url The path to the API endpoint.
*
* @returns string The full path.
*/
public static function apiUrl($url='')
{
$apiBase = Stripe::$apiBase;
return "$apiBase$url";
}
/**
* @param string|mixed $value A string to UTF8-encode.
*
* @returns string|mixed The UTF8-encoded string, or the object passed in if
* it wasn't a string.
*/
public static function utf8($value)
{
if (is_string($value))
if (is_string($value)
&& mb_detect_encoding($value, "UTF-8", TRUE) != "UTF-8") {
return utf8_encode($value);
else
} else {
return $value;
}
}
private static function _encodeObjects($d)
{
if ($d instanceof Stripe_ApiRequestor) {
return $d->id;
if ($d instanceof Stripe_ApiResource) {
return self::utf8($d->id);
} else if ($d === true) {
return 'true';
} else if ($d === false) {
@@ -34,70 +60,142 @@ class Stripe_ApiRequestor
} else if (is_array($d)) {
$res = array();
foreach ($d as $k => $v)
$res[$k] = self::_encodeObjects($v);
$res[$k] = self::_encodeObjects($v);
return $res;
} else {
return $d;
return self::utf8($d);
}
}
public static function encode($d)
/**
* @param array $arr An map of param keys to values.
* @param string|null $prefix (It doesn't look like we ever use $prefix...)
*
* @returns string A querystring, essentially.
*/
public static function encode($arr, $prefix=null)
{
return http_build_query($d, null, '&');
if (!is_array($arr))
return $arr;
$r = array();
foreach ($arr as $k => $v) {
if (is_null($v))
continue;
if ($prefix && $k && !is_int($k))
$k = $prefix."[".$k."]";
else if ($prefix)
$k = $prefix."[]";
if (is_array($v)) {
$r[] = self::encode($v, $k, true);
} else {
$r[] = urlencode($k)."=".urlencode($v);
}
}
return implode("&", $r);
}
public function request($meth, $url, $params=null)
/**
* @param string $method
* @param string $url
* @param array|null $params
*
* @return array An array whose first element is the response and second
* element is the API key used to make the request.
*/
public function request($method, $url, $params=null)
{
if (!$params)
$params = array();
list($rbody, $rcode, $myApiKey) = $this->_requestRaw($meth, $url, $params);
list($rbody, $rcode, $myApiKey) =
$this->_requestRaw($method, $url, $params);
$resp = $this->_interpretResponse($rbody, $rcode);
return array($resp, $myApiKey);
}
/**
* @param string $rbody A JSON string.
* @param int $rcode
* @param array $resp
*
* @throws Stripe_InvalidRequestError if the error is caused by the user.
* @throws Stripe_AuthenticationError if the error is caused by a lack of
* permissions.
* @throws Stripe_CardError if the error is the error code is 402 (payment
* required)
* @throws Stripe_ApiError otherwise.
*/
public function handleApiError($rbody, $rcode, $resp)
{
if (!is_array($resp) || !isset($resp['error']))
throw new Stripe_ApiError("Invalid response object from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody, $resp);
if (!is_array($resp) || !isset($resp['error'])) {
$msg = "Invalid response object from API: $rbody "
."(HTTP response code was $rcode)";
throw new Stripe_ApiError($msg, $rcode, $rbody, $resp);
}
$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:
if ($code == 'rate_limit') {
throw new Stripe_RateLimitError(
$msg, $param, $rcode, $rbody, $resp
);
}
case 404:
throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null,
isset($error['param']) ? $error['param'] : null,
$rcode, $rbody, $resp);
throw new Stripe_InvalidRequestError(
$msg, $param, $rcode, $rbody, $resp
);
case 401:
throw new Stripe_AuthenticationError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
throw new Stripe_AuthenticationError($msg, $rcode, $rbody, $resp);
case 402:
throw new Stripe_CardError(isset($error['message']) ? $error['message'] : null,
isset($error['param']) ? $error['param'] : null,
isset($error['code']) ? $error['code'] : null,
$rcode, $rbody, $resp);
throw new Stripe_CardError($msg, $param, $code, $rcode, $rbody, $resp);
default:
throw new Stripe_ApiError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
throw new Stripe_ApiError($msg, $rcode, $rbody, $resp);
}
}
private function _requestRaw($meth, $url, $params)
private function _requestRaw($method, $url, $params)
{
$myApiKey = $this->_apiKey;
if (!$myApiKey)
$myApiKey = Stripe::$apiKey;
if (!$myApiKey)
throw new Stripe_AuthenticationError('No API key provided. (HINT: set your API key using "Stripe::setApiKey(<API-KEY>)". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.');
if (!$myApiKey) {
$msg = 'No API key provided. (HINT: set your API key using '
. '"Stripe::setApiKey(<API-KEY>)". You can generate API keys from '
. 'the Stripe web interface. See https://stripe.com/api for '
. 'details, or email support@stripe.com if you have any questions.';
throw new Stripe_AuthenticationError($msg);
}
$absUrl = $this->apiUrl($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);
'lang' => 'php',
'lang_version' => $langVersion,
'publisher' => 'stripe',
'uname' => $uname);
$headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua),
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION);
list($rbody, $rcode) = $this->_curlRequest($meth, $absUrl, $headers, $params, $myApiKey);
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION,
'Authorization: Bearer ' . $myApiKey);
if (Stripe::$apiVersion)
$headers[] = 'Stripe-Version: ' . Stripe::$apiVersion;
list($rbody, $rcode) = $this->_curlRequest(
$method,
$absUrl,
$headers,
$params
);
return array($rbody, $rcode, $myApiKey);
}
@@ -106,7 +204,9 @@ class Stripe_ApiRequestor
try {
$resp = json_decode($rbody, true);
} catch (Exception $e) {
throw new Stripe_ApiError("Invalid response body from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody);
$msg = "Invalid response body from API: $rbody "
. "(HTTP response code was $rcode)";
throw new Stripe_ApiError($msg, $rcode, $rbody);
}
if ($rcode < 200 || $rcode >= 300) {
@@ -115,28 +215,33 @@ class Stripe_ApiRequestor
return $resp;
}
private function _curlRequest($meth, $absUrl, $headers, $params, $myApiKey)
private function _curlRequest($method, $absUrl, $headers, $params)
{
if (!self::$_preFlight) {
self::$_preFlight = $this->checkSslCert($this->apiUrl());
}
$curl = curl_init();
$meth = strtolower($meth);
$method = strtolower($method);
$opts = array();
if ($meth == 'get') {
if ($method == 'get') {
$opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else if ($meth == 'post') {
} else if ($method == 'post') {
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = self::encode($params);
} else if ($meth == 'delete') {
} else if ($method == 'delete') {
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else {
throw new Stripe_ApiError("Unrecognized method $meth");
throw new Stripe_ApiError("Unrecognized method $method");
}
$absUrl = self::utf8($absUrl);
@@ -146,19 +251,27 @@ class Stripe_ApiRequestor
$opts[CURLOPT_TIMEOUT] = 80;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_USERPWD] = $myApiKey . ':';
if (!Stripe::$verifySslCerts)
$opts[CURLOPT_SSL_VERIFYPEER] = false;
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) {
array_push($headers, 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}');
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 = $this->caBundle();
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_CAINFO,
dirname(__FILE__) . '/../data/ca-certificates.crt');
curl_setopt($curl, CURLOPT_CAINFO, $cert);
$rbody = curl_exec($curl);
}
@@ -174,6 +287,11 @@ class Stripe_ApiRequestor
return array($rbody, $rcode);
}
/**
* @param number $errno
* @param string $message
* @throws Stripe_ApiConnectionError
*/
public function handleCurlError($errno, $message)
{
$apiBase = Stripe::$apiBase;
@@ -181,17 +299,119 @@ class Stripe_ApiRequestor
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$msg = "Could not connect to Stripe ($apiBase). 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 let us know at support@stripe.com.";
break;
$msg = "Could not connect to Stripe ($apiBase). 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 $apiBase in your browser.) If this problem persists, let us know at support@stripe.com.";
break;
$msg = "Could not verify Stripe's SSL certificate. Please make sure "
. "that your network is not intercepting certificates. "
. "(Try going to $apiBase in your browser.) "
. "If this problem persists,";
break;
default:
$msg = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com.";
$msg = "Unexpected error communicating with Stripe. "
. "If this problem persists,";
}
$msg .= " let us know at support@stripe.com.";
$msg .= "\n\n(Network error: $message)";
$msg .= "\n\n(Network error [errno $errno]: $message)";
throw new Stripe_ApiConnectionError($msg);
}
/**
* Preflight the SSL certificate presented by the backend. This isn't 100%
* bulletproof, in that we're not actually validating the transport used to
* communicate with Stripe, merely that the first attempt to does not use a
* revoked certificate.
*
* Unfortunately the interface to OpenSSL doesn't make it easy to check the
* certificate before sending potentially sensitive data on the wire. This
* approach raises the bar for an attacker significantly.
*/
private function checkSslCert($url)
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
error_log(
'Warning: This version of PHP is too old to check SSL certificates '.
'correctly. Stripe cannot guarantee that the server has a '.
'certificate which is not blacklisted'
);
return true;
}
if (strpos(PHP_VERSION, 'hiphop') !== false) {
error_log(
'Warning: HHVM does not support Stripe\'s SSL certificate '.
'verification. (See http://docs.hhvm.com/manual/en/context.ssl.php) '.
'Stripe cannot guarantee that the server has a certificate which is '.
'not blacklisted'
);
return true;
}
$url = parse_url($url);
$port = isset($url["port"]) ? $url["port"] : 443;
$url = "ssl://{$url["host"]}:{$port}";
$sslContext = stream_context_create(
array('ssl' => array(
'capture_peer_cert' => true,
'verify_peer' => true,
'cafile' => $this->caBundle(),
))
);
$result = stream_socket_client(
$url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $sslContext
);
if ($errno !== 0) {
$apiBase = Stripe::$apiBase;
throw new Stripe_ApiConnectionError(
'Could not connect to Stripe (' . $apiBase . '). Please check your '.
'internet connection and try again. If this problem persists, '.
'you should check Stripe\'s service status at '.
'https://twitter.com/stripestatus. Reason was: '.$errstr
);
}
$params = stream_context_get_params($result);
$cert = $params['options']['ssl']['peer_certificate'];
openssl_x509_export($cert, $pemCert);
if (self::isBlackListed($pemCert)) {
throw new Stripe_ApiConnectionError(
'Invalid server certificate. You tried to connect to a server that '.
'has a revoked SSL certificate, which means we cannot securely send '.
'data to that server. Please email support@stripe.com if you need '.
'help connecting to the correct API server.'
);
}
return true;
}
/* Checks if a valid PEM encoded certificate is blacklisted
* @return boolean
*/
public static function isBlackListed($certificate)
{
$certificate = trim($certificate);
$lines = explode("\n", $certificate);
// Kludgily remove the PEM padding
array_shift($lines); array_pop($lines);
$derCert = base64_decode(implode("", $lines));
$fingerprint = sha1($derCert);
return in_array($fingerprint, self::blacklistedCerts());
}
private function caBundle()
{
return dirname(__FILE__) . '/../data/ca-certificates.crt';
}
}

View File

@@ -9,54 +9,102 @@ abstract class Stripe_ApiResource extends Stripe_Object
return $instance;
}
/**
* @returns Stripe_ApiResource The refreshed resource.
*/
public function refresh()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('get', $url);
list($response, $apiKey) = $requestor->request(
'get',
$url,
$this->_retrieveOptions
);
$this->refreshFrom($response, $apiKey);
return $this;
}
}
public static function classUrl($class)
/**
* @param string $class
*
* @returns string The name of the class, with namespacing and underscores
* stripped.
*/
public static function className($class)
{
// Useful for namespaces: Foo\Stripe_Charge
if ($postfix = strrchr($class, '\\'))
$class = substr($postfix, 1);
if (substr($class, 0, strlen('Stripe')) == 'Stripe')
if ($postfixNamespaces = strrchr($class, '\\')) {
$class = substr($postfixNamespaces, 1);
}
// Useful for underscored 'namespaces': Foo_Stripe_Charge
if ($postfixFakeNamespaces = strrchr($class, 'Stripe_')) {
$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}s";
return $name;
}
/**
* @param string $class
*
* @returns string The endpoint URL for the given class.
*/
public static function classUrl($class)
{
$base = self::_scopedLsb($class, 'className', $class);
return "/v1/${base}s";
}
/**
* @returns string The full API URL for this API resource.
*/
public function instanceUrl()
{
$id = $this['id'];
$class = get_class($this);
if (!$id) {
throw new Stripe_InvalidRequestError("Could not determine which URL to request: $class instance has invalid ID: $id");
if ($id === null) {
$message = "Could not determine which URL to request: "
. "$class instance has invalid ID: $id";
throw new Stripe_InvalidRequestError($message, null);
}
$id = Stripe_ApiRequestor::utf8($id);
$base = self::classUrl($class);
$base = $this->_lsb('classUrl', $class);
$extn = urlencode($id);
return "$base/$extn";
}
private static function _validateCall($method, $params=null, $apiKey=null)
{
if ($params && !is_array($params))
throw new Stripe_Error("You must pass an array as the first argument to Stripe API method calls. (HINT: an example call to create a charge would be: \"StripeCharge::create(array('amount' => 100, 'currency' => 'usd', 'card' => array('number' => 4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")");
if ($apiKey && !is_string($apiKey))
throw new Stripe_Error('The second argument to Stripe API method calls is an optional per-request apiKey, which must be a string. (HINT: you can set a global apiKey by "Stripe::setApiKey(<apiKey>)")');
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: \"StripeCharge::create(array('amount' => 100, "
. "'currency' => 'usd', 'card' => array('number' => "
. "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")";
throw new Stripe_Error($message);
}
if ($apiKey && !is_string($apiKey)) {
$message = 'The second argument to Stripe API method calls is an '
. 'optional per-request apiKey, which must be a string. '
. '(HINT: you can set a global apiKey by '
. '"Stripe::setApiKey(<apiKey>)")';
throw new Stripe_Error($message);
}
}
protected static function _scopedAll($class, $params=null, $apiKey=null)
{
self::_validateCall('all', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class);
$url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
@@ -65,19 +113,18 @@ abstract class Stripe_ApiResource extends Stripe_Object
{
self::_validateCall('create', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class);
$url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('post', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
protected function _scopedSave($class)
protected function _scopedSave($class, $apiKey=null)
{
self::_validateCall('save');
if ($this->_unsavedValues) {
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$params = array();
foreach ($this->_unsavedValues->toArray() as $k)
$params[$k] = $this->$k;
$requestor = new Stripe_ApiRequestor($apiKey);
$params = $this->serializeParameters();
if (count($params) > 0) {
$url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom($response, $apiKey);

View File

@@ -0,0 +1,53 @@
<?php
class Stripe_ApplicationFee extends Stripe_ApiResource
{
/**
* This is a special case because the application fee endpoint has an
* underscore in it. The parent `className` function strips underscores.
*
* @return string The name of the class.
*/
public static function className($class)
{
return 'application_fee';
}
/**
* @param string $id The ID of the application fee to retrieve.
* @param string|null $apiKey
*
* @return Stripe_ApplicationFee
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param string|null $params
* @param string|null $apiKey
*
* @return array An array of application fees.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param string|null $params
*
* @return Stripe_ApplicationFee The refunded application fee.
*/
public function refund($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/refund';
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View File

@@ -0,0 +1,23 @@
<?php
// e.g. metadata on Stripe objects.
class Stripe_AttachedObject extends Stripe_Object
{
/**
* Updates this object.
*
* @param array $properties A mapping of properties to update on this object.
*/
public function replaceWith($properties)
{
$removed = array_diff(array_keys($this->_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;
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
class Stripe_Balance extends Stripe_SingletonApiResource
{
/**
* @param string|null $apiKey
*
* @return Stripe_Balance
*/
public static function retrieve($apiKey=null)
{
$class = get_class();
return self::_scopedSingletonRetrieve($class, $apiKey);
}
}

View File

@@ -0,0 +1,39 @@
<?php
class Stripe_BalanceTransaction extends Stripe_ApiResource
{
/**
* @param string $class Ignored.
*
* @return string The class URL for this resource. It needs to be special
* cased because it doesn't fit into the standard resource pattern.
*/
public static function classUrl($class)
{
return "/v1/balance/history";
}
/**
* @param string $id The ID of the balance transaction to retrieve.
* @param string|null $apiKey
*
* @return Stripe_BalanceTransaction
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_BalanceTransactions.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
}

View File

@@ -0,0 +1,66 @@
<?php
class Stripe_Card extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @return string The instance URL for this resource. It needs to be special
* cased because it doesn't fit into the standard resource pattern.
*/
public function instanceUrl()
{
$id = $this['id'];
if (!$id) {
$class = get_class($this);
$msg = "Could not determine which URL to request: $class instance "
. "has invalid ID: $id";
throw new Stripe_InvalidRequestError($msg, null);
}
if (isset($this['customer'])) {
$parent = $this['customer'];
$base = self::classUrl('Stripe_Customer');
} else if (isset($this['recipient'])) {
$parent = $this['recipient'];
$base = self::classUrl('Stripe_Recipient');
} else {
return null;
}
$parent = Stripe_ApiRequestor::utf8($parent);
$id = Stripe_ApiRequestor::utf8($id);
$parentExtn = urlencode($parent);
$extn = urlencode($id);
return "$base/$parentExtn/cards/$extn";
}
/**
* @param array|null $params
*
* @return Stripe_Card The deleted card.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Card The saved card.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View File

@@ -2,9 +2,11 @@
class Stripe_CardError extends Stripe_Error
{
public function __construct($message, $param, $code, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $param, $code, $httpStatus,
$httpBody, $jsonBody
)
{
parent::__construct($message, $http_status, $http_body, $json_body);
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
$this->code = $code;
}

View File

@@ -2,30 +2,56 @@
class Stripe_Charge extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the charge to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Charge
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Charges.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Charge The created charge.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Charge The saved charge.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @return Stripe_Charge The refunded charge.
*/
public function refund($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@@ -35,6 +61,11 @@ class Stripe_Charge extends Stripe_ApiResource
return $this;
}
/**
* @param array|null $params
*
* @return Stripe_Charge The captured charge.
*/
public function capture($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@@ -43,4 +74,30 @@ class Stripe_Charge extends Stripe_ApiResource
$this->refreshFrom($response, $apiKey);
return $this;
}
/**
* @param array|null $params
*
* @return array The updated dispute.
*/
public function updateDispute($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/dispute';
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom(array('dispute' => $response), $apiKey, true);
return $this->dispute;
}
/**
* @return Stripe_Charge The updated charge.
*/
public function closeDispute()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/dispute/close';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View File

@@ -2,30 +2,47 @@
class Stripe_Coupon extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the coupon to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Coupon
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Coupon The created coupon.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param array|null $params
*
* @return Stripe_Coupon The deleted coupon.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Coupons.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View File

@@ -2,42 +2,67 @@
class Stripe_Customer extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the customer to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Customer
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Customers.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Customer The created customer.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @returns Stripe_Customer The saved customer.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @returns Stripe_Customer The deleted customer.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
*
* @returns Stripe_InvoiceItem The resulting invoice item.
*/
public function addInvoiceItem($params=null)
{
if (!$params)
@@ -47,6 +72,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $ii;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_Invoices.
*/
public function invoices($params=null)
{
if (!$params)
@@ -56,6 +86,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $invoices;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_InvoiceItems.
*/
public function invoiceItems($params=null)
{
if (!$params)
@@ -65,6 +100,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $iis;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_Charges.
*/
public function charges($params=null)
{
if (!$params)
@@ -74,6 +114,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $charges;
}
/**
* @param array|null $params
*
* @returns Stripe_Subscription The updated subscription.
*/
public function updateSubscription($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@@ -83,6 +128,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $this->subscription;
}
/**
* @param array|null $params
*
* @returns Stripe_Subscription The cancelled subscription.
*/
public function cancelSubscription($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@@ -91,4 +141,17 @@ class Stripe_Customer extends Stripe_ApiResource
$this->refreshFrom(array('subscription' => $response), $apiKey, true);
return $this->subscription;
}
/**
* @param array|null $params
*
* @returns Stripe_Customer The updated customer.
*/
public function deleteDiscount()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/discount';
list($response, $apiKey) = $requestor->request('delete', $url);
$this->refreshFrom(array('discount' => null), $apiKey, true);
}
}

View File

@@ -2,26 +2,28 @@
class Stripe_Error extends Exception
{
public function __construct($message=null, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message);
$this->http_status = $http_status;
$this->http_body = $http_body;
$this->json_body = $json_body;
$this->httpStatus = $httpStatus;
$this->httpBody = $httpBody;
$this->jsonBody = $jsonBody;
}
public function getHttpStatus()
{
return $this->http_status;
return $this->httpStatus;
}
public function getHttpBody()
{
return $this->http_body;
return $this->httpBody;
}
public function getJsonBody()
{
return $this->json_body;
return $this->jsonBody;
}
}

View File

@@ -2,18 +2,24 @@
class Stripe_Event extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the event to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Event
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Events.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View File

@@ -2,9 +2,11 @@
class Stripe_InvalidRequestError extends Stripe_Error
{
public function __construct($message, $param, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $param, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message, $http_status, $http_body, $json_body);
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
}
}

View File

@@ -2,24 +2,48 @@
class Stripe_Invoice extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Invoice The created invoice.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param string $id The ID of the invoice to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Invoice
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Invoices.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Invoice The upcoming invoice.
*/
public static function upcoming($params=null, $apiKey=null)
{
$requestor = new Stripe_ApiRequestor($apiKey);
@@ -27,4 +51,25 @@ class Stripe_Invoice extends Stripe_ApiResource
list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
/**
* @return Stripe_Invoice The saved invoice.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_Invoice The paid invoice.
*/
public function pay()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/pay';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View File

@@ -2,36 +2,54 @@
class Stripe_InvoiceItem extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the invoice item to retrieve.
* @param string|null $apiKey
*
* @return Stripe_InvoiceItem
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_InvoiceItems.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_InvoiceItem The created invoice item.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_InvoiceItem The saved invoice item.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_InvoiceItem The deleted invoice item.
*/
public function delete($params=null)
{
$class = get_class();

View File

@@ -0,0 +1,37 @@
<?php
class Stripe_List extends Stripe_Object
{
public function all($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
list($response, $apiKey) = $requestor->request(
'get',
$this['url'],
$params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
public function create($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
list($response, $apiKey) = $requestor->request(
'post', $this['url'], $params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
public function retrieve($id, $params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$base = $this['url'];
$id = Stripe_ApiRequestor::utf8($id);
$extn = urlencode($id);
list($response, $apiKey) = $requestor->request(
'get', "$base/$extn", $params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
}

View File

@@ -2,17 +2,28 @@
class Stripe_Object implements ArrayAccess
{
public static $_permanentAttributes;
/**
* @var Stripe_Util_Set Attributes that should not be sent to the API because
* they're not updatable (e.g. API key, ID).
*/
public static $permanentAttributes;
/**
* @var Stripe_Util_Set Attributes that are nested but still updatable from
* the parent class's URL (e.g. metadata).
*/
public static $nestedUpdatableAttributes;
public static function init()
{
self::$_permanentAttributes = new Stripe_Util_Set(array('_apiKey'));
self::$permanentAttributes = new Stripe_Util_Set(array('_apiKey', 'id'));
self::$nestedUpdatableAttributes = new Stripe_Util_Set(array('metadata'));
}
protected $_apiKey;
protected $_values;
protected $_unsavedValues;
protected $_transientValues;
protected $_retrieveOptions;
public function __construct($id=null, $apiKey=null)
{
@@ -20,16 +31,41 @@ class Stripe_Object implements ArrayAccess
$this->_values = array();
$this->_unsavedValues = new Stripe_Util_Set();
$this->_transientValues = new Stripe_Util_Set();
if ($id)
$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)
{
// TODO: may want to clear from $_transientValues. (Won't be user-visible.)
$this->_values[$k] = $v;
if (!self::$_permanentAttributes->includes($k))
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) && 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)
@@ -44,12 +80,18 @@ class Stripe_Object implements ArrayAccess
}
public function __get($k)
{
if (isset($this->_values[$k])) {
if (array_key_exists($k, $this->_values)) {
return $this->_values[$k];
} else if ($this->_transientValues->includes($k)) {
$class = get_class($this);
$attrs = join(', ', array_keys($this->_values));
error_log("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");
$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 null;
} else {
$class = get_class($this);
@@ -63,20 +105,35 @@ class Stripe_Object implements ArrayAccess
{
$this->$k = $v;
}
public function offsetExists($k)
{
return isset($this->$k);
return array_key_exists($k, $this->_values);
}
public function offsetUnset($k)
{
unset($this->$k);
}
public function offsetGet($k)
{
return isset($this->_values[$k]) ? $this->_values[$k] : null;
return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
}
// This unfortunately needs to be public to be used in Util.php
public function keys()
{
return array_keys($this->_values);
}
/**
* This unfortunately needs to be public to be used in Util.php
*
* @param string $class
* @param array $values
* @param string|null $apiKey
*
* @return Stripe_Object The object constructed from the given values.
*/
public static function scopedConstructFrom($class, $values, $apiKey=null)
{
$obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
@@ -84,44 +141,108 @@ class Stripe_Object implements ArrayAccess
return $obj;
}
/**
* @param array $values
* @param string|null $apiKey
*
* @return Stripe_Object The object of the same class as $this constructed
* from the given values.
*/
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
return self::scopedConstructFrom(__CLASS__, $values, $apiKey);
}
/**
* Refreshes this object using the provided values.
*
* @param array $values
* @param string $apiKey
* @param boolean $partial Defaults to false.
*/
public function refreshFrom($values, $apiKey, $partial=false)
{
$this->_apiKey = $apiKey;
// 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)
if ($partial) {
$removed = new Stripe_Util_Set();
else
} else {
$removed = array_diff(array_keys($this->_values), array_keys($values));
}
foreach ($removed as $k) {
if (self::$_permanentAttributes->includes($k))
if (self::$permanentAttributes->includes($k))
continue;
unset($this->$k);
}
foreach ($values as $k => $v) {
if (self::$_permanentAttributes->includes($k))
if (self::$permanentAttributes->includes($k) && isset($this[$k]))
continue;
$this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
$this->_values[$k] = Stripe_Object::scopedConstructFrom(
'Stripe_AttachedObject', $v, $apiKey
);
} else {
$this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
}
$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)
&& $this->$property instanceOf Stripe_Object) {
$params[$property] = $this->$property->serializeParameters();
}
}
return $params;
}
// Pretend to have late static bindings, even in PHP 5.2
protected function _lsb($method)
{
$class = get_class($this);
$args = array_slice(func_get_args(), 1);
return call_user_func_array(array($class, $method), $args);
}
protected static function _scopedLsb($class, $method)
{
$args = array_slice(func_get_args(), 2);
return call_user_func_array(array($class, $method), $args);
}
public function __toJSON()
{
if (defined('JSON_PRETTY_PRINT'))
if (defined('JSON_PRETTY_PRINT')) {
return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
else
} else {
return json_encode($this->__toArray(true));
}
}
public function __toString()
@@ -131,10 +252,11 @@ class Stripe_Object implements ArrayAccess
public function __toArray($recursive=false)
{
if ($recursive)
if ($recursive) {
return Stripe_Util::convertStripeObjectToArray($this->_values);
else
} else {
return $this->_values;
}
}
}

View File

@@ -2,36 +2,56 @@
class Stripe_Plan extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the plan to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Plan
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Plan The created plan.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param array|null $params
*
* @return Stripe_Plan The deleted plan.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Plan The saved plan.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Plans.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View File

@@ -0,0 +1,11 @@
<?php
class Stripe_RateLimitError extends Stripe_InvalidRequestError
{
public function __construct($message, $param, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
}
}

View File

@@ -0,0 +1,75 @@
<?php
class Stripe_Recipient extends Stripe_ApiResource
{
/**
* @param string $id The ID of the recipient to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Recipient
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Recipients.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Recipient The created recipient.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Recipient The saved recipient.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @return Stripe_Recipient The deleted recipient.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
*
* @return array An array of the recipient's Stripe_Transfers.
*/
public function transfers($params=null)
{
if (!$params)
$params = array();
$params['recipient'] = $this->id;
$transfers = Stripe_Transfer::all($params, $this->_apiKey);
return $transfers;
}
}

View File

@@ -0,0 +1,36 @@
<?php
class Stripe_Refund extends Stripe_ApiResource
{
/**
* @return string The API URL for this Stripe refund.
*/
public function instanceUrl()
{
$id = $this['id'];
$charge = $this['charge'];
if (!$id) {
throw new Stripe_InvalidRequestError(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = Stripe_ApiRequestor::utf8($id);
$charge = Stripe_ApiRequestor::utf8($charge);
$base = self::classUrl('Stripe_Charge');
$chargeExtn = urlencode($charge);
$extn = urlencode($id);
return "$base/$chargeExtn/refunds/$extn";
}
/**
* @return Stripe_Refund The saved refund.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View File

@@ -0,0 +1,31 @@
<?php
abstract class Stripe_SingletonApiResource extends Stripe_ApiResource
{
protected static function _scopedSingletonRetrieve($class, $apiKey=null)
{
$instance = new $class(null, $apiKey);
$instance->refresh();
return $instance;
}
/**
* @param Stripe_SingletonApiResource $class
* @return string The endpoint associated with this singleton class.
*/
public static function classUrl($class)
{
$base = self::className($class);
return "/v1/${base}";
}
/**
* @return string The endpoint associated with this singleton API resource.
*/
public function instanceUrl()
{
$class = get_class($this);
$base = self::classUrl($class);
return "$base";
}
}

View File

@@ -0,0 +1,73 @@
<?php
abstract class Stripe
{
/**
* @var string The Stripe API key to be used for requests.
*/
public static $apiKey;
/**
* @var string The base URL for the Stripe API.
*/
public static $apiBase = 'https://api.stripe.com';
/**
* @var string|null The version of the Stripe API to use for requests.
*/
public static $apiVersion = null;
/**
* @var boolean Defaults to true.
*/
public static $verifySslCerts = true;
const VERSION = '1.16.0';
/**
* @return string The API key used for requests.
*/
public static function getApiKey()
{
return self::$apiKey;
}
/**
* Sets the API key to be used for requests.
*
* @param string $apiKey
*/
public static function setApiKey($apiKey)
{
self::$apiKey = $apiKey;
}
/**
* @return string The API version used for requests. null if we're using the
* latest version.
*/
public static function getApiVersion()
{
return self::$apiVersion;
}
/**
* @param string $apiVersion The API version to use for requests.
*/
public static function setApiVersion($apiVersion)
{
self::$apiVersion = $apiVersion;
}
/**
* @return boolean
*/
public static function getVerifySslCerts()
{
return self::$verifySslCerts;
}
/**
* @param boolean $verify
*/
public static function setVerifySslCerts($verify)
{
self::$verifySslCerts = $verify;
}
}

View File

@@ -0,0 +1,57 @@
<?php
class Stripe_Subscription extends Stripe_ApiResource
{
/**
* @return string The API URL for this Stripe subscription.
*/
public function instanceUrl()
{
$id = $this['id'];
$customer = $this['customer'];
if (!$id) {
throw new Stripe_InvalidRequestError(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = Stripe_ApiRequestor::utf8($id);
$customer = Stripe_ApiRequestor::utf8($customer);
$base = self::classUrl('Stripe_Customer');
$customerExtn = urlencode($customer);
$extn = urlencode($id);
return "$base/$customerExtn/subscriptions/$extn";
}
/**
* @param array|null $params
* @return Stripe_Subscription The deleted subscription.
*/
public function cancel($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Subscription The saved subscription.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_Subscription The updated subscription.
*/
public function deleteDiscount()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/discount';
list($response, $apiKey) = $requestor->request('delete', $url);
$this->refreshFrom(array('discount' => null), $apiKey, true);
}
}

View File

@@ -2,18 +2,24 @@
class Stripe_Token extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the token to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Token
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Coupon The created token.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();

View File

@@ -0,0 +1,62 @@
<?php
class Stripe_Transfer extends Stripe_ApiResource
{
/**
* @param string $id The ID of the transfer to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Transfer
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Transfers.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Transfer The created transfer.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Transfer The canceled transfer.
*/
public function cancel()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/cancel';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
/**
* @return Stripe_Transfer The saved transfer.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View File

@@ -2,11 +2,18 @@
abstract class Stripe_Util
{
/**
* Whether the provided array (or other) is a list rather than a dictionary.
*
* @param array|mixed $array
* @return boolean True if the given object is a list.
*/
public static function isList($array)
{
if (!is_array($array))
return false;
// TODO: this isn't actually correct in general, but it's correct given Stripe's responses
// TODO: generally incorrect, but it's correct given Stripe's response
foreach (array_keys($array) as $k) {
if (!is_numeric($k))
return false;
@@ -14,43 +21,67 @@ abstract class Stripe_Util
return true;
}
/**
* Recursively converts the PHP Stripe object to an array.
*
* @param array $values The PHP Stripe object to convert.
* @return array
*/
public static function convertStripeObjectToArray($values)
{
$results = array();
foreach ($values as $k => $v) {
// FIXME: this is an encapsulation violation
if (Stripe_Object::$_permanentAttributes->includes($k)) {
if ($k[0] == '_') {
continue;
}
if ($v instanceof Stripe_Object) {
$results[$k] = $v->__toArray(true);
}
else if (is_array($v)) {
} else if (is_array($v)) {
$results[$k] = self::convertStripeObjectToArray($v);
}
else {
} 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 string $apiKey
* @return Stripe_Object|array
*/
public static function convertToStripeObject($resp, $apiKey)
{
$types = array('charge' => 'Stripe_Charge',
'customer' => 'Stripe_Customer',
'invoice' => 'Stripe_Invoice',
'invoiceitem' => 'Stripe_InvoiceItem', 'event' => 'Stripe_Event');
$types = array(
'card' => 'Stripe_Card',
'charge' => 'Stripe_Charge',
'customer' => 'Stripe_Customer',
'list' => 'Stripe_List',
'invoice' => 'Stripe_Invoice',
'invoiceitem' => 'Stripe_InvoiceItem',
'event' => 'Stripe_Event',
'transfer' => 'Stripe_Transfer',
'plan' => 'Stripe_Plan',
'recipient' => 'Stripe_Recipient',
'refund' => 'Stripe_Refund',
'subscription' => 'Stripe_Subscription'
);
if (self::isList($resp)) {
$mapped = array();
foreach ($resp as $i)
array_push($mapped, self::convertToStripeObject($i, $apiKey));
return $mapped;
} else if (is_array($resp)) {
if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']]))
if (isset($resp['object'])
&& is_string($resp['object'])
&& isset($types[$resp['object']])) {
$class = $types[$resp['object']];
else
} else {
$class = 'Stripe_Object';
}
return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey);
} else {
return $resp;