1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 03:20:59 +01:00

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

17
externals/stripe-php/.travis.yml vendored Normal file
View file

@ -0,0 +1,17 @@
language: php
php:
- 5.2
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.2' ]; then wget http://iweb.dl.sourceforge.net/project/simpletest/simpletest/simpletest_1.1/simpletest_1.1.0.tar.gz; tar xf simpletest_1.1.0.tar.gz -C test; else composer install --dev --prefer-source; fi"
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.6' ]; then pear install pear/PHP_CodeSniffer; phpenv rehash; fi"
script:
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.6' ]; then phpcs --standard=zend --encoding=UTF-8 --ignore=vendor -p ./; fi"
- php test/Stripe.php

156
externals/stripe-php/CHANGELOG vendored Normal file
View file

@ -0,0 +1,156 @@
=== 1.16.0 2014-06-17
* Add metadata for refunds and disputes
=== 1.15.0 2014-05-28
* Support canceling transfers
=== 1.14.1 2014-05-21
* Support cards for recipients.
=== 1.13.1 2014-05-15
* Fix bug in account resource where `id` wasn't in the result
=== 1.13.0 2014-04-10
* Add support for certificate blacklisting
* Update ca bundle
* Drop support for HHVM (Temporarily)
=== 1.12.0 2014-04-01
* Add Stripe_RateLimitError for catching rate limit errors.
* Update to Zend coding style (thanks, @jpiasetz)
=== 1.11.0 2014-01-29
* Add support for multiple subscriptions per customer
=== 1.10.1 2013-12-02
* Add new ApplicationFee
=== 1.9.1 2013-11-08
* Fix a bug where a null nestable object causes warnings to fire.
=== 1.9.0 2013-10-16
* Add support for metadata API.
=== 1.8.4 2013-09-18
* Add support for closing disputes.
=== 1.8.3 2013-08-13
* Add new Balance and BalanceTransaction
=== 1.8.2 2013-08-12
* Add support for unsetting attributes by updating to NULL.
Setting properties to a blank string is now an error.
=== 1.8.1 2013-07-12
* Add support for multiple cards API (Stripe API version 2013-07-12: https://stripe.com/docs/upgrades#2013-07-05)
=== 1.8.0 2013-04-11
* Allow Transfers to be creatable
* Add new Recipient resource
=== 1.7.15 2013-02-21
* Add 'id' to the list of permanent object attributes
=== 1.7.14 2013-02-20
* Don't re-encode strings that are already encoded in UTF-8. If you
were previously using plan or coupon objects with UTF-8 IDs, they
may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a
2nd time. You may now need to pass the IDs to utf8_encode before
passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve.
* Ensure that all input is encoded in UTF-8 before submitting it to
Stripe's servers. (github issue #27)
=== 1.7.13 2013-02-01
* Add support for passing options when retrieving Stripe objects
e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer")))
Stripe_Charge::retrieve("foo") will continue to work
=== 1.7.12 2013-01-15
* Add support for setting a Stripe API version override
=== 1.7.11 2012-12-30
* Version bump to cleanup constants and such (github issue #26)
=== 1.7.10 2012-11-08
* Add support for updating charge disputes.
* Fix bug preventing retrieval of null attributes
=== 1.7.9 2012-11-08
* Fix usage under autoloaders such as the one generated by composer
(github issue #22)
=== 1.7.8 2012-10-30
* Add support for creating invoices.
* Add support for new invoice lines return format
* Add support for new list objects
=== 1.7.7 2012-09-14
* Get all of the various version numbers in the repo in sync (no other
changes)
=== 1.7.6 2012-08-31
* Add update and pay methods to Invoice resource
=== 1.7.5 2012-08-23
* Change internal function names so that Stripe_SingletonApiRequest is
E_STRICT-clean (github issue #16)
=== 1.7.4 2012-08-21
* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used
in API calls are transparently converted to their object IDs
=== 1.7.3 2012-08-15
* Add new Account resource
=== 1.7.2 2012-06-26
* Make clearer that you should be including lib/Stripe.php, not
test/Stripe.php (github issue #14)
=== 1.7.1 2012-05-24
* Add missing argument to Stripe_InvalidRequestError constructor in
Stripe_ApiResource::instanceUrl. Fixes a warning when
Stripe_ApiResource::instanceUrl is called on a resource with no ID
(github issue #12)
=== 1.7.0 2012-05-17
* Support Composer and Packagist (github issue #9)
* Add new deleteDiscount method to Stripe_Customer
* Add new Transfer resource
* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will
support Basic auth for the indefinite future, but recommends Bearer
auth when possible going forward)
* Numerous test suite improvements

View file

@ -1,6 +1,6 @@
The MIT License The MIT License
Copyright (c) 2010 Stripe Copyright (c) 2010-2014 Stripe
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,13 +1,49 @@
= Installation = Stripe PHP bindings
{<img src="https://travis-ci.org/stripe/stripe-php.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/v/stable.svg" alt="Latest Stable Version" />}[https://packagist.org/packages/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/downloads.svg" alt="Total Downloads" />}[https://packagist.org/packages/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/license.svg" alt="License" />}[https://packagist.org/packages/stripe/stripe-php]
You can sign up for a Stripe account at https://stripe.com.
== Requirements
PHP 5.2 and later.
== Composer
You can install the bindings via Composer[http://getcomposer.org/]. Add this to your +composer.json+:
{
"require": {
"stripe/stripe-php": "1.*"
}
}
Then install via:
composer.phar install
To use the bindings, either user Composer's autoload[https://getcomposer.org/doc/00-intro.md#autoloading]:
require_once('vendor/autoload.php');
Or manually:
require_once('/path/to/vendor/stripe/stripe-php/lib/Stripe.php');
== Manual Installation
Obtain the latest version of the Stripe PHP bindings with: Obtain the latest version of the Stripe PHP bindings with:
git clone https://github.com/stripe/stripe-php git clone https://github.com/stripe/stripe-php
To get started, add the following to your PHP script: To use the bindings, add the following to your PHP script:
require_once("/path/to/stripe-php/lib/Stripe.php"); require_once("/path/to/stripe-php/lib/Stripe.php");
== Getting Started
Simple usage looks like: Simple usage looks like:
Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249'); Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
@ -15,6 +51,17 @@ Simple usage looks like:
$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd')); $charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
echo $charge; echo $charge;
= Documentation == Documentation
Please see https://stripe.com/api for up-to-date documentation. Please see https://stripe.com/api for up-to-date documentation.
== Tests
In order to run tests you have to install SimpleTest[http://packagist.org/packages/simpletest/simpletest] via Composer[http://getcomposer.org/] (recommended way):
composer.phar update --dev
Run test suite:
php ./test/Stripe.php

View file

@ -1 +1 @@
1.6.3 1.16.0

29
externals/stripe-php/composer.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
"name": "stripe/stripe-php",
"description": "Stripe PHP Library",
"keywords": [
"stripe",
"payment processing",
"api"
],
"homepage": "https://stripe.com/",
"license": "MIT",
"authors": [
{
"name": "Stripe and contributors",
"homepage": "https://github.com/stripe/stripe-php/contributors"
}
],
"require": {
"php": ">=5.2",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*"
},
"require-dev": {
"simpletest/simpletest": "*"
},
"autoload": {
"classmap": ["lib/Stripe/"]
}
}

View file

@ -9,34 +9,12 @@ if (!function_exists('curl_init')) {
if (!function_exists('json_decode')) { if (!function_exists('json_decode')) {
throw new Exception('Stripe needs the JSON PHP extension.'); throw new Exception('Stripe needs the JSON PHP extension.');
} }
if (!function_exists('mb_detect_encoding')) {
throw new Exception('Stripe needs the Multibyte String PHP extension.');
abstract class Stripe
{
public static $apiKey;
public static $apiBase = 'https://api.stripe.com/v1';
public static $verifySslCerts = true;
const VERSION = '1.6.3';
public static function getApiKey()
{
return self::$apiKey;
}
public static function setApiKey($apiKey)
{
self::$apiKey = $apiKey;
}
public static function getVerifySslCerts() {
return self::$verifySslCerts;
}
public static function setVerifySslCerts($verify) {
self::$verifySslCerts = $verify;
}
} }
// Stripe singleton
require(dirname(__FILE__) . '/Stripe/Stripe.php');
// Utilities // Utilities
require(dirname(__FILE__) . '/Stripe/Util.php'); require(dirname(__FILE__) . '/Stripe/Util.php');
@ -49,18 +27,31 @@ require(dirname(__FILE__) . '/Stripe/ApiConnectionError.php');
require(dirname(__FILE__) . '/Stripe/AuthenticationError.php'); require(dirname(__FILE__) . '/Stripe/AuthenticationError.php');
require(dirname(__FILE__) . '/Stripe/CardError.php'); require(dirname(__FILE__) . '/Stripe/CardError.php');
require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php'); require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php');
require(dirname(__FILE__) . '/Stripe/RateLimitError.php');
// Plumbing // Plumbing
require(dirname(__FILE__) . '/Stripe/Object.php'); require(dirname(__FILE__) . '/Stripe/Object.php');
require(dirname(__FILE__) . '/Stripe/ApiRequestor.php'); require(dirname(__FILE__) . '/Stripe/ApiRequestor.php');
require(dirname(__FILE__) . '/Stripe/ApiResource.php'); require(dirname(__FILE__) . '/Stripe/ApiResource.php');
require(dirname(__FILE__) . '/Stripe/SingletonApiResource.php');
require(dirname(__FILE__) . '/Stripe/AttachedObject.php');
require(dirname(__FILE__) . '/Stripe/List.php');
// Stripe API Resources // Stripe API Resources
require(dirname(__FILE__) . '/Stripe/Account.php');
require(dirname(__FILE__) . '/Stripe/Card.php');
require(dirname(__FILE__) . '/Stripe/Balance.php');
require(dirname(__FILE__) . '/Stripe/BalanceTransaction.php');
require(dirname(__FILE__) . '/Stripe/Charge.php'); require(dirname(__FILE__) . '/Stripe/Charge.php');
require(dirname(__FILE__) . '/Stripe/Customer.php'); require(dirname(__FILE__) . '/Stripe/Customer.php');
require(dirname(__FILE__) . '/Stripe/Invoice.php'); require(dirname(__FILE__) . '/Stripe/Invoice.php');
require(dirname(__FILE__) . '/Stripe/InvoiceItem.php'); require(dirname(__FILE__) . '/Stripe/InvoiceItem.php');
require(dirname(__FILE__) . '/Stripe/Plan.php'); require(dirname(__FILE__) . '/Stripe/Plan.php');
require(dirname(__FILE__) . '/Stripe/Subscription.php');
require(dirname(__FILE__) . '/Stripe/Token.php'); require(dirname(__FILE__) . '/Stripe/Token.php');
require(dirname(__FILE__) . '/Stripe/Coupon.php'); require(dirname(__FILE__) . '/Stripe/Coupon.php');
require(dirname(__FILE__) . '/Stripe/Event.php'); require(dirname(__FILE__) . '/Stripe/Event.php');
require(dirname(__FILE__) . '/Stripe/Transfer.php');
require(dirname(__FILE__) . '/Stripe/Recipient.php');
require(dirname(__FILE__) . '/Stripe/Refund.php');
require(dirname(__FILE__) . '/Stripe/ApplicationFee.php');

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 class Stripe_ApiRequestor
{ {
/**
* @var string $apiKey The API key that's to be used to make requests.
*/
public $apiKey; public $apiKey;
private static $_preFlight;
private static function blacklistedCerts()
{
return array(
'05c0b3643694470a888c6e7feb5c9e24e823dc53',
'5b7dc7fbc98d78bf76d4d4fa6f597a0c901fad5c',
);
}
public function __construct($apiKey=null) public function __construct($apiKey=null)
{ {
$this->_apiKey = $apiKey; $this->_apiKey = $apiKey;
} }
/**
* @param string $url The path to the API endpoint.
*
* @returns string The full path.
*/
public static function apiUrl($url='') public static function apiUrl($url='')
{ {
$apiBase = Stripe::$apiBase; $apiBase = Stripe::$apiBase;
return "$apiBase$url"; 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) 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); return utf8_encode($value);
else } else {
return $value; return $value;
}
} }
private static function _encodeObjects($d) private static function _encodeObjects($d)
{ {
if ($d instanceof Stripe_ApiRequestor) { if ($d instanceof Stripe_ApiResource) {
return $d->id; return self::utf8($d->id);
} else if ($d === true) { } else if ($d === true) {
return 'true'; return 'true';
} else if ($d === false) { } else if ($d === false) {
@ -34,70 +60,142 @@ class Stripe_ApiRequestor
} else if (is_array($d)) { } else if (is_array($d)) {
$res = array(); $res = array();
foreach ($d as $k => $v) foreach ($d as $k => $v)
$res[$k] = self::_encodeObjects($v); $res[$k] = self::_encodeObjects($v);
return $res; return $res;
} else { } 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) if (!$params)
$params = array(); $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); $resp = $this->_interpretResponse($rbody, $rcode);
return array($resp, $myApiKey); 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) public function handleApiError($rbody, $rcode, $resp)
{ {
if (!is_array($resp) || !isset($resp['error'])) 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); $msg = "Invalid response object from API: $rbody "
."(HTTP response code was $rcode)";
throw new Stripe_ApiError($msg, $rcode, $rbody, $resp);
}
$error = $resp['error']; $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) { switch ($rcode) {
case 400: case 400:
if ($code == 'rate_limit') {
throw new Stripe_RateLimitError(
$msg, $param, $rcode, $rbody, $resp
);
}
case 404: case 404:
throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null, throw new Stripe_InvalidRequestError(
isset($error['param']) ? $error['param'] : null, $msg, $param, $rcode, $rbody, $resp
$rcode, $rbody, $resp); );
case 401: 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: case 402:
throw new Stripe_CardError(isset($error['message']) ? $error['message'] : null, throw new Stripe_CardError($msg, $param, $code, $rcode, $rbody, $resp);
isset($error['param']) ? $error['param'] : null,
isset($error['code']) ? $error['code'] : null,
$rcode, $rbody, $resp);
default: 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; $myApiKey = $this->_apiKey;
if (!$myApiKey) if (!$myApiKey)
$myApiKey = Stripe::$apiKey; $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); $absUrl = $this->apiUrl($url);
$params = self::_encodeObjects($params); $params = self::_encodeObjects($params);
$langVersion = phpversion(); $langVersion = phpversion();
$uname = php_uname(); $uname = php_uname();
$ua = array('bindings_version' => Stripe::VERSION, $ua = array('bindings_version' => Stripe::VERSION,
'lang' => 'php', 'lang' => 'php',
'lang_version' => $langVersion, 'lang_version' => $langVersion,
'publisher' => 'stripe', 'publisher' => 'stripe',
'uname' => $uname); 'uname' => $uname);
$headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua), $headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua),
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION); 'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION,
list($rbody, $rcode) = $this->_curlRequest($meth, $absUrl, $headers, $params, $myApiKey); '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); return array($rbody, $rcode, $myApiKey);
} }
@ -106,7 +204,9 @@ class Stripe_ApiRequestor
try { try {
$resp = json_decode($rbody, true); $resp = json_decode($rbody, true);
} catch (Exception $e) { } 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) { if ($rcode < 200 || $rcode >= 300) {
@ -115,28 +215,33 @@ class Stripe_ApiRequestor
return $resp; 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(); $curl = curl_init();
$meth = strtolower($meth); $method = strtolower($method);
$opts = array(); $opts = array();
if ($meth == 'get') { if ($method == 'get') {
$opts[CURLOPT_HTTPGET] = 1; $opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) { if (count($params) > 0) {
$encoded = self::encode($params); $encoded = self::encode($params);
$absUrl = "$absUrl?$encoded"; $absUrl = "$absUrl?$encoded";
} }
} else if ($meth == 'post') { } else if ($method == 'post') {
$opts[CURLOPT_POST] = 1; $opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = self::encode($params); $opts[CURLOPT_POSTFIELDS] = self::encode($params);
} else if ($meth == 'delete') { } else if ($method == 'delete') {
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE'; $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) { if (count($params) > 0) {
$encoded = self::encode($params); $encoded = self::encode($params);
$absUrl = "$absUrl?$encoded"; $absUrl = "$absUrl?$encoded";
} }
} else { } else {
throw new Stripe_ApiError("Unrecognized method $meth"); throw new Stripe_ApiError("Unrecognized method $method");
} }
$absUrl = self::utf8($absUrl); $absUrl = self::utf8($absUrl);
@ -146,19 +251,27 @@ class Stripe_ApiRequestor
$opts[CURLOPT_TIMEOUT] = 80; $opts[CURLOPT_TIMEOUT] = 80;
$opts[CURLOPT_RETURNTRANSFER] = true; $opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HTTPHEADER] = $headers; $opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_USERPWD] = $myApiKey . ':';
if (!Stripe::$verifySslCerts) if (!Stripe::$verifySslCerts)
$opts[CURLOPT_SSL_VERIFYPEER] = false; $opts[CURLOPT_SSL_VERIFYPEER] = false;
curl_setopt_array($curl, $opts); curl_setopt_array($curl, $opts);
$rbody = curl_exec($curl); $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); $errno = curl_errno($curl);
if ($errno == CURLE_SSL_CACERT || $errno == CURLE_SSL_PEER_CERTIFICATE) { if ($errno == CURLE_SSL_CACERT ||
array_push($headers, 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}'); $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_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_CAINFO, curl_setopt($curl, CURLOPT_CAINFO, $cert);
dirname(__FILE__) . '/../data/ca-certificates.crt');
$rbody = curl_exec($curl); $rbody = curl_exec($curl);
} }
@ -174,6 +287,11 @@ class Stripe_ApiRequestor
return array($rbody, $rcode); return array($rbody, $rcode);
} }
/**
* @param number $errno
* @param string $message
* @throws Stripe_ApiConnectionError
*/
public function handleCurlError($errno, $message) public function handleCurlError($errno, $message)
{ {
$apiBase = Stripe::$apiBase; $apiBase = Stripe::$apiBase;
@ -181,17 +299,119 @@ class Stripe_ApiRequestor
case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED: 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."; $msg = "Could not connect to Stripe ($apiBase). Please check your "
break; . "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_CACERT:
case CURLE_SSL_PEER_CERTIFICATE: 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."; $msg = "Could not verify Stripe's SSL certificate. Please make sure "
break; . "that your network is not intercepting certificates. "
. "(Try going to $apiBase in your browser.) "
. "If this problem persists,";
break;
default: 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); 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; return $instance;
} }
/**
* @returns Stripe_ApiResource The refreshed resource.
*/
public function refresh() public function refresh()
{ {
$requestor = new Stripe_ApiRequestor($this->_apiKey); $requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl(); $url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('get', $url);
list($response, $apiKey) = $requestor->request(
'get',
$url,
$this->_retrieveOptions
);
$this->refreshFrom($response, $apiKey); $this->refreshFrom($response, $apiKey);
return $this; 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 // Useful for namespaces: Foo\Stripe_Charge
if ($postfix = strrchr($class, '\\')) if ($postfixNamespaces = strrchr($class, '\\')) {
$class = substr($postfix, 1); $class = substr($postfixNamespaces, 1);
if (substr($class, 0, strlen('Stripe')) == 'Stripe') }
// 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 = substr($class, strlen('Stripe'));
}
$class = str_replace('_', '', $class); $class = str_replace('_', '', $class);
$name = urlencode($class); $name = urlencode($class);
$name = strtolower($name); $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() public function instanceUrl()
{ {
$id = $this['id']; $id = $this['id'];
$class = get_class($this); $class = get_class($this);
if (!$id) { if ($id === null) {
throw new Stripe_InvalidRequestError("Could not determine which URL to request: $class instance has invalid ID: $id"); $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); $id = Stripe_ApiRequestor::utf8($id);
$base = self::classUrl($class); $base = $this->_lsb('classUrl', $class);
$extn = urlencode($id); $extn = urlencode($id);
return "$base/$extn"; return "$base/$extn";
} }
private static function _validateCall($method, $params=null, $apiKey=null) private static function _validateCall($method, $params=null, $apiKey=null)
{ {
if ($params && !is_array($params)) 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)))\")"); $message = "You must pass an array as the first argument to Stripe API "
if ($apiKey && !is_string($apiKey)) . "method calls. (HINT: an example call to create a charge "
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>)")'); . "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) protected static function _scopedAll($class, $params=null, $apiKey=null)
{ {
self::_validateCall('all', $params, $apiKey); self::_validateCall('all', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey); $requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class); $url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('get', $url, $params); list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey); return Stripe_Util::convertToStripeObject($response, $apiKey);
} }
@ -65,19 +113,18 @@ abstract class Stripe_ApiResource extends Stripe_Object
{ {
self::_validateCall('create', $params, $apiKey); self::_validateCall('create', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey); $requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class); $url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('post', $url, $params); list($response, $apiKey) = $requestor->request('post', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey); return Stripe_Util::convertToStripeObject($response, $apiKey);
} }
protected function _scopedSave($class) protected function _scopedSave($class, $apiKey=null)
{ {
self::_validateCall('save'); self::_validateCall('save');
if ($this->_unsavedValues) { $requestor = new Stripe_ApiRequestor($apiKey);
$requestor = new Stripe_ApiRequestor($this->_apiKey); $params = $this->serializeParameters();
$params = array();
foreach ($this->_unsavedValues->toArray() as $k) if (count($params) > 0) {
$params[$k] = $this->$k;
$url = $this->instanceUrl(); $url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('post', $url, $params); list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom($response, $apiKey); $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 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->param = $param;
$this->code = $code; $this->code = $code;
} }

View file

@ -2,30 +2,56 @@
class Stripe_Charge extends Stripe_ApiResource class Stripe_Charge extends Stripe_ApiResource
{ {
public static function constructFrom($values, $apiKey=null) /**
{ * @param string $id The ID of the charge to retrieve.
$class = get_class(); * @param string|null $apiKey
return self::scopedConstructFrom($class, $values, $apiKey); *
} * @return Stripe_Charge
*/
public static function retrieve($id, $apiKey=null) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function all($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedAll($class, $params, $apiKey); 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) public static function create($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedCreate($class, $params, $apiKey); 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) public function refund($params=null)
{ {
$requestor = new Stripe_ApiRequestor($this->_apiKey); $requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -35,6 +61,11 @@ class Stripe_Charge extends Stripe_ApiResource
return $this; return $this;
} }
/**
* @param array|null $params
*
* @return Stripe_Charge The captured charge.
*/
public function capture($params=null) public function capture($params=null)
{ {
$requestor = new Stripe_ApiRequestor($this->_apiKey); $requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -43,4 +74,30 @@ class Stripe_Charge extends Stripe_ApiResource
$this->refreshFrom($response, $apiKey); $this->refreshFrom($response, $apiKey);
return $this; 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 class Stripe_Coupon extends Stripe_ApiResource
{ {
public static function constructFrom($values, $apiKey=null) /**
{ * @param string $id The ID of the coupon to retrieve.
$class = get_class(); * @param string|null $apiKey
return self::scopedConstructFrom($class, $values, $apiKey); *
} * @return Stripe_Coupon
*/
public static function retrieve($id, $apiKey=null) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function create($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedCreate($class, $params, $apiKey); return self::_scopedCreate($class, $params, $apiKey);
} }
/**
* @param array|null $params
*
* @return Stripe_Coupon The deleted coupon.
*/
public function delete($params=null) public function delete($params=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedDelete($class, $params); 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) public static function all($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();

View file

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

View file

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

View file

@ -2,9 +2,11 @@
class Stripe_InvalidRequestError extends Stripe_Error 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; $this->param = $param;
} }
} }

View file

@ -2,24 +2,48 @@
class Stripe_Invoice extends Stripe_ApiResource 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(); $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) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function all($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedAll($class, $params, $apiKey); 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) public static function upcoming($params=null, $apiKey=null)
{ {
$requestor = new Stripe_ApiRequestor($apiKey); $requestor = new Stripe_ApiRequestor($apiKey);
@ -27,4 +51,25 @@ class Stripe_Invoice extends Stripe_ApiResource
list($response, $apiKey) = $requestor->request('get', $url, $params); list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey); 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 class Stripe_InvoiceItem extends Stripe_ApiResource
{ {
public static function constructFrom($values, $apiKey=null) /**
{ * @param string $id The ID of the invoice item to retrieve.
$class = get_class(); * @param string|null $apiKey
return self::scopedConstructFrom($class, $values, $apiKey); *
} * @return Stripe_InvoiceItem
*/
public static function retrieve($id, $apiKey=null) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function all($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedAll($class, $params, $apiKey); 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) public static function create($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedCreate($class, $params, $apiKey); return self::_scopedCreate($class, $params, $apiKey);
} }
/**
* @return Stripe_InvoiceItem The saved invoice item.
*/
public function save() public function save()
{ {
$class = get_class(); $class = get_class();
return self::_scopedSave($class); return self::_scopedSave($class);
} }
/**
* @return Stripe_InvoiceItem The deleted invoice item.
*/
public function delete($params=null) public function delete($params=null)
{ {
$class = get_class(); $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 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() 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 $_apiKey;
protected $_values; protected $_values;
protected $_unsavedValues; protected $_unsavedValues;
protected $_transientValues; protected $_transientValues;
protected $_retrieveOptions;
public function __construct($id=null, $apiKey=null) public function __construct($id=null, $apiKey=null)
{ {
@ -20,16 +31,41 @@ class Stripe_Object implements ArrayAccess
$this->_values = array(); $this->_values = array();
$this->_unsavedValues = new Stripe_Util_Set(); $this->_unsavedValues = new Stripe_Util_Set();
$this->_transientValues = 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; $this->id = $id;
}
} }
// Standard accessor magic methods // Standard accessor magic methods
public function __set($k, $v) public function __set($k, $v)
{ {
// TODO: may want to clear from $_transientValues. (Won't be user-visible.) if ($v === "") {
$this->_values[$k] = $v; throw new InvalidArgumentException(
if (!self::$_permanentAttributes->includes($k)) '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); $this->_unsavedValues->add($k);
} }
public function __isset($k) public function __isset($k)
@ -44,12 +80,18 @@ class Stripe_Object implements ArrayAccess
} }
public function __get($k) public function __get($k)
{ {
if (isset($this->_values[$k])) { if (array_key_exists($k, $this->_values)) {
return $this->_values[$k]; return $this->_values[$k];
} else if ($this->_transientValues->includes($k)) { } else if ($this->_transientValues->includes($k)) {
$class = get_class($this); $class = get_class($this);
$attrs = join(', ', array_keys($this->_values)); $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; return null;
} else { } else {
$class = get_class($this); $class = get_class($this);
@ -63,20 +105,35 @@ class Stripe_Object implements ArrayAccess
{ {
$this->$k = $v; $this->$k = $v;
} }
public function offsetExists($k) public function offsetExists($k)
{ {
return isset($this->$k); return array_key_exists($k, $this->_values);
} }
public function offsetUnset($k) public function offsetUnset($k)
{ {
unset($this->$k); unset($this->$k);
} }
public function offsetGet($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) public static function scopedConstructFrom($class, $values, $apiKey=null)
{ {
$obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey); $obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
@ -84,44 +141,108 @@ class Stripe_Object implements ArrayAccess
return $obj; 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) 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) public function refreshFrom($values, $apiKey, $partial=false)
{ {
$this->_apiKey = $apiKey; $this->_apiKey = $apiKey;
// Wipe old state before setting new. This is useful for e.g. updating a // 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 // customer, where there is no persistent card parameter. Mark those values
// which don't persist as transient // which don't persist as transient
if ($partial) if ($partial) {
$removed = new Stripe_Util_Set(); $removed = new Stripe_Util_Set();
else } else {
$removed = array_diff(array_keys($this->_values), array_keys($values)); $removed = array_diff(array_keys($this->_values), array_keys($values));
}
foreach ($removed as $k) { foreach ($removed as $k) {
if (self::$_permanentAttributes->includes($k)) if (self::$permanentAttributes->includes($k))
continue; continue;
unset($this->$k); unset($this->$k);
} }
foreach ($values as $k => $v) { foreach ($values as $k => $v) {
if (self::$_permanentAttributes->includes($k)) if (self::$permanentAttributes->includes($k) && isset($this[$k]))
continue; 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->_transientValues->discard($k);
$this->_unsavedValues->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() public function __toJSON()
{ {
if (defined('JSON_PRETTY_PRINT')) if (defined('JSON_PRETTY_PRINT')) {
return json_encode($this->__toArray(true), JSON_PRETTY_PRINT); return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
else } else {
return json_encode($this->__toArray(true)); return json_encode($this->__toArray(true));
}
} }
public function __toString() public function __toString()
@ -131,10 +252,11 @@ class Stripe_Object implements ArrayAccess
public function __toArray($recursive=false) public function __toArray($recursive=false)
{ {
if ($recursive) if ($recursive) {
return Stripe_Util::convertStripeObjectToArray($this->_values); return Stripe_Util::convertStripeObjectToArray($this->_values);
else } else {
return $this->_values; return $this->_values;
}
} }
} }

View file

@ -2,36 +2,56 @@
class Stripe_Plan extends Stripe_ApiResource class Stripe_Plan extends Stripe_ApiResource
{ {
public static function constructFrom($values, $apiKey=null) /**
{ * @param string $id The ID of the plan to retrieve.
$class = get_class(); * @param string|null $apiKey
return self::scopedConstructFrom($class, $values, $apiKey); *
} * @return Stripe_Plan
*/
public static function retrieve($id, $apiKey=null) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function create($params=null, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedCreate($class, $params, $apiKey); return self::_scopedCreate($class, $params, $apiKey);
} }
/**
* @param array|null $params
*
* @return Stripe_Plan The deleted plan.
*/
public function delete($params=null) public function delete($params=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedDelete($class, $params); return self::_scopedDelete($class, $params);
} }
/**
* @return Stripe_Plan The saved plan.
*/
public function save() public function save()
{ {
$class = get_class(); $class = get_class();
return self::_scopedSave($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) public static function all($params=null, $apiKey=null)
{ {
$class = get_class(); $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 class Stripe_Token extends Stripe_ApiResource
{ {
public static function constructFrom($values, $apiKey=null) /**
{ * @param string $id The ID of the token to retrieve.
$class = get_class(); * @param string|null $apiKey
return self::scopedConstructFrom($class, $values, $apiKey); *
} * @return Stripe_Token
*/
public static function retrieve($id, $apiKey=null) public static function retrieve($id, $apiKey=null)
{ {
$class = get_class(); $class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey); 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) public static function create($params=null, $apiKey=null)
{ {
$class = get_class(); $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 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) public static function isList($array)
{ {
if (!is_array($array)) if (!is_array($array))
return false; 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) { foreach (array_keys($array) as $k) {
if (!is_numeric($k)) if (!is_numeric($k))
return false; return false;
@ -14,43 +21,67 @@ abstract class Stripe_Util
return true; 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) public static function convertStripeObjectToArray($values)
{ {
$results = array(); $results = array();
foreach ($values as $k => $v) { foreach ($values as $k => $v) {
// FIXME: this is an encapsulation violation // FIXME: this is an encapsulation violation
if (Stripe_Object::$_permanentAttributes->includes($k)) { if ($k[0] == '_') {
continue; continue;
} }
if ($v instanceof Stripe_Object) { if ($v instanceof Stripe_Object) {
$results[$k] = $v->__toArray(true); $results[$k] = $v->__toArray(true);
} } else if (is_array($v)) {
else if (is_array($v)) {
$results[$k] = self::convertStripeObjectToArray($v); $results[$k] = self::convertStripeObjectToArray($v);
} } else {
else {
$results[$k] = $v; $results[$k] = $v;
} }
} }
return $results; 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) public static function convertToStripeObject($resp, $apiKey)
{ {
$types = array('charge' => 'Stripe_Charge', $types = array(
'customer' => 'Stripe_Customer', 'card' => 'Stripe_Card',
'invoice' => 'Stripe_Invoice', 'charge' => 'Stripe_Charge',
'invoiceitem' => 'Stripe_InvoiceItem', 'event' => 'Stripe_Event'); '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)) { if (self::isList($resp)) {
$mapped = array(); $mapped = array();
foreach ($resp as $i) foreach ($resp as $i)
array_push($mapped, self::convertToStripeObject($i, $apiKey)); array_push($mapped, self::convertToStripeObject($i, $apiKey));
return $mapped; return $mapped;
} else if (is_array($resp)) { } 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']]; $class = $types[$resp['object']];
else } else {
$class = 'Stripe_Object'; $class = 'Stripe_Object';
}
return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey); return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey);
} else { } else {
return $resp; return $resp;

File diff suppressed because it is too large Load diff